1
$\begingroup$

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()
$\endgroup$
3
  • 1
    $\begingroup$ Have you tried increasing d ? $\endgroup$
    – Gorgious
    Jan 16, 2022 at 18:53
  • $\begingroup$ Yes, but I want to increase and decrease while zooming in and out so that it can maintain the ratio $\endgroup$
    – Karan
    Jan 17, 2022 at 13:55
  • $\begingroup$ It's kind of complicated, you need to compute the real-world dimensions depending on the distance from the object to the virtual camera in the 3D viewport, along with the size of the viewport area. $\endgroup$
    – Gorgious
    Jan 17, 2022 at 18:58

0

You must log in to answer this question.

Browse other questions tagged .