Newer
Older
if settings.draw_T1:
bgl.glColor4f(1, 0, 1, 1)
draw_arrow(p0, y, _z, x) # X (1st tangential)
if settings.draw_T2:
bgl.glColor4f(1, 1, 0, 1)
draw_arrow(p0, _z, x, y) # Y (2nd tangential)
CoDEmanX
committed
bgl.glEnable(bgl.GL_BLEND)
bgl.glDisable(bgl.GL_DEPTH_TEST)
CoDEmanX
committed
if settings.draw_N:
bgl.glColor4f(0, 1, 1, 0.25)
draw_arrow(p0, _x, y, z) # Z (normal)
if settings.draw_T1:
bgl.glColor4f(1, 0, 1, 0.25)
draw_arrow(p0, y, _z, x) # X (1st tangential)
if settings.draw_T2:
bgl.glColor4f(1, 1, 0, 0.25)
draw_arrow(p0, _z, x, y) # Y (2nd tangential)
CoDEmanX
committed
if settings.draw_guides:
p0 = dest_point
try:
p00 = sys_matrix.inverted() * p0
except:
# this is some degenerate system
p00 = p0.copy()
CoDEmanX
committed
axes_line_params = [
(Vector((0, p00.y, p00.z)), (1, 0, 0)),
(Vector((p00.x, 0, p00.z)), (0, 1, 0)),
(Vector((p00.x, p00.y, 0)), (0, 0, 1)),
]
CoDEmanX
committed
for i in range(3):
p1, color = axes_line_params[i]
p1 = sys_matrix * p1
constrained = (self.axes_coords[i] is not None) or \
(not self.allowed_axes[i])
alpha = (0.25 if constrained else 1.0)
draw_line_hidden_depth(p0, p1, color, \
alpha, alpha, False, True)
CoDEmanX
committed
# line from origin to cursor
p0 = sys_origin
p1 = dest_point
CoDEmanX
committed
bgl.glEnable(bgl.GL_LINE_STIPPLE)
bgl.glColor4f(1, 1, 0, 1)
CoDEmanX
committed
draw_line_hidden_depth(p0, p1, (1, 1, 0), 1.0, 0.5, True, True)
CoDEmanX
committed
if settings.draw_snap_elements:
sui = self.su.implementation
if sui.potential_snap_elements and (sui.snap_type == 'EDGE'):
bgl.glDisable(bgl.GL_LINE_STIPPLE)
CoDEmanX
committed
bgl.glEnable(bgl.GL_BLEND)
bgl.glDisable(bgl.GL_DEPTH_TEST)
CoDEmanX
committed
bgl.glLineWidth(2)
bgl.glColor4f(0, 0, 1, 0.5)
CoDEmanX
committed
bgl.glBegin(bgl.GL_LINE_LOOP)
for p in sui.potential_snap_elements:
bgl.glVertex3f(p[0], p[1], p[2])
bgl.glEnd()
elif sui.potential_snap_elements and (sui.snap_type == 'FACE'):
bgl.glEnable(bgl.GL_BLEND)
bgl.glDisable(bgl.GL_DEPTH_TEST)
CoDEmanX
committed
CoDEmanX
committed
Dima Glib
committed
tris = tessellate_polygon([co])
bgl.glBegin(bgl.GL_TRIANGLES)
for tri in tris:
for vi in tri:
p = co[vi]
bgl.glVertex3f(p[0], p[1], p[2])
bgl.glEnd()
CoDEmanX
committed
if self.check_v3d_local(context):
return
CoDEmanX
committed
r = context.region
rv3d = context.region_data
CoDEmanX
committed
CoDEmanX
committed
if settings.draw_snap_elements:
sui = self.su.implementation
CoDEmanX
committed
snap_points = []
if sui.potential_snap_elements and \
(sui.snap_type in {'VERTEX', 'VOLUME'}):
snap_points.extend(sui.potential_snap_elements)
if sui.extra_snap_points:
snap_points.extend(sui.extra_snap_points)
CoDEmanX
committed
if snap_points:
bgl.glEnable(bgl.GL_BLEND)
CoDEmanX
committed
bgl.glPointSize(5)
bgl.glColor4f(1, 0, 0, 0.5)
CoDEmanX
committed
bgl.glBegin(bgl.GL_POINTS)
for p in snap_points:
p = location_3d_to_region_2d(r, rv3d, p)
if p is not None:
bgl.glVertex2f(p[0], p[1])
bgl.glEnd()
CoDEmanX
committed
CoDEmanX
committed
if self.transform_mode == 'MOVE':
return
CoDEmanX
committed
CoDEmanX
committed
CoDEmanX
committed
bgl.glColor4f(0, 0, 0, 1)
draw_line_2d(self.origin_xy, self.xy)
CoDEmanX
committed
CoDEmanX
committed
line_width = 3
bgl.glLineWidth(line_width)
CoDEmanX
committed
L = 12.0
arrow_len = 6.0
arrow_width = 8.0
arrow_space = 5.0
CoDEmanX
committed
Lmax = arrow_space * 2 + L * 2 + line_width
CoDEmanX
committed
pos = self.xy.to_2d()
normal = self.prev_delta_xy.to_2d().normalized()
dist = self.prev_delta_xy.length
tangential = Vector((-normal[1], normal[0]))
CoDEmanX
committed
if self.transform_mode == 'ROTATE':
n_axes = sum(int(v) for v in self.allowed_axes)
if n_axes == 2:
bgl.glColor4f(0.4, 0.15, 0.15, 1)
for sgn in (-1, 1):
n = sgn * Vector((0, 1))
p0 = pos + arrow_space * n
draw_arrow_2d(p0, n, L, arrow_len, arrow_width)
CoDEmanX
committed
bgl.glColor4f(0.11, 0.51, 0.11, 1)
for sgn in (-1, 1):
n = sgn * Vector((1, 0))
p0 = pos + arrow_space * n
draw_arrow_2d(p0, n, L, arrow_len, arrow_width)
else:
bgl.glColor4f(0, 0, 0, 1)
for sgn in (-1, 1):
n = sgn * tangential
if dist < Lmax:
n *= dist / Lmax
p0 = pos + arrow_space * n
draw_arrow_2d(p0, n, L, arrow_len, arrow_width)
elif self.transform_mode == 'SCALE':
bgl.glColor4f(0, 0, 0, 1)
for sgn in (-1, 1):
n = sgn * normal
p0 = pos + arrow_space * n
draw_arrow_2d(p0, n, L, arrow_len, arrow_width)
CoDEmanX
committed
CoDEmanX
committed
def draw_axes_coords(self, context, header_size):
if self.check_v3d_local(context):
return
CoDEmanX
committed
if time.time() < (self.click_start + self.click_period):
return
CoDEmanX
committed
CoDEmanX
committed
userprefs_view = context.preferences.view
CoDEmanX
committed
CoDEmanX
committed
settings = find_settings()
tfm_opts = settings.transform_options
CoDEmanX
committed
localmat = CursorDynamicSettings.local_matrix
CoDEmanX
committed
CoDEmanX
committed
font_size = 11
blf.size(font_id, font_size, 72) # font, point size, dpi
CoDEmanX
committed
tet = context.preferences.themes[0].text_editor
CoDEmanX
committed
# Prepare the table...
if self.transform_mode == 'MOVE':
axis_prefix = ("D" if tfm_opts.use_relative_coords else "")
elif self.transform_mode == 'SCALE':
axis_prefix = "S"
else:
axis_prefix = "R"
axis_names = ["X", "Y", "Z"]
CoDEmanX
committed
axis_cells = []
coord_cells = []
#caret_cell = TextCell("_", tet.cursor)
caret_cell = TextCell("|", tet.cursor)
CoDEmanX
committed
CoDEmanX
committed
Dima Glib
committed
color = tet.space.text
alpha = (1.0 if self.allowed_axes[i] else 0.5)
text = axis_prefix + axis_names[i] + " : "
axis_cells.append(TextCell(text, color, alpha))
CoDEmanX
committed
if self.axes_values[i]:
if self.axes_eval_success[i]:
color = tet.syntax_numbers
else:
color = tet.syntax_string
else:
Dima Glib
committed
color = tet.space.text
text = axes_text[i]
coord_cells.append(TextCell(text, color))
except Exception as e:
print(repr(e))
CoDEmanX
committed
CoDEmanX
committed
try:
snap_type = self.su.implementation.snap_type
if snap_type is None:
Dima Glib
committed
color = tet.space.text
elif (not self.use_object_centers) or \
(snap_type == 'INCREMENT'):
color = tet.syntax_numbers
else:
color = tet.syntax_special
text = snap_type or tool_settings.snap_element
if text == 'VOLUME':
text = "BBOX"
mode_cells.append(TextCell(text, color))
CoDEmanX
committed
Dima Glib
committed
color = tet.space.text
else:
color = tet.syntax_builtin
text = self.csu.tou.get_title()
mode_cells.append(TextCell(text, color))
CoDEmanX
committed
Dima Glib
committed
color = tet.space.text
text = self.csu.get_pivot_name(raw=True)
if self.use_object_centers:
color = tet.syntax_special
mode_cells.append(TextCell(text, color))
except Exception as e:
print(repr(e))
CoDEmanX
committed
CoDEmanX
committed
try:
xyz_x_start_min = 12
xyz_x_start = xyz_x_start_min
mode_x_start = 6
CoDEmanX
committed
mode_margin = 4
xyz_margin = 16
blend_margin = 32
CoDEmanX
committed
Dima Glib
committed
color = tet.space.back
bgl.glColor4f(color[0], color[1], color[2], 1.0)
draw_rect(0, 0, hdr_w, hdr_h)
CoDEmanX
committed
if tool_settings.use_snap_self:
x = hdr_w - mode_x_start
y = hdr_h / 2
cell = mode_cells[0]
x -= cell.w
y -= cell.h * 0.5
bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
draw_rect(x, y, cell.w, cell.h, 1, True)
CoDEmanX
committed
x = hdr_w - mode_x_start
y = hdr_h / 2
for cell in mode_cells:
cell.draw(x, y, (1, 0.5))
x -= (cell.w + mode_margin)
CoDEmanX
committed
curr_axis_x_start = 0
curr_axis_x_end = 0
caret_x = 0
CoDEmanX
committed
xyz_width = 0
for i in range(3):
if i == self.current_axis:
CoDEmanX
committed
CoDEmanX
committed
if i == self.current_axis:
char_offset = 0
if self.axes_values[i]:
char_offset = blf.dimensions(font_id,
coord_cells[i].text[:self.caret_pos])[0]
caret_x = xyz_width + char_offset
CoDEmanX
committed
CoDEmanX
committed
CoDEmanX
committed
CoDEmanX
committed
xyz_width = int(xyz_width)
xyz_width_ext = xyz_width + blend_margin
CoDEmanX
committed
offset = (xyz_x_start + curr_axis_x_end) - hdr_w
if offset > 0:
xyz_x_start -= offset
CoDEmanX
committed
offset = xyz_x_start_min - (xyz_x_start + curr_axis_x_start)
if offset > 0:
xyz_x_start += offset
CoDEmanX
committed
offset = (xyz_x_start + caret_x) - hdr_w
if offset > 0:
xyz_x_start -= offset
CoDEmanX
committed
# somewhy GL_BLEND should be set right here
# to actually draw the box with blending %)
# (perhaps due to text draw happened before)
bgl.glEnable(bgl.GL_BLEND)
bgl.glShadeModel(bgl.GL_SMOOTH)
gl_enable(bgl.GL_SMOOTH, True)
Dima Glib
committed
color = tet.space.back
bgl.glBegin(bgl.GL_TRIANGLE_STRIP)
bgl.glColor4f(color[0], color[1], color[2], 1.0)
bgl.glVertex2i(0, 0)
bgl.glVertex2i(0, hdr_h)
bgl.glVertex2i(xyz_width, 0)
bgl.glVertex2i(xyz_width, hdr_h)
bgl.glColor4f(color[0], color[1], color[2], 0.0)
bgl.glVertex2i(xyz_width_ext, 0)
bgl.glVertex2i(xyz_width_ext, hdr_h)
bgl.glEnd()
CoDEmanX
committed
x = xyz_x_start
y = hdr_h / 2
for i in range(3):
cell = axis_cells[i]
cell.draw(x, y, (0, 0.5))
x += cell.w
CoDEmanX
committed
cell = coord_cells[i]
cell.draw(x, y, (0, 0.5))
x += (cell.w + xyz_margin)
CoDEmanX
committed
caret_x -= blf.dimensions(font_id, caret_cell.text)[0] * 0.5
caret_cell.draw(xyz_x_start + caret_x, y, (0, 0.5))
CoDEmanX
committed
bgl.glEnable(bgl.GL_BLEND)
bgl.glShadeModel(bgl.GL_SMOOTH)
gl_enable(bgl.GL_SMOOTH, True)
Dima Glib
committed
color = tet.space.back
bgl.glBegin(bgl.GL_TRIANGLE_STRIP)
bgl.glColor4f(color[0], color[1], color[2], 1.0)
bgl.glVertex2i(0, 0)
bgl.glVertex2i(0, hdr_h)
bgl.glVertex2i(xyz_x_start_min, 0)
bgl.glColor4f(color[0], color[1], color[2], 0.0)
bgl.glVertex2i(xyz_x_start_min, hdr_h)
bgl.glEnd()
CoDEmanX
committed
print(repr(e))
CoDEmanX
committed
CoDEmanX
committed
# ====== NORMAL SNAPSHOT ====== #
def is_normal_visible(self):
if self.csu.tou.get() == "Surface":
return True
CoDEmanX
committed
if self.use_object_centers:
return False
CoDEmanX
committed
return self.su.implementation.snap_type \
not in {None, 'INCREMENT', 'VOLUME'}
CoDEmanX
committed
def get_normal_params(self, tfm_opts, dest_point):
surf_matrix = self.csu.get_matrix("Surface")
if tfm_opts.use_relative_coords:
surf_origin = dest_point
else:
surf_origin = surf_matrix.to_translation()
CoDEmanX
committed
m3 = surf_matrix.to_3x3()
p0 = surf_origin
scl = self.gizmo_scale(p0)
CoDEmanX
committed
# Normal and tangential are not always orthogonal
# (e.g. when normal is interpolated)
x = (m3 * Vector((1, 0, 0))).normalized()
y = (m3 * Vector((0, 1, 0))).normalized()
z = (m3 * Vector((0, 0, 1))).normalized()
CoDEmanX
committed
CoDEmanX
committed
return p0, x * scl, y * scl, z * scl, _x * scl, _z * scl
CoDEmanX
committed
def make_normal_snapshot(self, collection, tangential=False):
settings = find_settings()
tfm_opts = settings.transform_options
CoDEmanX
committed
dest_point = self.particles[0].get_location()
CoDEmanX
committed
if self.is_normal_visible():
p0, x, y, z, _x, _z = \
self.get_normal_params(tfm_opts, dest_point)
CoDEmanX
committed
snapshot = bpy.data.objects.new("normal_snapshot", None)
CoDEmanX
committed
Dima Glib
committed
snapshot.matrix_world = m
CoDEmanX
committed
snapshot.empty_display_type = 'SINGLE_ARROW'
#snapshot.empty_display_type = 'ARROWS'
collection.objects.link(snapshot)
#============================================================================#
class Particle:
pass
class View3D_Cursor(Particle):
def __init__(self, context):
assert context.space_data.type == 'VIEW_3D'
self.v3d = context.space_data
self.initial_pos = self.get_location()
self.initial_matrix = Matrix.Translation(self.initial_pos)
CoDEmanX
committed
def revert(self):
self.set_location(self.initial_pos)
CoDEmanX
committed
return get_cursor_location(v3d=self.v3d)
CoDEmanX
committed
set_cursor_location(Vector(value), v3d=self.v3d)
CoDEmanX
committed
def get_rotation(self):
return Quaternion()
CoDEmanX
committed
CoDEmanX
committed
def get_scale(self):
return Vector((1.0, 1.0, 1.0))
CoDEmanX
committed
CoDEmanX
committed
def get_matrix(self):
return Matrix.Translation(self.get_location())
CoDEmanX
committed
def set_matrix(self, value):
self.set_location(value.to_translation())
CoDEmanX
committed
def get_initial_matrix(self):
return self.initial_matrix
class View3D_Object(Particle):
def __init__(self, obj):
self.obj = obj
CoDEmanX
committed
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
def get_location(self):
# obj.location seems to be in parent's system...
# or even maybe not bounded by constraints %)
return self.obj.matrix_world.to_translation()
class View3D_EditMesh_Vertex(Particle):
pass
class View3D_EditMesh_Edge(Particle):
pass
class View3D_EditMesh_Face(Particle):
pass
class View3D_EditSpline_Point(Particle):
pass
class View3D_EditSpline_BezierPoint(Particle):
pass
class View3D_EditSpline_BezierHandle(Particle):
pass
class View3D_EditMeta_Element(Particle):
pass
class View3D_EditBone_Bone(Particle):
pass
class View3D_EditBone_HeadTail(Particle):
pass
class View3D_PoseBone(Particle):
pass
class UV_Cursor(Particle):
pass
class UV_Vertex(Particle):
pass
class UV_Edge(Particle):
pass
class UV_Face(Particle):
pass
# Other types:
# NLA / Dopesheet / Graph editor ...
# Particles are used in the following situations:
# - as subjects of transformation
# - as reference point(s) for cursor transformation
# Note: particles 'dragged' by Proportional Editing
# are a separate issue (they can come and go).
def gather_particles(**kwargs):
context = kwargs.get("context", bpy.context)
CoDEmanX
committed
area_type = kwargs.get("area_type", context.area.type)
CoDEmanX
committed
view_layer = kwargs.get("view_layer", context.view_layer)
CoDEmanX
committed
space_data = kwargs.get("space_data", context.space_data)
region_data = kwargs.get("region_data", context.region_data)
CoDEmanX
committed
particles = []
pivots = {}
normal_system = None
CoDEmanX
committed
active_element = None
cursor_pos = None
median = None
CoDEmanX
committed
if area_type == 'VIEW_3D':
context_mode = kwargs.get("context_mode", context.mode)
CoDEmanX
committed
selected_objects = kwargs.get("selected_objects",
context.selected_objects)
CoDEmanX
committed
active_object = kwargs.get("active_object",
context.active_object)
CoDEmanX
committed
if context_mode == 'OBJECT':
for obj in selected_objects:
particle = View3D_Object(obj)
particles.append(particle)
CoDEmanX
committed
if active_object:
active_element = active_object.\
matrix_world.to_translation()
CoDEmanX
committed
# On Undo/Redo scene hash value is changed ->
# -> the monitor tries to update the CSU ->
# -> object.mode_set seem to somehow conflict
# with Undo/Redo mechanisms.
elif active_object and active_object.data and \
(context_mode in {
'EDIT_MESH', 'EDIT_METABALL',
'EDIT_CURVE', 'EDIT_SURFACE',
'EDIT_ARMATURE', 'POSE'}):
CoDEmanX
committed
CoDEmanX
committed
positions = []
normal = Vector((0, 0, 0))
CoDEmanX
committed
bm = bmesh.from_edit_mesh(active_object.data)
CoDEmanX
committed
if bm.select_history:
elem = bm.select_history[-1]
if isinstance(elem, bmesh.types.BMVert):
active_element = elem.co.copy()
else:
active_element = Vector()
for v in elem.verts:
active_element += v.co
active_element *= 1.0 / len(elem.verts)
CoDEmanX
committed
for v in bm.verts:
if v.select:
positions.append(v.co)
normal += v.normal
CoDEmanX
committed
# mimic Blender's behavior (as of now,
# order of selection is ignored)
if len(positions) == 2:
normal = positions[1] - positions[0]
elif len(positions) == 3:
a = positions[0] - positions[1]
b = positions[2] - positions[1]
normal = a.cross(b)
elif context_mode == 'EDIT_METABALL':
active_elem = active_object.data.elements.active
if active_elem:
active_element = active_elem.co.copy()
active_element = active_object.\
matrix_world * active_element
CoDEmanX
committed
# Currently there is no API for element.select
#for element in active_object.data.elements:
# if element.select:
# positions.append(element.co)
elif context_mode == 'EDIT_ARMATURE':
# active bone seems to have the same pivot
# as median of the selection
'''
active_bone = active_object.data.edit_bones.active
if active_bone:
active_element = active_bone.head + \
active_bone.tail
active_element = active_object.\
matrix_world * active_element
'''
CoDEmanX
committed
for bone in active_object.data.edit_bones:
if bone.select_head:
positions.append(bone.head)
if bone.select_tail:
positions.append(bone.tail)
elif context_mode == 'POSE':
active_bone = active_object.data.bones.active
if active_bone:
active_element = active_bone.\
matrix_local.translation.to_3d()
active_element = active_object.\
matrix_world * active_element
CoDEmanX
committed
# consider only topmost parents
bones = set()
for bone in active_object.data.bones:
if bone.select:
bones.add(bone)
CoDEmanX
committed
parents = set()
for bone in bones:
if not set(bone.parent_recursive).intersection(bones):
parents.add(bone)
CoDEmanX
committed
positions.append(bone.matrix_local.translation.to_3d())
else:
for spline in active_object.data.splines:
for point in spline.bezier_points:
if point.select_control_point:
positions.append(point.co)
else:
if point.select_left_handle:
positions.append(point.handle_left)
if point.select_right_handle:
positions.append(point.handle_right)
CoDEmanX
committed
n = None
nL = point.co - point.handle_left
nR = point.co - point.handle_right
#nL = point.handle_left.copy()
#nR = point.handle_right.copy()
if point.select_control_point:
n = nL + nR
elif point.select_left_handle or \
point.select_right_handle:
n = nL + nR
else:
if point.select_left_handle:
n = -nL
if point.select_right_handle:
n = nR
CoDEmanX
committed
if n is not None:
if n.length_squared < epsilon:
n = -nL
normal += n.normalized()
CoDEmanX
committed
for point in spline.points:
if point.select:
positions.append(point.co)
CoDEmanX
committed
if len(positions) != 0:
if normal.length_squared < epsilon:
normal = Vector((0, 0, 1))
normal.rotate(m)
normal.normalize()
CoDEmanX
committed
if (1.0 - abs(normal.z)) < epsilon:
t1 = Vector((1, 0, 0))
else:
t1 = Vector((0, 0, 1)).cross(normal)
t2 = t1.cross(normal)
normal_system = MatrixCompose(t1, t2, normal)
CoDEmanX
committed
median, bbox_center = calc_median_bbox_pivots(positions)
median = m * median
bbox_center = m * bbox_center
CoDEmanX
committed
# Currently I don't know how to get active mesh element
if active_element is None:
if context_mode == 'EDIT_ARMATURE':
# Somewhy EDIT_ARMATURE has such behavior
active_element = bbox_center
else:
active_element = median
else:
if active_element is None:
active_element = active_object.\
matrix_world.to_translation()
CoDEmanX
committed
median = active_element
bbox_center = active_element
CoDEmanX
committed
normal_system = active_object.matrix_world.to_3x3()
normal_system.col[0].normalize()
normal_system.col[1].normalize()
normal_system.col[2].normalize()
else:
# paint/sculpt, etc.?
particle = View3D_Object(active_object)
particles.append(particle)
CoDEmanX
committed
if active_object:
active_element = active_object.\
matrix_world.to_translation()
CoDEmanX
committed
cursor_pos = get_cursor_location(v3d=space_data)
CoDEmanX
committed
#elif area_type == 'IMAGE_EDITOR':
# currently there is no way to get UV editor's
# offset (and maybe some other parameters
# required to implement these operators)
#cursor_pos = space_data.uv_editor.cursor_location
CoDEmanX
committed
#elif area_type == 'EMPTY':
#elif area_type == 'GRAPH_EDITOR':
#elif area_type == 'OUTLINER':
#elif area_type == 'PROPERTIES':
#elif area_type == 'FILE_BROWSER':
#elif area_type == 'INFO':
#elif area_type == 'SEQUENCE_EDITOR':
#elif area_type == 'TEXT_EDITOR':
#elif area_type == 'AUDIO_WINDOW':
#elif area_type == 'DOPESHEET_EDITOR':
#elif area_type == 'NLA_EDITOR':
#elif area_type == 'SCRIPTS_WINDOW':
#elif area_type == 'TIMELINE':
#elif area_type == 'NODE_EDITOR':
#elif area_type == 'LOGIC_EDITOR':
#elif area_type == 'CONSOLE':
#elif area_type == 'USER_PREFERENCES':
CoDEmanX
committed
else:
print("gather_particles() not implemented for '{}'".\
format(area_type))
return None, None
CoDEmanX
committed
CoDEmanX
committed
if cursor_pos:
pivots['CURSOR'] = cursor_pos.copy()
CoDEmanX
committed
if active_element:
# in v3d: ACTIVE_ELEMENT
pivots['ACTIVE'] = active_element.copy()
CoDEmanX
committed
if (len(particles) != 0) and (median is None):
positions = (p.get_location() for p in particles)
median, bbox_center = calc_median_bbox_pivots(positions)
CoDEmanX
committed
if median:
# in v3d: MEDIAN_POINT, in UV editor: MEDIAN
pivots['MEDIAN'] = median.copy()
# in v3d: BOUNDING_BOX_CENTER, in UV editor: CENTER
pivots['CENTER'] = bbox_center.copy()
CoDEmanX
committed
csu = CoordinateSystemUtility(scene, space_data, region_data, \
pivots, normal_system, view_layer)
CoDEmanX
committed
return particles, csu
def calc_median_bbox_pivots(positions):
median = None # pos can be 3D or 2D
bbox = [None, None]
CoDEmanX
committed
n = 0
for pos in positions:
extend_bbox(bbox, pos)
try:
median += pos
except:
median = pos.copy()
n += 1
CoDEmanX
committed
median = median / n
bbox_center = (Vector(bbox[0]) + Vector(bbox[1])) * 0.5
CoDEmanX
committed
return median, bbox_center
def extend_bbox(bbox, pos):
try:
bbox[0] = tuple(min(e0, e1) for e0, e1 in zip(bbox[0], pos))
bbox[1] = tuple(max(e0, e1) for e0, e1 in zip(bbox[1], pos))
except:
bbox[0] = tuple(pos)
bbox[1] = tuple(pos)
# ====== COORDINATE SYSTEM UTILITY ====== #
class CoordinateSystemUtility:
pivot_name_map = {
'CENTER':'CENTER',
'BOUNDING_BOX_CENTER':'CENTER',
'MEDIAN':'MEDIAN',
'MEDIAN_POINT':'MEDIAN',
CoDEmanX
committed
'CURSOR':'CURSOR',
'INDIVIDUAL_ORIGINS':'INDIVIDUAL',
'ACTIVE_ELEMENT':'ACTIVE',
'WORLD':'WORLD',
'SURFACE':'SURFACE', # ?
'BOOKMARK':'BOOKMARK',
}
pivot_v3d_map = {
'CENTER':'BOUNDING_BOX_CENTER',
'MEDIAN':'MEDIAN_POINT',
CoDEmanX
committed
'CURSOR':'CURSOR',
'INDIVIDUAL':'INDIVIDUAL_ORIGINS',
'ACTIVE':'ACTIVE_ELEMENT',
}
CoDEmanX
committed
def __init__(self, scene, space_data, region_data, \
pivots, normal_system, view_layer):
self.space_data = space_data
self.region_data = region_data
CoDEmanX
committed
if space_data.type == 'VIEW_3D':
self.pivot_map_inv = self.pivot_v3d_map
CoDEmanX
committed
scene, space_data, region_data, view_layer)
CoDEmanX
committed
CoDEmanX
committed
# Assigned by caller (for cursor or selection)
self.source_pos = None
self.source_rot = None
self.source_scale = None
CoDEmanX
committed
def set_orientation(self, name):
self.tou.set(name)
CoDEmanX
committed
def set_pivot(self, pivot):
self.space_data.pivot_point = self.pivot_map_inv[pivot]
CoDEmanX
committed
def get_pivot_name(self, name=None, relative=None, raw=False):
pivot = self.pivot_name_map[self.space_data.pivot_point]
if raw:
return pivot
CoDEmanX
committed
CoDEmanX
committed
if relative is None:
settings = find_settings()
tfm_opts = settings.transform_options
relative = tfm_opts.use_relative_coords
CoDEmanX
committed
if relative:
pivot = "RELATIVE"
elif (name == 'GLOBAL') or (pivot == 'WORLD'):
pivot = 'WORLD'
elif (name == "Surface") or (pivot == 'SURFACE'):
pivot = "SURFACE"
CoDEmanX
committed
CoDEmanX
committed
def get_origin(self, name=None, relative=None, pivot=None):
if not pivot:
pivot = self.get_pivot_name(name, relative)
CoDEmanX
committed
if relative or (pivot == "RELATIVE"):
# "relative" parameter overrides "pivot"
return self.source_pos
elif pivot == 'WORLD':
return Vector()
elif pivot == "SURFACE":
runtime_settings = find_runtime_settings()
return Vector(runtime_settings.surface_pos)
else:
if pivot == 'INDIVIDUAL':
pivot = 'MEDIAN'
CoDEmanX
committed
#if pivot == 'ACTIVE':
# print(self.pivots)
CoDEmanX
committed
try:
return self.pivots[pivot]
except:
return Vector()
CoDEmanX
committed
def get_matrix(self, name=None, relative=None, pivot=None):
if not name:
name = self.tou.get()
CoDEmanX
committed
CoDEmanX
committed
if isinstance(pivot, Vector):
pos = pivot
else:
pos = self.get_origin(name, relative, pivot)
CoDEmanX
committed
return to_matrix4x4(matrix, pos)
# ====== TRANSFORM ORIENTATION UTILITIES ====== #
class TransformOrientationUtility:
special_systems = {"Surface", "Scaled"}
predefined_systems = {
'GLOBAL', 'LOCAL', 'VIEW', 'NORMAL', 'GIMBAL',
"Scaled", "Surface",
}
CoDEmanX
committed
def __init__(self, scene, v3d, rv3d, vwly):
self.scene = scene
self.v3d = v3d
self.rv3d = rv3d
CoDEmanX
committed
self.custom_systems = [item for item in scene.orientations \
if item.name not in self.special_systems]
CoDEmanX
committed
self.is_custom = False
self.custom_id = -1
CoDEmanX
committed
# This is calculated elsewhere
self.normal_system = None
CoDEmanX
committed
CoDEmanX
committed
def get(self):
return self.transform_orientation
CoDEmanX
committed
def get_title(self):
if self.is_custom:
return self.transform_orientation
CoDEmanX
committed
name = self.transform_orientation
return name[:1].upper() + name[1:].lower()
CoDEmanX
committed
def set(self, name, set_v3d=True):
if isinstance(name, int):
n = len(self.custom_systems)
if n == 0:
# No custom systems, do nothing
return
CoDEmanX
committed
CoDEmanX
committed
if self.is_custom:
# If already custom, switch to next custom system
self.custom_id = (self.custom_id + increment) % n
CoDEmanX
committed
CoDEmanX
committed
name = self.custom_systems[self.custom_id].name
else:
self.is_custom = name not in self.predefined_systems
CoDEmanX
committed
if self.is_custom:
self.custom_id = next((i for i, v in \
enumerate(self.custom_systems) if v.name == name), -1)
CoDEmanX
committed
if name in self.special_systems:
# Ensure such system exists
self.get_custom(name)