Custom Sprite Component

Hello,

I am trying my hand at creating a custom sprite component for use in Grasshopper that will take a Point list (P input) and File Path (F input) and add the image sprite at the P locations.

I feel like I am close but struggling to figure out how to use the BitmapDrawList method?

Graph Space:

Here’s the API info:
https://mcneel-apidocs.herokuapp.com/api/rhinocommon/rhino.display.displaybitmapdrawlist

Here’s my code thus far:

import Rhino
import scriptcontext as sc
import Grasshopper as gh

bitmap = None

def create_display_bitmap(F):
    global bitmap  # Declare bitmap as global to modify the global variable
    try:
        # Load the image from the specified file path
        bitmap = Rhino.Display.DisplayBitmap.Load(F)
        return bitmap
    except Exception as ex:
        print("Error loading the image:", ex)
        return None

def custom_sprite(P, bitmap):
    # Check if the input image is valid
    if not isinstance(bitmap, Rhino.Display.DisplayBitmap):
        raise ValueError("Invalid input image. Please provide a valid DisplayBitmap.")
    
    # Get the active display pipeline
    dp = Rhino.Display.DisplayPipeline
    
    # Draw the sprite at each specified point location
    for point in P:
        if point.IsValid:
            size = 1.0  # You can adjust the size of the sprite if needed
            translation = Rhino.Geometry.Vector3d(0, 0, 0)  # No translation from the original point
            size_in_world_space = False  # Size is in screen space
            
            # Draw the sprite
            dp.DrawSprites(bitmap, Rhino.Display.DisplayBitmapDrawList, size, translation, size_in_world_space)
    
    return "Sprites drawn successfully."

# Run the component in Grasshopper
if P and F:
    create_display_bitmap(F)
    custom_sprite(P, bitmap)

I would appreciate some guidance in how to handle the draw list and any other issues you all see.

Thank you for your time and assistance!

Hi @michaelvollrath
Check this, i use

bitmap = System.Drawing.Bitmap.FromFile(F)
self.B = rh.Display.DisplayBitmap(bitmap)

Because: bitmap = Rhino.Display.DisplayBitmap.Load(F) always freeze Grasshopper

sprite.gh (4.7 KB)

1 Like

That’s odd, I’ve never had that happen. Might be a security issue if you’re loading from a server maybe?

Anywho, I started embedding icons within the literal code of the component itself (i.e. as loooong base64 strings). Meaning you don’t need to issue or load the image files, it’s pretty neat (building on this code to draw into the foreground):


230629_DrawByteStringBitmap_00 (1).gh (7.5 KB)

2 Likes

I load the image from the local disk

Thank you both @anon39580149 and @AndersDeleuran! I look forward to getting into this when I’m back at my desk.

@AndersDeleuran encoding the image as a string is really quite awesome and I was looking for something like that but settled for an image import as it quickly went over my head on how to implement.

My use case is I’m making some “visual aid” icons that sit in front of the draw order of the points they are created at. So you can click the icon and it gets the point selection and runs the associated logic for those specific point selections.

I actually messed around with Unicode emojis for a moment (since those can exist in text dots, text entities, and panels) but graphically those are all over the place and I wanted more control over the visual aids then choosing between a brick wall :brick: and a door :door: for example haha.

Also fitting that the BIG logo is indeed a very BIG logo (at least byte string wise :grin:)

1 Like

Hehe, indeed. I encoded a horribly compressed Teams channel icon for this example. I suspect using a clean .png will yield a shorter string. To testing :man_scientist:

1 Like

Thank you both again!

I ended up combining both of your scripts to make a Custom Symbol Display component that can optionally be drawn in the foreground.

I think I’m going to eventually hardcode a library of the symbols as the base64 strings but for now and for testing the file path method works well enough as I’m still graphically iterating quite a bit.

Here’s the code I went with (mostly @anon39580149’s method with the draw order method from you @AndersDeleuran :

from ghpythonlib.componentbase import executingcomponent as component
import Grasshopper, GhPython
import System
import Rhino
import Rhino as rh
import Rhino.Geometry as rg
import rhinoscriptsyntax as rs

class MyComponent(component):
    
    def RunScript(self, P, F, S, D, V):

        if S == None: S = 20
        
        self.F = F
        self.P = P
        self.D = D  # Store the value of D as an instance variable
        
        if self.F:
            bitmap = System.Drawing.Bitmap.FromFile(F)
            self.B = rh.Display.DisplayBitmap(bitmap)

        self.S = S
        self.size = V
        
        if self.P and len(P) > 0:
            bb = rg.BoundingBox(P)
            self.bb = bb
        else:
            self.bb = rh.Geometry.BoundingBox.Empty

    def DrawForeground(self, sender, e):
        if self.D:  # If D is True, draw the sprite in the foreground
            if self.P and self.F:
                items = rh.Display.DisplayBitmapDrawList()
                items.SetPoints(self.P)
                e.Display.DrawSprites(self.B, items, self.S, self.size)

    def DrawViewportWires(self, arg):
        if not self.D:  # If D is False, draw the sprite as before using DrawViewportWires
            if self.P and self.F:
                items = rh.Display.DisplayBitmapDrawList()
                items.SetPoints(self.P)
                arg.Display.DrawSprites(self.B, items, self.S, self.size)
    
    def get_ClippingBox(self):
        return self.bb
    
    def IsPreviewCapable(self):
        return True

    def __exit__(self):
        rh.Display.DisplayPipeline.DrawForeground -= self.DrawForeground

    def __enter__(self):
        rh.Display.DisplayPipeline.DrawForeground += self.DrawForeground

@AndersDeleuran playing around with your script I have a lot of new, inspired ideas about interesting use cases for drawing the text and symbol to the viewport 2D like that. Thank you very much, I’m excited to experiment more with it.

Custom Symbols In Action:

1 Like

Hi @michaelvollrath

Veeery interesting aplication, always a pleasure reading good things in rhino forum! Just curious, are you inserting 2 different images to get that result (warning symbol plus text) or is it just one image which contains everything?

I’m pretty sure the texts are drawn using the DrawDot method:

1 Like