0

How to maintain the ratio of the gizmo while zooming in and out?

enter image description here

import bpy, blf, bgl, gpu
from bpy.types import Operator
from gpu_extras.batch import batch_for_shader
from math import radians, sin, cos
from bpy_extras.view3d_utils import *

class VIEW3D_OT_gizmo(Operator): bl_label = 'Gizmo' bl_idname = 'view3d.gizmo' bl_options = {'REGISTER', 'UNDO'}

color_x = bpy.context.preferences.themes['Default'].user_interface.axis_x[:]
color_y = bpy.context.preferences.themes['Default'].user_interface.axis_y[:]
color_z = bpy.context.preferences.themes['Default'].user_interface.axis_z[:]

def overlay(self, context):
    self.draw_gizmo(context.object)

def invoke(self, context, event):
    args = (context,)
    self._2d_handle = bpy.types.SpaceView3D.draw_handler_add(self.overlay, args, 'WINDOW', 'POST_PIXEL')
    context.window_manager.modal_handler_add(self)
    return {'RUNNING_MODAL'}

def modal(self, context, event):
    context.area.tag_redraw()

    if event.type in {'ESC', 'RIGHTMOUSE'}:
        bpy.types.SpaceView3D.draw_handler_remove(self._2d_handle, 'WINDOW')
        return {'CANCELLED'}
    elif event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
        return {'PASS_THROUGH'}

    return {'RUNNING_MODAL'}

def l3d_to_r2d(self, coord):
    '''
    Return the region relative 2d location of a 3d position.

    coord (3d Vector) - 3d worldspace location.
    '''
    return location_3d_to_region_2d(bpy.context.region, bpy.context.region_data, coord, default=None)

def draw_gizmo(self, object):
    ''' Draw a gizmo for object.

    object (bpy.types.Object) - Object to draw gizmo for.
    '''
    radius = 10 # Radius of the circle
    d = 0.75 # Distance from object center to circle center
    text_color = bpy.context.preferences.themes['Default'].user_interface.wcol_tool.text
    alpha = 0.8

    x, y, z = object.location

    # Get 2d relative coordinates from 3d coordinates of the object
    pos_x = self.l3d_to_r2d((x+d, y, z))
    pos_y = self.l3d_to_r2d((x, y+d, z))
    pos_z = self.l3d_to_r2d((x, y, z+d))
    neg_x = self.l3d_to_r2d((x-d, y, z))
    neg_y = self.l3d_to_r2d((x, y-d, z))
    neg_z = self.l3d_to_r2d((x, y, z-d))

    bgl.glEnable(bgl.GL_BLEND)
    bgl.glLineWidth(2)
    bgl.glEnable(bgl.GL_LINE_SMOOTH)

    # Draw lines
    coord_x = (self.l3d_to_r2d((x, y, z)), self.l3d_to_r2d((x+d, y, z)))
    shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
    batch = batch_for_shader(shader, 'LINES', {'pos': coord_x})
    shader.bind()
    shader.uniform_float('color', self.color_x + (alpha,))
    batch.draw(shader)

    coord_y = (self.l3d_to_r2d((x, y, z)), self.l3d_to_r2d((x, y+d, z)))
    shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
    batch = batch_for_shader(shader, 'LINES', {'pos': coord_y})
    shader.bind()
    shader.uniform_float('color', self.color_y + (alpha,))
    batch.draw(shader)

    coord_z = (self.l3d_to_r2d((x, y, z)), self.l3d_to_r2d((x, y, z+d)))
    shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
    batch = batch_for_shader(shader, 'LINES', {'pos': coord_z})
    shader.bind()
    shader.uniform_float('color', self.color_z + (alpha,))
    batch.draw(shader)

    # Draw circles
    self.draw_circle(pos_x, radius, self.color_x + (alpha,))
    self.draw_circle(pos_y, radius, self.color_y + (alpha,))
    self.draw_circle(pos_z, radius, self.color_z + (alpha,))
    self.draw_circle(neg_x, radius, self.color_x + (0.2,), self.color_x + (alpha,))
    self.draw_circle(neg_y, radius, self.color_y + (0.2,), self.color_y + (alpha,))
    self.draw_circle(neg_z, radius, self.color_z + (0.2,), self.color_z + (alpha,))


    # Get text dimensions
    pos_dim_x = self.get_text_dims('X')
    pos_dim_y = self.get_text_dims('Y')
    pos_dim_z = self.get_text_dims('Z')
    neg_dim_x = self.get_text_dims('-X')
    neg_dim_y = self.get_text_dims('-Y')
    neg_dim_z = self.get_text_dims('-Z')

    # Draw texts
    self.draw_text((pos_x[0]-(pos_dim_x[0]/2), pos_x[2]-(pos_dim_x[2]/2)), 'X', text_color)
    self.draw_text((pos_y[0]-(pos_dim_y[0]/2), pos_y[2]-(pos_dim_y[2]/2)), 'Y', text_color)
    self.draw_text((pos_z[0]-(pos_dim_z[0]/2), pos_z[2]-(pos_dim_z[2]/2)), 'Z', text_color)
    self.draw_text((neg_x[0]-(neg_dim_x[0]/2), neg_x[2]-(neg_dim_x[2]/2)), '-X', text_color)
    self.draw_text((neg_y[0]-(neg_dim_y[0]/2), neg_y[2]-(neg_dim_y[2]/2)), '-Y', text_color)
    self.draw_text((neg_z[0]-(neg_dim_z[0]/2), neg_z[2]-(neg_dim_z[2]/2)), '-Z', text_color)

