1

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!

amaizel
  • 167
  • 1
  • 11
  • Recommend posting a minimal run-able example. A tip would be to make use of vectors for positioning and dimension. – batFINGER Aug 11 '20 at 10:25
  • https://blender.stackexchange.com/questions/163302/render-order-of-triangles-is-wrong-with-custom-draw-handler – batFINGER Aug 11 '20 at 10:35
  • @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. – amaizel Aug 11 '20 at 11:14

1 Answers1

2

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. : )

Reigen
  • 895
  • 5
  • 15