import bpy, mathutils, math, gpu
from math import cos, sin
from mathutils import Vector
from gpu_extras.batch import batch_for_shader
from ..utility.screen import dpi_factor
from . space_3d import get_3D_point_from_mouse, get_2d_point_from_3d_point
class Axial:
def __init__(self):
# States
self.open = False
self.just_closed = False
self.exit_val = None
# 3D
self.loc_spawn = Vector()
# 2D
self.loc_center = None
built_in_shader = 'UNIFORM_COLOR' if bpy.app.version[0] >=4 else '2D_UNIFORM_COLOR'
self.shader = gpu.shader.from_builtin(built_in_shader)
# Outer ring
self.radius = 60 * dpi_factor()
self.outer_color = (1,1,1,.125)
self.outer_verts = []
self.outer_batch = None
# Outer X
self.x_loc = Vector((0,0))
self.x_color = (1,0,0,.75)
self.x_verts = []
self.x_batch = None
# Outer Y
self.y_loc = Vector((0,0))
self.y_color = (0,1,0,.75)
self.y_verts = []
self.y_batch = None
# Outer Z
self.z_loc = Vector((0,0))
self.z_color = (0,0,1,.75)
self.z_verts = []
self.z_batch = None
# Outer NEG X
self.neg_x_loc = Vector((0,0))
self.neg_x_color = (.5,0,0,.75)
self.neg_x_verts = []
self.neg_x_batch = None
# Outer NEG Y
self.neg_y_loc = Vector((0,0))
self.neg_y_color = (0,.5,0,.75)
self.neg_y_verts = []
self.neg_y_batch = None
# Outer NEG Z
self.neg_z_loc = Vector((0,0))
self.neg_z_color = (0,0,.5,.75)
self.neg_z_verts = []
self.neg_z_batch = None
def update(self, context, event, callback):
# Update open widget
if self.open:
self.__axial_change(context, event)
return
else:
if self.just_closed:
self.just_closed = False
callback(self.exit_val)
self.exit_val = None
# Reset
self.loc_center = None
# Key control to open
action = False
if context.preferences.inputs.use_mouse_emulate_3_button:
if event.type == 'C' and event.value == 'PRESS':
action = True
else:
action = event.alt and event.value == 'PRESS'
# Not supported
if action:
if context.region_data.is_orthographic_side_view:
bpy.ops.hops.display_notification(info="Ortho side view not available")
return
if not action: return
# Cast to view plane
mouse_pos = Vector((event.mouse_region_x, event.mouse_region_y))
view_quat = context.region_data.view_rotation
view_normal = view_quat @ Vector((0,0,1))
self.loc_spawn = get_3D_point_from_mouse(mouse_pos, context, Vector(), view_normal)
# 2D screen space
loc = get_2d_point_from_3d_point(context, self.loc_spawn)
if not loc: return
self.loc_center = loc
self.open = True
self.__setup_batches(context)
return
def __axial_change(self, context, event):
mouse_pos = Vector((event.mouse_region_x, event.mouse_region_y))
mag = (mouse_pos - self.loc_center).magnitude
if mag > self.radius:
self.open = False
self.just_closed = True
return
mouse_pos = self.loc_center - mouse_pos
vec_x = self.loc_center - self.x_loc
vec_y = self.loc_center - self.y_loc
vec_z = self.loc_center - self.z_loc
vec_neg_x = self.loc_center - self.neg_x_loc
vec_neg_y = self.loc_center - self.neg_y_loc
vec_neg_z = self.loc_center - self.neg_z_loc
# TODO: keep vecs with mag <= 0 out of list for ortho side views
angles = [
mouse_pos.angle(vec_x),
mouse_pos.angle(vec_y),
mouse_pos.angle(vec_z),
mouse_pos.angle(vec_neg_x),
mouse_pos.angle(vec_neg_y),
mouse_pos.angle(vec_neg_z)]
minpos = angles.index(min(angles))
if minpos == 0:
self.outer_color = self.x_color
self.exit_val = 'X'
elif minpos == 1:
self.outer_color = self.y_color
self.exit_val = 'Y'
elif minpos == 2:
self.outer_color = self.z_color
self.exit_val = 'Z'
if minpos == 3:
self.outer_color = self.neg_x_color
self.exit_val = '-X'
elif minpos == 4:
self.outer_color = self.neg_y_color
self.exit_val = '-Y'
elif minpos == 5:
self.outer_color = self.neg_z_color
self.exit_val = '-Z'
def __setup_batches(self, context):
self.__outer_batch()
self.__x_batch(context)
self.__y_batch(context)
self.__z_batch(context)
self.__neg_x_batch(context)
self.__neg_y_batch(context)
self.__neg_z_batch(context)
def __outer_batch(self):
self.outer_color = (1,1,1,.125)
self.outer_verts = []
segments = 32
for i in range(segments):
index = i + 1
angle = i * 3.14159 * 2 / segments
x = (cos(angle) * self.radius) + self.loc_center[0]
y = (sin(angle) * self.radius) + self.loc_center[1]
self.outer_verts.append((x, y))
self.outer_verts.append(self.outer_verts[0])
self.outer_batch = batch_for_shader(self.shader, 'LINE_STRIP', {"pos": self.outer_verts})
def __x_batch(self, context):
loc = get_2d_point_from_3d_point(context, self.loc_spawn + Vector((1,0,0)))
if not loc: return
loc -= self.loc_center
loc.normalize()
loc *= self.radius * .75
loc += self.loc_center
self.x_loc = loc
self.x_verts = [self.loc_center, loc]
self.x_batch = batch_for_shader(self.shader, 'LINE_STRIP', {"pos": self.x_verts})
def __y_batch(self, context):
loc = get_2d_point_from_3d_point(context, self.loc_spawn + Vector((0,1,0)))
if not loc: return
loc -= self.loc_center
loc.normalize()
loc *= self.radius * .75
loc += self.loc_center
self.y_loc = loc
self.y_verts = [self.loc_center, loc]
self.y_batch = batch_for_shader(self.shader, 'LINE_STRIP', {"pos": self.y_verts})
def __z_batch(self, context):
loc = get_2d_point_from_3d_point(context, self.loc_spawn + Vector((0,0,1)))
if not loc: return
loc -= self.loc_center
loc.normalize()
loc *= self.radius * .75
loc += self.loc_center
self.z_loc = loc
self.z_verts = [self.loc_center, loc]
self.z_batch = batch_for_shader(self.shader, 'LINE_STRIP', {"pos": self.z_verts})
def __neg_x_batch(self, context):
loc = get_2d_point_from_3d_point(context, self.loc_spawn + Vector((-1,0,0)))
if not loc: return
loc -= self.loc_center
loc.normalize()
loc *= self.radius * .75
loc += self.loc_center
self.neg_x_loc = loc
self.neg_x_verts = [self.loc_center, loc]
self.neg_x_batch = batch_for_shader(self.shader, 'LINE_STRIP', {"pos": self.neg_x_verts})
def __neg_y_batch(self, context):
loc = get_2d_point_from_3d_point(context, self.loc_spawn + Vector((0,-1,0)))
if not loc: return
loc -= self.loc_center
loc.normalize()
loc *= self.radius * .75
loc += self.loc_center
self.neg_y_loc = loc
self.neg_y_verts = [self.loc_center, loc]
self.neg_y_batch = batch_for_shader(self.shader, 'LINE_STRIP', {"pos": self.neg_y_verts})
def __neg_z_batch(self, context):
loc = get_2d_point_from_3d_point(context, self.loc_spawn + Vector((0,0,-1)))
if not loc: return
loc -= self.loc_center
loc.normalize()
loc *= self.radius * .75
loc += self.loc_center
self.neg_z_loc = loc
self.neg_z_verts = [self.loc_center, loc]
self.neg_z_batch = batch_for_shader(self.shader, 'LINE_STRIP', {"pos": self.neg_z_verts})
def draw(self):
if not self.open: return
#Enable(GL_LINE_SMOOTH)
gpu.state.blend_set('ALPHA')
self.shader.bind()
if self.outer_batch:
gpu.state.line_width_set(2)
self.shader.uniform_float("color", self.outer_color)
self.outer_batch.draw(self.shader)
if self.x_batch:
gpu.state.line_width_set(4)
self.shader.uniform_float("color", self.x_color)
self.x_batch.draw(self.shader)
if self.y_batch:
gpu.state.line_width_set(4)
self.shader.uniform_float("color", self.y_color)
self.y_batch.draw(self.shader)
if self.z_batch:
gpu.state.line_width_set(4)
self.shader.uniform_float("color", self.z_color)
self.z_batch.draw(self.shader)
if self.neg_x_batch:
gpu.state.line_width_set(3)
self.shader.uniform_float("color", self.neg_x_color)
self.neg_x_batch.draw(self.shader)
if self.neg_y_batch:
gpu.state.line_width_set(3)
self.shader.uniform_float("color", self.neg_y_color)
self.neg_y_batch.draw(self.shader)
if self.neg_z_batch:
gpu.state.line_width_set(3)
self.shader.uniform_float("color", self.neg_z_color)
self.neg_z_batch.draw(self.shader)
#Disable(GL_LINE_SMOOTH)
gpu.state.blend_set('NONE')