def draw_circle(self, position, radius, color, border_color=None):
    ''' Draw a circle.

    position (2D Vector) - Position where the circle will be drawn.
    radius (float) - Radius of the circle.
    color (tuple containing RGBA values) - Color of the circle.
    border_color (tuple containing RGBA values, optional) - Border Color of the circle.
    '''
    coords = []

    for angle in range(0, 361, 5):
        radian = radians(angle)
        x = position[0] + radius * cos(radian)
        y = position[2] + radius * sin(radian)
        coords.append((x,y))

    bgl.glEnable(bgl.GL_BLEND)
    bgl.glEnable(bgl.GL_LINE_SMOOTH)
    shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
    batch = batch_for_shader(shader, 'TRI_FAN', {'pos': coords})
    shader.bind()
    shader.uniform_float('color', color)
    batch.draw(shader)

    if border_color is not None:
        bgl.glEnable(bgl.GL_BLEND)
        bgl.glLineWidth(2)
        bgl.glEnable(bgl.GL_LINE_SMOOTH)
        shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
        batch = batch_for_shader(shader, 'LINE_STRIP', {'pos': coords})
        shader.bind()
        shader.uniform_float('color', border_color)
        batch.draw(shader)
    else:
        bgl.glEnable(bgl.GL_BLEND)
        bgl.glLineWidth(2)
        bgl.glEnable(bgl.GL_LINE_SMOOTH)
        shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
        batch = batch_for_shader(shader, 'LINE_STRIP', {'pos': coords})
        shader.bind()
        shader.uniform_float('color', color)
        batch.draw(shader)

text_color = bpy.context.preferences.themes['Default'].user_interface.wcol_tool.text

def draw_text(self, position, text, text_color=text_color):
    ''' Draw text.

    position (2D Vector) - Position to draw text at.
    text (str) - Text to draw.
    text_color (tuple containing RGB values, optional) - Color of text.
    '''
    x, y = position
    fontid = 0
    text_size = 11
    blf.enable(fontid, blf.SHADOW)
    blf.shadow(fontid, 3, 0, 0, 0, 0.5)
    blf.shadow_offset(fontid, 0, -1)
    blf.size(fontid, text_size, 72)
    r,g,b = text_color
    blf.color(fontid, r,g,b,1)
    blf.position(fontid, x, y, 0)
    blf.draw(fontid, text)

def get_text_dims(self, text) -> tuple:
    ''' Get text dimensions.

    text (str) - Text to get dimensions for.
    return (tuple) - Dimension of the text in pixels.
    '''
    fontid = 0
    text_size = 11
    blf.size(fontid, text_size, 72)
    return blf.dimensions(fontid, text)

def register(): bpy.utils.register_class(VIEW3D_OT_gizmo)

def unregister(): bpy.utils.unregister_class(VIEW3D_OT_gizmo)

if name == "main": register()

Karan
  • 1,984
  • 5
  • 21

0 Answers0