1
$\begingroup$

My addon makes measurements on objects and assign a color to the objects according to the measured value. Now I would like to display a series of stacked coloured squares in the viewport to serve as a lookup table, as on this mockup:

enter image description here

This runable code should draw n 2D squares (with n being the number of colors in my palette) and color them:

import bgl
import bpy
import gpu
from gpu_extras.batch import batch_for_shader

# my color palette:
Seq_viridis = [(68,1,84), (69,16,97), (70,31,110), (71,44,122), (67,58,128), (62,71,134), (58,83,139), (53,94,140), (47,106,141), (43,116,142), (39,127,142), (35,139,141), (34,149,139), (36,159,135), (38,168,131), (48,178,124), (69,188,112), (88,198,101), (112,205,87), (138,212,70), (165,219,53), (192,223,47), (223,227,42), (253,231,37)]

# Convert a rgb color (123, 45, 234) to a rgbaf (0.123, 0.06, 0.12, 1)
def rgb_to_rgbaf(_rgb):
    _rgbaf = tuple(ti/255 for ti in _rgb) + (1,)
    return _rgbaf

def Display2D_LUT(pos_dim = (100, 100, 10)):
    # Get Palette
    n_elements = len(Seq_viridis)

    # Origin and aspect of the LUT
    x_orig = pos_dim[0]
    y_orig = pos_dim[1]
    size = pos_dim[2]

    for k in range(0, n_elements):
        vertices = (
        (x_orig, y_orig+k*size), (x_orig+size, y_orig+k*size),
        (x_orig, y_orig+size+k*size), (x_orig+size, y_orig+size+k*size))

        indices = ((0, 1, 2), (2, 1, 3))

        shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
        batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)

        def draw():
            shader.bind()
            shader.uniform_float("color", rgb_to_rgbaf(Seq_viridis[k]))
            batch.draw(shader)
        bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')

Display2D_LUT()

But this only draws one element, the last one.

  • How can I draw several squares at once?
  • How can I add text?

Thanks a lot in advance!

$\endgroup$
3
  • $\begingroup$ Recommend posting a minimal run-able example. A tip would be to make use of vectors for positioning and dimension. $\endgroup$
    – batFINGER
    Aug 11, 2020 at 10:25
  • $\begingroup$ blender.stackexchange.com/questions/163302/… $\endgroup$
    – batFINGER
    Aug 11, 2020 at 10:35
  • $\begingroup$ @batFINGER: I updated the original code as you recommended (run-able & use of vector). I looked at the link, but I could not see how it can help me... Very sorry. $\endgroup$
    – amaizel
    Aug 11, 2020 at 11:14

1 Answer 1

2
$\begingroup$

Problem with your code is that when handler is called and it's callback method draw is called, for that draw method k will be len(Seq_viridis)-1 means pointing to last element.

When script is executed, blender pauses and after completion of execution blender resumes. Now after your script is executed k is pointing to last element.

What i did is dynamically created draw functions for each square. Look here for More info on dynamic creation of functions.

I used method of storing handlers on some blender class for management of handlers in development process.

  • For the better management(removing/adding) of handlers look here

Also added control for x and y axis of square

import bgl
import bpy
import gpu
from gpu_extras.batch import batch_for_shader

# my color palette:
Seq_viridis = [(68,1,84), (69,16,97), (70,31,110), (71,44,122), (67,58,128), (62,71,134), (58,83,139), (53,94,140), (47,106,141), (43,116,142), (39,127,142), (35,139,141), (34,149,139), (36,159,135), (38,168,131), (48,178,124), (69,188,112), (88,198,101), (112,205,87), (138,212,70), (165,219,53), (192,223,47), (223,227,42), (253,231,37)]
                                                                
n = len(Seq_viridis)                                            # This section is for handling 
                                                                # draw handlers in developemnt process               
try:                                                            #                              
    for i in bpy.context.scene.my_handlers:                     # I'm using method of storing list of handler 
        bpy.types.SpaceView3D.draw_handler_remove(i,'WINDOW')   # on some kind blender class i.e, bpy.types.Scene.my_handlers = []
except:                                                         # Or you can use driver namespace.                               
    pass                                                        # Check  below link for reference
#https://blender.stackexchange.com/questions/75612/how-do-you-remove-a-draw-handler-after-its-been-added
                                                                        
# Convert a rgb color (123, 45, 234) to a rgbaf (0.123, 0.06, 0.12, 1)
def rgb_to_rgbaf(_rgb):
    _rgbaf = tuple(ti/255 for ti in _rgb) + (1,)
    return _rgbaf

def bindFunction1(k,vertices,indices):
    name = f"func{k}"
    shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
    batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)
    
    def draw():
        shader.bind()
        shader.uniform_float("color", rgb_to_rgbaf(Seq_viridis[k]))
        batch.draw(shader)
    draw.__name__ = name
    return draw

def Display2D_LUT(pos_dim = (100, 100, 10, 25)):
    # Get Palette
    n_elements = len(Seq_viridis)

    # Origin and aspect of the LUT
    x_orig = pos_dim[0]
    y_orig = pos_dim[1]
    x_size = pos_dim[2]
    y_size = pos_dim[3]

    for k in range(0, n_elements):
        vertices = (
        (x_orig, y_orig+k*y_size), (x_orig+x_size, y_orig+k*y_size),
        (x_orig, y_orig+y_size+k*y_size), (x_orig+x_size, y_orig+y_size+k*y_size))

        indices = ((0, 1, 2), (2, 1, 3))

        new_func = bindFunction1(k,vertices,indices)
        
        handler = bpy.types.SpaceView3D.draw_handler_add(new_func, (), 'WINDOW', 'POST_PIXEL')
        bpy.context.scene.my_handlers.append(handler)
        
bpy.types.Scene.my_handlers = [] #this line for development purpose only.
Display2D_LUT()

Please mark accepted if this solves your problem. : )

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.