From 9bc76f9902e756c149695b2b3d86ebe2d690ae65 Mon Sep 17 00:00:00 2001 From: lijenstina <lijenstina@gmail.com> Date: Fri, 7 Apr 2017 16:22:32 +0200 Subject: [PATCH] LoopTools: Update panel Rename, Cleanup Bumped version to 4.6.7 As a part of the task T50726: Update the Panel rename code to more generic one PEP8 cleanup: Tuple imports and use them for class types Consistent Scene props definitions Update url link --- mesh_looptools.py | 2479 +++++++++++++++++++++++++-------------------- 1 file changed, 1377 insertions(+), 1102 deletions(-) diff --git a/mesh_looptools.py b/mesh_looptools.py index b05c78300..e3fd302b3 100644 --- a/mesh_looptools.py +++ b/mesh_looptools.py @@ -19,12 +19,12 @@ bl_info = { "name": "LoopTools", "author": "Bart Crouch", - "version": (4, 6, 6), + "version": (4, 6, 7), "blender": (2, 72, 2), "location": "View3D > Toolbar and View3D > Specials (W-key)", "warning": "", "description": "Mesh modelling toolkit. Several tools to aid modelling", - "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/" + "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/" "Scripts/Modeling/LoopTools", "category": "Mesh", } @@ -36,12 +36,25 @@ import collections import mathutils import math from bpy_extras import view3d_utils - - -########################################## -####### General functions ################ -########################################## - +from bpy.types import ( + Operator, + Menu, + Panel, + PropertyGroup, + AddonPreferences, + ) +from bpy.props import ( + BoolProperty, + EnumProperty, + FloatProperty, + IntProperty, + PointerProperty, + StringProperty, + ) + +# ######################################## +# ##### General functions ################ +# ######################################## # used by all tools to improve speed on reruns Unlink looptools_cache = {} @@ -73,8 +86,8 @@ def cache_read(tool, object, bm, input_method, boundaries): return(False, False, False, False, False) if boundaries != looptools_cache[tool]["boundaries"]: return(False, False, False, False, False) - modifiers = [mod.name for mod in object.modifiers if mod.show_viewport \ - and mod.type == 'MIRROR'] + modifiers = [mod.name for mod in object.modifiers if mod.show_viewport and + mod.type == 'MIRROR'] if modifiers != looptools_cache[tool]["modifiers"]: return(False, False, False, False, False) input = [v.index for v in bm.verts if v.select and not v.hide] @@ -97,8 +110,8 @@ loops, derived, mapping): del looptools_cache[tool] # prepare values to be saved to cache input = [v.index for v in bm.verts if v.select and not v.hide] - modifiers = [mod.name for mod in object.modifiers if mod.show_viewport \ - and mod.type == 'MIRROR'] + modifiers = [mod.name for mod in object.modifiers if mod.show_viewport and + mod.type == 'MIRROR'] # update cache looptools_cache[tool] = {"input": input, "object": object.name, "input_method": input_method, "boundaries": boundaries, @@ -115,12 +128,12 @@ def calculate_cubic_splines(bm_mod, tknots, knots): for k in range(-1, -5, -1): if k - 1 < -len(knots): k += len(knots) - k_new1.append(knots[k-1]) + k_new1.append(knots[k - 1]) k_new2 = [] for k in range(4): if k + 1 > len(knots) - 1: k -= len(knots) - k_new2.append(knots[k+1]) + k_new2.append(knots[k + 1]) for k in k_new1: knots.insert(0, k) for k in k_new2: @@ -130,14 +143,14 @@ def calculate_cubic_splines(bm_mod, tknots, knots): for t in range(-1, -5, -1): if t - 1 < -len(tknots): t += len(tknots) - total1 += tknots[t] - tknots[t-1] + total1 += tknots[t] - tknots[t - 1] t_new1.append(tknots[0] - total1) t_new2 = [] total2 = 0 for t in range(4): if t + 1 > len(tknots) - 1: t -= len(tknots) - total2 += tknots[t+1] - tknots[t] + total2 += tknots[t + 1] - tknots[t] t_new2.append(tknots[-1] + total2) for t in t_new1: tknots.insert(0, t) @@ -158,39 +171,39 @@ def calculate_cubic_splines(bm_mod, tknots, knots): for i in locs: a.append(i[j]) h = [] - for i in range(n-1): - if x[i+1] - x[i] == 0: + for i in range(n - 1): + if x[i + 1] - x[i] == 0: h.append(1e-8) else: - h.append(x[i+1] - x[i]) + h.append(x[i + 1] - x[i]) q = [False] - for i in range(1, n-1): - q.append(3/h[i]*(a[i+1]-a[i]) - 3/h[i-1]*(a[i]-a[i-1])) + for i in range(1, n - 1): + q.append(3 / h[i] * (a[i + 1] - a[i]) - 3 / h[i - 1] * (a[i] - a[i - 1])) l = [1.0] u = [0.0] z = [0.0] - for i in range(1, n-1): - l.append(2*(x[i+1]-x[i-1]) - h[i-1]*u[i-1]) + for i in range(1, n - 1): + l.append(2 * (x[i + 1] - x[i - 1]) - h[i - 1] * u[i - 1]) if l[i] == 0: l[i] = 1e-8 u.append(h[i] / l[i]) - z.append((q[i] - h[i-1] * z[i-1]) / l[i]) + z.append((q[i] - h[i - 1] * z[i - 1]) / l[i]) l.append(1.0) z.append(0.0) - b = [False for i in range(n-1)] + b = [False for i in range(n - 1)] c = [False for i in range(n)] - d = [False for i in range(n-1)] - c[n-1] = 0.0 - for i in range(n-2, -1, -1): - c[i] = z[i] - u[i]*c[i+1] - b[i] = (a[i+1]-a[i])/h[i] - h[i]*(c[i+1]+2*c[i])/3 - d[i] = (c[i+1]-c[i]) / (3*h[i]) - for i in range(n-1): + d = [False for i in range(n - 1)] + c[n - 1] = 0.0 + for i in range(n - 2, -1, -1): + c[i] = z[i] - u[i] * c[i + 1] + b[i] = (a[i + 1] - a[i]) / h[i] - h[i] * (c[i + 1] + 2 * c[i]) / 3 + d[i] = (c[i + 1] - c[i]) / (3 * h[i]) + for i in range(n - 1): result.append([a[i], b[i], c[i], d[i], x[i]]) splines = [] - for i in range(len(knots)-1): - splines.append([result[i], result[i+n-1], result[i+(n-1)*2]]) - if circular: # cleaning up after hack + for i in range(len(knots) - 1): + splines.append([result[i], result[i + n - 1], result[i + (n - 1) * 2]]) + if circular: # cleaning up after hack knots = knots[4:-4] tknots = tknots[4:-4] @@ -200,13 +213,13 @@ def calculate_cubic_splines(bm_mod, tknots, knots): # calculates linear splines through all given knots def calculate_linear_splines(bm_mod, tknots, knots): splines = [] - for i in range(len(knots)-1): + for i in range(len(knots) - 1): a = bm_mod.verts[knots[i]].co - b = bm_mod.verts[knots[i+1]].co - d = b-a + b = bm_mod.verts[knots[i + 1]].co + d = b - a t = tknots[i] - u = tknots[i+1]-t - splines.append([a, d, t, u]) # [locStart, locDif, tStart, tDif] + u = tknots[i + 1] - t + splines.append([a, d, t, u]) # [locStart, locDif, tStart, tDif] return(splines) @@ -230,15 +243,15 @@ def calculate_plane(bm_mod, loop, method="best_fit", object=False): (0.0, 0.0, 0.0), )) for loc in locs: - mat[0][0] += (loc[0]-x)**2 - mat[1][0] += (loc[0]-x)*(loc[1]-y) - mat[2][0] += (loc[0]-x)*(loc[2]-z) - mat[0][1] += (loc[1]-y)*(loc[0]-x) - mat[1][1] += (loc[1]-y)**2 - mat[2][1] += (loc[1]-y)*(loc[2]-z) - mat[0][2] += (loc[2]-z)*(loc[0]-x) - mat[1][2] += (loc[2]-z)*(loc[1]-y) - mat[2][2] += (loc[2]-z)**2 + mat[0][0] += (loc[0] - x) ** 2 + mat[1][0] += (loc[0] - x) * (loc[1] - y) + mat[2][0] += (loc[0] - x) * (loc[2] - z) + mat[0][1] += (loc[1] - y) * (loc[0] - x) + mat[1][1] += (loc[1] - y) ** 2 + mat[2][1] += (loc[1] - y) * (loc[2] - z) + mat[0][2] += (loc[2] - z) * (loc[0] - x) + mat[1][2] += (loc[2] - z) * (loc[1] - y) + mat[2][2] += (loc[2] - z) ** 2 # calculating the normal to the plane normal = False @@ -297,7 +310,7 @@ def calculate_plane(bm_mod, loop, method="best_fit", object=False): def calculate_splines(interpolation, bm_mod, tknots, knots): if interpolation == 'cubic': splines = calculate_cubic_splines(bm_mod, tknots, knots[:]) - else: # interpolations == 'linear' + else: # interpolations == 'linear' splines = calculate_linear_splines(bm_mod, tknots, knots[:]) return(splines) @@ -322,8 +335,7 @@ def check_loops(loops, mapping, bm_mod): # vertices can not all be at the same location stacked = True for i in range(len(loop) - 1): - if (bm_mod.verts[loop[i]].co - \ - bm_mod.verts[loop[i+1]].co).length > 1e-6: + if (bm_mod.verts[loop[i]].co - bm_mod.verts[loop[i + 1]].co).length > 1e-6: stacked = False break if stacked: @@ -336,8 +348,7 @@ def check_loops(loops, mapping, bm_mod): # input: bmesh, output: dict with the edge-key as key and face-index as value def dict_edge_faces(bm): - edge_faces = dict([[edgekey(edge), []] for edge in bm.edges if \ - not edge.hide]) + edge_faces = dict([[edgekey(edge), []] for edge in bm.edges if not edge.hide]) for face in bm.faces: if face.hide: continue @@ -352,8 +363,7 @@ def dict_face_faces(bm, edge_faces=False): if not edge_faces: edge_faces = dict_edge_faces(bm) - connected_faces = dict([[face.index, []] for face in bm.faces if \ - not face.hide]) + connected_faces = dict([[face.index, []] for face in bm.faces if not face.hide]) for face in bm.faces: if face.hide: continue @@ -397,9 +407,9 @@ def dict_vert_verts(edge_keys): for ek in edge_keys: for i in range(2): if ek[i] in vert_verts: - vert_verts[ek[i]].append(ek[1-i]) + vert_verts[ek[i]].append(ek[1 - i]) else: - vert_verts[ek[i]] = [ek[1-i]] + vert_verts[ek[i]] = [ek[1 - i]] return(vert_verts) @@ -411,8 +421,7 @@ def edgekey(edge): # returns the edgekeys of a bmesh face def face_edgekeys(face): - return([tuple(sorted([edge.verts[0].index, edge.verts[1].index])) for \ - edge in face.edges]) + return([tuple(sorted([edge.verts[0].index, edge.verts[1].index])) for edge in face.edges]) # calculate input loops @@ -421,8 +430,7 @@ def get_connected_input(object, bm, scene, input): derived, bm_mod = get_derived_bmesh(object, bm, scene) # calculate selected loops - edge_keys = [edgekey(edge) for edge in bm_mod.edges if \ - edge.select and not edge.hide] + edge_keys = [edgekey(edge) for edge in bm_mod.edges if edge.select and not edge.hide] loops = get_connected_selections(edge_keys) # if only selected loops are needed, we're done @@ -511,8 +519,7 @@ def get_derived_bmesh(object, bm, scene): if 'MIRROR' in [mod.type for mod in object.modifiers if mod.show_viewport]: derived = True # disable other modifiers - show_viewport = [mod.name for mod in object.modifiers if \ - mod.show_viewport] + show_viewport = [mod.name for mod in object.modifiers if mod.show_viewport] for mod in object.modifiers: if mod.type != 'MIRROR': mod.show_viewport = False @@ -555,15 +562,14 @@ def get_mapping(derived, bm, bm_mod, single_vertices, full_search, loops): if (v.co - v_mod.co).length < 1e-6: mapping[v_mod.index] = v.index break - real_singles = [v_real for v_real in mapping.values() if v_real>-1] + real_singles = [v_real for v_real in mapping.values() if v_real > -1] verts_indices = [vert.index for vert in verts] - for face in [face for face in bm.faces if not face.select \ - and not face.hide]: + for face in [face for face in bm.faces if not face.select and not face.hide]: for vert in face.verts: if vert.index in real_singles: for v in face.verts: - if not v.index in verts_indices: + if v.index not in verts_indices: if v not in verts: verts.append(v) break @@ -596,12 +602,12 @@ def matrix_determinant(m): # custom matrix inversion, to provide higher precision than the built-in one def matrix_invert(m): r = mathutils.Matrix(( - (m[1][1]*m[2][2] - m[1][2]*m[2][1], m[0][2]*m[2][1] - m[0][1]*m[2][2], - m[0][1]*m[1][2] - m[0][2]*m[1][1]), - (m[1][2]*m[2][0] - m[1][0]*m[2][2], m[0][0]*m[2][2] - m[0][2]*m[2][0], - m[0][2]*m[1][0] - m[0][0]*m[1][2]), - (m[1][0]*m[2][1] - m[1][1]*m[2][0], m[0][1]*m[2][0] - m[0][0]*m[2][1], - m[0][0]*m[1][1] - m[0][1]*m[1][0]))) + (m[1][1] * m[2][2] - m[1][2] * m[2][1], m[0][2] * m[2][1] - m[0][1] * m[2][2], + m[0][1] * m[1][2] - m[0][2] * m[1][1]), + (m[1][2] * m[2][0] - m[1][0] * m[2][2], m[0][0] * m[2][2] - m[0][2] * m[2][0], + m[0][2] * m[1][0] - m[0][0] * m[1][2]), + (m[1][0] * m[2][1] - m[1][1] * m[2][0], m[0][1] * m[2][0] - m[0][0] * m[2][1], + m[0][0] * m[1][1] - m[0][1] * m[1][0]))) return (r * (1 / matrix_determinant(m))) @@ -614,9 +620,9 @@ def get_parallel_loops(bm_mod, loops): # turn vertex loops into edge loops edgeloops = [] for loop in loops: - edgeloop = [[sorted([loop[0][i], loop[0][i+1]]) for i in \ - range(len(loop[0])-1)], loop[1]] - if loop[1]: # circular + edgeloop = [[sorted([loop[0][i], loop[0][i + 1]]) for i in + range(len(loop[0]) - 1)], loop[1]] + if loop[1]: # circular edgeloop[0].append(sorted([loop[0][-1], loop[0][0]])) edgeloops.append(edgeloop[:]) # variables to keep track while iterating @@ -641,7 +647,7 @@ def get_parallel_loops(bm_mod, loops): for i in newloops[-1]: i = tuple(i) forbidden_side = False - if not i in edge_faces: + if i not in edge_faces: # weird input with branches has_branches = True break @@ -709,9 +715,9 @@ def get_parallel_loops(bm_mod, loops): for edgeloop in all_edgeloops: loop = [] # grow loop by comparing vertices between consecutive edge-keys - for i in range(len(edgeloop)-1): + for i in range(len(edgeloop) - 1): for vert in range(2): - if edgeloop[i][vert] in edgeloop[i+1]: + if edgeloop[i][vert] in edgeloop[i + 1]: loop.append(edgeloop[i][vert]) break if loop: @@ -767,7 +773,7 @@ def move_verts(object, bm, mapping, move, lock, influence): elif orientation == 'VIEW': mat = bpy.context.region_data.view_matrix.copy() * \ object.matrix_world.copy() - else: # orientation == 'GLOBAL' + else: # orientation == 'GLOBAL' mat = object.matrix_world.copy() mat_inv = mat.inverted() @@ -791,8 +797,8 @@ def move_verts(object, bm, mapping, move, lock, influence): if influence < 0: new_loc = loc else: - new_loc = loc*(influence/100) + \ - bm.verts[index].co*((100-influence)/100) + new_loc = loc * (influence / 100) + \ + bm.verts[index].co * ((100 - influence) / 100) bm.verts[index].co = new_loc bm.normal_update() object.data.update() @@ -830,9 +836,9 @@ def terminate(global_undo): bpy.context.user_preferences.edit.use_global_undo = global_undo -########################################## -####### Bridge functions ################# -########################################## +# ######################################## +# ##### Bridge functions ################# +# ######################################## # calculate a cubic spline through the middle section of 4 given coordinates def bridge_calculate_cubic_spline(bm, coordinates): @@ -845,27 +851,27 @@ def bridge_calculate_cubic_spline(bm, coordinates): a.append(float(i[j])) h = [] for i in range(3): - h.append(x[i+1]-x[i]) + h.append(x[i + 1] - x[i]) q = [False] - for i in range(1,3): - q.append(3.0/h[i]*(a[i+1]-a[i])-3.0/h[i-1]*(a[i]-a[i-1])) + for i in range(1, 3): + q.append(3.0 / h[i] * (a[i + 1] - a[i]) - 3.0 / h[i - 1] * (a[i] - a[i - 1])) l = [1.0] u = [0.0] z = [0.0] - for i in range(1,3): - l.append(2.0*(x[i+1]-x[i-1])-h[i-1]*u[i-1]) - u.append(h[i]/l[i]) - z.append((q[i]-h[i-1]*z[i-1])/l[i]) + for i in range(1, 3): + l.append(2.0 * (x[i + 1] - x[i - 1]) - h[i - 1] * u[i - 1]) + u.append(h[i] / l[i]) + z.append((q[i] - h[i - 1] * z[i - 1]) / l[i]) l.append(1.0) z.append(0.0) b = [False for i in range(3)] c = [False for i in range(4)] d = [False for i in range(3)] c[3] = 0.0 - for i in range(2,-1,-1): - c[i] = z[i]-u[i]*c[i+1] - b[i] = (a[i+1]-a[i])/h[i]-h[i]*(c[i+1]+2.0*c[i])/3.0 - d[i] = (c[i+1]-c[i])/(3.0*h[i]) + for i in range(2, -1, -1): + c[i] = z[i] - u[i] * c[i + 1] + b[i] = (a[i + 1] - a[i]) / h[i] - h[i] * (c[i + 1] + 2.0 * c[i]) / 3.0 + d[i] = (c[i + 1] - c[i]) / (3.0 * h[i]) for i in range(3): result.append([a[i], b[i], c[i], d[i], x[i]]) spline = [result[1], result[4], result[7]] @@ -885,22 +891,22 @@ interpolation, cubic_strength, min_width, max_vert_index): v1 = bm.verts[lines[line][0]].co v2 = bm.verts[lines[line][1]].co if interpolation == 'linear': - return v1 + (segment/segments) * (v2-v1) - else: # interpolation == 'cubic' - m = (segment/segments) - ax,bx,cx,dx,tx = splines[line][0] - x = ax+bx*m+cx*m**2+dx*m**3 - ay,by,cy,dy,ty = splines[line][1] - y = ay+by*m+cy*m**2+dy*m**3 - az,bz,cz,dz,tz = splines[line][2] - z = az+bz*m+cz*m**2+dz*m**3 + return v1 + (segment / segments) * (v2 - v1) + else: # interpolation == 'cubic' + m = (segment / segments) + ax, bx, cx, dx, tx = splines[line][0] + x = ax + bx * m + cx * m ** 2 + dx * m ** 3 + ay, by, cy, dy, ty = splines[line][1] + y = ay + by * m + cy * m ** 2 + dy * m ** 3 + az, bz, cz, dz, tz = splines[line][2] + z = az + bz * m + cz * m ** 2 + dz * m ** 3 return mathutils.Vector((x, y, z)) # no interpolation needed if segments == 1: for i, line in enumerate(lines): - if i < len(lines)-1: - faces.append([line[0], lines[i+1][0], lines[i+1][1], line[1]]) + if i < len(lines) - 1: + faces.append([line[0], lines[i + 1][0], lines[i + 1][1], line[1]]) # more than 1 segment, interpolate else: # calculate splines (if necessary) once, so no recalculations needed @@ -909,64 +915,64 @@ interpolation, cubic_strength, min_width, max_vert_index): for line in lines: v1 = bm.verts[line[0]].co v2 = bm.verts[line[1]].co - size = (v2-v1).length * cubic_strength + size = (v2 - v1).length * cubic_strength splines.append(bridge_calculate_cubic_spline(bm, - [v1+size*vertex_normals[line[0]], v1, v2, - v2+size*vertex_normals[line[1]]])) + [v1 + size * vertex_normals[line[0]], v1, v2, + v2 + size * vertex_normals[line[1]]])) else: splines = False # create starting situation virtual_width = [(bm.verts[lines[i][0]].co - - bm.verts[lines[i+1][0]].co).length for i - in range(len(lines)-1)] + bm.verts[lines[i + 1][0]].co).length for i + in range(len(lines) - 1)] new_verts = [get_location(0, seg, splines) for seg in range(1, segments)] - first_line_indices = [i for i in range(max_vert_index+1, - max_vert_index+segments)] + first_line_indices = [i for i in range(max_vert_index + 1, + max_vert_index + segments)] - prev_verts = new_verts[:] # vertex locations of verts on previous line + prev_verts = new_verts[:] # vertex locations of verts on previous line prev_vert_indices = first_line_indices[:] - max_vert_index += segments - 1 # highest vertex index in virtual mesh - next_verts = [] # vertex locations of verts on current line + max_vert_index += segments - 1 # highest vertex index in virtual mesh + next_verts = [] # vertex locations of verts on current line next_vert_indices = [] for i, line in enumerate(lines): - if i < len(lines)-1: + if i < len(lines) - 1: v1 = line[0] - v2 = lines[i+1][0] + v2 = lines[i + 1][0] end_face = True for seg in range(1, segments): - loc1 = prev_verts[seg-1] - loc2 = get_location(i+1, seg, splines) - if (loc1-loc2).length < (min_width/100)*virtual_width[i] \ - and line[1]==lines[i+1][1]: + loc1 = prev_verts[seg - 1] + loc2 = get_location(i + 1, seg, splines) + if (loc1 - loc2).length < (min_width / 100) * virtual_width[i] \ + and line[1] == lines[i + 1][1]: # triangle, no new vertex - faces.append([v1, v2, prev_vert_indices[seg-1], - prev_vert_indices[seg-1]]) - next_verts += prev_verts[seg-1:] - next_vert_indices += prev_vert_indices[seg-1:] + faces.append([v1, v2, prev_vert_indices[seg - 1], + prev_vert_indices[seg - 1]]) + next_verts += prev_verts[seg - 1:] + next_vert_indices += prev_vert_indices[seg - 1:] end_face = False break else: - if i == len(lines)-2 and lines[0] == lines[-1]: + if i == len(lines) - 2 and lines[0] == lines[-1]: # quad with first line, no new vertex - faces.append([v1, v2, first_line_indices[seg-1], - prev_vert_indices[seg-1]]) - v2 = first_line_indices[seg-1] - v1 = prev_vert_indices[seg-1] + faces.append([v1, v2, first_line_indices[seg - 1], + prev_vert_indices[seg - 1]]) + v2 = first_line_indices[seg - 1] + v1 = prev_vert_indices[seg - 1] else: # quad, add new vertex max_vert_index += 1 faces.append([v1, v2, max_vert_index, - prev_vert_indices[seg-1]]) + prev_vert_indices[seg - 1]]) v2 = max_vert_index - v1 = prev_vert_indices[seg-1] + v1 = prev_vert_indices[seg - 1] new_verts.append(loc2) next_verts.append(loc2) next_vert_indices.append(max_vert_index) if end_face: - faces.append([v1, v2, lines[i+1][1], line[1]]) + faces.append([v1, v2, lines[i + 1][1], line[1]]) prev_verts = next_verts[:] prev_vert_indices = next_vert_indices[:] @@ -1010,15 +1016,15 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse): (0.0, 0.0, 0.0))) x, y, z = centers[i] for loc in [bm.verts[vertex].co for vertex in loop]: - mat[0][0] += (loc[0]-x)**2 - mat[1][0] += (loc[0]-x)*(loc[1]-y) - mat[2][0] += (loc[0]-x)*(loc[2]-z) - mat[0][1] += (loc[1]-y)*(loc[0]-x) - mat[1][1] += (loc[1]-y)**2 - mat[2][1] += (loc[1]-y)*(loc[2]-z) - mat[0][2] += (loc[2]-z)*(loc[0]-x) - mat[1][2] += (loc[2]-z)*(loc[1]-y) - mat[2][2] += (loc[2]-z)**2 + mat[0][0] += (loc[0] - x) ** 2 + mat[1][0] += (loc[0] - x) * (loc[1] - y) + mat[2][0] += (loc[0] - x) * (loc[2] - z) + mat[0][1] += (loc[1] - y) * (loc[0] - x) + mat[1][1] += (loc[1] - y) ** 2 + mat[2][1] += (loc[1] - y) * (loc[2] - z) + mat[0][2] += (loc[2] - z) * (loc[0] - x) + mat[1][2] += (loc[2] - z) * (loc[1] - y) + mat[2][2] += (loc[2] - z) ** 2 # plane normal normal = False if sum(mat[0]) < 1e-6 or sum(mat[1]) < 1e-6 or sum(mat[2]) < 1e-6: @@ -1037,9 +1043,9 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse): itermax = 500 iter = 0 vec = mathutils.Vector((1.0, 1.0, 1.0)) - vec2 = (mat * vec)/(mat * vec).length - while vec != vec2 and iter<itermax: - iter+=1 + vec2 = (mat * vec) / (mat * vec).length + while vec != vec2 and iter < itermax: + iter += 1 vec = vec2 vec2 = mat * vec if vec2.length != 0: @@ -1078,24 +1084,29 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse): i, vertex in enumerate(loop1)] dif_angles.sort() if len(loop1) != len(loop2): - angle_limit = dif_angles[0][0] * 1.2 # 20% margin - dif_angles = [[(bm.verts[loop2[0]].co - \ - bm.verts[loop1[index]].co).length, angle, index] for \ - angle, distance, index in dif_angles if angle <= angle_limit] + angle_limit = dif_angles[0][0] * 1.2 # 20% margin + dif_angles = [ + [(bm.verts[loop2[0]].co - + bm.verts[loop1[index]].co).length, angle, index] for + angle, distance, index in dif_angles if angle <= angle_limit + ] dif_angles.sort() loop1 = loop1[dif_angles[0][2]:] + loop1[:dif_angles[0][2]] # have both loops face the same way if normal_plurity and not circular: - second_to_first, second_to_second, second_to_last = \ - [(bm.verts[loop1[1]].co - center1).\ - angle(bm.verts[loop2[i]].co - center2) for i in [0, 1, -1]] - last_to_first, last_to_second = [(bm.verts[loop1[-1]].co - \ - center1).angle(bm.verts[loop2[i]].co - center2) for \ - i in [0, 1]] - if (min(last_to_first, last_to_second)*1.1 < min(second_to_first, \ - second_to_second)) or (loop2_circular and second_to_last*1.1 < \ - min(second_to_first, second_to_second)): + second_to_first, second_to_second, second_to_last = [ + (bm.verts[loop1[1]].co - center1).angle( + bm.verts[loop2[i]].co - center2) for i in [0, 1, -1] + ] + last_to_first, last_to_second = [ + (bm.verts[loop1[-1]].co - + center1).angle(bm.verts[loop2[i]].co - center2) for + i in [0, 1] + ] + if (min(last_to_first, last_to_second) * 1.1 < min(second_to_first, + second_to_second)) or (loop2_circular and second_to_last * 1.1 < + min(second_to_first, second_to_second)): loop1.reverse() if circular: loop1 = [loop1[-1]] + loop1[:-1] @@ -1104,9 +1115,9 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse): cross(bm.verts[loop1[1]].co - center1).angle(normals[0], 0) target_angle = (bm.verts[loop2[0]].co - center2).\ cross(bm.verts[loop2[1]].co - center2).angle(normals[1], 0) - limit = 1.5707964 # 0.5*pi, 90 degrees - if not ((angle > limit and target_angle > limit) or \ - (angle < limit and target_angle < limit)): + limit = 1.5707964 # 0.5*pi, 90 degrees + if not ((angle > limit and target_angle > limit) or + (angle < limit and target_angle < limit)): loop1.reverse() if circular: loop1 = [loop1[-1]] + loop1[:-1] @@ -1120,7 +1131,7 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse): # manual override if twist: if abs(twist) < len(loop1): - loop1 = loop1[twist:]+loop1[:twist] + loop1 = loop1[twist:] + loop1[:twist] if reverse: loop1.reverse() @@ -1138,7 +1149,7 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse): # manual override if twist: if abs(twist) < len(loop1): - loop1 = loop1[twist:]+loop1[:twist] + loop1 = loop1[twist:] + loop1[:twist] if reverse: loop1.reverse() @@ -1149,9 +1160,10 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse): if len(loop1) - shifting < len(loop2): shifting = False break - to_last, to_first = [(rotation_matrix * - (bm.verts[loop1[-1]].co - center1)).angle((bm.\ - verts[loop2[i]].co - center2), 0) for i in [-1, 0]] + to_last, to_first = [ + (rotation_matrix * (bm.verts[loop1[-1]].co - center1)).angle( + (bm.verts[loop2[i]].co - center2), 0) for i in [-1, 0] + ] if to_first < to_last: loop1 = [loop1[-1]] + loop1[:-1] shifting += 1 @@ -1171,16 +1183,16 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse): lines.append([loop1[i], loop2[i]]) # shortest edge algorithm - else: # mode == 'shortest' + else: # mode == 'shortest' lines.append([loop1[0], loop2[0]]) prev_vert2 = 0 - for i in range(len(loop1) -1): + for i in range(len(loop1) - 1): if prev_vert2 == len(loop2) - 1 and not loop2_circular: # force triangles, reached end of loop2 tri, quad = 0, 1 elif prev_vert2 == len(loop2) - 1 and loop2_circular: # at end of loop2, but circular, so check with first vert - tri, quad = [(bm.verts[loop1[i+1]].co - + tri, quad = [(bm.verts[loop1[i + 1]].co - bm.verts[loop2[j]].co).length for j in [prev_vert2, 0]] circle_full = 2 @@ -1190,22 +1202,22 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse): tri, quad = 1, 0 else: # calculate if tri or quad gives shortest edge - tri, quad = [(bm.verts[loop1[i+1]].co - + tri, quad = [(bm.verts[loop1[i + 1]].co - bm.verts[loop2[j]].co).length - for j in range(prev_vert2, prev_vert2+2)] + for j in range(prev_vert2, prev_vert2 + 2)] # triangle if tri < quad: - lines.append([loop1[i+1], loop2[prev_vert2]]) + lines.append([loop1[i + 1], loop2[prev_vert2]]) if circle_full == 2: circle_full = False # quad elif not circle_full: - lines.append([loop1[i+1], loop2[prev_vert2+1]]) + lines.append([loop1[i + 1], loop2[prev_vert2 + 1]]) prev_vert2 += 1 # quad to first vertex of loop2 else: - lines.append([loop1[i+1], loop2[0]]) + lines.append([loop1[i + 1], loop2[0]]) prev_vert2 = 0 circle_full = True @@ -1223,17 +1235,23 @@ def bridge_calculate_segments(bm, lines, loops, segments): return segments # edge lengths - average_edge_length = [(bm.verts[vertex].co - \ - bm.verts[loop[0][i+1]].co).length for loop in loops for \ - i, vertex in enumerate(loop[0][:-1])] + average_edge_length = [ + (bm.verts[vertex].co - + bm.verts[loop[0][i + 1]].co).length for loop in loops for + i, vertex in enumerate(loop[0][:-1]) + ] # closing edges of circular loops - average_edge_length += [(bm.verts[loop[0][-1]].co - \ - bm.verts[loop[0][0]].co).length for loop in loops if loop[1]] + average_edge_length += [ + (bm.verts[loop[0][-1]].co - + bm.verts[loop[0][0]].co).length for loop in loops if loop[1] + ] # average lengths average_edge_length = sum(average_edge_length) / len(average_edge_length) - average_bridge_length = sum([(bm.verts[v1].co - \ - bm.verts[v2].co).length for v1, v2 in lines]) / len(lines) + average_bridge_length = sum( + [(bm.verts[v1].co - + bm.verts[v2].co).length for v1, v2 in lines] + ) / len(lines) segments = max(1, round(average_bridge_length / average_edge_length)) @@ -1243,13 +1261,13 @@ def bridge_calculate_segments(bm, lines, loops, segments): # return dictionary with vertex index as key, and the normal vector as value def bridge_calculate_virtual_vertex_normals(bm, lines, loops, edge_faces, edgekey_to_edge): - if not edge_faces: # interpolation isn't set to cubic + if not edge_faces: # interpolation isn't set to cubic return False # pity reduce() isn't one of the basic functions in python anymore def average_vector_dictionary(dic): for key, vectors in dic.items(): - #if type(vectors) == type([]) and len(vectors) > 1: + # if type(vectors) == type([]) and len(vectors) > 1: if len(vectors) > 1: average = mathutils.Vector() for vector in vectors: @@ -1259,12 +1277,14 @@ edgekey_to_edge): return dic # get all edges of the loop - edges = [[edgekey_to_edge[tuple(sorted([loops[j][0][i], - loops[j][0][i+1]]))] for i in range(len(loops[j][0])-1)] for \ - j in [0,1]] + edges = [ + [edgekey_to_edge[tuple(sorted([loops[j][0][i], + loops[j][0][i + 1]]))] for i in range(len(loops[j][0]) - 1)] for + j in [0, 1] + ] edges = edges[0] + edges[1] for j in [0, 1]: - if loops[j][1]: # circular + if loops[j][1]: # circular edges.append(edgekey_to_edge[tuple(sorted([loops[j][0][0], loops[j][0][-1]]))]) @@ -1274,13 +1294,13 @@ edgekey_to_edge): edge_normal = face_normal x edge_vector vertex_normal = average(edge_normals) """ - vertex_normals = dict([(vertex, []) for vertex in loops[0][0]+loops[1][0]]) + vertex_normals = dict([(vertex, []) for vertex in loops[0][0] + loops[1][0]]) for edge in edges: - faces = edge_faces[edgekey(edge)] # valid faces connected to edge + faces = edge_faces[edgekey(edge)] # valid faces connected to edge if faces: # get edge coordinates - v1, v2 = [bm.verts[edgekey(edge)[i]].co for i in [0,1]] + v1, v2 = [bm.verts[edgekey(edge)[i]].co for i in [0, 1]] edge_vector = v1 - v2 if edge_vector.length < 1e-4: # zero-length edge, vertices at same location @@ -1322,8 +1342,9 @@ edgekey_to_edge): plane_normal = edge_vector x connection_vector vertex_normal = plane_normal x edge_vector """ - vertices = [vertex for vertex, normal in vertex_normals.items() if not \ - normal] + vertices = [ + vertex for vertex, normal in vertex_normals.items() if not normal + ] if vertices: # edge vectors connected to vertices @@ -1355,8 +1376,10 @@ edgekey_to_edge): connection_vectors[v2].append(new_vector) connections[v2].append(v1) connection_vectors = average_vector_dictionary(connection_vectors) - connection_vectors = dict([[vertex, vector[0]] if vector else \ - [vertex, []] for vertex, vector in connection_vectors.items()]) + connection_vectors = dict( + [[vertex, vector[0]] if vector else + [vertex, []] for vertex, vector in connection_vectors.items()] + ) for vertex, values in edge_vectors.items(): # vertex normal doesn't matter, just assign a random vector to it @@ -1378,13 +1401,11 @@ edgekey_to_edge): # can't do proper calculations, because of zero-length vector if not values: - if (connected_center - (bm.verts[vertex].co + \ - connection_vectors[vertex])).length < (connected_center - \ - (bm.verts[vertex].co - connection_vectors[vertex])).\ - length: + if (connected_center - (bm.verts[vertex].co + + connection_vectors[vertex])).length < (connected_center - + (bm.verts[vertex].co - connection_vectors[vertex])).length: connection_vectors[vertex].negate() - vertex_normals[vertex] = [connection_vectors[vertex].\ - normalized()] + vertex_normals[vertex] = [connection_vectors[vertex].normalized()] continue # calculate vertex normals using edge-vectors, @@ -1393,18 +1414,17 @@ edgekey_to_edge): plane_normal = edge_vector.cross(connection_vectors[vertex]) vertex_normal = edge_vector.cross(plane_normal) vertex_normal.length = 0.1 - if (connected_center - (bm.verts[vertex].co + \ - vertex_normal)).length < (connected_center - \ + if (connected_center - (bm.verts[vertex].co + + vertex_normal)).length < (connected_center - (bm.verts[vertex].co - vertex_normal)).length: - # make normal face the correct way + # make normal face the correct way vertex_normal.negate() vertex_normal.normalize() vertex_normals[vertex].append(vertex_normal) # average virtual vertex normals, based on all edges it's connected to vertex_normals = average_vector_dictionary(vertex_normals) - vertex_normals = dict([[vertex, vector[0]] for vertex, vector in \ - vertex_normals.items()]) + vertex_normals = dict([[vertex, vector[0]] for vertex, vector in vertex_normals.items()]) return(vertex_normals) @@ -1421,8 +1441,7 @@ def bridge_create_faces(object, bm, faces, twist): # have the normal point the correct way if twist < 0: [face.reverse() for face in faces] - faces = [face[2:]+face[:2] if face[0]==face[1] else face for \ - face in faces] + faces = [face[2:] + face[:2] if face[0] == face[1] else face for face in faces] # eekadoodle prevention for i in range(len(faces)): @@ -1439,7 +1458,7 @@ def bridge_create_faces(object, bm, faces, twist): for i in range(len(faces)): new_faces.append(bm.faces.new([bm.verts[v] for v in faces[i]])) bm.normal_update() - object.data.update(calc_edges=True) # calc_edges prevents memory-corruption + object.data.update(calc_edges=True) # calc_edges prevents memory-corruption bm.verts.ensure_lookup_table() bm.edges.ensure_lookup_table() @@ -1451,8 +1470,10 @@ def bridge_create_faces(object, bm, faces, twist): # calculate input loops def bridge_get_input(bm): # create list of internal edges, which should be skipped - eks_of_selected_faces = [item for sublist in [face_edgekeys(face) for \ - face in bm.faces if face.select and not face.hide] for item in sublist] + eks_of_selected_faces = [ + item for sublist in [face_edgekeys(face) for + face in bm.faces if face.select and not face.hide] for item in sublist + ] edge_count = {} for ek in eks_of_selected_faces: if ek in edge_count: @@ -1462,8 +1483,10 @@ def bridge_get_input(bm): internal_edges = [ek for ek in edge_count if edge_count[ek] > 1] # sort correct edges into loops - selected_edges = [edgekey(edge) for edge in bm.edges if edge.select \ - and not edge.hide and edgekey(edge) not in internal_edges] + selected_edges = [ + edgekey(edge) for edge in bm.edges if edge.select and + not edge.hide and edgekey(edge) not in internal_edges + ] loops = get_connected_selections(selected_edges) return(loops) @@ -1473,31 +1496,35 @@ def bridge_get_input(bm): def bridge_initialise(bm, interpolation): if interpolation == 'cubic': # dict with edge-key as key and list of connected valid faces as value - face_blacklist = [face.index for face in bm.faces if face.select or \ - face.hide] - edge_faces = dict([[edgekey(edge), []] for edge in bm.edges if not \ - edge.hide]) + face_blacklist = [ + face.index for face in bm.faces if face.select or + face.hide + ] + edge_faces = dict( + [[edgekey(edge), []] for edge in bm.edges if not edge.hide] + ) for face in bm.faces: if face.index in face_blacklist: continue for key in face_edgekeys(face): edge_faces[key].append(face) # dictionary with the edge-key as key and edge as value - edgekey_to_edge = dict([[edgekey(edge), edge] for edge in \ - bm.edges if edge.select and not edge.hide]) + edgekey_to_edge = dict( + [[edgekey(edge), edge] for edge in bm.edges if edge.select and not edge.hide] + ) else: edge_faces = False edgekey_to_edge = False # selected faces input - old_selected_faces = [face.index for face in bm.faces if face.select \ - and not face.hide] + old_selected_faces = [ + face.index for face in bm.faces if face.select and not face.hide + ] # find out if faces created by bridging should be smoothed smooth = False if bm.faces: - if sum([face.smooth for face in bm.faces])/len(bm.faces) \ - >= 0.5: + if sum([face.smooth for face in bm.faces]) / len(bm.faces) >= 0.5: smooth = True return(edge_faces, edgekey_to_edge, old_selected_faces, smooth) @@ -1536,17 +1563,18 @@ def bridge_match_loops(bm, loops): matches = dict([[i, []] for i in range(len(loops))]) matches_amount = 0 for i in range(len(loops) + 1): - for j in range(i+1, len(loops)): - if (centers[i] - centers[j]).length > (centers[i] - (centers[j] \ - + normals[j])).length and (centers[j] - centers[i]).length > \ - (centers[j] - (centers[i] + normals[i])).length: + for j in range(i + 1, len(loops)): + if (centers[i] - centers[j]).length > \ + (centers[i] - (centers[j] + normals[j])).length and \ + (centers[j] - centers[i]).length > \ + (centers[j] - (centers[i] + normals[i])).length: matches_amount += 1 matches[i].append([(centers[i] - centers[j]).length, i, j]) matches[j].append([(centers[i] - centers[j]).length, j, i]) # if no loops face each other, just make matches between all the loops if matches_amount == 0: for i in range(len(loops) + 1): - for j in range(i+1, len(loops)): + for j in range(i + 1, len(loops)): matches[i].append([(centers[i] - centers[j]).length, i, j]) matches[j].append([(centers[i] - centers[j]).length, j, i]) for key, value in matches.items(): @@ -1562,9 +1590,11 @@ def bridge_match_loops(bm, loops): continue shortest_distance = loop_matches[0][0] shortest_distance *= 1.1 - loop_matches = [[abs(len(loops[loop_index][0]) - \ - len(loops[loop[2]][0])), loop[0], loop[1], loop[2]] for loop in \ - loop_matches if loop[0] < shortest_distance] + loop_matches = [ + [abs(len(loops[loop_index][0]) - + len(loops[loop[2]][0])), loop[0], loop[1], loop[2]] for loop in + loop_matches if loop[0] < shortest_distance + ] loop_matches.sort() for match in loop_matches: if match[3] not in new_order: @@ -1582,8 +1612,9 @@ def bridge_match_loops(bm, loops): def bridge_remove_internal_faces(bm, old_selected_faces): # collect bmesh faces and internal bmesh edges remove_faces = [bm.faces[face] for face in old_selected_faces] - edges = collections.Counter([edge.index for face in remove_faces for \ - edge in face.edges]) + edges = collections.Counter( + [edge.index for face in remove_faces for edge in face.edges] + ) remove_edges = [bm.edges[edge] for edge in edges if edges[edge] > 1] # remove internal faces and edges @@ -1602,8 +1633,8 @@ def bridge_save_unused_faces(bm, old_selected_faces, loops): # key: vertex index, value: lists of selected faces using it vertex_to_face = dict([[i, []] for i in range(len(bm.verts))]) - [[vertex_to_face[vertex.index].append(face) for vertex in \ - bm.faces[face].verts] for face in old_selected_faces] + [[vertex_to_face[vertex.index].append(face) for vertex in + bm.faces[face].verts] for face in old_selected_faces] # group selected faces that are connected groups = [] @@ -1617,8 +1648,10 @@ def bridge_save_unused_faces(bm, old_selected_faces, loops): while new_faces: grow_face = new_faces[0] for vertex in bm.faces[grow_face].verts: - vertex_face_group = [face for face in vertex_to_face[\ - vertex.index] if face not in grouped_faces] + vertex_face_group = [ + face for face in vertex_to_face[vertex.index] if + face not in grouped_faces + ] new_faces += vertex_face_group grouped_faces += vertex_face_group group += vertex_face_group @@ -1656,13 +1689,15 @@ def bridge_select_new_faces(new_faces, smooth): # sort loops, so they are connected in the correct order when lofting def bridge_sort_loops(bm, loops, loft_loop): # simplify loops to single points, and prepare for pathfinding - x, y, z = [[sum([bm.verts[i].co[j] for i in loop[0]]) / \ - len(loop[0]) for loop in loops] for j in range(3)] + x, y, z = [ + [sum([bm.verts[i].co[j] for i in loop[0]]) / + len(loop[0]) for loop in loops] for j in range(3) + ] nodes = [mathutils.Vector((x[i], y[i], z[i])) for i in range(len(loops))] active_node = 0 open = [i for i in range(1, len(loops))] - path = [[0,0]] + path = [[0, 0]] # connect node to path, that is shortest to active_node while len(open) > 0: distances = [(nodes[active_node] - nodes[i]).length for i in open] @@ -1671,7 +1706,7 @@ def bridge_sort_loops(bm, loops, loft_loop): path.append([active_node, min(distances)]) # check if we didn't start in the middle of the path for i in range(2, len(path)): - if (nodes[path[i][0]]-nodes[0]).length < path[i][1]: + if (nodes[path[i][0]] - nodes[0]).length < path[i][1]: temp = path[:i] path.reverse() path = path[:-i] + temp @@ -1688,21 +1723,23 @@ def bridge_sort_loops(bm, loops, loft_loop): # remapping old indices to new position in list def bridge_update_old_selection(bm, old_selected_faces): - #old_indices = old_selected_faces[:] - #old_selected_faces = [] - #for i, face in enumerate(bm.faces): - # if face.index in old_indices: - # old_selected_faces.append(i) - - old_selected_faces = [i for i, face in enumerate(bm.faces) if face.index \ - in old_selected_faces] + """ + old_indices = old_selected_faces[:] + old_selected_faces = [] + for i, face in enumerate(bm.faces): + if face.index in old_indices: + old_selected_faces.append(i) + """ + old_selected_faces = [ + i for i, face in enumerate(bm.faces) if face.index in old_selected_faces + ] return(old_selected_faces) -########################################## -####### Circle functions ################# -########################################## +# ######################################## +# ##### Circle functions ################# +# ######################################## # convert 3d coordinates to 2d coordinates on plane def circle_3d_to_2d(bm_mod, loop, com, normal): @@ -1742,22 +1779,22 @@ def circle_calculate_best_fit(locs_2d): jmat = [] k = [] for v in locs_2d: - d = (v[0]**2-2.0*x0*v[0]+v[1]**2-2.0*y0*v[1]+x0**2+y0**2)**0.5 - jmat.append([(x0-v[0])/d, (y0-v[1])/d, -1.0]) - k.append(-(((v[0]-x0)**2+(v[1]-y0)**2)**0.5-r)) + d = (v[0] ** 2 - 2.0 * x0 * v[0] + v[1] ** 2 - 2.0 * y0 * v[1] + x0 ** 2 + y0 ** 2) ** 0.5 + jmat.append([(x0 - v[0]) / d, (y0 - v[1]) / d, -1.0]) + k.append(-(((v[0] - x0) ** 2 + (v[1] - y0) ** 2) ** 0.5 - r)) jmat2 = mathutils.Matrix(((0.0, 0.0, 0.0), (0.0, 0.0, 0.0), (0.0, 0.0, 0.0), )) k2 = mathutils.Vector((0.0, 0.0, 0.0)) for i in range(len(jmat)): - k2 += mathutils.Vector(jmat[i])*k[i] - jmat2[0][0] += jmat[i][0]**2 - jmat2[1][0] += jmat[i][0]*jmat[i][1] - jmat2[2][0] += jmat[i][0]*jmat[i][2] - jmat2[1][1] += jmat[i][1]**2 - jmat2[2][1] += jmat[i][1]*jmat[i][2] - jmat2[2][2] += jmat[i][2]**2 + k2 += mathutils.Vector(jmat[i]) * k[i] + jmat2[0][0] += jmat[i][0] ** 2 + jmat2[1][0] += jmat[i][0] * jmat[i][1] + jmat2[2][0] += jmat[i][0] * jmat[i][2] + jmat2[1][1] += jmat[i][1] ** 2 + jmat2[2][1] += jmat[i][1] * jmat[i][2] + jmat2[2][2] += jmat[i][2] ** 2 jmat2[0][1] = jmat2[1][0] jmat2[0][2] = jmat2[2][0] jmat2[1][2] = jmat2[2][1] @@ -1770,7 +1807,7 @@ def circle_calculate_best_fit(locs_2d): y0 += dy0 r += dr # stop iterating if we're close enough to optimal solution - if abs(dx0)<1e-6 and abs(dy0)<1e-6 and abs(dr)<1e-6: + if abs(dx0) < 1e-6 and abs(dy0) < 1e-6 and abs(dr) < 1e-6: break # return center of circle and radius @@ -1780,11 +1817,11 @@ def circle_calculate_best_fit(locs_2d): # calculate circle so no vertices have to be moved away from the center def circle_calculate_min_fit(locs_2d): # center of circle - x0 = (min([i[0] for i in locs_2d])+max([i[0] for i in locs_2d]))/2.0 - y0 = (min([i[1] for i in locs_2d])+max([i[1] for i in locs_2d]))/2.0 + x0 = (min([i[0] for i in locs_2d]) + max([i[0] for i in locs_2d])) / 2.0 + y0 = (min([i[1] for i in locs_2d]) + max([i[1] for i in locs_2d])) / 2.0 center = mathutils.Vector([x0, y0]) # radius of circle - r = min([(mathutils.Vector([i[0], i[1]])-center).length for i in locs_2d]) + r = min([(mathutils.Vector([i[0], i[1]]) - center).length for i in locs_2d]) # return center of circle and radius return(x0, y0, r) @@ -1795,12 +1832,12 @@ def circle_calculate_verts(flatten, bm_mod, locs_2d, com, p, q, normal): # changing 2d coordinates back to 3d coordinates locs_3d = [] for loc in locs_2d: - locs_3d.append([loc[2], loc[0]*p + loc[1]*q + com]) + locs_3d.append([loc[2], loc[0] * p + loc[1] * q + com]) - if flatten: # flat circle + if flatten: # flat circle return(locs_3d) - else: # project the locations on the existing mesh + else: # project the locations on the existing mesh vert_edges = dict_vert_edges(bm_mod) vert_faces = dict_vert_faces(bm_mod) faces = [f for f in bm_mod.faces if not f.hide] @@ -1808,21 +1845,21 @@ def circle_calculate_verts(flatten, bm_mod, locs_2d, com, p, q, normal): new_locs = [] for loc in locs_3d: projection = False - if bm_mod.verts[loc[0]].co == loc[1]: # vertex hasn't moved + if bm_mod.verts[loc[0]].co == loc[1]: # vertex hasn't moved projection = loc[1] else: - dif = normal.angle(loc[1]-bm_mod.verts[loc[0]].co) - if -1e-6 < dif < 1e-6 or math.pi-1e-6 < dif < math.pi+1e-6: + dif = normal.angle(loc[1] - bm_mod.verts[loc[0]].co) + if -1e-6 < dif < 1e-6 or math.pi - 1e-6 < dif < math.pi + 1e-6: # original location is already along projection normal projection = bm_mod.verts[loc[0]].co else: # quick search through adjacent faces for face in vert_faces[loc[0]]: verts = [v.co for v in bm_mod.faces[face].verts] - if len(verts) == 3: # triangle + if len(verts) == 3: # triangle v1, v2, v3 = verts v4 = False - else: # assume quad + else: # assume quad v1, v2, v3, v4 = verts[:4] for ray in rays: intersect = mathutils.geometry.\ @@ -1843,8 +1880,9 @@ def circle_calculate_verts(flatten, bm_mod, locs_2d, com, p, q, normal): for edgekey in vert_edges[loc[0]]: line1 = bm_mod.verts[edgekey[0]].co line2 = bm_mod.verts[edgekey[1]].co - intersect, dist = mathutils.geometry.intersect_point_line(\ - loc[1], line1, line2) + intersect, dist = mathutils.geometry.intersect_point_line( + loc[1], line1, line2 + ) if 1e-6 < dist < 1 - 1e-6: projection = intersect break @@ -1853,21 +1891,23 @@ def circle_calculate_verts(flatten, bm_mod, locs_2d, com, p, q, normal): hits = [] for face in faces: verts = [v.co for v in face.verts] - if len(verts) == 3: # triangle + if len(verts) == 3: # triangle v1, v2, v3 = verts v4 = False - else: # assume quad + else: # assume quad v1, v2, v3, v4 = verts[:4] for ray in rays: - intersect = mathutils.geometry.intersect_ray_tri(\ - v1, v2, v3, ray, loc[1]) + intersect = mathutils.geometry.intersect_ray_tri( + v1, v2, v3, ray, loc[1] + ) if intersect: hits.append([(loc[1] - intersect).length, intersect]) break elif v4: - intersect = mathutils.geometry.intersect_ray_tri(\ - v1, v3, v4, ray, loc[1]) + intersect = mathutils.geometry.intersect_ray_tri( + v1, v3, v4, ray, loc[1] + ) if intersect: hits.append([(loc[1] - intersect).length, intersect]) @@ -1912,8 +1952,8 @@ def circle_check_loops(single_loops, loops, mapping, bm_mod): loc0 = loc1 loc1 = locn continue - d1 = loc1-loc0 - d2 = locn-loc1 + d1 = loc1 - loc0 + d2 = locn - loc1 if -1e-6 < d1.angle(d2, 0) < 1e-6: loc0 = loc1 loc1 = locn @@ -1924,7 +1964,7 @@ def circle_check_loops(single_loops, loops, mapping, bm_mod): continue # passed all tests, loop is valid valid_loops.append([loop, circular]) - valid_single_loops[len(valid_loops)-1] = single_loops[i] + valid_single_loops[len(valid_loops) - 1] = single_loops[i] return(valid_single_loops, valid_loops) @@ -1934,7 +1974,7 @@ def circle_flatten_singles(bm_mod, com, p, q, normal, single_loop): new_locs = [] for vert in single_loop: loc = mathutils.Vector(bm_mod.verts[vert].co[:]) - new_locs.append([vert, loc - (loc-com).dot(normal)*normal]) + new_locs.append([vert, loc - (loc - com).dot(normal) * normal]) return(new_locs) @@ -1952,39 +1992,48 @@ def circle_get_input(object, bm, scene): break if faces: # get selected, non-hidden , non-internal edge-keys - eks_selected = [key for keys in [face_edgekeys(face) for face in \ - bm_mod.faces if face.select and not face.hide] for key in keys] + eks_selected = [ + key for keys in [face_edgekeys(face) for face in + bm_mod.faces if face.select and not face.hide] for key in keys + ] edge_count = {} for ek in eks_selected: if ek in edge_count: edge_count[ek] += 1 else: edge_count[ek] = 1 - edge_keys = [edgekey(edge) for edge in bm_mod.edges if edge.select \ - and not edge.hide and edge_count.get(edgekey(edge), 1)==1] + edge_keys = [ + edgekey(edge) for edge in bm_mod.edges if edge.select and + not edge.hide and edge_count.get(edgekey(edge), 1) == 1 + ] else: # no faces, so no internal edges either - edge_keys = [edgekey(edge) for edge in bm_mod.edges if edge.select \ - and not edge.hide] + edge_keys = [ + edgekey(edge) for edge in bm_mod.edges if edge.select and not edge.hide + ] # add edge-keys around single vertices - verts_connected = dict([[vert, 1] for edge in [edge for edge in \ - bm_mod.edges if edge.select and not edge.hide] for vert in \ - edgekey(edge)]) - single_vertices = [vert.index for vert in bm_mod.verts if \ - vert.select and not vert.hide and not \ - verts_connected.get(vert.index, False)] - - if single_vertices and len(bm.faces)>0: - vert_to_single = dict([[v.index, []] for v in bm_mod.verts \ - if not v.hide]) - for face in [face for face in bm_mod.faces if not face.select \ - and not face.hide]: + verts_connected = dict( + [[vert, 1] for edge in [edge for edge in + bm_mod.edges if edge.select and not edge.hide] for vert in + edgekey(edge)] + ) + single_vertices = [ + vert.index for vert in bm_mod.verts if + vert.select and not vert.hide and + not verts_connected.get(vert.index, False) + ] + + if single_vertices and len(bm.faces) > 0: + vert_to_single = dict( + [[v.index, []] for v in bm_mod.verts if not v.hide] + ) + for face in [face for face in bm_mod.faces if not face.select and not face.hide]: for vert in face.verts: vert = vert.index if vert in single_vertices: for ek in face_edgekeys(face): - if not vert in ek: + if vert not in ek: edge_keys.append(ek) if vert not in vert_to_single[ek[0]]: vert_to_single[ek[0]].append(vert) @@ -1997,7 +2046,7 @@ def circle_get_input(object, bm, scene): # find out to which loops the single vertices belong single_loops = dict([[i, []] for i in range(len(loops))]) - if single_vertices and len(bm.faces)>0: + if single_vertices and len(bm.faces) > 0: for i, [loop, circular] in enumerate(loops): for vert in loop: if vert_to_single[vert]: @@ -2013,8 +2062,8 @@ def circle_influence_locs(locs_2d, new_locs_2d, influence): for i in range(len(locs_2d)): oldx, oldy, j = locs_2d[i] newx, newy, k = new_locs_2d[i] - altx = newx*(influence/100)+ oldx*((100-influence)/100) - alty = newy*(influence/100)+ oldy*((100-influence)/100) + altx = newx * (influence / 100) + oldx * ((100 - influence) / 100) + alty = newy * (influence / 100) + oldy * ((100 - influence) / 100) locs_2d[i] = [altx, alty, j] return(locs_2d) @@ -2024,7 +2073,7 @@ def circle_influence_locs(locs_2d, new_locs_2d, influence): def circle_project_non_regular(locs_2d, x0, y0, r): for i in range(len(locs_2d)): x, y, j = locs_2d[i] - loc = mathutils.Vector([x-x0, y-y0]) + loc = mathutils.Vector([x - x0, y - y0]) loc.length = r locs_2d[i] = [loc[0], loc[1], j] @@ -2035,14 +2084,14 @@ def circle_project_non_regular(locs_2d, x0, y0, r): def circle_project_regular(locs_2d, x0, y0, r): # find offset angle and circling direction x, y, i = locs_2d[0] - loc = mathutils.Vector([x-x0, y-y0]) + loc = mathutils.Vector([x - x0, y - y0]) loc.length = r offset_angle = loc.angle(mathutils.Vector([1.0, 0.0]), 0.0) - loca = mathutils.Vector([x-x0, y-y0, 0.0]) + loca = mathutils.Vector([x - x0, y - y0, 0.0]) if loc[1] < -1e-6: offset_angle *= -1 x, y, j = locs_2d[1] - locb = mathutils.Vector([x-x0, y-y0, 0.0]) + locb = mathutils.Vector([x - x0, y - y0, 0.0]) if loca.cross(locb)[2] >= 0: ccw = 1 else: @@ -2060,8 +2109,9 @@ def circle_project_regular(locs_2d, x0, y0, r): # shift loop, so the first vertex is closest to the center def circle_shift_loop(bm_mod, loop, com): verts, circular = loop - distances = [[(bm_mod.verts[vert].co - com).length, i] \ - for i, vert in enumerate(verts)] + distances = [ + [(bm_mod.verts[vert].co - com).length, i] for i, vert in enumerate(verts) + ] distances.sort() shift = distances[0][1] loop = [verts[shift:] + verts[:shift], circular] @@ -2069,9 +2119,9 @@ def circle_shift_loop(bm_mod, loop, com): return(loop) -########################################## -####### Curve functions ################## -########################################## +# ######################################## +# ##### Curve functions ################## +# ######################################## # create lists with knots and points, all correctly sorted def curve_calculate_knots(loop, verts_selected): @@ -2085,7 +2135,7 @@ def curve_calculate_knots(loop, verts_selected): kpos.append(loop[0].index(k)) kdif = [] for i in range(len(kpos) - 1): - kdif.append(kpos[i+1] - kpos[i]) + kdif.append(kpos[i + 1] - kpos[i]) kdif.append(len(loop[0]) - kpos[-1] + kpos[0]) kadd = [] for k in kdif: @@ -2093,53 +2143,52 @@ def curve_calculate_knots(loop, verts_selected): kadd.append([kdif.index(k), True]) # next 2 lines are optional, they insert # an extra control point in small gaps - #elif k > offset: + # elif k > offset: # kadd.append([kdif.index(k), False]) kins = [] krot = False - for k in kadd: # extra knots to be added - if k[1]: # big gap (break circular spline) + for k in kadd: # extra knots to be added + if k[1]: # big gap (break circular spline) kpos = loop[0].index(knots[k[0]]) + offset if kpos > len(loop[0]) - 1: kpos -= len(loop[0]) kins.append([knots[k[0]], loop[0][kpos]]) kpos2 = k[0] + 1 - if kpos2 > len(knots)-1: + if kpos2 > len(knots) - 1: kpos2 -= len(knots) kpos2 = loop[0].index(knots[kpos2]) - offset if kpos2 < 0: kpos2 += len(loop[0]) kins.append([loop[0][kpos], loop[0][kpos2]]) krot = loop[0][kpos2] - else: # small gap (keep circular spline) + else: # small gap (keep circular spline) k1 = loop[0].index(knots[k[0]]) k2 = k[0] + 1 - if k2 > len(knots)-1: + if k2 > len(knots) - 1: k2 -= len(knots) k2 = loop[0].index(knots[k2]) if k2 < k1: dif = len(loop[0]) - 1 - k1 + k2 else: dif = k2 - k1 - kn = k1 + int(dif/2) + kn = k1 + int(dif / 2) if kn > len(loop[0]) - 1: kn -= len(loop[0]) kins.append([loop[0][k1], loop[0][kn]]) - for j in kins: # insert new knots + for j in kins: # insert new knots knots.insert(knots.index(j[0]) + 1, j[1]) - if not krot: # circular loop + if not krot: # circular loop knots.append(knots[0]) points = loop[0][loop[0].index(knots[0]):] points += loop[0][0:loop[0].index(knots[0]) + 1] - else: # non-circular loop (broken by script) + else: # non-circular loop (broken by script) krot = knots.index(krot) knots = knots[krot:] + knots[0:krot] if loop[0].index(knots[0]) > loop[0].index(knots[-1]): points = loop[0][loop[0].index(knots[0]):] - points += loop[0][0:loop[0].index(knots[-1])+1] + points += loop[0][0:loop[0].index(knots[-1]) + 1] else: - points = loop[0][loop[0].index(knots[0]):\ - loop[0].index(knots[-1]) + 1] + points = loop[0][loop[0].index(knots[0]):loop[0].index(knots[-1]) + 1] # non-circular loop, add first and last point as knots else: if loop[0][0] not in knots: @@ -2158,12 +2207,12 @@ def curve_calculate_t(bm_mod, knots, points, pknots, regular, circular): for p in points: if p in knots: - loc = pknots[knots.index(p)] # use projected knot location + loc = pknots[knots.index(p)] # use projected knot location else: loc = mathutils.Vector(bm_mod.verts[p].co[:]) if not loc_prev: loc_prev = loc - len_total += (loc-loc_prev).length + len_total += (loc - loc_prev).length tpoints.append(len_total) loc_prev = loc tknots = [] @@ -2210,22 +2259,22 @@ interpolation, restriction): if interpolation == 'cubic': ax, bx, cx, dx, tx = splines[n][0] - x = ax + bx*(m-tx) + cx*(m-tx)**2 + dx*(m-tx)**3 + x = ax + bx * (m - tx) + cx * (m - tx) ** 2 + dx * (m - tx) ** 3 ay, by, cy, dy, ty = splines[n][1] - y = ay + by*(m-ty) + cy*(m-ty)**2 + dy*(m-ty)**3 + y = ay + by * (m - ty) + cy * (m - ty) ** 2 + dy * (m - ty) ** 3 az, bz, cz, dz, tz = splines[n][2] - z = az + bz*(m-tz) + cz*(m-tz)**2 + dz*(m-tz)**3 - newloc = mathutils.Vector([x,y,z]) - else: # interpolation == 'linear' + z = az + bz * (m - tz) + cz * (m - tz) ** 2 + dz * (m - tz) ** 3 + newloc = mathutils.Vector([x, y, z]) + else: # interpolation == 'linear' a, d, t, u = splines[n] - newloc = ((m-t)/u)*d + a + newloc = ((m - t) / u) * d + a - if restriction != 'none': # vertex movement is restricted + if restriction != 'none': # vertex movement is restricted newlocs[p] = newloc - else: # set the vertex to its new location + else: # set the vertex to its new location move.append([p, newloc]) - if restriction != 'none': # vertex movement is restricted + if restriction != 'none': # vertex movement is restricted for p in points: if p in newlocs: newloc = newlocs[p] @@ -2237,10 +2286,10 @@ interpolation, restriction): dloc = newloc - oldloc if dloc.length < 1e-6: move.append([p, newloc]) - elif restriction == 'extrude': # only extrusions + elif restriction == 'extrude': # only extrusions if dloc.angle(normal, 0) < 0.5 * math.pi + 1e-6: move.append([p, newloc]) - else: # restriction == 'indent' only indentations + else: # restriction == 'indent' only indentations if dloc.angle(normal) > 0.5 * math.pi - 1e-6: move.append([p, newloc]) @@ -2273,8 +2322,9 @@ def curve_get_input(object, bm, boundaries, scene): derived, bm_mod = get_derived_bmesh(object, bm, scene) # vertices that still need a loop to run through it - verts_unsorted = [v.index for v in bm_mod.verts if \ - v.select and not v.hide] + verts_unsorted = [ + v.index for v in bm_mod.verts if v.select and not v.hide + ] # necessary dictionaries vert_edges = dict_vert_edges(bm_mod) edge_faces = dict_edge_faces(bm_mod) @@ -2334,8 +2384,9 @@ def curve_perpendicular_loops(bm_mod, start_loop, vert_edges, edge_faces): perp_loops.append([loop, circular, loop.index(start_vert)]) # trim loops to same lengths - shortest = [[len(loop[0]), i] for i, loop in enumerate(perp_loops)\ - if not loop[1]] + shortest = [ + [len(loop[0]), i] for i, loop in enumerate(perp_loops) if not loop[1] + ] if not shortest: # all loops are circular, not trimming return([[loop[0], loop[1]] for loop in perp_loops]) @@ -2364,7 +2415,7 @@ def curve_perpendicular_loops(bm_mod, start_loop, vert_edges, edge_faces): loop[2] += shift if loop[2] < 0: loop[2] += len(loop[0]) - elif loop[2] > len(loop[0]) -1: + elif loop[2] > len(loop[0]) - 1: loop[2] -= len(loop[0]) # trim start = max(0, loop[2] - before_start) @@ -2385,37 +2436,35 @@ def curve_project_knots(bm_mod, verts_selected, knots, points, circular): p = v3.project(v2) return(p + v1) - if circular: # project all knots + if circular: # project all knots start = 0 end = len(knots) pknots = [] - else: # first and last knot shouldn't be projected + else: # first and last knot shouldn't be projected start = 1 end = -1 pknots = [mathutils.Vector(bm_mod.verts[knots[0]].co[:])] for knot in knots[start:end]: if knot in verts_selected: knot_left = knot_right = False - for i in range(points.index(knot)-1, -1*len(points), -1): + for i in range(points.index(knot) - 1, -1 * len(points), -1): if points[i] not in knots: knot_left = points[i] break - for i in range(points.index(knot)+1, 2*len(points)): + for i in range(points.index(knot) + 1, 2 * len(points)): if i > len(points) - 1: i -= len(points) if points[i] not in knots: knot_right = points[i] break if knot_left and knot_right and knot_left != knot_right: - knot_left = mathutils.Vector(\ - bm_mod.verts[knot_left].co[:]) - knot_right = mathutils.Vector(\ - bm_mod.verts[knot_right].co[:]) + knot_left = mathutils.Vector(bm_mod.verts[knot_left].co[:]) + knot_right = mathutils.Vector(bm_mod.verts[knot_right].co[:]) knot = mathutils.Vector(bm_mod.verts[knot].co[:]) pknots.append(project(knot_left, knot_right, knot)) else: pknots.append(mathutils.Vector(bm_mod.verts[knot].co[:])) - else: # knot isn't selected, so shouldn't be changed + else: # knot isn't selected, so shouldn't be changed pknots.append(mathutils.Vector(bm_mod.verts[knot].co[:])) if not circular: pknots.append(mathutils.Vector(bm_mod.verts[knots[-1]].co[:])) @@ -2478,14 +2527,15 @@ def curve_vertex_loops(bm_mod, start_vert, vert_edges, edge_faces): return(loops) -########################################## -####### Flatten functions ################ -########################################## +# ######################################## +# ##### Flatten functions ################ +# ######################################## # sort input into loops def flatten_get_input(bm): - vert_verts = dict_vert_verts([edgekey(edge) for edge in bm.edges \ - if edge.select and not edge.hide]) + vert_verts = dict_vert_verts( + [edgekey(edge) for edge in bm.edges if edge.select and not edge.hide] + ) verts = [v.index for v in bm.verts if v.select and not v.hide] # no connected verts, consider all selected verts as a single input @@ -2519,15 +2569,17 @@ def flatten_get_input(bm): # calculate position of vertex projections on plane def flatten_project(bm, loop, com, normal): verts = [bm.verts[v] for v in loop[0]] - verts_projected = [[v.index, mathutils.Vector(v.co[:]) - \ - (mathutils.Vector(v.co[:])-com).dot(normal)*normal] for v in verts] + verts_projected = [ + [v.index, mathutils.Vector(v.co[:]) - + (mathutils.Vector(v.co[:]) - com).dot(normal) * normal] for v in verts + ] return(verts_projected) -########################################## -####### Gstretch functions ############### -########################################## +# ######################################## +# ##### Gstretch functions ############### +# ######################################## # fake stroke class, used to create custom strokes if no GP data is found class gstretch_fake_stroke(): @@ -2599,7 +2651,6 @@ def gstretch_calculate_verts(loop, stroke, object, bm_mod, method): return(None) if method == 'project': - projection_vectors = [] vert_edges = dict_vert_edges(bm_mod) for v_index in loop[0]: @@ -2628,7 +2679,7 @@ def gstretch_calculate_verts(loop, stroke, object, bm_mod, method): for i, v_index in enumerate(loop[0]): if method == 'regular': relative_distance = i / (loop_length - 1) - else: # method == 'irregular' + else: # method == 'irregular' relative_distance = relative_lengths[i] loc, stroke_lengths_cache = gstretch_eval_stroke(stroke, relative_distance, stroke_lengths_cache) @@ -2661,8 +2712,7 @@ conversion_distance, conversion_max, conversion_min, conversion_vertices): if conversion == 'distance': method = 'project' prev_point = stroke.points[0] - stroke_verts[-1][1].append(bm_mod.verts.new(mat_world * \ - prev_point.co)) + stroke_verts[-1][1].append(bm_mod.verts.new(mat_world * prev_point.co)) distance = 0 limit = conversion_distance for point in stroke.points: @@ -2672,8 +2722,7 @@ conversion_distance, conversion_max, conversion_min, conversion_vertices): to_cover = limit - distance + (limit * iteration) new_loc = prev_point.co + to_cover * \ (point.co - prev_point.co).normalized() - stroke_verts[-1][1].append(bm_mod.verts.new(mat_world * \ - new_loc)) + stroke_verts[-1][1].append(bm_mod.verts.new(mat_world * new_loc)) new_distance -= limit iteration += 1 distance = new_distance @@ -2682,14 +2731,12 @@ conversion_distance, conversion_max, conversion_min, conversion_vertices): else: # add vertices at stroke points for point in stroke.points[:end_point]: - stroke_verts[-1][1].append(bm_mod.verts.new(\ - mat_world * point.co)) + stroke_verts[-1][1].append(bm_mod.verts.new(mat_world * point.co)) # add more vertices, beyond the points that are available if min_end_point > min(len(stroke.points), end_point): for i in range(min_end_point - (min(len(stroke.points), end_point))): - stroke_verts[-1][1].append(bm_mod.verts.new(\ - mat_world * point.co)) + stroke_verts[-1][1].append(bm_mod.verts.new(mat_world * point.co)) # force even spreading of points, so they are placed on stroke method = 'regular' bm_mod.verts.ensure_lookup_table() @@ -2705,7 +2752,7 @@ conversion_distance, conversion_max, conversion_min, conversion_vertices): # create edges for i, vert in enumerate(verts_seq): if i > 0: - bm_mod.edges.new((verts_seq[i-1], verts_seq[i])) + bm_mod.edges.new((verts_seq[i - 1], verts_seq[i])) vert.select = True # connect single vertices to the closest stroke if singles: @@ -2727,8 +2774,10 @@ def gstretch_erase_stroke(stroke, context): 'pen_flip': False, 'is_start': False, 'location': (0, 0, 0), - 'mouse': (view3d_utils.location_3d_to_region_2d(\ - context.region, context.space_data.region_3d, loc)), + 'mouse': ( + view3d_utils.location_3d_to_region_2d( + context.region, context.space_data.region_3d, loc) + ), 'pressure': 1, 'size': 0, 'time': 0} @@ -2750,8 +2799,7 @@ def gstretch_eval_stroke(stroke, distance, stroke_lengths_cache=False): if not stroke_lengths_cache: lengths = [0] for i, p in enumerate(stroke.points[1:]): - lengths.append((p.co - stroke.points[i].co).length + \ - lengths[-1]) + lengths.append((p.co - stroke.points[i].co).length + lengths[-1]) total_length = max(lengths[-1], 1e-7) stroke_lengths_cache = [length / total_length for length in lengths] @@ -2766,14 +2814,12 @@ def gstretch_eval_stroke(stroke, distance, stroke_lengths_cache=False): stroke_lengths.append(distance) stroke_lengths.sort() stroke_index = stroke_lengths.index(distance) - interval_length = stroke_lengths[stroke_index+1] - \ - stroke_lengths[stroke_index-1] - distance_relative = (distance - stroke_lengths[stroke_index-1]) / \ - interval_length - interval_vector = stroke.points[stroke_index].co - \ - stroke.points[stroke_index-1].co - loc = stroke.points[stroke_index-1].co + \ - distance_relative * interval_vector + interval_length = stroke_lengths[ + stroke_index + 1] - stroke_lengths[stroke_index - 1 + ] + distance_relative = (distance - stroke_lengths[stroke_index - 1]) / interval_length + interval_vector = stroke.points[stroke_index].co - stroke.points[stroke_index - 1].co + loc = stroke.points[stroke_index - 1].co + distance_relative * interval_vector return(loc, stroke_lengths_cache) @@ -2841,7 +2887,7 @@ def gstretch_match_loops_strokes(loops, strokes, object, bm_mod): distances.sort() best_stroke = distances[0][2] ls_pairs.append([lc[1], stroke_centers[best_stroke][1]]) - stroke_centers[best_stroke][2] += 1 # increase stroke use count + stroke_centers[best_stroke][2] += 1 # increase stroke use count return(ls_pairs) @@ -2897,8 +2943,10 @@ def gstretch_match_single_verts(bm_mod, strokes, mat_world): def gstretch_relative_lengths(loop, bm_mod): lengths = [0] for i, v_index in enumerate(loop[0][1:]): - lengths.append((bm_mod.verts[v_index].co - \ - bm_mod.verts[loop[0][i]].co).length + lengths[-1]) + lengths.append( + (bm_mod.verts[v_index].co - + bm_mod.verts[loop[0][i]].co).length + lengths[-1] + ) total_length = max(lengths[-1], 1e-7) relative_lengths = [length / total_length for length in lengths] @@ -2950,9 +2998,9 @@ def gstretch_update_min(self, context): lt.gstretch_conversion_min = lt.gstretch_conversion_max -########################################## -####### Relax functions ################## -########################################## +# ######################################## +# ##### Relax functions ################## +# ######################################## # create lists with knots and points, all correctly sorted def relax_calculate_knots(loops): @@ -2962,21 +3010,21 @@ def relax_calculate_knots(loops): knots = [[], []] points = [[], []] if circular: - if len(loop)%2 == 1: # odd + if len(loop) % 2 == 1: # odd extend = [False, True, 0, 1, 0, 1] - else: # even + else: # even extend = [True, False, 0, 1, 1, 2] else: - if len(loop)%2 == 1: # odd + if len(loop) % 2 == 1: # odd extend = [False, False, 0, 1, 1, 2] - else: # even + else: # even extend = [False, False, 0, 1, 1, 2] for j in range(2): if extend[j]: loop = [loop[-1]] + loop + [loop[0]] - for i in range(extend[2+2*j], len(loop), 2): + for i in range(extend[2 + 2 * j], len(loop), 2): knots[j].append(loop[i]) - for i in range(extend[3+2*j], len(loop), 2): + for i in range(extend[3 + 2 * j], len(loop), 2): if loop[i] == loop[-1] and not circular: continue if len(points[j]) == 0: @@ -3003,14 +3051,14 @@ def relax_calculate_t(bm_mod, knots, points, regular): all_tpoints = [] for i in range(len(knots)): amount = len(knots[i]) + len(points[i]) - mix = [] + mix = [] for j in range(amount): - if j%2 == 0: - mix.append([True, knots[i][round(j/2)]]) - elif j == amount-1: + if j % 2 == 0: + mix.append([True, knots[i][round(j / 2)]]) + elif j == amount - 1: mix.append([True, knots[i][-1]]) else: - mix.append([False, points[i][int(j/2)]]) + mix.append([False, points[i][int(j / 2)]]) len_total = 0 loc_prev = False tknots = [] @@ -3028,7 +3076,7 @@ def relax_calculate_t(bm_mod, knots, points, regular): if regular: tpoints = [] for p in range(len(points[i])): - tpoints.append((tknots[p] + tknots[p+1]) / 2) + tpoints.append((tknots[p] + tknots[p + 1]) / 2) all_tknots.append(tknots) all_tpoints.append(tpoints) @@ -3049,7 +3097,7 @@ points, splines): t = tknots[i][:] t.append(m) t.sort() - n = t.index(m)-1 + n = t.index(m) - 1 if n > len(splines[i]) - 1: n = len(splines[i]) - 1 elif n < 0: @@ -3057,26 +3105,26 @@ points, splines): if interpolation == 'cubic': ax, bx, cx, dx, tx = splines[i][n][0] - x = ax + bx*(m-tx) + cx*(m-tx)**2 + dx*(m-tx)**3 + x = ax + bx * (m - tx) + cx * (m - tx) ** 2 + dx * (m - tx) ** 3 ay, by, cy, dy, ty = splines[i][n][1] - y = ay + by*(m-ty) + cy*(m-ty)**2 + dy*(m-ty)**3 + y = ay + by * (m - ty) + cy * (m - ty) ** 2 + dy * (m - ty) ** 3 az, bz, cz, dz, tz = splines[i][n][2] - z = az + bz*(m-tz) + cz*(m-tz)**2 + dz*(m-tz)**3 - change.append([p, mathutils.Vector([x,y,z])]) - else: # interpolation == 'linear' + z = az + bz * (m - tz) + cz * (m - tz) ** 2 + dz * (m - tz) ** 3 + change.append([p, mathutils.Vector([x, y, z])]) + else: # interpolation == 'linear' a, d, t, u = splines[i][n] if u == 0: u = 1e-8 - change.append([p, ((m-t)/u)*d + a]) + change.append([p, ((m - t) / u) * d + a]) for c in change: move.append([c[0], (bm_mod.verts[c[0]].co + c[1]) / 2]) return(move) -########################################## -####### Space functions ################## -########################################## +# ######################################## +# ##### Space functions ################## +# ######################################## # calculate relative positions compared to first knot def space_calculate_t(bm_mod, knots): @@ -3117,77 +3165,95 @@ splines): if interpolation == 'cubic': ax, bx, cx, dx, tx = splines[n][0] - x = ax + bx*(m-tx) + cx*(m-tx)**2 + dx*(m-tx)**3 + x = ax + bx * (m - tx) + cx * (m - tx) ** 2 + dx * (m - tx) ** 3 ay, by, cy, dy, ty = splines[n][1] - y = ay + by*(m-ty) + cy*(m-ty)**2 + dy*(m-ty)**3 + y = ay + by * (m - ty) + cy * (m - ty) ** 2 + dy * (m - ty) ** 3 az, bz, cz, dz, tz = splines[n][2] - z = az + bz*(m-tz) + cz*(m-tz)**2 + dz*(m-tz)**3 - move.append([p, mathutils.Vector([x,y,z])]) - else: # interpolation == 'linear' + z = az + bz * (m - tz) + cz * (m - tz) ** 2 + dz * (m - tz) ** 3 + move.append([p, mathutils.Vector([x, y, z])]) + else: # interpolation == 'linear' a, d, t, u = splines[n] - move.append([p, ((m-t)/u)*d + a]) + move.append([p, ((m - t) / u) * d + a]) return(move) -########################################## -####### Operators ######################## -########################################## +# ######################################## +# ##### Operators ######################## +# ######################################## # bridge operator -class Bridge(bpy.types.Operator): +class Bridge(Operator): bl_idname = 'mesh.looptools_bridge' bl_label = "Bridge / Loft" bl_description = "Bridge two, or loft several, loops of vertices" bl_options = {'REGISTER', 'UNDO'} - cubic_strength = bpy.props.FloatProperty(name = "Strength", - description = "Higher strength results in more fluid curves", - default = 1.0, - soft_min = -3.0, - soft_max = 3.0) - interpolation = bpy.props.EnumProperty(name = "Interpolation mode", - items = (('cubic', "Cubic", "Gives curved results"), + cubic_strength = FloatProperty( + name="Strength", + description="Higher strength results in more fluid curves", + default=1.0, + soft_min=-3.0, + soft_max=3.0 + ) + interpolation = EnumProperty( + name="Interpolation mode", + items=(('cubic', "Cubic", "Gives curved results"), ('linear', "Linear", "Basic, fast, straight interpolation")), - description = "Interpolation mode: algorithm used when creating "\ - "segments", - default = 'cubic') - loft = bpy.props.BoolProperty(name = "Loft", - description = "Loft multiple loops, instead of considering them as "\ - "a multi-input for bridging", - default = False) - loft_loop = bpy.props.BoolProperty(name = "Loop", - description = "Connect the first and the last loop with each other", - default = False) - min_width = bpy.props.IntProperty(name = "Minimum width", - description = "Segments with an edge smaller than this are merged "\ - "(compared to base edge)", - default = 0, - min = 0, - max = 100, - subtype = 'PERCENTAGE') - mode = bpy.props.EnumProperty(name = "Mode", - items = (('basic', "Basic", "Fast algorithm"), ('shortest', - "Shortest edge", "Slower algorithm with better vertex matching")), - description = "Algorithm used for bridging", - default = 'shortest') - remove_faces = bpy.props.BoolProperty(name = "Remove faces", - description = "Remove faces that are internal after bridging", - default = True) - reverse = bpy.props.BoolProperty(name = "Reverse", - description = "Manually override the direction in which the loops "\ - "are bridged. Only use if the tool gives the wrong " \ - "result", - default = False) - segments = bpy.props.IntProperty(name = "Segments", - description = "Number of segments used to bridge the gap "\ - "(0 = automatic)", - default = 1, - min = 0, - soft_max = 20) - twist = bpy.props.IntProperty(name = "Twist", - description = "Twist what vertices are connected to each other", - default = 0) + description="Interpolation mode: algorithm used when creating " + "segments", + default='cubic' + ) + loft = BoolProperty( + name="Loft", + description="Loft multiple loops, instead of considering them as " + "a multi-input for bridging", + default=False + ) + loft_loop = BoolProperty( + name="Loop", + description="Connect the first and the last loop with each other", + default=False + ) + min_width = IntProperty( + name="Minimum width", + description="Segments with an edge smaller than this are merged " + "(compared to base edge)", + default=0, + min=0, + max=100, + subtype='PERCENTAGE' + ) + mode = EnumProperty( + name="Mode", + items=(('basic', "Basic", "Fast algorithm"), + ('shortest', "Shortest edge", "Slower algorithm with better vertex matching")), + description="Algorithm used for bridging", + default='shortest' + ) + remove_faces = BoolProperty( + name="Remove faces", + description="Remove faces that are internal after bridging", + default=True + ) + reverse = BoolProperty( + name="Reverse", + description="Manually override the direction in which the loops " + "are bridged. Only use if the tool gives the wrong result", + default=False + ) + segments = IntProperty( + name="Segments", + description="Number of segments used to bridge the gap (0=automatic)", + default=1, + min=0, + soft_max=20 + ) + twist = IntProperty( + name="Twist", + description="Twist what vertices are connected to each other", + default=0 + ) @classmethod def poll(cls, context): @@ -3196,7 +3262,7 @@ class Bridge(bpy.types.Operator): def draw(self, context): layout = self.layout - #layout.prop(self, "mode") # no cases yet where 'basic' mode is needed + # layout.prop(self, "mode") # no cases yet where 'basic' mode is needed # top row col_top = layout.column(align=True) @@ -3220,7 +3286,7 @@ class Bridge(bpy.types.Operator): # override properties col_top.separator() - row = layout.row(align = True) + row = layout.row(align=True) row.prop(self, "twist") row.prop(self, "reverse") @@ -3261,20 +3327,22 @@ class Bridge(bpy.types.Operator): # calculate new geometry vertices = [] faces = [] - max_vert_index = len(bm.verts)-1 + max_vert_index = len(bm.verts) - 1 for i in range(1, len(loops)): - if not self.loft and i%2 == 0: + if not self.loft and i % 2 == 0: continue - lines = bridge_calculate_lines(bm, loops[i-1:i+1], + lines = bridge_calculate_lines(bm, loops[i - 1:i + 1], self.mode, self.twist, self.reverse) vertex_normals = bridge_calculate_virtual_vertex_normals(bm, - lines, loops[i-1:i+1], edge_faces, edgekey_to_edge) + lines, loops[i - 1:i + 1], edge_faces, edgekey_to_edge) segments = bridge_calculate_segments(bm, lines, - loops[i-1:i+1], self.segments) + loops[i - 1:i + 1], self.segments) new_verts, new_faces, max_vert_index = \ - bridge_calculate_geometry(bm, lines, vertex_normals, - segments, self.interpolation, self.cubic_strength, - self.min_width, max_vert_index) + bridge_calculate_geometry( + bm, lines, vertex_normals, + segments, self.interpolation, self.cubic_strength, + self.min_width, max_vert_index + ) if new_verts: vertices += new_verts if new_faces: @@ -3288,8 +3356,9 @@ class Bridge(bpy.types.Operator): # create faces if faces: new_faces = bridge_create_faces(object, bm, faces, self.twist) - old_selected_faces = [i for i, face in enumerate(bm.faces) \ - if face.index in old_selected_faces] # updating list + old_selected_faces = [ + i for i, face in enumerate(bm.faces) if face.index in old_selected_faces + ] # updating list bridge_select_new_faces(new_faces, smooth) # edge-data could have changed, can't use cache next run if faces and not vertices: @@ -3309,49 +3378,64 @@ class Bridge(bpy.types.Operator): # circle operator -class Circle(bpy.types.Operator): +class Circle(Operator): bl_idname = "mesh.looptools_circle" bl_label = "Circle" bl_description = "Move selected vertices into a circle shape" bl_options = {'REGISTER', 'UNDO'} - custom_radius = bpy.props.BoolProperty(name = "Radius", - description = "Force a custom radius", - default = False) - fit = bpy.props.EnumProperty(name = "Method", - items = (("best", "Best fit", "Non-linear least squares"), - ("inside", "Fit inside","Only move vertices towards the center")), - description = "Method used for fitting a circle to the vertices", - default = 'best') - flatten = bpy.props.BoolProperty(name = "Flatten", - description = "Flatten the circle, instead of projecting it on the " \ - "mesh", - default = True) - influence = bpy.props.FloatProperty(name = "Influence", - description = "Force of the tool", - default = 100.0, - min = 0.0, - max = 100.0, - precision = 1, - subtype = 'PERCENTAGE') - lock_x = bpy.props.BoolProperty(name = "Lock X", - description = "Lock editing of the x-coordinate", - default = False) - lock_y = bpy.props.BoolProperty(name = "Lock Y", - description = "Lock editing of the y-coordinate", - default = False) - lock_z = bpy.props.BoolProperty(name = "Lock Z", - description = "Lock editing of the z-coordinate", - default = False) - radius = bpy.props.FloatProperty(name = "Radius", - description = "Custom radius for circle", - default = 1.0, - min = 0.0, - soft_max = 1000.0) - regular = bpy.props.BoolProperty(name = "Regular", - description = "Distribute vertices at constant distances along the " \ - "circle", - default = True) + custom_radius = BoolProperty( + name="Radius", + description="Force a custom radius", + default=False + ) + fit = EnumProperty( + name="Method", + items=(("best", "Best fit", "Non-linear least squares"), + ("inside", "Fit inside", "Only move vertices towards the center")), + description="Method used for fitting a circle to the vertices", + default='best' + ) + flatten = BoolProperty( + name="Flatten", + description="Flatten the circle, instead of projecting it on the mesh", + default=True + ) + influence = FloatProperty( + name="Influence", + description="Force of the tool", + default=100.0, + min=0.0, + max=100.0, + precision=1, + subtype='PERCENTAGE' + ) + lock_x = BoolProperty( + name="Lock X", + description="Lock editing of the x-coordinate", + default=False + ) + lock_y = BoolProperty( + name="Lock Y", + description="Lock editing of the y-coordinate", + default=False + ) + lock_z = BoolProperty(name="Lock Z", + description="Lock editing of the z-coordinate", + default=False + ) + radius = FloatProperty( + name="Radius", + description="Custom radius for circle", + default=1.0, + min=0.0, + soft_max=1000.0 + ) + regular = BoolProperty( + name="Regular", + description="Distribute vertices at constant distances along the circle", + default=True + ) @classmethod def poll(cls, context): @@ -3377,17 +3461,17 @@ class Circle(bpy.types.Operator): col_move = col.column(align=True) row = col_move.row(align=True) if self.lock_x: - row.prop(self, "lock_x", text = "X", icon='LOCKED') + row.prop(self, "lock_x", text="X", icon='LOCKED') else: - row.prop(self, "lock_x", text = "X", icon='UNLOCKED') + row.prop(self, "lock_x", text="X", icon='UNLOCKED') if self.lock_y: - row.prop(self, "lock_y", text = "Y", icon='LOCKED') + row.prop(self, "lock_y", text="Y", icon='LOCKED') else: - row.prop(self, "lock_y", text = "Y", icon='UNLOCKED') + row.prop(self, "lock_y", text="Y", icon='UNLOCKED') if self.lock_z: - row.prop(self, "lock_z", text = "Z", icon='LOCKED') + row.prop(self, "lock_z", text="Z", icon='LOCKED') else: - row.prop(self, "lock_z", text = "Z", icon='UNLOCKED') + row.prop(self, "lock_z", text="Z", icon='UNLOCKED') col_move.prop(self, "influence") def invoke(self, context, event): @@ -3430,7 +3514,7 @@ class Circle(bpy.types.Operator): # calculate circle if self.fit == 'best': x0, y0, r = circle_calculate_best_fit(locs_2d) - else: # self.fit == 'inside' + else: # self.fit == 'inside' x0, y0, r = circle_calculate_min_fit(locs_2d) # radius override if self.custom_radius: @@ -3467,49 +3551,61 @@ class Circle(bpy.types.Operator): # curve operator -class Curve(bpy.types.Operator): +class Curve(Operator): bl_idname = "mesh.looptools_curve" bl_label = "Curve" bl_description = "Turn a loop into a smooth curve" bl_options = {'REGISTER', 'UNDO'} - boundaries = bpy.props.BoolProperty(name = "Boundaries", - description = "Limit the tool to work within the boundaries of the "\ - "selected vertices", - default = False) - influence = bpy.props.FloatProperty(name = "Influence", - description = "Force of the tool", - default = 100.0, - min = 0.0, - max = 100.0, - precision = 1, - subtype = 'PERCENTAGE') - interpolation = bpy.props.EnumProperty(name = "Interpolation", - items = (("cubic", "Cubic", "Natural cubic spline, smooth results"), - ("linear", "Linear", "Simple and fast linear algorithm")), - description = "Algorithm used for interpolation", - default = 'cubic') - lock_x = bpy.props.BoolProperty(name = "Lock X", - description = "Lock editing of the x-coordinate", - default = False) - lock_y = bpy.props.BoolProperty(name = "Lock Y", - description = "Lock editing of the y-coordinate", - default = False) - lock_z = bpy.props.BoolProperty(name = "Lock Z", - description = "Lock editing of the z-coordinate", - default = False) - regular = bpy.props.BoolProperty(name = "Regular", - description = "Distribute vertices at constant distances along the" \ - "curve", - default = True) - restriction = bpy.props.EnumProperty(name = "Restriction", - items = (("none", "None", "No restrictions on vertex movement"), - ("extrude", "Extrude only","Only allow extrusions (no "\ - "indentations)"), - ("indent", "Indent only", "Only allow indentation (no "\ - "extrusions)")), - description = "Restrictions on how the vertices can be moved", - default = 'none') + boundaries = BoolProperty( + name="Boundaries", + description="Limit the tool to work within the boundaries of the selected vertices", + default=False + ) + influence = FloatProperty( + name="Influence", + description="Force of the tool", + default=100.0, + min=0.0, + max=100.0, + precision=1, + subtype='PERCENTAGE' + ) + interpolation = EnumProperty( + name="Interpolation", + items=(("cubic", "Cubic", "Natural cubic spline, smooth results"), + ("linear", "Linear", "Simple and fast linear algorithm")), + description="Algorithm used for interpolation", + default='cubic' + ) + lock_x = BoolProperty( + name="Lock X", + description="Lock editing of the x-coordinate", + default=False + ) + lock_y = BoolProperty( + name="Lock Y", + description="Lock editing of the y-coordinate", + default=False + ) + lock_z = BoolProperty( + name="Lock Z", + description="Lock editing of the z-coordinate", + default=False + ) + regular = BoolProperty( + name="Regular", + description="Distribute vertices at constant distances along the curve", + default=True + ) + restriction = EnumProperty( + name="Restriction", + items=(("none", "None", "No restrictions on vertex movement"), + ("extrude", "Extrude only", "Only allow extrusions (no indentations)"), + ("indent", "Indent only", "Only allow indentation (no extrusions)")), + description="Restrictions on how the vertices can be moved", + default='none' + ) @classmethod def poll(cls, context): @@ -3529,17 +3625,17 @@ class Curve(bpy.types.Operator): col_move = col.column(align=True) row = col_move.row(align=True) if self.lock_x: - row.prop(self, "lock_x", text = "X", icon='LOCKED') + row.prop(self, "lock_x", text="X", icon='LOCKED') else: - row.prop(self, "lock_x", text = "X", icon='UNLOCKED') + row.prop(self, "lock_x", text="X", icon='UNLOCKED') if self.lock_y: - row.prop(self, "lock_y", text = "Y", icon='LOCKED') + row.prop(self, "lock_y", text="Y", icon='LOCKED') else: - row.prop(self, "lock_y", text = "Y", icon='UNLOCKED') + row.prop(self, "lock_y", text="Y", icon='UNLOCKED') if self.lock_z: - row.prop(self, "lock_z", text = "Z", icon='LOCKED') + row.prop(self, "lock_z", text="Z", icon='LOCKED') else: - row.prop(self, "lock_z", text = "Z", icon='UNLOCKED') + row.prop(self, "lock_z", text="Z", icon='UNLOCKED') col_move.prop(self, "influence") def invoke(self, context, event): @@ -3562,8 +3658,9 @@ class Curve(bpy.types.Operator): self.boundaries, context.scene) mapping = get_mapping(derived, bm, bm_mod, False, True, loops) loops = check_loops(loops, mapping, bm_mod) - verts_selected = [v.index for v in bm_mod.verts if v.select \ - and not v.hide] + verts_selected = [ + v.index for v in bm_mod.verts if v.select and not v.hide + ] # saving cache for faster execution next time if not cached: @@ -3599,42 +3696,51 @@ class Curve(bpy.types.Operator): # flatten operator -class Flatten(bpy.types.Operator): +class Flatten(Operator): bl_idname = "mesh.looptools_flatten" bl_label = "Flatten" bl_description = "Flatten vertices on a best-fitting plane" bl_options = {'REGISTER', 'UNDO'} - influence = bpy.props.FloatProperty(name = "Influence", - description = "Force of the tool", - default = 100.0, - min = 0.0, - max = 100.0, - precision = 1, - subtype = 'PERCENTAGE') - lock_x = bpy.props.BoolProperty(name = "Lock X", - description = "Lock editing of the x-coordinate", - default = False) - lock_y = bpy.props.BoolProperty(name = "Lock Y", - description = "Lock editing of the y-coordinate", - default = False) - lock_z = bpy.props.BoolProperty(name = "Lock Z", - description = "Lock editing of the z-coordinate", - default = False) - plane = bpy.props.EnumProperty(name = "Plane", - items = (("best_fit", "Best fit", "Calculate a best fitting plane"), - ("normal", "Normal", "Derive plane from averaging vertex "\ - "normals"), - ("view", "View", "Flatten on a plane perpendicular to the "\ - "viewing angle")), - description = "Plane on which vertices are flattened", - default = 'best_fit') - restriction = bpy.props.EnumProperty(name = "Restriction", - items = (("none", "None", "No restrictions on vertex movement"), - ("bounding_box", "Bounding box", "Vertices are restricted to "\ - "movement inside the bounding box of the selection")), - description = "Restrictions on how the vertices can be moved", - default = 'none') + influence = FloatProperty( + name="Influence", + description="Force of the tool", + default=100.0, + min=0.0, + max=100.0, + precision=1, + subtype='PERCENTAGE' + ) + lock_x = BoolProperty( + name="Lock X", + description="Lock editing of the x-coordinate", + default=False + ) + lock_y = BoolProperty( + name="Lock Y", + description="Lock editing of the y-coordinate", + default=False + ) + lock_z = BoolProperty(name="Lock Z", + description="Lock editing of the z-coordinate", + default=False + ) + plane = EnumProperty( + name="Plane", + items=(("best_fit", "Best fit", "Calculate a best fitting plane"), + ("normal", "Normal", "Derive plane from averaging vertex normals"), + ("view", "View", "Flatten on a plane perpendicular to the viewing angle")), + description="Plane on which vertices are flattened", + default='best_fit' + ) + restriction = EnumProperty( + name="Restriction", + items=(("none", "None", "No restrictions on vertex movement"), + ("bounding_box", "Bounding box", "Vertices are restricted to " + "movement inside the bounding box of the selection")), + description="Restrictions on how the vertices can be moved", + default='none' + ) @classmethod def poll(cls, context): @@ -3646,23 +3752,23 @@ class Flatten(bpy.types.Operator): col = layout.column() col.prop(self, "plane") - #col.prop(self, "restriction") + # col.prop(self, "restriction") col.separator() col_move = col.column(align=True) row = col_move.row(align=True) if self.lock_x: - row.prop(self, "lock_x", text = "X", icon='LOCKED') + row.prop(self, "lock_x", text="X", icon='LOCKED') else: - row.prop(self, "lock_x", text = "X", icon='UNLOCKED') + row.prop(self, "lock_x", text="X", icon='UNLOCKED') if self.lock_y: - row.prop(self, "lock_y", text = "Y", icon='LOCKED') + row.prop(self, "lock_y", text="Y", icon='LOCKED') else: - row.prop(self, "lock_y", text = "Y", icon='UNLOCKED') + row.prop(self, "lock_y", text="Y", icon='UNLOCKED') if self.lock_z: - row.prop(self, "lock_z", text = "Z", icon='LOCKED') + row.prop(self, "lock_z", text="Z", icon='LOCKED') else: - row.prop(self, "lock_z", text = "Z", icon='UNLOCKED') + row.prop(self, "lock_z", text="Z", icon='UNLOCKED') col_move.prop(self, "influence") def invoke(self, context, event): @@ -3712,7 +3818,7 @@ class Flatten(bpy.types.Operator): # gstretch operator -class RemoveGP(bpy.types.Operator): +class RemoveGP(Operator): bl_idname = "remove.gp" bl_label = "Remove GP" bl_description = "Remove all Grease Pencil Strokes" @@ -3728,83 +3834,106 @@ class RemoveGP(bpy.types.Operator): return{'FINISHED'} -class GStretch(bpy.types.Operator): + +class GStretch(Operator): bl_idname = "mesh.looptools_gstretch" bl_label = "Gstretch" bl_description = "Stretch selected vertices to Grease Pencil stroke" bl_options = {'REGISTER', 'UNDO'} - conversion = bpy.props.EnumProperty(name = "Conversion", - items = (("distance", "Distance", "Set the distance between vertices "\ - "of the converted grease pencil stroke"), - ("limit_vertices", "Limit vertices", "Set the minimum and maximum "\ - "number of vertices that converted GP strokes will have"), - ("vertices", "Exact vertices", "Set the exact number of vertices "\ - "that converted grease pencil strokes will have. Short strokes "\ - "with few points may contain less vertices than this number."), - ("none", "No simplification", "Convert each grease pencil point "\ - "to a vertex")), - description = "If grease pencil strokes are converted to geometry, "\ - "use this simplification method", - default = 'limit_vertices') - conversion_distance = bpy.props.FloatProperty(name = "Distance", - description = "Absolute distance between vertices along the converted "\ - "grease pencil stroke", - default = 0.1, - min = 0.000001, - soft_min = 0.01, - soft_max = 100) - conversion_max = bpy.props.IntProperty(name = "Max Vertices", - description = "Maximum number of vertices grease pencil strokes will "\ - "have, when they are converted to geomtery", - default = 32, - min = 3, - soft_max = 500, - update = gstretch_update_min) - conversion_min = bpy.props.IntProperty(name = "Min Vertices", - description = "Minimum number of vertices grease pencil strokes will "\ - "have, when they are converted to geomtery", - default = 8, - min = 3, - soft_max = 500, - update = gstretch_update_max) - conversion_vertices = bpy.props.IntProperty(name = "Vertices", - description = "Number of vertices grease pencil strokes will "\ - "have, when they are converted to geometry. If strokes have less "\ - "points than required, the 'Spread evenly' method is used", - default = 32, - min = 3, - soft_max = 500) - delete_strokes = bpy.props.BoolProperty(name="Delete strokes", - description = "Remove Grease Pencil strokes if they have been used "\ - "for Gstretch. WARNING: DOES NOT SUPPORT UNDO", - default = False) - influence = bpy.props.FloatProperty(name = "Influence", - description = "Force of the tool", - default = 100.0, - min = 0.0, - max = 100.0, - precision = 1, - subtype = 'PERCENTAGE') - lock_x = bpy.props.BoolProperty(name = "Lock X", - description = "Lock editing of the x-coordinate", - default = False) - lock_y = bpy.props.BoolProperty(name = "Lock Y", - description = "Lock editing of the y-coordinate", - default = False) - lock_z = bpy.props.BoolProperty(name = "Lock Z", - description = "Lock editing of the z-coordinate", - default = False) - method = bpy.props.EnumProperty(name = "Method", - items = (("project", "Project", "Project vertices onto the stroke, "\ - "using vertex normals and connected edges"), - ("irregular", "Spread", "Distribute vertices along the full "\ - "stroke, retaining relative distances between the vertices"), - ("regular", "Spread evenly", "Distribute vertices at regular "\ - "distances along the full stroke")), - description = "Method of distributing the vertices over the Grease "\ - "Pencil stroke", - default = 'regular') + conversion = EnumProperty( + name="Conversion", + items=(("distance", "Distance", "Set the distance between vertices " + "of the converted grease pencil stroke"), + ("limit_vertices", "Limit vertices", "Set the minimum and maximum " + "number of vertices that converted GP strokes will have"), + ("vertices", "Exact vertices", "Set the exact number of vertices " + "that converted grease pencil strokes will have. Short strokes " + "with few points may contain less vertices than this number."), + ("none", "No simplification", "Convert each grease pencil point " + "to a vertex")), + description="If grease pencil strokes are converted to geometry, " + "use this simplification method", + default='limit_vertices' + ) + conversion_distance = FloatProperty( + name="Distance", + description="Absolute distance between vertices along the converted " + "grease pencil stroke", + default=0.1, + min=0.000001, + soft_min=0.01, + soft_max=100 + ) + conversion_max = IntProperty( + name="Max Vertices", + description="Maximum number of vertices grease pencil strokes will " + "have, when they are converted to geomtery", + default=32, + min=3, + soft_max=500, + update=gstretch_update_min + ) + conversion_min = IntProperty( + name="Min Vertices", + description="Minimum number of vertices grease pencil strokes will " + "have, when they are converted to geomtery", + default=8, + min=3, + soft_max=500, + update=gstretch_update_max + ) + conversion_vertices = IntProperty( + name="Vertices", + description="Number of vertices grease pencil strokes will " + "have, when they are converted to geometry. If strokes have less " + "points than required, the 'Spread evenly' method is used", + default=32, + min=3, + soft_max=500 + ) + delete_strokes = BoolProperty( + name="Delete strokes", + description="Remove Grease Pencil strokes if they have been used " + "for Gstretch. WARNING: DOES NOT SUPPORT UNDO", + default=False + ) + influence = FloatProperty( + name="Influence", + description="Force of the tool", + default=100.0, + min=0.0, + max=100.0, + precision=1, + subtype='PERCENTAGE' + ) + lock_x = BoolProperty( + name="Lock X", + description="Lock editing of the x-coordinate", + default=False + ) + lock_y = BoolProperty( + name="Lock Y", + description="Lock editing of the y-coordinate", + default=False + ) + lock_z = BoolProperty( + name="Lock Z", + description="Lock editing of the z-coordinate", + default=False + ) + method = EnumProperty( + name="Method", + items=(("project", "Project", "Project vertices onto the stroke, " + "using vertex normals and connected edges"), + ("irregular", "Spread", "Distribute vertices along the full " + "stroke, retaining relative distances between the vertices"), + ("regular", "Spread evenly", "Distribute vertices at regular " + "distances along the full stroke")), + description="Method of distributing the vertices over the Grease " + "Pencil stroke", + default='regular' + ) @classmethod def poll(cls, context): @@ -3833,20 +3962,20 @@ class GStretch(bpy.types.Operator): col_move = col.column(align=True) row = col_move.row(align=True) if self.lock_x: - row.prop(self, "lock_x", text = "X", icon='LOCKED') + row.prop(self, "lock_x", text="X", icon='LOCKED') else: - row.prop(self, "lock_x", text = "X", icon='UNLOCKED') + row.prop(self, "lock_x", text="X", icon='UNLOCKED') if self.lock_y: - row.prop(self, "lock_y", text = "Y", icon='LOCKED') + row.prop(self, "lock_y", text="Y", icon='LOCKED') else: - row.prop(self, "lock_y", text = "Y", icon='UNLOCKED') + row.prop(self, "lock_y", text="Y", icon='UNLOCKED') if self.lock_z: - row.prop(self, "lock_z", text = "Z", icon='LOCKED') + row.prop(self, "lock_z", text="Z", icon='LOCKED') else: - row.prop(self, "lock_z", text = "Z", icon='UNLOCKED') + row.prop(self, "lock_z", text="Z", icon='UNLOCKED') col_move.prop(self, "influence") col.separator() - col.operator("remove.gp", text = "Delete GP Strokes") + col.operator("remove.gp", text="Delete GP Strokes") def invoke(self, context, event): # flush cached strokes @@ -3858,7 +3987,6 @@ class GStretch(bpy.types.Operator): def execute(self, context): # initialise - scene = context.scene global_undo, object, bm = initialise() settings_write(self) @@ -3886,7 +4014,7 @@ class GStretch(bpy.types.Operator): else: # get loops and strokes if get_grease_pencil(object, context): - # find loops + # find loops derived, bm_mod, loops = get_connected_input(object, bm, context.scene, input='selected') mapping = get_mapping(derived, bm, bm_mod, False, False, loops) @@ -3901,8 +4029,10 @@ class GStretch(bpy.types.Operator): bm_mod.verts.ensure_lookup_table() bm_mod.edges.ensure_lookup_table() bm_mod.faces.ensure_lookup_table() - edge_keys = [edgekey(edge) for edge in bm_mod.edges if \ - edge.select and not edge.hide] + edge_keys = [ + edgekey(edge) for edge in bm_mod.edges if + edge.select and not edge.hide + ] loops = get_connected_selections(edge_keys) loops = check_loops(loops, mapping, bm_mod) # create fake strokes @@ -3968,35 +4098,42 @@ class GStretch(bpy.types.Operator): # relax operator -class Relax(bpy.types.Operator): +class Relax(Operator): bl_idname = "mesh.looptools_relax" bl_label = "Relax" bl_description = "Relax the loop, so it is smoother" bl_options = {'REGISTER', 'UNDO'} - input = bpy.props.EnumProperty(name = "Input", - items = (("all", "Parallel (all)", "Also use non-selected "\ - "parallel loops as input"), - ("selected", "Selection","Only use selected vertices as input")), - description = "Loops that are relaxed", - default = 'selected') - interpolation = bpy.props.EnumProperty(name = "Interpolation", - items = (("cubic", "Cubic", "Natural cubic spline, smooth results"), - ("linear", "Linear", "Simple and fast linear algorithm")), - description = "Algorithm used for interpolation", - default = 'cubic') - iterations = bpy.props.EnumProperty(name = "Iterations", - items = (("1", "1", "One"), - ("3", "3", "Three"), - ("5", "5", "Five"), - ("10", "10", "Ten"), - ("25", "25", "Twenty-five")), - description = "Number of times the loop is relaxed", - default = "1") - regular = bpy.props.BoolProperty(name = "Regular", - description = "Distribute vertices at constant distances along the" \ - "loop", - default = True) + input = EnumProperty( + name="Input", + items=(("all", "Parallel (all)", "Also use non-selected " + "parallel loops as input"), + ("selected", "Selection", "Only use selected vertices as input")), + description="Loops that are relaxed", + default='selected' + ) + interpolation = EnumProperty( + name="Interpolation", + items=(("cubic", "Cubic", "Natural cubic spline, smooth results"), + ("linear", "Linear", "Simple and fast linear algorithm")), + description="Algorithm used for interpolation", + default='cubic' + ) + iterations = EnumProperty( + name="Iterations", + items=(("1", "1", "One"), + ("3", "3", "Three"), + ("5", "5", "Five"), + ("10", "10", "Ten"), + ("25", "25", "Twenty-five")), + description="Number of times the loop is relaxed", + default="1" + ) + regular = BoolProperty( + name="Regular", + description="Distribute vertices at constant distances along the loop", + default=True + ) @classmethod def poll(cls, context): @@ -4060,39 +4197,51 @@ class Relax(bpy.types.Operator): # space operator -class Space(bpy.types.Operator): +class Space(Operator): bl_idname = "mesh.looptools_space" bl_label = "Space" bl_description = "Space the vertices in a regular distrubtion on the loop" bl_options = {'REGISTER', 'UNDO'} - influence = bpy.props.FloatProperty(name = "Influence", - description = "Force of the tool", - default = 100.0, - min = 0.0, - max = 100.0, - precision = 1, - subtype = 'PERCENTAGE') - input = bpy.props.EnumProperty(name = "Input", - items = (("all", "Parallel (all)", "Also use non-selected "\ + influence = FloatProperty( + name="Influence", + description="Force of the tool", + default=100.0, + min=0.0, + max=100.0, + precision=1, + subtype='PERCENTAGE' + ) + input = EnumProperty( + name="Input", + items=(("all", "Parallel (all)", "Also use non-selected " "parallel loops as input"), - ("selected", "Selection","Only use selected vertices as input")), - description = "Loops that are spaced", - default = 'selected') - interpolation = bpy.props.EnumProperty(name = "Interpolation", - items = (("cubic", "Cubic", "Natural cubic spline, smooth results"), - ("linear", "Linear", "Vertices are projected on existing edges")), - description = "Algorithm used for interpolation", - default = 'cubic') - lock_x = bpy.props.BoolProperty(name = "Lock X", - description = "Lock editing of the x-coordinate", - default = False) - lock_y = bpy.props.BoolProperty(name = "Lock Y", - description = "Lock editing of the y-coordinate", - default = False) - lock_z = bpy.props.BoolProperty(name = "Lock Z", - description = "Lock editing of the z-coordinate", - default = False) + ("selected", "Selection", "Only use selected vertices as input")), + description="Loops that are spaced", + default='selected' + ) + interpolation = EnumProperty( + name="Interpolation", + items=(("cubic", "Cubic", "Natural cubic spline, smooth results"), + ("linear", "Linear", "Vertices are projected on existing edges")), + description="Algorithm used for interpolation", + default='cubic' + ) + lock_x = BoolProperty( + name="Lock X", + description="Lock editing of the x-coordinate", + default=False + ) + lock_y = BoolProperty( + name="Lock Y", + description="Lock editing of the y-coordinate", + default=False + ) + lock_z = BoolProperty( + name="Lock Z", + description="Lock editing of the z-coordinate", + default=False + ) @classmethod def poll(cls, context): @@ -4110,17 +4259,17 @@ class Space(bpy.types.Operator): col_move = col.column(align=True) row = col_move.row(align=True) if self.lock_x: - row.prop(self, "lock_x", text = "X", icon='LOCKED') + row.prop(self, "lock_x", text="X", icon='LOCKED') else: - row.prop(self, "lock_x", text = "X", icon='UNLOCKED') + row.prop(self, "lock_x", text="X", icon='UNLOCKED') if self.lock_y: - row.prop(self, "lock_y", text = "Y", icon='LOCKED') + row.prop(self, "lock_y", text="Y", icon='LOCKED') else: - row.prop(self, "lock_y", text = "Y", icon='UNLOCKED') + row.prop(self, "lock_y", text="Y", icon='UNLOCKED') if self.lock_z: - row.prop(self, "lock_z", text = "Z", icon='LOCKED') + row.prop(self, "lock_z", text="Z", icon='LOCKED') else: - row.prop(self, "lock_z", text = "Z", icon='UNLOCKED') + row.prop(self, "lock_z", text="Z", icon='UNLOCKED') col_move.prop(self, "influence") def invoke(self, context, event): @@ -4152,7 +4301,7 @@ class Space(bpy.types.Operator): move = [] for loop in loops: # calculate splines and new positions - if loop[1]: # circular + if loop[1]: # circular loop[0].append(loop[0][0]) tknots, tpoints = space_calculate_t(bm_mod, loop[0][:]) splines = calculate_splines(self.interpolation, bm_mod, @@ -4174,12 +4323,12 @@ class Space(bpy.types.Operator): return{'FINISHED'} -########################################## -####### GUI and registration ############# -########################################## +# ######################################## +# ##### GUI and registration ############# +# ######################################## # menu containing all tools -class VIEW3D_MT_edit_mesh_looptools(bpy.types.Menu): +class VIEW3D_MT_edit_mesh_looptools(Menu): bl_label = "LoopTools" def draw(self, context): @@ -4196,7 +4345,7 @@ class VIEW3D_MT_edit_mesh_looptools(bpy.types.Menu): # panel containing all tools -class VIEW3D_PT_tools_looptools(bpy.types.Panel): +class VIEW3D_PT_tools_looptools(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'TOOLS' bl_category = 'Tools' @@ -4219,7 +4368,7 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel): # bridge - settings if lt.display_bridge: box = col.column(align=True).box().column() - #box.prop(self, "mode") + # box.prop(self, "mode") # top row col_top = box.column(align=True) @@ -4241,7 +4390,7 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel): # override properties col_top.separator() - row = box.row(align = True) + row = box.row(align=True) row.prop(lt, "bridge_twist") row.prop(lt, "bridge_reverse") @@ -4270,17 +4419,17 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel): col_move = box.column(align=True) row = col_move.row(align=True) if lt.circle_lock_x: - row.prop(lt, "circle_lock_x", text = "X", icon='LOCKED') + row.prop(lt, "circle_lock_x", text="X", icon='LOCKED') else: - row.prop(lt, "circle_lock_x", text = "X", icon='UNLOCKED') + row.prop(lt, "circle_lock_x", text="X", icon='UNLOCKED') if lt.circle_lock_y: - row.prop(lt, "circle_lock_y", text = "Y", icon='LOCKED') + row.prop(lt, "circle_lock_y", text="Y", icon='LOCKED') else: - row.prop(lt, "circle_lock_y", text = "Y", icon='UNLOCKED') + row.prop(lt, "circle_lock_y", text="Y", icon='UNLOCKED') if lt.circle_lock_z: - row.prop(lt, "circle_lock_z", text = "Z", icon='LOCKED') + row.prop(lt, "circle_lock_z", text="Z", icon='LOCKED') else: - row.prop(lt, "circle_lock_z", text = "Z", icon='UNLOCKED') + row.prop(lt, "circle_lock_z", text="Z", icon='UNLOCKED') col_move.prop(lt, "circle_influence") # curve - first line @@ -4302,17 +4451,17 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel): col_move = box.column(align=True) row = col_move.row(align=True) if lt.curve_lock_x: - row.prop(lt, "curve_lock_x", text = "X", icon='LOCKED') + row.prop(lt, "curve_lock_x", text="X", icon='LOCKED') else: - row.prop(lt, "curve_lock_x", text = "X", icon='UNLOCKED') + row.prop(lt, "curve_lock_x", text="X", icon='UNLOCKED') if lt.curve_lock_y: - row.prop(lt, "curve_lock_y", text = "Y", icon='LOCKED') + row.prop(lt, "curve_lock_y", text="Y", icon='LOCKED') else: - row.prop(lt, "curve_lock_y", text = "Y", icon='UNLOCKED') + row.prop(lt, "curve_lock_y", text="Y", icon='UNLOCKED') if lt.curve_lock_z: - row.prop(lt, "curve_lock_z", text = "Z", icon='LOCKED') + row.prop(lt, "curve_lock_z", text="Z", icon='LOCKED') else: - row.prop(lt, "curve_lock_z", text = "Z", icon='UNLOCKED') + row.prop(lt, "curve_lock_z", text="Z", icon='UNLOCKED') col_move.prop(lt, "curve_influence") # flatten - first line @@ -4326,23 +4475,23 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel): if lt.display_flatten: box = col.column(align=True).box().column() box.prop(lt, "flatten_plane") - #box.prop(lt, "flatten_restriction") + # box.prop(lt, "flatten_restriction") box.separator() col_move = box.column(align=True) row = col_move.row(align=True) if lt.flatten_lock_x: - row.prop(lt, "flatten_lock_x", text = "X", icon='LOCKED') + row.prop(lt, "flatten_lock_x", text="X", icon='LOCKED') else: - row.prop(lt, "flatten_lock_x", text = "X", icon='UNLOCKED') + row.prop(lt, "flatten_lock_x", text="X", icon='UNLOCKED') if lt.flatten_lock_y: - row.prop(lt, "flatten_lock_y", text = "Y", icon='LOCKED') + row.prop(lt, "flatten_lock_y", text="Y", icon='LOCKED') else: - row.prop(lt, "flatten_lock_y", text = "Y", icon='UNLOCKED') + row.prop(lt, "flatten_lock_y", text="Y", icon='UNLOCKED') if lt.flatten_lock_z: - row.prop(lt, "flatten_lock_z", text = "Z", icon='LOCKED') + row.prop(lt, "flatten_lock_z", text="Z", icon='LOCKED') else: - row.prop(lt, "flatten_lock_z", text = "Z", icon='UNLOCKED') + row.prop(lt, "flatten_lock_z", text="Z", icon='UNLOCKED') col_move.prop(lt, "flatten_influence") # gstretch - first line @@ -4357,7 +4506,6 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel): box = col.column(align=True).box().column() box.prop(lt, "gstretch_method") - col_conv = box.column(align=True) col_conv.prop(lt, "gstretch_conversion", text="") if lt.gstretch_conversion == 'distance': @@ -4373,19 +4521,19 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel): col_move = box.column(align=True) row = col_move.row(align=True) if lt.gstretch_lock_x: - row.prop(lt, "gstretch_lock_x", text = "X", icon='LOCKED') + row.prop(lt, "gstretch_lock_x", text="X", icon='LOCKED') else: - row.prop(lt, "gstretch_lock_x", text = "X", icon='UNLOCKED') + row.prop(lt, "gstretch_lock_x", text="X", icon='UNLOCKED') if lt.gstretch_lock_y: - row.prop(lt, "gstretch_lock_y", text = "Y", icon='LOCKED') + row.prop(lt, "gstretch_lock_y", text="Y", icon='LOCKED') else: - row.prop(lt, "gstretch_lock_y", text = "Y", icon='UNLOCKED') + row.prop(lt, "gstretch_lock_y", text="Y", icon='UNLOCKED') if lt.gstretch_lock_z: - row.prop(lt, "gstretch_lock_z", text = "Z", icon='LOCKED') + row.prop(lt, "gstretch_lock_z", text="Z", icon='LOCKED') else: - row.prop(lt, "gstretch_lock_z", text = "Z", icon='UNLOCKED') + row.prop(lt, "gstretch_lock_z", text="Z", icon='UNLOCKED') col_move.prop(lt, "gstretch_influence") - box.operator("remove.gp", text = "Delete GP Strokes") + box.operator("remove.gp", text="Delete GP Strokes") # loft - first line split = col.split(percentage=0.15, align=True) @@ -4397,7 +4545,7 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel): # loft - settings if lt.display_loft: box = col.column(align=True).box().column() - #box.prop(self, "mode") + # box.prop(self, "mode") # top row col_top = box.column(align=True) @@ -4420,7 +4568,7 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel): # override properties col_top.separator() - row = box.row(align = True) + row = box.row(align=True) row.prop(lt, "bridge_twist") row.prop(lt, "bridge_reverse") @@ -4456,339 +4604,448 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel): col_move = box.column(align=True) row = col_move.row(align=True) if lt.space_lock_x: - row.prop(lt, "space_lock_x", text = "X", icon='LOCKED') + row.prop(lt, "space_lock_x", text="X", icon='LOCKED') else: - row.prop(lt, "space_lock_x", text = "X", icon='UNLOCKED') + row.prop(lt, "space_lock_x", text="X", icon='UNLOCKED') if lt.space_lock_y: - row.prop(lt, "space_lock_y", text = "Y", icon='LOCKED') + row.prop(lt, "space_lock_y", text="Y", icon='LOCKED') else: - row.prop(lt, "space_lock_y", text = "Y", icon='UNLOCKED') + row.prop(lt, "space_lock_y", text="Y", icon='UNLOCKED') if lt.space_lock_z: - row.prop(lt, "space_lock_z", text = "Z", icon='LOCKED') + row.prop(lt, "space_lock_z", text="Z", icon='LOCKED') else: - row.prop(lt, "space_lock_z", text = "Z", icon='UNLOCKED') + row.prop(lt, "space_lock_z", text="Z", icon='UNLOCKED') col_move.prop(lt, "space_influence") # property group containing all properties for the gui in the panel -class LoopToolsProps(bpy.types.PropertyGroup): +class LoopToolsProps(PropertyGroup): """ Fake module like class bpy.context.window_manager.looptools """ - # general display properties - display_bridge = bpy.props.BoolProperty(name = "Bridge settings", - description = "Display settings of the Bridge tool", - default = False) - display_circle = bpy.props.BoolProperty(name = "Circle settings", - description = "Display settings of the Circle tool", - default = False) - display_curve = bpy.props.BoolProperty(name = "Curve settings", - description = "Display settings of the Curve tool", - default = False) - display_flatten = bpy.props.BoolProperty(name = "Flatten settings", - description = "Display settings of the Flatten tool", - default = False) - display_gstretch = bpy.props.BoolProperty(name = "Gstretch settings", - description = "Display settings of the Gstretch tool", - default = False) - display_loft = bpy.props.BoolProperty(name = "Loft settings", - description = "Display settings of the Loft tool", - default = False) - display_relax = bpy.props.BoolProperty(name = "Relax settings", - description = "Display settings of the Relax tool", - default = False) - display_space = bpy.props.BoolProperty(name = "Space settings", - description = "Display settings of the Space tool", - default = False) + display_bridge = BoolProperty( + name="Bridge settings", + description="Display settings of the Bridge tool", + default=False + ) + display_circle = BoolProperty( + name="Circle settings", + description="Display settings of the Circle tool", + default=False + ) + display_curve = BoolProperty( + name="Curve settings", + description="Display settings of the Curve tool", + default=False + ) + display_flatten = BoolProperty( + name="Flatten settings", + description="Display settings of the Flatten tool", + default=False + ) + display_gstretch = BoolProperty( + name="Gstretch settings", + description="Display settings of the Gstretch tool", + default=False + ) + display_loft = BoolProperty( + name="Loft settings", + description="Display settings of the Loft tool", + default=False + ) + display_relax = BoolProperty( + name="Relax settings", + description="Display settings of the Relax tool", + default=False + ) + display_space = BoolProperty( + name="Space settings", + description="Display settings of the Space tool", + default=False + ) # bridge properties - bridge_cubic_strength = bpy.props.FloatProperty(name = "Strength", - description = "Higher strength results in more fluid curves", - default = 1.0, - soft_min = -3.0, - soft_max = 3.0) - bridge_interpolation = bpy.props.EnumProperty(name = "Interpolation mode", - items = (('cubic', "Cubic", "Gives curved results"), - ('linear', "Linear", "Basic, fast, straight interpolation")), - description = "Interpolation mode: algorithm used when creating "\ - "segments", - default = 'cubic') - bridge_loft = bpy.props.BoolProperty(name = "Loft", - description = "Loft multiple loops, instead of considering them as "\ - "a multi-input for bridging", - default = False) - bridge_loft_loop = bpy.props.BoolProperty(name = "Loop", - description = "Connect the first and the last loop with each other", - default = False) - bridge_min_width = bpy.props.IntProperty(name = "Minimum width", - description = "Segments with an edge smaller than this are merged "\ - "(compared to base edge)", - default = 0, - min = 0, - max = 100, - subtype = 'PERCENTAGE') - bridge_mode = bpy.props.EnumProperty(name = "Mode", - items = (('basic', "Basic", "Fast algorithm"), - ('shortest', "Shortest edge", "Slower algorithm with " \ + bridge_cubic_strength = FloatProperty( + name="Strength", + description="Higher strength results in more fluid curves", + default=1.0, + soft_min=-3.0, + soft_max=3.0 + ) + bridge_interpolation = EnumProperty( + name="Interpolation mode", + items=(('cubic', "Cubic", "Gives curved results"), + ('linear', "Linear", "Basic, fast, straight interpolation")), + description="Interpolation mode: algorithm used when creating segments", + default='cubic' + ) + bridge_loft = BoolProperty( + name="Loft", + description="Loft multiple loops, instead of considering them as " + "a multi-input for bridging", + default=False + ) + bridge_loft_loop = BoolProperty( + name="Loop", + description="Connect the first and the last loop with each other", + default=False + ) + bridge_min_width = IntProperty( + name="Minimum width", + description="Segments with an edge smaller than this are merged " + "(compared to base edge)", + default=0, + min=0, + max=100, + subtype='PERCENTAGE' + ) + bridge_mode = EnumProperty( + name="Mode", + items=(('basic', "Basic", "Fast algorithm"), + ('shortest', "Shortest edge", "Slower algorithm with " "better vertex matching")), - description = "Algorithm used for bridging", - default = 'shortest') - bridge_remove_faces = bpy.props.BoolProperty(name = "Remove faces", - description = "Remove faces that are internal after bridging", - default = True) - bridge_reverse = bpy.props.BoolProperty(name = "Reverse", - description = "Manually override the direction in which the loops "\ - "are bridged. Only use if the tool gives the wrong " \ - "result", - default = False) - bridge_segments = bpy.props.IntProperty(name = "Segments", - description = "Number of segments used to bridge the gap "\ - "(0 = automatic)", - default = 1, - min = 0, - soft_max = 20) - bridge_twist = bpy.props.IntProperty(name = "Twist", - description = "Twist what vertices are connected to each other", - default = 0) + description="Algorithm used for bridging", + default='shortest' + ) + bridge_remove_faces = BoolProperty( + name="Remove faces", + description="Remove faces that are internal after bridging", + default=True + ) + bridge_reverse = BoolProperty( + name="Reverse", + description="Manually override the direction in which the loops " + "are bridged. Only use if the tool gives the wrong result", + default=False + ) + bridge_segments = IntProperty( + name="Segments", + description="Number of segments used to bridge the gap (0=automatic)", + default=1, + min=0, + soft_max=20 + ) + bridge_twist = IntProperty( + name="Twist", + description="Twist what vertices are connected to each other", + default=0 + ) # circle properties - circle_custom_radius = bpy.props.BoolProperty(name = "Radius", - description = "Force a custom radius", - default = False) - circle_fit = bpy.props.EnumProperty(name = "Method", - items = (("best", "Best fit", "Non-linear least squares"), - ("inside", "Fit inside","Only move vertices towards the center")), - description = "Method used for fitting a circle to the vertices", - default = 'best') - circle_flatten = bpy.props.BoolProperty(name = "Flatten", - description = "Flatten the circle, instead of projecting it on the " \ - "mesh", - default = True) - circle_influence = bpy.props.FloatProperty(name = "Influence", - description = "Force of the tool", - default = 100.0, - min = 0.0, - max = 100.0, - precision = 1, - subtype = 'PERCENTAGE') - circle_lock_x = bpy.props.BoolProperty(name = "Lock X", - description = "Lock editing of the x-coordinate", - default = False) - circle_lock_y = bpy.props.BoolProperty(name = "Lock Y", - description = "Lock editing of the y-coordinate", - default = False) - circle_lock_z = bpy.props.BoolProperty(name = "Lock Z", - description = "Lock editing of the z-coordinate", - default = False) - circle_radius = bpy.props.FloatProperty(name = "Radius", - description = "Custom radius for circle", - default = 1.0, - min = 0.0, - soft_max = 1000.0) - circle_regular = bpy.props.BoolProperty(name = "Regular", - description = "Distribute vertices at constant distances along the " \ - "circle", - default = True) - + circle_custom_radius = BoolProperty( + name="Radius", + description="Force a custom radius", + default=False + ) + circle_fit = EnumProperty( + name="Method", + items=(("best", "Best fit", "Non-linear least squares"), + ("inside", "Fit inside", "Only move vertices towards the center")), + description="Method used for fitting a circle to the vertices", + default='best' + ) + circle_flatten = BoolProperty( + name="Flatten", + description="Flatten the circle, instead of projecting it on the mesh", + default=True + ) + circle_influence = FloatProperty( + name="Influence", + description="Force of the tool", + default=100.0, + min=0.0, + max=100.0, + precision=1, + subtype='PERCENTAGE' + ) + circle_lock_x = BoolProperty( + name="Lock X", + description="Lock editing of the x-coordinate", + default=False + ) + circle_lock_y = BoolProperty( + name="Lock Y", + description="Lock editing of the y-coordinate", + default=False + ) + circle_lock_z = BoolProperty( + name="Lock Z", + description="Lock editing of the z-coordinate", + default=False + ) + circle_radius = FloatProperty( + name="Radius", + description="Custom radius for circle", + default=1.0, + min=0.0, + soft_max=1000.0 + ) + circle_regular = BoolProperty( + name="Regular", + description="Distribute vertices at constant distances along the circle", + default=True + ) # curve properties - curve_boundaries = bpy.props.BoolProperty(name = "Boundaries", - description = "Limit the tool to work within the boundaries of the "\ - "selected vertices", - default = False) - curve_influence = bpy.props.FloatProperty(name = "Influence", - description = "Force of the tool", - default = 100.0, - min = 0.0, - max = 100.0, - precision = 1, - subtype = 'PERCENTAGE') - curve_interpolation = bpy.props.EnumProperty(name = "Interpolation", - items = (("cubic", "Cubic", "Natural cubic spline, smooth results"), + curve_boundaries = BoolProperty( + name="Boundaries", + description="Limit the tool to work within the boundaries of the " + "selected vertices", + default=False + ) + curve_influence = FloatProperty( + name="Influence", + description="Force of the tool", + default=100.0, + min=0.0, + max=100.0, + precision=1, + subtype='PERCENTAGE' + ) + curve_interpolation = EnumProperty( + name="Interpolation", + items=(("cubic", "Cubic", "Natural cubic spline, smooth results"), ("linear", "Linear", "Simple and fast linear algorithm")), - description = "Algorithm used for interpolation", - default = 'cubic') - curve_lock_x = bpy.props.BoolProperty(name = "Lock X", - description = "Lock editing of the x-coordinate", - default = False) - curve_lock_y = bpy.props.BoolProperty(name = "Lock Y", - description = "Lock editing of the y-coordinate", - default = False) - curve_lock_z = bpy.props.BoolProperty(name = "Lock Z", - description = "Lock editing of the z-coordinate", - default = False) - curve_regular = bpy.props.BoolProperty(name = "Regular", - description = "Distribute vertices at constant distances along the " \ - "curve", - default = True) - curve_restriction = bpy.props.EnumProperty(name = "Restriction", - items = (("none", "None", "No restrictions on vertex movement"), - ("extrude", "Extrude only","Only allow extrusions (no "\ - "indentations)"), - ("indent", "Indent only", "Only allow indentation (no "\ - "extrusions)")), - description = "Restrictions on how the vertices can be moved", - default = 'none') + description="Algorithm used for interpolation", + default='cubic' + ) + curve_lock_x = BoolProperty( + name="Lock X", + description="Lock editing of the x-coordinate", + default=False + ) + curve_lock_y = BoolProperty( + name="Lock Y", + description="Lock editing of the y-coordinate", + default=False + ) + curve_lock_z = BoolProperty( + name="Lock Z", + description="Lock editing of the z-coordinate", + default=False + ) + curve_regular = BoolProperty( + name="Regular", + description="Distribute vertices at constant distances along the curve", + default=True + ) + curve_restriction = EnumProperty( + name="Restriction", + items=(("none", "None", "No restrictions on vertex movement"), + ("extrude", "Extrude only", "Only allow extrusions (no indentations)"), + ("indent", "Indent only", "Only allow indentation (no extrusions)")), + description="Restrictions on how the vertices can be moved", + default='none' + ) # flatten properties - flatten_influence = bpy.props.FloatProperty(name = "Influence", - description = "Force of the tool", - default = 100.0, - min = 0.0, - max = 100.0, - precision = 1, - subtype = 'PERCENTAGE') - flatten_lock_x = bpy.props.BoolProperty(name = "Lock X", - description = "Lock editing of the x-coordinate", - default = False) - flatten_lock_y = bpy.props.BoolProperty(name = "Lock Y", - description = "Lock editing of the y-coordinate", - default = False) - flatten_lock_z = bpy.props.BoolProperty(name = "Lock Z", - description = "Lock editing of the z-coordinate", - default = False) - flatten_plane = bpy.props.EnumProperty(name = "Plane", - items = (("best_fit", "Best fit", "Calculate a best fitting plane"), - ("normal", "Normal", "Derive plane from averaging vertex "\ + flatten_influence = FloatProperty( + name="Influence", + description="Force of the tool", + default=100.0, + min=0.0, + max=100.0, + precision=1, + subtype='PERCENTAGE' + ) + flatten_lock_x = BoolProperty( + name="Lock X", + description="Lock editing of the x-coordinate", + default=False) + flatten_lock_y = BoolProperty(name="Lock Y", + description="Lock editing of the y-coordinate", + default=False + ) + flatten_lock_z = BoolProperty( + name="Lock Z", + description="Lock editing of the z-coordinate", + default=False + ) + flatten_plane = EnumProperty( + name="Plane", + items=(("best_fit", "Best fit", "Calculate a best fitting plane"), + ("normal", "Normal", "Derive plane from averaging vertex " "normals"), - ("view", "View", "Flatten on a plane perpendicular to the "\ + ("view", "View", "Flatten on a plane perpendicular to the " "viewing angle")), - description = "Plane on which vertices are flattened", - default = 'best_fit') - flatten_restriction = bpy.props.EnumProperty(name = "Restriction", - items = (("none", "None", "No restrictions on vertex movement"), - ("bounding_box", "Bounding box", "Vertices are restricted to "\ + description="Plane on which vertices are flattened", + default='best_fit' + ) + flatten_restriction = EnumProperty( + name="Restriction", + items=(("none", "None", "No restrictions on vertex movement"), + ("bounding_box", "Bounding box", "Vertices are restricted to " "movement inside the bounding box of the selection")), - description = "Restrictions on how the vertices can be moved", - default = 'none') + description="Restrictions on how the vertices can be moved", + default='none' + ) # gstretch properties - gstretch_conversion = bpy.props.EnumProperty(name = "Conversion", - items = (("distance", "Distance", "Set the distance between vertices "\ + gstretch_conversion = EnumProperty( + name="Conversion", + items=(("distance", "Distance", "Set the distance between vertices " "of the converted grease pencil stroke"), - ("limit_vertices", "Limit vertices", "Set the minimum and maximum "\ + ("limit_vertices", "Limit vertices", "Set the minimum and maximum " "number of vertices that converted GP strokes will have"), - ("vertices", "Exact vertices", "Set the exact number of vertices "\ - "that converted grease pencil strokes will have. Short strokes "\ + ("vertices", "Exact vertices", "Set the exact number of vertices " + "that converted grease pencil strokes will have. Short strokes " "with few points may contain less vertices than this number."), - ("none", "No simplification", "Convert each grease pencil point "\ + ("none", "No simplification", "Convert each grease pencil point " "to a vertex")), - description = "If grease pencil strokes are converted to geometry, "\ - "use this simplification method", - default = 'limit_vertices') - gstretch_conversion_distance = bpy.props.FloatProperty(name = "Distance", - description = "Absolute distance between vertices along the converted "\ - "grease pencil stroke", - default = 0.1, - min = 0.000001, - soft_min = 0.01, - soft_max = 100) - gstretch_conversion_max = bpy.props.IntProperty(name = "Max Vertices", - description = "Maximum number of vertices grease pencil strokes will "\ - "have, when they are converted to geomtery", - default = 32, - min = 3, - soft_max = 500, - update = gstretch_update_min) - gstretch_conversion_min = bpy.props.IntProperty(name = "Min Vertices", - description = "Minimum number of vertices grease pencil strokes will "\ - "have, when they are converted to geomtery", - default = 8, - min = 3, - soft_max = 500, - update = gstretch_update_max) - gstretch_conversion_vertices = bpy.props.IntProperty(name = "Vertices", - description = "Number of vertices grease pencil strokes will "\ - "have, when they are converted to geometry. If strokes have less "\ - "points than required, the 'Spread evenly' method is used", - default = 32, - min = 3, - soft_max = 500) - gstretch_delete_strokes = bpy.props.BoolProperty(name="Delete strokes", - description = "Remove Grease Pencil strokes if they have been used "\ - "for Gstretch. WARNING: DOES NOT SUPPORT UNDO", - default = False) - gstretch_influence = bpy.props.FloatProperty(name = "Influence", - description = "Force of the tool", - default = 100.0, - min = 0.0, - max = 100.0, - precision = 1, - subtype = 'PERCENTAGE') - gstretch_lock_x = bpy.props.BoolProperty(name = "Lock X", - description = "Lock editing of the x-coordinate", - default = False) - gstretch_lock_y = bpy.props.BoolProperty(name = "Lock Y", - description = "Lock editing of the y-coordinate", - default = False) - gstretch_lock_z = bpy.props.BoolProperty(name = "Lock Z", - description = "Lock editing of the z-coordinate", - default = False) - gstretch_method = bpy.props.EnumProperty(name = "Method", - items = (("project", "Project", "Project vertices onto the stroke, "\ - "using vertex normals and connected edges"), - ("irregular", "Spread", "Distribute vertices along the full "\ - "stroke, retaining relative distances between the vertices"), - ("regular", "Spread evenly", "Distribute vertices at regular "\ - "distances along the full stroke")), - description = "Method of distributing the vertices over the Grease "\ - "Pencil stroke", - default = 'regular') + description="If grease pencil strokes are converted to geometry, " + "use this simplification method", + default='limit_vertices' + ) + gstretch_conversion_distance = FloatProperty( + name="Distance", + description="Absolute distance between vertices along the converted " + "grease pencil stroke", + default=0.1, + min=0.000001, + soft_min=0.01, + soft_max=100 + ) + gstretch_conversion_max = IntProperty( + name="Max Vertices", + description="Maximum number of vertices grease pencil strokes will " + "have, when they are converted to geomtery", + default=32, + min=3, + soft_max=500, + update=gstretch_update_min + ) + gstretch_conversion_min = IntProperty( + name="Min Vertices", + description="Minimum number of vertices grease pencil strokes will " + "have, when they are converted to geomtery", + default=8, + min=3, + soft_max=500, + update=gstretch_update_max + ) + gstretch_conversion_vertices = IntProperty( + name="Vertices", + description="Number of vertices grease pencil strokes will " + "have, when they are converted to geometry. If strokes have less " + "points than required, the 'Spread evenly' method is used", + default=32, + min=3, + soft_max=500 + ) + gstretch_delete_strokes = BoolProperty( + name="Delete strokes", + description="Remove Grease Pencil strokes if they have been used " + "for Gstretch. WARNING: DOES NOT SUPPORT UNDO", + default=False + ) + gstretch_influence = FloatProperty( + name="Influence", + description="Force of the tool", + default=100.0, + min=0.0, + max=100.0, + precision=1, + subtype='PERCENTAGE' + ) + gstretch_lock_x = BoolProperty( + name="Lock X", + description="Lock editing of the x-coordinate", + default=False + ) + gstretch_lock_y = BoolProperty( + name="Lock Y", + description="Lock editing of the y-coordinate", + default=False + ) + gstretch_lock_z = BoolProperty( + name="Lock Z", + description="Lock editing of the z-coordinate", + default=False + ) + gstretch_method = EnumProperty( + name="Method", + items=(("project", "Project", "Project vertices onto the stroke, " + "using vertex normals and connected edges"), + ("irregular", "Spread", "Distribute vertices along the full " + "stroke, retaining relative distances between the vertices"), + ("regular", "Spread evenly", "Distribute vertices at regular " + "distances along the full stroke")), + description="Method of distributing the vertices over the Grease " + "Pencil stroke", + default='regular' + ) # relax properties - relax_input = bpy.props.EnumProperty(name = "Input", - items = (("all", "Parallel (all)", "Also use non-selected "\ - "parallel loops as input"), - ("selected", "Selection","Only use selected vertices as input")), - description = "Loops that are relaxed", - default = 'selected') - relax_interpolation = bpy.props.EnumProperty(name = "Interpolation", - items = (("cubic", "Cubic", "Natural cubic spline, smooth results"), - ("linear", "Linear", "Simple and fast linear algorithm")), - description = "Algorithm used for interpolation", - default = 'cubic') - relax_iterations = bpy.props.EnumProperty(name = "Iterations", - items = (("1", "1", "One"), - ("3", "3", "Three"), - ("5", "5", "Five"), - ("10", "10", "Ten"), - ("25", "25", "Twenty-five")), - description = "Number of times the loop is relaxed", - default = "1") - relax_regular = bpy.props.BoolProperty(name = "Regular", - description = "Distribute vertices at constant distances along the" \ - "loop", - default = True) + relax_input = EnumProperty(name="Input", + items=(("all", "Parallel (all)", "Also use non-selected " + "parallel loops as input"), + ("selected", "Selection", "Only use selected vertices as input")), + description="Loops that are relaxed", + default='selected' + ) + relax_interpolation = EnumProperty( + name="Interpolation", + items=(("cubic", "Cubic", "Natural cubic spline, smooth results"), + ("linear", "Linear", "Simple and fast linear algorithm")), + description="Algorithm used for interpolation", + default='cubic' + ) + relax_iterations = EnumProperty(name="Iterations", + items=(("1", "1", "One"), + ("3", "3", "Three"), + ("5", "5", "Five"), + ("10", "10", "Ten"), + ("25", "25", "Twenty-five")), + description="Number of times the loop is relaxed", + default="1" + ) + relax_regular = BoolProperty( + name="Regular", + description="Distribute vertices at constant distances along the loop", + default=True + ) # space properties - space_influence = bpy.props.FloatProperty(name = "Influence", - description = "Force of the tool", - default = 100.0, - min = 0.0, - max = 100.0, - precision = 1, - subtype = 'PERCENTAGE') - space_input = bpy.props.EnumProperty(name = "Input", - items = (("all", "Parallel (all)", "Also use non-selected "\ + space_influence = FloatProperty( + name="Influence", + description="Force of the tool", + default=100.0, + min=0.0, + max=100.0, + precision=1, + subtype='PERCENTAGE' + ) + space_input = EnumProperty( + name="Input", + items=(("all", "Parallel (all)", "Also use non-selected " "parallel loops as input"), - ("selected", "Selection","Only use selected vertices as input")), - description = "Loops that are spaced", - default = 'selected') - space_interpolation = bpy.props.EnumProperty(name = "Interpolation", - items = (("cubic", "Cubic", "Natural cubic spline, smooth results"), + ("selected", "Selection", "Only use selected vertices as input")), + description="Loops that are spaced", + default='selected' + ) + space_interpolation = EnumProperty( + name="Interpolation", + items=(("cubic", "Cubic", "Natural cubic spline, smooth results"), ("linear", "Linear", "Vertices are projected on existing edges")), - description = "Algorithm used for interpolation", - default = 'cubic') - space_lock_x = bpy.props.BoolProperty(name = "Lock X", - description = "Lock editing of the x-coordinate", - default = False) - space_lock_y = bpy.props.BoolProperty(name = "Lock Y", - description = "Lock editing of the y-coordinate", - default = False) - space_lock_z = bpy.props.BoolProperty(name = "Lock Z", - description = "Lock editing of the z-coordinate", - default = False) + description="Algorithm used for interpolation", + default='cubic' + ) + space_lock_x = BoolProperty( + name="Lock X", + description="Lock editing of the x-coordinate", + default=False + ) + space_lock_y = BoolProperty( + name="Lock Y", + description="Lock editing of the y-coordinate", + default=False + ) + space_lock_z = BoolProperty( + name="Lock Z", + description="Lock editing of the z-coordinate", + default=False + ) # draw function for integration in menus @@ -4796,34 +5053,52 @@ def menu_func(self, context): self.layout.menu("VIEW3D_MT_edit_mesh_looptools") self.layout.separator() -## Addons Preferences Update Panel + +# Add-ons Preferences Update Panel + +# Define Panel classes for updating +panels = ( + VIEW3D_PT_tools_looptools, + ) + + def update_panel(self, context): + message = "LoopTools: Updating Panel locations has failed" try: - bpy.utils.unregister_class(VIEW3D_PT_tools_looptools) - except: + for panel in panels: + if "bl_rna" in panel.__dict__: + bpy.utils.unregister_class(panel) + + for panel in panels: + panel.bl_category = context.user_preferences.addons[__name__].preferences.category + bpy.utils.register_class(panel) + + except Exception as e: + print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e)) pass - VIEW3D_PT_tools_looptools.bl_category = context.user_preferences.addons[__name__].preferences.category - bpy.utils.register_class(VIEW3D_PT_tools_looptools) -class LoopPreferences(bpy.types.AddonPreferences): + +class LoopPreferences(AddonPreferences): # this must match the addon name, use '__package__' # when defining this in a submodule of a python package. bl_idname = __name__ - category = bpy.props.StringProperty( + category = StringProperty( name="Tab Category", description="Choose a name for the category of the panel", default="Tools", - update=update_panel) + update=update_panel + ) def draw(self, context): - layout = self.layout + row = layout.row() col = row.column() col.label(text="Tab Category:") col.prop(self, "category", text="") + # define classes for registration classes = [ VIEW3D_MT_edit_mesh_looptools, @@ -4846,10 +5121,10 @@ def register(): for c in classes: bpy.utils.register_class(c) bpy.types.VIEW3D_MT_edit_mesh_specials.prepend(menu_func) - bpy.types.WindowManager.looptools = bpy.props.PointerProperty(\ - type = LoopToolsProps) + bpy.types.WindowManager.looptools = PointerProperty(type=LoopToolsProps) update_panel(None, bpy.context) + # unregistering and removing menus def unregister(): for c in classes: -- GitLab