1433 lines
43 KiB
Python
1433 lines
43 KiB
Python
# -*- coding:utf-8 -*-
|
|
|
|
# ##### BEGIN GPL LICENSE BLOCK #####
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# ##### END GPL LICENSE BLOCK #####
|
|
|
|
# <pep8 compliant>
|
|
|
|
# ----------------------------------------------------------
|
|
# Author: Stephen Leger (s-leger)
|
|
#
|
|
# ----------------------------------------------------------
|
|
import bgl
|
|
import blf
|
|
import gpu
|
|
from gpu_extras.batch import batch_for_shader
|
|
import bpy
|
|
import numpy as np
|
|
from math import sin, cos, atan2, pi
|
|
from mathutils import Vector, Matrix
|
|
from bpy_extras import view3d_utils, object_utils
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
# Define Gl Handle types
|
|
# ------------------------------------------------------------------
|
|
|
|
|
|
class DefaultColorScheme:
|
|
"""
|
|
Font sizes and basic colour scheme
|
|
default to this when not found in addon prefs
|
|
Colors are FloatVectorProperty of size 4 and type COLOR_GAMMA
|
|
"""
|
|
text_size = 14
|
|
feedback_size_main = 16
|
|
feedback_size_title = 14
|
|
feedback_size_shortcut = 11
|
|
feedback_colour_main = (0.95, 0.95, 0.95, 1.0)
|
|
feedback_colour_key = (0.67, 0.67, 0.67, 1.0)
|
|
feedback_colour_shortcut = (0.51, 0.51, 0.51, 1.0)
|
|
feedback_shortcut_area = (0, 0.4, 0.6, 0.2)
|
|
feedback_title_area = (0, 0.4, 0.6, 0.5)
|
|
handle_colour_normal = (1.0, 1.0, 1.0, 1.0)
|
|
handle_colour_hover = (1.0, 1.0, 0.0, 1.0)
|
|
handle_colour_active = (1.0, 0.0, 0.0, 1.0)
|
|
handle_colour_selected = (0.0, 0.0, 0.7, 1.0)
|
|
handle_colour_inactive = (0.3, 0.3, 0.3, 1.0)
|
|
|
|
|
|
def get_prefs(context):
|
|
global __name__
|
|
try:
|
|
addon_name = __name__.split('.')[0]
|
|
prefs = context.preferences.addons[addon_name].preferences
|
|
except:
|
|
prefs = DefaultColorScheme
|
|
pass
|
|
return prefs
|
|
|
|
|
|
g_poly = None
|
|
g_image = None
|
|
g_line = None
|
|
|
|
|
|
line_vertSrc = '''
|
|
uniform mat4 ModelViewProjectionMatrix;
|
|
|
|
in vec2 pos;
|
|
|
|
void main()
|
|
{
|
|
gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0);
|
|
}
|
|
|
|
'''
|
|
|
|
line_fragSrc = '''
|
|
uniform vec4 color;
|
|
|
|
out vec4 fragColor;
|
|
|
|
void main()
|
|
{
|
|
fragColor = color;
|
|
}
|
|
'''
|
|
|
|
"""
|
|
# TESTS
|
|
_format = gpu.types.GPUVertFormat()
|
|
_pos_id = _format.attr_add(
|
|
id="pos",
|
|
comp_type="F32",
|
|
len=2,
|
|
fetch_mode="FLOAT")
|
|
coords = [(0,0), (200,100)]
|
|
vbo = gpu.types.GPUVertBuf(len=len(coords), format=_format)
|
|
"""
|
|
|
|
|
|
class GPU_Line:
|
|
def __init__(self):
|
|
# never call this in -b mode
|
|
if bpy.app.background:
|
|
return
|
|
self._format = gpu.types.GPUVertFormat()
|
|
self._pos_id = self._format.attr_add(
|
|
id="pos",
|
|
comp_type="F32",
|
|
len=2,
|
|
fetch_mode="FLOAT")
|
|
self.shader = gpu.types.GPUShader(line_vertSrc, line_fragSrc)
|
|
self.unif_color = self.shader.uniform_from_name("color")
|
|
self.color = np.array([0.0, 0.0, 0.0, 1.0], 'f')
|
|
|
|
def batch_line_strip_create(self, coords):
|
|
vbo = gpu.types.GPUVertBuf(len=len(coords), format=self._format)
|
|
vbo.attr_fill(id=self._pos_id, data=coords)
|
|
batch_lines = gpu.types.GPUBatch(type="LINE_STRIP", buf=vbo)
|
|
batch_lines.program_set(self.shader)
|
|
return batch_lines
|
|
|
|
def draw(self, color, list_verts_co):
|
|
if list_verts_co:
|
|
batch = self.batch_line_strip_create(list_verts_co)
|
|
self.color[0:4] = color[0:4]
|
|
self.shader.uniform_vector_float(self.unif_color, self.color, 4)
|
|
batch.draw()
|
|
del batch
|
|
|
|
|
|
class GPU_Poly:
|
|
|
|
def __init__(self):
|
|
if bpy.app.background:
|
|
return
|
|
self._format = gpu.types.GPUVertFormat()
|
|
self._pos_id = self._format.attr_add(
|
|
id="pos",
|
|
comp_type="F32",
|
|
len=2,
|
|
fetch_mode="FLOAT")
|
|
self.shader = gpu.types.GPUShader(line_vertSrc, line_fragSrc)
|
|
self.unif_color = self.shader.uniform_from_name("color")
|
|
self.color = np.array([0.0, 0.0, 0.0, 0.5], 'f')
|
|
|
|
def batch_create(self, coords):
|
|
vbo = gpu.types.GPUVertBuf(len=len(coords), format=self._format)
|
|
vbo.attr_fill(id=self._pos_id, data=coords)
|
|
batch = gpu.types.GPUBatch(type="TRI_FAN", buf=vbo)
|
|
# batch.program_set(self.shader)
|
|
# batch = batch_for_shader(self.shader, 'TRI_FAN', {"position": coords})
|
|
return batch
|
|
|
|
def draw(self, color, list_verts_co):
|
|
if list_verts_co:
|
|
self.shader.bind()
|
|
batch = self.batch_create(list_verts_co)
|
|
self.color[0:4] = color[0:4]
|
|
self.shader.uniform_vector_float(self.unif_color, self.color, 4)
|
|
batch.draw(self.shader)
|
|
|
|
|
|
class GPU_Image:
|
|
uvs = [(0, 0), (1, 0), (0, 1), (1, 1)]
|
|
indices = [(0, 1, 2), (2, 1, 3)]
|
|
|
|
def __init__(self):
|
|
if bpy.app.background:
|
|
return
|
|
self._format = gpu.types.GPUVertFormat()
|
|
self._pos_id = self._format.attr_add(
|
|
id="pos",
|
|
comp_type="F32",
|
|
len=2,
|
|
fetch_mode="FLOAT")
|
|
self.shader = gpu.shader.from_builtin('2D_IMAGE')
|
|
|
|
def batch_create(self, coords):
|
|
batch = batch_for_shader(self.shader, 'TRIS',
|
|
{"pos": coords,
|
|
"texCoord": self.uvs},
|
|
indices=self.indices)
|
|
return batch
|
|
|
|
def draw(self, texture_id, list_verts_co):
|
|
if list_verts_co:
|
|
batch = self.batch_create(list_verts_co)
|
|
|
|
# in case someone disabled it before
|
|
bgl.glEnable(bgl.GL_TEXTURE_2D)
|
|
|
|
# bind texture to image unit 0
|
|
bgl.glActiveTexture(bgl.GL_TEXTURE0)
|
|
bgl.glBindTexture(bgl.GL_TEXTURE_2D, texture_id)
|
|
|
|
self.shader.bind()
|
|
# tell shader to use the image that is bound to image unit 0
|
|
self.shader.uniform_int("image", 0)
|
|
batch.draw(self.shader)
|
|
|
|
bgl.glDisable(bgl.GL_TEXTURE_2D)
|
|
|
|
|
|
class Gl():
|
|
"""
|
|
handle 3d -> 2d gl drawing
|
|
d : dimensions
|
|
3 to convert pos from 3d
|
|
2 to keep pos as 2d absolute screen position
|
|
"""
|
|
|
|
def __init__(self,
|
|
d=3,
|
|
colour=(0.0, 0.0, 0.0, 1.0)):
|
|
|
|
global g_poly, g_image, g_line
|
|
|
|
if g_poly is None:
|
|
g_poly = GPU_Poly()
|
|
|
|
if g_image is None:
|
|
g_image = GPU_Image()
|
|
|
|
if g_line is None:
|
|
g_line = GPU_Line()
|
|
|
|
# nth dimensions of input coords 3=word coords 2=pixel screen coords
|
|
self.d = d
|
|
self.pos_2d = Vector((0, 0))
|
|
self.colour_inactive = colour
|
|
|
|
@property
|
|
def colour(self):
|
|
return self.colour_inactive
|
|
|
|
def position_2d_from_coord(self, context, coord, render=False):
|
|
""" coord given in local input coordsys
|
|
"""
|
|
if self.d == 2:
|
|
return Vector(coord)
|
|
if render:
|
|
return self.get_render_location(context, coord)
|
|
region = context.region
|
|
rv3d = context.region_data
|
|
loc = view3d_utils.location_3d_to_region_2d(region, rv3d, coord, self.pos_2d)
|
|
return Vector(loc)
|
|
|
|
def get_render_location(self, context, coord):
|
|
scene = context.scene
|
|
co_2d = object_utils.world_to_camera_view(scene, scene.camera, coord)
|
|
# Get pixel coords
|
|
render_scale = scene.render.resolution_percentage / 100
|
|
render_size = (int(scene.render.resolution_x * render_scale),
|
|
int(scene.render.resolution_y * render_scale))
|
|
return Vector(round(co_2d.x * render_size[0]), round(co_2d.y * render_size[1]))
|
|
|
|
|
|
class GlText(Gl):
|
|
|
|
def __init__(self,
|
|
d=3,
|
|
label="",
|
|
value=None,
|
|
precision=4,
|
|
unit_mode='AUTO',
|
|
unit_type='SIZE',
|
|
dimension=1,
|
|
angle=0,
|
|
font_size=None,
|
|
colour=(1, 1, 1, 1),
|
|
z_axis=Vector((0, 0, 1))):
|
|
"""
|
|
d: [2|3] coords type: 2 for coords in screen pixels, 3 for 3d world location
|
|
label : string label
|
|
value : float value (will add unit according following settings)
|
|
precision : integer rounding for values
|
|
dimension : [1 - 3] nth dimension of unit (single, square, cubic)
|
|
unit_mode : ['ADAPTIVE','METER','CENTIMETER','MILIMETER','FEET','INCH','RADIANS','DEGREE']
|
|
unit type to use to postfix values
|
|
ADAPTIVE use scene units setup
|
|
unit_type : ['SIZE','ANGLE']
|
|
unit type to add to value
|
|
angle : angle to rotate text
|
|
|
|
"""
|
|
self.z_axis = z_axis
|
|
# text, add as prefix to value
|
|
self.label = label
|
|
# value with unit related
|
|
self.value = value
|
|
self.precision = precision
|
|
self.dimension = dimension
|
|
self.unit_type = unit_type
|
|
self.unit_mode = unit_mode
|
|
if font_size is None:
|
|
prefs = get_prefs(bpy.context)
|
|
self.font_size = prefs.text_size
|
|
else:
|
|
self.font_size = font_size
|
|
self.angle = angle
|
|
Gl.__init__(self, d)
|
|
self.colour_inactive = colour
|
|
# store text with units
|
|
self._text = ""
|
|
self.cbuff = bgl.Buffer(bgl.GL_FLOAT, 4)
|
|
|
|
def text_size(self, context):
|
|
"""
|
|
overall on-screen size in pixels
|
|
"""
|
|
dpi, font_id = context.preferences.system.dpi, 0
|
|
if self.angle != 0:
|
|
blf.enable(font_id, blf.ROTATION)
|
|
blf.rotation(font_id, self.angle)
|
|
blf.aspect(font_id, 1.0)
|
|
blf.size(font_id, self.font_size, dpi)
|
|
x, y = blf.dimensions(font_id, self.text)
|
|
if self.angle != 0:
|
|
blf.disable(font_id, blf.ROTATION)
|
|
return Vector((x, y))
|
|
|
|
@property
|
|
def pts(self):
|
|
return [self.pos_3d]
|
|
|
|
@property
|
|
def text(self):
|
|
s = self.label + self._text
|
|
return s.strip()
|
|
|
|
def add_units(self, context):
|
|
if self.value is None:
|
|
return ""
|
|
system = context.scene.unit_settings.system
|
|
if self.unit_type == 'ANGLE':
|
|
scale = 1
|
|
mode = 'ADAPTIVE'
|
|
else:
|
|
scale = context.scene.unit_settings.scale_length
|
|
mode = self.unit_mode
|
|
if mode == 'AUTO':
|
|
mode = context.scene.unit_settings.length_unit.upper()
|
|
|
|
val = self.value * scale
|
|
|
|
if mode == 'ADAPTIVE':
|
|
if self.unit_type == 'ANGLE':
|
|
mode = context.scene.unit_settings.system_rotation
|
|
else:
|
|
if system == "IMPERIAL":
|
|
if round(val * (3.2808399 ** self.dimension), 2) >= 1.0:
|
|
mode = 'FOOT'
|
|
else:
|
|
mode = 'INCH'
|
|
elif context.scene.unit_settings.system == "METRIC":
|
|
if round(val, 2) >= 1.0:
|
|
mode = 'METER'
|
|
else:
|
|
if round(val, 2) >= 0.01:
|
|
mode = 'CENTIMETER'
|
|
else:
|
|
mode = 'MILIMETER'
|
|
|
|
# TODO: support for separate units (through 2.8 api)
|
|
# convert values
|
|
unit = ""
|
|
if mode == 'METER':
|
|
unit = "m"
|
|
elif mode == 'CENTIMETER':
|
|
val *= (100 ** self.dimension)
|
|
unit = "cm"
|
|
elif mode == 'MILIMETER':
|
|
val *= (1000 ** self.dimension)
|
|
unit = 'mm'
|
|
elif mode in {'FOOT', 'FEET'}:
|
|
val *= (3.2808399 ** self.dimension)
|
|
unit = "ft"
|
|
elif mode == 'INCH':
|
|
val *= (39.3700787 ** self.dimension)
|
|
unit = "in"
|
|
elif mode == 'RADIANS':
|
|
unit = ""
|
|
elif mode == 'DEGREES':
|
|
val = self.value / pi * 180
|
|
unit = "°"
|
|
if system == 'IMPERIAL':
|
|
if self.dimension == 2:
|
|
unit = "sq " + unit
|
|
elif self.dimension == 3:
|
|
unit = "cu " + unit
|
|
elif system == 'METRIC':
|
|
if self.dimension == 2:
|
|
unit += "\u00b2" # Superscript two
|
|
elif self.dimension == 3:
|
|
unit += "\u00b3" # Superscript three
|
|
|
|
fmt = "%1." + str(self.precision) + "f"
|
|
# remove trailing zeros
|
|
res = fmt % val
|
|
while res[-1] == '0':
|
|
res = res[:-1]
|
|
|
|
if res[-1] == ".":
|
|
res = res + '0'
|
|
|
|
return "{} {}".format(res, unit)
|
|
|
|
def set_pos(self, context, value, pos_3d, direction, angle=0, normal=Vector((0, 0, 1))):
|
|
self.up_axis = direction.normalized()
|
|
self.c_axis = self.up_axis.cross(normal)
|
|
self.pos_3d = pos_3d
|
|
self.value = value
|
|
self.angle = angle
|
|
self._text = self.add_units(context)
|
|
|
|
def draw(self, context, render=False):
|
|
|
|
# print("draw_text %s %s" % (self.text, type(self).__name__))
|
|
self.render = render
|
|
p = self.position_2d_from_coord(context, self.pts[0], render)
|
|
|
|
# dirty fast assignment
|
|
dpi, font_id = context.preferences.system.dpi, 0
|
|
|
|
# self.cbuff[0:4] = self.colour
|
|
|
|
# bgl.glEnableClientState(bgl.GL_COLOR_ARRAY)
|
|
# bgl.glColorPointer(4, bgl.GL_FLOAT, 0, self.cbuff)
|
|
blf.color(0, *self.colour)
|
|
if self.angle != 0:
|
|
blf.enable(font_id, blf.ROTATION)
|
|
blf.rotation(font_id, self.angle)
|
|
blf.size(font_id, self.font_size, dpi)
|
|
blf.position(font_id, p.x, p.y, 0)
|
|
blf.draw(font_id, self.text)
|
|
if self.angle != 0:
|
|
blf.disable(font_id, blf.ROTATION)
|
|
|
|
# bgl.glDisableClientState(bgl.GL_COLOR_ARRAY)
|
|
|
|
|
|
class GlBaseLine(Gl):
|
|
|
|
def __init__(self,
|
|
d=3,
|
|
width=1,
|
|
style=bgl.GL_LINE,
|
|
closed=False,
|
|
n_pts=2):
|
|
Gl.__init__(self, d)
|
|
# default line width
|
|
self.width = width
|
|
# default line style
|
|
self.style = style
|
|
# allow closed lines
|
|
self.closed = closed
|
|
|
|
self.n_pts = n_pts
|
|
|
|
def draw(self, context, render=False):
|
|
"""
|
|
render flag when rendering
|
|
"""
|
|
self.render = render
|
|
bgl.glEnable(bgl.GL_BLEND)
|
|
bgl.glLineWidth(self.width)
|
|
list_verts_co = [
|
|
tuple(self.position_2d_from_coord(context, pt, render)[0:2])
|
|
for i, pt in enumerate(self.pts)]
|
|
if self.closed:
|
|
list_verts_co.append(list_verts_co[0])
|
|
g_line.draw(self.colour, list_verts_co)
|
|
bgl.glLineWidth(1.0)
|
|
bgl.glDisable(bgl.GL_BLEND)
|
|
|
|
|
|
class GlLine(GlBaseLine):
|
|
"""
|
|
2d/3d Line
|
|
"""
|
|
|
|
def __init__(self, d=3, p=None, v=None, p0=None, p1=None, z_axis=None):
|
|
"""
|
|
d=3 use 3d coords, d=2 use 2d pixels coords
|
|
Init by either
|
|
p: Vector or tuple origin
|
|
v: Vector or tuple size and direction
|
|
or
|
|
p0: Vector or tuple 1 point location
|
|
p1: Vector or tuple 2 point location
|
|
Will convert any into Vector 3d
|
|
both optionnals
|
|
"""
|
|
if p is not None and v is not None:
|
|
self.p = Vector(p)
|
|
self.v = Vector(v)
|
|
elif p0 is not None and p1 is not None:
|
|
self.p = Vector(p0)
|
|
self.v = Vector(p1) - self.p
|
|
else:
|
|
self.p = Vector()
|
|
self.v = Vector()
|
|
if z_axis is not None:
|
|
self.z_axis = z_axis
|
|
else:
|
|
self.z_axis = Vector((0, 0, 1))
|
|
GlBaseLine.__init__(self, d, n_pts=2)
|
|
|
|
@property
|
|
def p0(self):
|
|
return self.p
|
|
|
|
@property
|
|
def p1(self):
|
|
return self.p + self.v
|
|
|
|
@p0.setter
|
|
def p0(self, p0):
|
|
"""
|
|
Note: setting p0
|
|
move p0 only
|
|
"""
|
|
p1 = self.p1
|
|
self.p = Vector(p0)
|
|
self.v = p1 - p0
|
|
|
|
@p1.setter
|
|
def p1(self, p1):
|
|
"""
|
|
Note: setting p1
|
|
move p1 only
|
|
"""
|
|
self.v = Vector(p1) - self.p
|
|
|
|
@property
|
|
def length(self):
|
|
return self.v.length
|
|
|
|
@property
|
|
def angle(self):
|
|
return atan2(self.v.y, self.v.x)
|
|
|
|
@property
|
|
def cross(self):
|
|
"""
|
|
Vector perpendicular on plane defined by z_axis
|
|
lie on the right side
|
|
p1
|
|
|--x
|
|
p0
|
|
"""
|
|
return self.v.cross(self.z_axis)
|
|
|
|
def normal(self, t=0):
|
|
"""
|
|
Line perpendicular on plane defined by z_axis
|
|
lie on the right side
|
|
p1
|
|
|--x
|
|
p0
|
|
"""
|
|
n = GlLine()
|
|
n.p = self.lerp(t)
|
|
n.v = self.cross
|
|
return n
|
|
|
|
def sized_normal(self, t, size):
|
|
"""
|
|
GlLine perpendicular on plane defined by z_axis and of given size
|
|
positioned at t in current line
|
|
lie on the right side
|
|
p1
|
|
|--x
|
|
p0
|
|
"""
|
|
n = GlLine()
|
|
n.p = self.lerp(t)
|
|
n.v = size * self.cross.normalized()
|
|
return n
|
|
|
|
def lerp(self, t):
|
|
"""
|
|
Interpolate along segment
|
|
t parameter [0, 1] where 0 is start of arc and 1 is end
|
|
"""
|
|
return self.p + self.v * t
|
|
|
|
def offset(self, offset):
|
|
"""
|
|
offset > 0 on the right part
|
|
"""
|
|
self.p += offset * self.cross.normalized()
|
|
|
|
def point_sur_segment(self, pt):
|
|
""" point_sur_segment (2d)
|
|
point: Vector 3d
|
|
t: param t de l'intersection sur le segment courant
|
|
d: distance laterale perpendiculaire positif a droite
|
|
"""
|
|
dp = (pt - self.p).to_2d()
|
|
v2d = self.v.to_2d()
|
|
dl = v2d.length
|
|
d = (self.v.x * dp.y - self.v.y * dp.x) / dl
|
|
t = v2d.dot(dp) / (dl * dl)
|
|
return t > 0 and t < 1, d, t
|
|
|
|
@property
|
|
def pts(self):
|
|
return [self.p0, self.p1]
|
|
|
|
|
|
class GlCircle(GlBaseLine):
|
|
|
|
def __init__(self,
|
|
d=3,
|
|
radius=0,
|
|
center=Vector((0, 0, 0)),
|
|
z_axis=Vector((0, 0, 1))):
|
|
|
|
self.r = radius
|
|
self.c = center
|
|
z = z_axis
|
|
|
|
if z.z < 1:
|
|
x = z.cross(Vector((0, 0, 1)))
|
|
y = x.cross(z)
|
|
else:
|
|
x = Vector((1, 0, 0))
|
|
y = Vector((0, 1, 0))
|
|
|
|
self.rM = Matrix([
|
|
Vector((x.x, y.x, z.x)),
|
|
Vector((x.y, y.y, z.y)),
|
|
Vector((x.z, y.z, z.z))
|
|
])
|
|
self.z_axis = z
|
|
self.a0 = 0
|
|
self.da = 2 * pi
|
|
GlBaseLine.__init__(self, d, n_pts=60)
|
|
|
|
def lerp(self, t):
|
|
"""
|
|
Linear interpolation
|
|
"""
|
|
a = self.a0 + t * self.da
|
|
return self.c + self.rM @ Vector((self.r * cos(a), self.r * sin(a), 0))
|
|
|
|
@property
|
|
def pts(self):
|
|
n_pts = max(1, int(round(abs(self.da) / pi * 30, 0)))
|
|
self.n_pts = n_pts
|
|
t_step = 1 / n_pts
|
|
return [self.lerp(i * t_step) for i in range(n_pts + 1)]
|
|
|
|
|
|
class GlArc(GlCircle):
|
|
|
|
def __init__(self,
|
|
d=3,
|
|
radius=0,
|
|
center=Vector((0, 0, 0)),
|
|
z_axis=Vector((0, 0, 1)),
|
|
a0=0,
|
|
da=0):
|
|
"""
|
|
a0 and da arguments are in radians
|
|
a0 = 0 on the x+ axis side
|
|
a0 = pi on the x- axis side
|
|
da > 0 CCW contrary-clockwise
|
|
da < 0 CW clockwise
|
|
"""
|
|
GlCircle.__init__(self, d, radius, center, z_axis)
|
|
self.da = da
|
|
self.a0 = a0
|
|
|
|
@property
|
|
def length(self):
|
|
return self.r * abs(self.da)
|
|
|
|
def normal(self, t=0):
|
|
"""
|
|
perpendicular line always on the right side
|
|
"""
|
|
n = GlLine(d=self.d, z_axis=self.z_axis)
|
|
n.p = self.lerp(t)
|
|
if self.da < 0:
|
|
n.v = self.c - n.p
|
|
else:
|
|
n.v = n.p - self.c
|
|
return n
|
|
|
|
def sized_normal(self, t, size):
|
|
n = GlLine(d=self.d, z_axis=self.z_axis)
|
|
n.p = self.lerp(t)
|
|
if self.da < 0:
|
|
n.v = size * (self.c - n.p).normalized()
|
|
else:
|
|
n.v = size * (n.p - self.c).normalized()
|
|
return n
|
|
|
|
def tangeant(self, t, length):
|
|
a = self.a0 + t * self.da
|
|
ca = cos(a)
|
|
sa = sin(a)
|
|
n = GlLine(d=self.d, z_axis=self.z_axis)
|
|
n.p = self.c + self.rM @ Vector((self.r * ca, self.r * sa, 0))
|
|
n.v = self.rM @ Vector((length * sa, -length * ca, 0))
|
|
if self.da > 0:
|
|
n.v = -n.v
|
|
return n
|
|
|
|
def offset(self, offset):
|
|
"""
|
|
offset > 0 on the right part
|
|
"""
|
|
if self.da > 0:
|
|
radius = self.r + offset
|
|
else:
|
|
radius = self.r - offset
|
|
return GlArc(d=self.d,
|
|
radius=radius,
|
|
center=self.c,
|
|
a0=self.a0,
|
|
da=self.da,
|
|
z_axis=self.z_axis)
|
|
|
|
|
|
class GlPolygon(Gl):
|
|
|
|
def __init__(self,
|
|
colour=(0.3, 0.3, 0.3, 1.0),
|
|
d=3, n_pts=60):
|
|
self.pts_3d = []
|
|
Gl.__init__(self, d, colour)
|
|
|
|
self.n_pts = min(n_pts, 60)
|
|
|
|
def set_pos(self, pts_3d):
|
|
self.pts_3d = pts_3d
|
|
|
|
@property
|
|
def pts(self):
|
|
return self.pts_3d
|
|
|
|
def draw(self, context, render=False):
|
|
"""
|
|
render flag when rendering
|
|
"""
|
|
# return
|
|
|
|
self.render = render
|
|
if self.n_pts == 0:
|
|
return
|
|
|
|
pts = self.pts
|
|
|
|
g_vertices = [
|
|
tuple(self.position_2d_from_coord(context, pt, render)[0:2])
|
|
for i, pt in enumerate(pts)]
|
|
bgl.glEnable(bgl.GL_BLEND)
|
|
g_poly.draw(self.colour, g_vertices)
|
|
bgl.glDisable(bgl.GL_BLEND)
|
|
|
|
|
|
class GlRect(GlPolygon):
|
|
def __init__(self,
|
|
colour=(0.0, 0.0, 0.0, 1.0),
|
|
d=2):
|
|
GlPolygon.__init__(self, colour, d, n_pts=4)
|
|
|
|
@property
|
|
def pts(self):
|
|
return self.pts_2d
|
|
|
|
def draw(self, context, render=False):
|
|
pts = [
|
|
self.position_2d_from_coord(context, pt, render)
|
|
for pt in self.pts_3d
|
|
]
|
|
x0, y0 = pts[0]
|
|
x1, y1 = pts[1]
|
|
self.pts_2d = [Vector((x, y)) for x, y in [(x0, y0), (x0, y1), (x1, y1), (x1, y0)]]
|
|
GlPolygon.draw(self, context, render)
|
|
|
|
|
|
class GlImage(Gl):
|
|
def __init__(self,
|
|
d=2,
|
|
image=None):
|
|
# GImage bindcode[0]
|
|
self.image = image
|
|
self.colour_inactive = (1, 1, 1, 1)
|
|
Gl.__init__(self, d)
|
|
self.pts_2d = [Vector((0, 0)), Vector((10, 10))]
|
|
self.n_pts = 4
|
|
|
|
def set_pos(self, pts):
|
|
self.pts_2d = pts
|
|
|
|
@property
|
|
def pts(self):
|
|
return self.pts_2d
|
|
|
|
def draw(self, context, render=False):
|
|
if self.image is None:
|
|
return
|
|
|
|
p0 = self.pts[0]
|
|
p1 = self.pts[1]
|
|
coords = [(p0.x, p0.y), (p1.x, p0.y), (p0.x, p1.y), (p1.x, p1.y)]
|
|
g_image.draw(self.image.bindcode, coords)
|
|
|
|
|
|
class GlPolyline(GlBaseLine):
|
|
def __init__(self, colour, d=3, n_pts=60):
|
|
self.pts_3d = []
|
|
GlBaseLine.__init__(self, d, n_pts=n_pts)
|
|
self.colour_inactive = colour
|
|
|
|
def set_pos(self, pts_3d):
|
|
self.pts_3d = pts_3d
|
|
# self.pts_3d.append(pts_3d[0])
|
|
|
|
@property
|
|
def pts(self):
|
|
return self.pts_3d
|
|
|
|
|
|
class GlHandle(GlPolygon):
|
|
|
|
def __init__(self, sensor_size, size, draggable=False, selectable=False, d=3, n_pts=4):
|
|
"""
|
|
sensor_size : 2d size in pixels of sensor area
|
|
size : 3d size of handle
|
|
"""
|
|
GlPolygon.__init__(self, d=d, n_pts=n_pts)
|
|
prefs = get_prefs(bpy.context)
|
|
self.colour_active = prefs.handle_colour_active
|
|
self.colour_hover = prefs.handle_colour_hover
|
|
self.colour_normal = prefs.handle_colour_normal
|
|
self.colour_selected = prefs.handle_colour_selected
|
|
self.colour_inactive = prefs.handle_colour_inactive
|
|
# variable symbol size in world coords
|
|
self.size = size
|
|
# constant symbol size in pixels
|
|
self.symbol_size = sensor_size
|
|
# scale factor for constant symbol pixel size
|
|
self.scale = 1
|
|
# sensor size in pixels
|
|
self.sensor_width = sensor_size
|
|
self.sensor_height = sensor_size
|
|
self.pos_3d = Vector((0, 0, 0))
|
|
self.up_axis = Vector((0, 0, 0))
|
|
self.c_axis = Vector((0, 0, 0))
|
|
self.hover = False
|
|
self.active = False
|
|
self.draggable = draggable
|
|
self.selectable = selectable
|
|
self.selected = False
|
|
|
|
def scale_factor(self, context, pts):
|
|
"""
|
|
Compute screen scale factor for symbol
|
|
given 2 points in symbol direction (eg start and end of arrow)
|
|
"""
|
|
prefs = get_prefs(context)
|
|
if not prefs.constant_handle_size:
|
|
return 1
|
|
|
|
p2d = []
|
|
for pt in pts:
|
|
p2d.append(self.position_2d_from_coord(context, pt))
|
|
sx = [p.x for p in p2d]
|
|
sy = [p.y for p in p2d]
|
|
x = max(sx) - min(sx)
|
|
y = max(sy) - min(sy)
|
|
s = max(x, y)
|
|
if s > 0:
|
|
fac = self.symbol_size / s
|
|
else:
|
|
fac = 1
|
|
return fac
|
|
|
|
def set_pos(self, context, pos_3d, direction, normal=Vector((0, 0, 1))):
|
|
self.up_axis = direction.normalized()
|
|
self.c_axis = self.up_axis.cross(normal)
|
|
self.pos_3d = pos_3d
|
|
self.pos_2d = self.position_2d_from_coord(context, self.sensor_center)
|
|
x = self.up_axis * 0.5 * self.size
|
|
y = self.c_axis * 0.5 * self.size
|
|
pts = [self.sensor_center + v for v in [-x, x, -y, y]]
|
|
self.scale = self.scale_factor(context, pts)
|
|
|
|
def check_hover(self, pos_2d):
|
|
if self.draggable:
|
|
dp = pos_2d - self.pos_2d
|
|
self.hover = abs(dp.x) < self.sensor_width and abs(dp.y) < self.sensor_height
|
|
|
|
@property
|
|
def sensor_center(self):
|
|
pts = self.pts
|
|
n = len(pts)
|
|
x, y, z = 0, 0, 0
|
|
for pt in pts:
|
|
x += pt.x
|
|
y += pt.y
|
|
z += pt.z
|
|
return Vector((x / n, y / n, z / n))
|
|
|
|
@property
|
|
def pts(self):
|
|
raise NotImplementedError
|
|
|
|
@property
|
|
def colour(self):
|
|
if self.render:
|
|
return self.colour_inactive
|
|
elif self.draggable:
|
|
if self.active:
|
|
return self.colour_active
|
|
elif self.hover:
|
|
return self.colour_hover
|
|
elif self.selected:
|
|
return self.colour_selected
|
|
return self.colour_normal
|
|
else:
|
|
return self.colour_inactive
|
|
|
|
|
|
class SquareHandle(GlHandle):
|
|
|
|
def __init__(self, sensor_size, size, draggable=False, selectable=False):
|
|
GlHandle.__init__(self, sensor_size, size, draggable, selectable, n_pts=4)
|
|
|
|
@property
|
|
def pts(self):
|
|
n = self.up_axis
|
|
c = self.c_axis
|
|
if self.selected or self.hover or self.active:
|
|
scale = 1
|
|
else:
|
|
scale = 0.5
|
|
x = n * self.scale * self.size * scale
|
|
y = c * self.scale * self.size * scale
|
|
return [self.pos_3d - x - y, self.pos_3d + x - y, self.pos_3d + x + y, self.pos_3d - x + y]
|
|
|
|
|
|
class TriHandle(GlHandle):
|
|
|
|
def __init__(self, sensor_size, size, draggable=False, selectable=False):
|
|
GlHandle.__init__(self, sensor_size, size, draggable, selectable, n_pts=3)
|
|
|
|
@property
|
|
def pts(self):
|
|
n = self.up_axis
|
|
c = self.c_axis
|
|
# does move sensitive area so disable for tri handle
|
|
# may implement sensor_center property to fix this
|
|
# if self.selected or self.hover or self.active:
|
|
scale = 1
|
|
# else:
|
|
# scale = 0.5
|
|
x = n * self.scale * self.size * 4 * scale
|
|
y = c * self.scale * self.size * scale
|
|
return [self.pos_3d - x + y, self.pos_3d - x - y, self.pos_3d]
|
|
|
|
|
|
class CruxHandle(GlHandle):
|
|
|
|
def __init__(self, sensor_size, size, draggable=True, selectable=False):
|
|
GlHandle.__init__(self, sensor_size, size, draggable, selectable, n_pts=0)
|
|
self.branch_0 = GlPolygon((1, 1, 1, 1), d=3, n_pts=4)
|
|
self.branch_1 = GlPolygon((1, 1, 1, 1), d=3, n_pts=4)
|
|
|
|
def set_pos(self, context, pos_3d, direction, normal=Vector((0, 0, 1))):
|
|
self.pos_3d = pos_3d
|
|
self.pos_2d = self.position_2d_from_coord(context, self.sensor_center)
|
|
o = self.pos_3d
|
|
d = 0.5 * self.size
|
|
x = direction.normalized()
|
|
y = x.cross(normal)
|
|
|
|
sx = x * 0.5 * self.size
|
|
sy = y * 0.5 * self.size
|
|
pts = [o + v for v in [-sx, sx, -sy, sy]]
|
|
self.scale = self.scale_factor(context, pts)
|
|
|
|
c = self.scale * d / 1.4242
|
|
w = self.scale * self.size
|
|
s = w - c
|
|
|
|
xs = x * s
|
|
xw = x * w
|
|
ys = y * s
|
|
yw = y * w
|
|
p0 = o + xs + yw
|
|
p1 = o + xw + ys
|
|
p2 = o - xs - yw
|
|
p3 = o - xw - ys
|
|
p4 = o - xs + yw
|
|
p5 = o + xw - ys
|
|
p6 = o + xs - yw
|
|
p7 = o - xw + ys
|
|
|
|
self.branch_0.set_pos([p0, p1, p2, p3])
|
|
self.branch_1.set_pos([p4, p5, p6, p7])
|
|
|
|
@property
|
|
def pts(self):
|
|
return [self.pos_3d]
|
|
|
|
def draw(self, context, render=False):
|
|
self.render = render
|
|
self.branch_0.colour_inactive = self.colour
|
|
self.branch_1.colour_inactive = self.colour
|
|
self.branch_0.draw(context)
|
|
self.branch_1.draw(context)
|
|
|
|
|
|
class PlusHandle(GlHandle):
|
|
|
|
def __init__(self, sensor_size, size, draggable=True, selectable=False):
|
|
GlHandle.__init__(self, sensor_size, size, draggable, selectable, n_pts=0)
|
|
self.branch_0 = GlPolygon((1, 1, 1, 1), d=3, n_pts=4)
|
|
self.branch_1 = GlPolygon((1, 1, 1, 1), d=3, n_pts=4)
|
|
|
|
def set_pos(self, context, pos_3d, direction, normal=Vector((0, 0, 1))):
|
|
self.pos_3d = pos_3d
|
|
self.pos_2d = self.position_2d_from_coord(context, self.sensor_center)
|
|
o = self.pos_3d
|
|
x = direction.normalized()
|
|
y = x.cross(normal)
|
|
sx = x * 0.5 * self.size
|
|
sy = y * 0.5 * self.size
|
|
pts = [o + v for v in [-sx, sx, -sy, sy]]
|
|
self.scale = self.scale_factor(context, pts)
|
|
w = self.scale * self.size
|
|
s = self.scale * 0.25 * w
|
|
|
|
xs = x * s
|
|
xw = x * w
|
|
ys = y * s
|
|
yw = y * w
|
|
p0 = o - xw + ys
|
|
p1 = o + xw + ys
|
|
p2 = o + xw - ys
|
|
p3 = o - xw - ys
|
|
p4 = o - xs + yw
|
|
p5 = o + xs + yw
|
|
p6 = o + xs - yw
|
|
p7 = o - xs - yw
|
|
self.branch_0.set_pos([p0, p1, p2, p3])
|
|
self.branch_1.set_pos([p4, p5, p6, p7])
|
|
|
|
@property
|
|
def pts(self):
|
|
return [self.pos_3d]
|
|
|
|
def draw(self, context, render=False):
|
|
self.render = render
|
|
self.branch_0.colour_inactive = self.colour
|
|
self.branch_1.colour_inactive = self.colour
|
|
self.branch_0.draw(context)
|
|
self.branch_1.draw(context)
|
|
|
|
|
|
class EditableText(GlText, GlHandle):
|
|
def __init__(self, sensor_size, size, draggable=False, selectable=False):
|
|
GlHandle.__init__(self, sensor_size, size, draggable, selectable)
|
|
GlText.__init__(self, colour=(0, 0, 0, 1))
|
|
|
|
def set_pos(self, context, value, pos_3d, direction, normal=Vector((0, 0, 1))):
|
|
self.up_axis = direction.normalized()
|
|
self.c_axis = self.up_axis.cross(normal)
|
|
self.pos_3d = pos_3d
|
|
self.value = value
|
|
self._text = self.add_units(context)
|
|
ts = self.text_size(context)
|
|
self.pos_2d = self.position_2d_from_coord(context, pos_3d)
|
|
self.pos_2d.x += 0.5 * ts.x
|
|
self.sensor_width, self.sensor_height = 0.5 * ts.x, ts.y
|
|
|
|
@property
|
|
def sensor_center(self):
|
|
return self.pos_3d
|
|
|
|
|
|
class ThumbHandle(GlHandle):
|
|
|
|
def __init__(self, size_2d, label, image=None, draggable=False, selectable=False, d=2):
|
|
GlHandle.__init__(self, size_2d, size_2d, draggable, selectable, d, n_pts=4)
|
|
self.image = GlImage(image=image)
|
|
self.label = GlText(d=2, label=label.replace("_", " ").capitalize())
|
|
self.frame = GlPolyline((1, 1, 1, 1), d=2, n_pts=4)
|
|
self.frame.closed = True
|
|
self.size_2d = size_2d
|
|
self.sensor_width = 0.5 * size_2d.x
|
|
self.sensor_height = 0.5 * size_2d.y
|
|
self.colour_normal = (0.715, 0.905, 1, 0.9)
|
|
self.colour_hover = (1, 1, 1, 1)
|
|
|
|
def set_pos(self, context, pos_2d):
|
|
"""
|
|
pos 2d is center !!
|
|
"""
|
|
self.pos_2d = pos_2d
|
|
ts = self.label.text_size(context)
|
|
self.label.pos_3d = pos_2d + Vector((-0.5 * ts.x, ts.y - 0.5 * self.size_2d.y))
|
|
p0, p1 = self.pts
|
|
self.image.set_pos(self.pts)
|
|
self.frame.set_pos([p0, Vector((p1.x, p0.y)), p1, Vector((p0.x, p1.y))])
|
|
|
|
@property
|
|
def pts(self):
|
|
s = 0.5 * self.size_2d
|
|
return [self.pos_2d - s, self.pos_2d + s]
|
|
|
|
@property
|
|
def sensor_center(self):
|
|
return self.pos_2d + 0.5 * self.size_2d
|
|
|
|
def draw(self, context, render=False):
|
|
self.render = render
|
|
self.image.colour_inactive = self.colour
|
|
# GlHandle.draw(self, context, render=False)
|
|
self.image.draw(context, render=False)
|
|
self.label.draw(context, render=False)
|
|
self.frame.draw(context, render=False)
|
|
|
|
|
|
class Screen():
|
|
def __init__(self, margin):
|
|
self.margin = margin
|
|
|
|
def size(self, context):
|
|
|
|
system = context.preferences.system
|
|
w = context.region.width
|
|
h = context.region.height
|
|
y_min = self.margin
|
|
y_max = h - self.margin
|
|
x_min = self.margin
|
|
x_max = w - self.margin
|
|
if system.use_region_overlap:
|
|
# system.window_draw_method in {'TRIPLE_BUFFER', 'ADAPTIVEMATIC'}):
|
|
area = context.area
|
|
for r in area.regions:
|
|
if r.type == 'TOOLS':
|
|
x_min += r.width
|
|
elif r.type == 'UI':
|
|
x_max -= r.width
|
|
return x_min, x_max, y_min, y_max
|
|
|
|
|
|
class FeedbackPanel():
|
|
"""
|
|
Feed-back panel
|
|
inspired by np_station
|
|
"""
|
|
|
|
def __init__(self, title='Archipack'):
|
|
|
|
prefs = get_prefs(bpy.context)
|
|
|
|
self.main_title = GlText(d=2,
|
|
label=title + " : ",
|
|
font_size=prefs.feedback_size_main,
|
|
colour=prefs.feedback_colour_main
|
|
)
|
|
self.title = GlText(d=2,
|
|
font_size=prefs.feedback_size_title,
|
|
colour=prefs.feedback_colour_main
|
|
)
|
|
self.spacing = Vector((
|
|
0.5 * prefs.feedback_size_shortcut,
|
|
0.5 * prefs.feedback_size_shortcut))
|
|
self.margin = 50
|
|
self.explanation = GlText(d=2,
|
|
font_size=prefs.feedback_size_shortcut,
|
|
colour=prefs.feedback_colour_main
|
|
)
|
|
self.shortcut_area = GlPolygon(colour=prefs.feedback_shortcut_area, d=2, n_pts=4)
|
|
self.title_area = GlPolygon(colour=prefs.feedback_title_area, d=2, n_pts=4)
|
|
self.shortcuts = []
|
|
self.on = False
|
|
self.show_title = True
|
|
self.show_main_title = True
|
|
# read only, when enabled, after draw() the top left coord of info box
|
|
self.top = Vector((0, 0))
|
|
self.screen = Screen(self.margin)
|
|
|
|
def disable(self):
|
|
self.on = False
|
|
|
|
def enable(self):
|
|
self.on = True
|
|
|
|
def instructions(self, context, title, explanation, shortcuts):
|
|
"""
|
|
position from bottom to top
|
|
"""
|
|
prefs = get_prefs(context)
|
|
|
|
self.explanation.label = explanation
|
|
self.title.label = title
|
|
|
|
self.shortcuts = []
|
|
|
|
for key, label in shortcuts:
|
|
key = GlText(d=2, label=key,
|
|
font_size=prefs.feedback_size_shortcut,
|
|
colour=prefs.feedback_colour_key)
|
|
label = GlText(d=2, label=' : ' + label,
|
|
font_size=prefs.feedback_size_shortcut,
|
|
colour=prefs.feedback_colour_shortcut)
|
|
ks = key.text_size(context)
|
|
ls = label.text_size(context)
|
|
self.shortcuts.append([key, ks, label, ls])
|
|
|
|
def draw(self, context, render=False):
|
|
|
|
if self.on:
|
|
"""
|
|
draw from bottom to top
|
|
so we are able to always fit needs
|
|
"""
|
|
x_min, x_max, y_min, y_max = self.screen.size(context)
|
|
available_w = x_max - x_min - 2 * self.spacing.x
|
|
main_title_size = self.main_title.text_size(context) + Vector((5, 0))
|
|
|
|
# h = context.region.height
|
|
# 0,0 = bottom left
|
|
pos = Vector((x_min + self.spacing.x, y_min))
|
|
shortcuts = []
|
|
|
|
# sort by lines
|
|
lines = []
|
|
line = []
|
|
space = 0
|
|
sum_txt = 0
|
|
|
|
for key, ks, label, ls in self.shortcuts:
|
|
space += ks.x + ls.x + self.spacing.x
|
|
if pos.x + space > available_w:
|
|
txt_spacing = (available_w - sum_txt) / (max(1, len(line) - 1))
|
|
sum_txt = 0
|
|
space = ks.x + ls.x + self.spacing.x
|
|
lines.append((txt_spacing, line))
|
|
line = []
|
|
sum_txt += ks.x + ls.x
|
|
line.append([key, ks, label, ls])
|
|
|
|
if len(line) > 0:
|
|
txt_spacing = (available_w - sum_txt) / (max(1, len(line) - 1))
|
|
lines.append((txt_spacing, line))
|
|
|
|
# reverse lines to draw from bottom to top
|
|
lines = list(reversed(lines))
|
|
for spacing, line in lines:
|
|
pos.y += self.spacing.y
|
|
pos.x = x_min + self.spacing.x
|
|
for key, ks, label, ls in line:
|
|
key.pos_3d = pos.copy()
|
|
pos.x += ks.x
|
|
label.pos_3d = pos.copy()
|
|
pos.x += ls.x + spacing
|
|
shortcuts.extend([key, label])
|
|
pos.y += ks.y + self.spacing.y
|
|
|
|
n_shortcuts = len(shortcuts)
|
|
# shortcut area
|
|
self.shortcut_area.pts_3d = [
|
|
(x_min, self.margin),
|
|
(x_max, self.margin),
|
|
(x_max, pos.y),
|
|
(x_min, pos.y)
|
|
]
|
|
|
|
# small space between shortcut area and main title bar
|
|
if n_shortcuts > 0:
|
|
pos.y += 0.5 * self.spacing.y
|
|
|
|
self.title_area.pts_3d = [
|
|
(x_min, pos.y),
|
|
(x_max, pos.y),
|
|
(x_max, pos.y + main_title_size.y + 2 * self.spacing.y),
|
|
(x_min, pos.y + main_title_size.y + 2 * self.spacing.y)
|
|
]
|
|
pos.y += self.spacing.y
|
|
|
|
title_size = self.title.text_size(context)
|
|
# check for space available:
|
|
# if explanation + title + main_title are too big
|
|
# 1 remove main title
|
|
# 2 remove title
|
|
explanation_size = self.explanation.text_size(context)
|
|
|
|
self.show_title = True
|
|
self.show_main_title = True
|
|
|
|
if title_size.x + explanation_size.x > available_w:
|
|
# keep only explanation
|
|
self.show_title = False
|
|
self.show_main_title = False
|
|
elif main_title_size.x + title_size.x + explanation_size.x > available_w:
|
|
# keep title + explanation
|
|
self.show_main_title = False
|
|
self.title.pos_3d = (x_min + self.spacing.x, pos.y)
|
|
else:
|
|
self.title.pos_3d = (x_min + self.spacing.x + main_title_size.x, pos.y)
|
|
|
|
self.explanation.pos_3d = (x_max - self.spacing.x - explanation_size.x, pos.y)
|
|
self.main_title.pos_3d = (x_min + self.spacing.x, pos.y)
|
|
|
|
self.shortcut_area.draw(context)
|
|
self.title_area.draw(context)
|
|
if self.show_title:
|
|
self.title.draw(context)
|
|
if self.show_main_title:
|
|
self.main_title.draw(context)
|
|
self.explanation.draw(context)
|
|
for s in shortcuts:
|
|
s.draw(context)
|
|
|
|
self.top = Vector((x_min, pos.y + main_title_size.y + self.spacing.y))
|
|
|
|
|
|
class GlCursorFence():
|
|
"""
|
|
Cursor crossing Fence
|
|
"""
|
|
|
|
def __init__(self, width=1, colour=(1.0, 1.0, 1.0, 0.5), style=2852):
|
|
self.line_x = GlLine(d=2, n_pts=2)
|
|
self.line_x.style = style
|
|
self.line_x.width = width
|
|
self.line_x.colour_inactive = colour
|
|
self.line_y = GlLine(d=2, n_pts=2)
|
|
self.line_y.style = style
|
|
self.line_y.width = width
|
|
self.line_y.colour_inactive = colour
|
|
self.on = True
|
|
|
|
def set_location(self, context, location):
|
|
w = context.region.width
|
|
h = context.region.height
|
|
p = Vector(location)
|
|
x, y = p.x, p.y
|
|
self.line_x.p = Vector((0, y))
|
|
self.line_x.v = Vector((w, 0))
|
|
self.line_y.p = Vector((x, 0))
|
|
self.line_y.v = Vector((0, h))
|
|
|
|
def enable(self):
|
|
self.on = True
|
|
|
|
def disable(self):
|
|
self.on = False
|
|
|
|
def draw(self, context, render=False):
|
|
if self.on:
|
|
self.line_x.draw(context)
|
|
self.line_y.draw(context)
|
|
|
|
|
|
class GlCursorArea():
|
|
def __init__(self,
|
|
width=1,
|
|
bordercolour=(1.0, 1.0, 1.0, 0.5),
|
|
areacolour=(0.5, 0.5, 0.5, 0.08),
|
|
style=2852):
|
|
|
|
self.border = GlPolyline(bordercolour, d=2, n_pts=4)
|
|
self.border.style = style
|
|
self.border.width = width
|
|
self.border.closed = True
|
|
self.area = GlPolygon(areacolour, d=2, n_pts=4)
|
|
self.min = Vector((0, 0))
|
|
self.max = Vector((0, 0))
|
|
self.on = False
|
|
|
|
def in_area(self, pt):
|
|
return (self.min.x <= pt.x and self.max.x >= pt.x and
|
|
self.min.y <= pt.y and self.max.y >= pt.y)
|
|
|
|
def set_location(self, context, p0, p1):
|
|
p = Vector(p0)
|
|
x0, y0 = p.x, p.y
|
|
p = Vector(p1)
|
|
x1, y1 = p.x, p.y
|
|
if x0 > x1:
|
|
x1, x0 = x0, x1
|
|
if y0 > y1:
|
|
y1, y0 = y0, y1
|
|
self.min = Vector((x0, y0))
|
|
self.max = Vector((x1, y1))
|
|
pos = [
|
|
Vector((x0, y0)),
|
|
Vector((x0, y1)),
|
|
Vector((x1, y1)),
|
|
Vector((x1, y0))]
|
|
self.area.set_pos(pos)
|
|
self.border.set_pos(pos)
|
|
|
|
def enable(self):
|
|
self.on = True
|
|
|
|
def disable(self):
|
|
self.on = False
|
|
|
|
def draw(self, context, render=False):
|
|
if self.on:
|
|
# print("GlCursorArea.draw()")
|
|
self.area.draw(context)
|
|
self.border.draw(context)
|