Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCoDEmanX <codemanx@gmx.de>2014-02-04 06:05:05 +0400
committerCoDEmanX <codemanx@gmx.de>2014-02-04 06:05:05 +0400
commit049a5a86edcf78b070b73b141b657825145ae9a1 (patch)
tree6e5edf67d7974e0ad8beb209c57fd48c456ecfbe /mesh_looptools.py
parentd9fcbd06018165cc2cfca0673c7c2db44b04a833 (diff)
Clean-up: Updated bl_info['tracker_url'] to developer.blender.org, some minor other edits
Diffstat (limited to 'mesh_looptools.py')
-rw-r--r--mesh_looptools.py625
1 files changed, 312 insertions, 313 deletions
diff --git a/mesh_looptools.py b/mesh_looptools.py
index cdbc26fd..5bfc832f 100644
--- a/mesh_looptools.py
+++ b/mesh_looptools.py
@@ -25,9 +25,8 @@ bl_info = {
"warning": "",
"description": "Mesh modelling toolkit. Several tools to aid modelling",
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/Modeling/LoopTools",
- "tracker_url": "http://projects.blender.org/tracker/index.php?"
- "func=detail&aid=26189",
+ "Scripts/Modeling/LoopTools",
+ "tracker_url": "https://developer.blender.org/T26189",
"category": "Mesh"}
@@ -79,7 +78,7 @@ def cache_read(tool, object, bm, input_method, boundaries):
loops = looptools_cache[tool]["loops"]
derived = looptools_cache[tool]["derived"]
mapping = looptools_cache[tool]["mapping"]
-
+
return(True, single_loops, loops, derived, mapping)
@@ -140,7 +139,7 @@ def calculate_cubic_splines(bm_mod, tknots, knots):
else:
circular = False
# end of hack
-
+
n = len(knots)
if n < 2:
return False
@@ -187,7 +186,7 @@ def calculate_cubic_splines(bm_mod, tknots, knots):
if circular: # cleaning up after hack
knots = knots[4:-4]
tknots = tknots[4:-4]
-
+
return(splines)
@@ -201,7 +200,7 @@ def calculate_linear_splines(bm_mod, tknots, knots):
t = tknots[i]
u = tknots[i+1]-t
splines.append([a, d, t, u]) # [locStart, locDif, tStart, tDif]
-
+
return(splines)
@@ -209,14 +208,14 @@ def calculate_linear_splines(bm_mod, tknots, knots):
def calculate_plane(bm_mod, loop, method="best_fit", object=False):
# getting the vertex locations
locs = [bm_mod.verts[v].co.copy() for v in loop[0]]
-
+
# calculating the center of masss
com = mathutils.Vector()
for loc in locs:
com += loc
com /= len(locs)
x, y, z = com
-
+
if method == 'best_fit':
# creating the covariance matrix
mat = mathutils.Matrix(((0.0, 0.0, 0.0),
@@ -233,7 +232,7 @@ def calculate_plane(bm_mod, loop, method="best_fit", object=False):
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
try:
@@ -266,7 +265,7 @@ def calculate_plane(bm_mod, loop, method="best_fit", object=False):
if vec2.length == 0:
vec2 = mathutils.Vector((1.0, 1.0, 1.0))
normal = vec2
-
+
elif method == 'normal':
# averaging the vertex normals
v_normals = [bm_mod.verts[v].normal for v in loop[0]]
@@ -275,7 +274,7 @@ def calculate_plane(bm_mod, loop, method="best_fit", object=False):
normal += v_normal
normal /= len(v_normals)
normal.normalize()
-
+
elif method == 'view':
# calculate view normal
rotation = bpy.context.space_data.region_3d.view_matrix.to_3x3().\
@@ -284,7 +283,7 @@ def calculate_plane(bm_mod, loop, method="best_fit", object=False):
if object:
normal = object.matrix_world.inverted().to_euler().to_matrix() * \
normal
-
+
return(com, normal)
@@ -294,7 +293,7 @@ def calculate_splines(interpolation, bm_mod, tknots, knots):
splines = calculate_cubic_splines(bm_mod, tknots, knots[:])
else: # interpolations == 'linear'
splines = calculate_linear_splines(bm_mod, tknots, knots[:])
-
+
return(splines)
@@ -322,10 +321,10 @@ def check_loops(loops, mapping, bm_mod):
stacked = False
break
if stacked:
- continue
+ continue
# passed all tests, loop is valid
valid_loops.append([loop, circular])
-
+
return(valid_loops)
@@ -338,7 +337,7 @@ def dict_edge_faces(bm):
continue
for key in face_edgekeys(face):
edge_faces[key].append(face.index)
-
+
return(edge_faces)
@@ -346,7 +345,7 @@ def dict_edge_faces(bm):
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])
for face in bm.faces:
@@ -357,7 +356,7 @@ def dict_face_faces(bm, edge_faces=False):
if connected_face == face.index:
continue
connected_faces[face.index].append(connected_face)
-
+
return(connected_faces)
@@ -370,7 +369,7 @@ def dict_vert_edges(bm):
ek = edgekey(edge)
for vert in ek:
vert_edges[vert].append(ek)
-
+
return(vert_edges)
@@ -381,7 +380,7 @@ def dict_vert_faces(bm):
if not face.hide:
for vert in face.verts:
vert_faces[vert.index].append(face.index)
-
+
return(vert_faces)
@@ -395,7 +394,7 @@ def dict_vert_verts(edge_keys):
vert_verts[ek[i]].append(ek[1-i])
else:
vert_verts[ek[i]] = [ek[1-i]]
-
+
return(vert_verts)
@@ -414,18 +413,18 @@ def face_edgekeys(face):
def get_connected_input(object, bm, scene, input):
# get mesh with modifiers applied
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]
loops = get_connected_selections(edge_keys)
-
+
# if only selected loops are needed, we're done
if input == 'selected':
return(derived, bm_mod, loops)
- # elif input == 'all':
+ # elif input == 'all':
loops = get_parallel_loops(bm_mod, loops)
-
+
return(derived, bm_mod, loops)
@@ -433,14 +432,14 @@ def get_connected_input(object, bm, scene, input):
def get_connected_selections(edge_keys):
# create connection data
vert_verts = dict_vert_verts(edge_keys)
-
+
# find loops consisting of connected selected edges
loops = []
while len(vert_verts) > 0:
loop = [iter(vert_verts.keys()).__next__()]
growing = True
flipped = False
-
+
# extend loop
while growing:
# no more connection data for current vertex
@@ -474,7 +473,7 @@ def get_connected_selections(edge_keys):
# found both ends of the loop, stop growing
else:
growing = False
-
+
# check if loop is circular
if loop[0] in vert_verts:
if loop[-1] in vert_verts[loop[0]]:
@@ -494,9 +493,9 @@ def get_connected_selections(edge_keys):
else:
# not circular
loop = [loop, False]
-
+
loops.append(loop)
-
+
return(loops)
@@ -523,7 +522,7 @@ def get_derived_bmesh(object, bm, scene):
else:
derived = False
bm_mod = bm
-
+
return(derived, bm_mod)
@@ -531,12 +530,12 @@ def get_derived_bmesh(object, bm, scene):
def get_mapping(derived, bm, bm_mod, single_vertices, full_search, loops):
if not derived:
return(False)
-
+
if full_search:
verts = [v for v in bm.verts if not v.hide]
else:
verts = [v for v in bm.verts if v.select and not v.hide]
-
+
# non-selected vertices around single vertices also need to be mapped
if single_vertices:
mapping = dict([[vert, -1] for vert in single_vertices])
@@ -547,7 +546,7 @@ def get_mapping(derived, bm, bm_mod, single_vertices, full_search, loops):
mapping[v_mod.index] = v.index
break
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]:
@@ -558,7 +557,7 @@ def get_mapping(derived, bm, bm_mod, single_vertices, full_search, loops):
if v not in verts:
verts.append(v)
break
-
+
# create mapping of derived indices to indices
mapping = dict([[vert, -1] for loop in loops for vert in loop[0]])
if single_vertices:
@@ -571,7 +570,7 @@ def get_mapping(derived, bm, bm_mod, single_vertices, full_search, loops):
mapping[v_mod.index] = v.index
verts_mod.remove(v_mod)
break
-
+
return(mapping)
@@ -593,7 +592,7 @@ def matrix_invert(m):
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)))
@@ -613,7 +612,7 @@ def get_parallel_loops(bm_mod, loops):
# variables to keep track while iterating
all_edgeloops = []
has_branches = False
-
+
for loop in edgeloops:
# initialise with original loop
all_edgeloops.append(loop[0])
@@ -624,7 +623,7 @@ def get_parallel_loops(bm_mod, loops):
verts_used.append(edge[0])
if edge[1] not in verts_used:
verts_used.append(edge[1])
-
+
# find parallel loops
while len(newloops) > 0:
side_a = []
@@ -663,18 +662,18 @@ def get_parallel_loops(bm_mod, loops):
break
forbidden_side = "b"
continue
-
+
if has_branches:
# weird input with branches
break
-
+
newloops.pop(-1)
sides = []
if side_a:
sides.append(side_a)
if side_b:
sides.append(side_b)
-
+
for side in sides:
extraloop = []
for fi in side:
@@ -690,11 +689,11 @@ def get_parallel_loops(bm_mod, loops):
verts_used.append(new_vert)
newloops.append(extraloop)
all_edgeloops.append(extraloop)
-
+
# input contains branches, only return selected loop
if has_branches:
return(loops)
-
+
# change edgeloops into normal loops
loops = []
for edgeloop in all_edgeloops:
@@ -723,7 +722,7 @@ def get_parallel_loops(bm_mod, loops):
else:
circular = False
loops.append([loop, circular])
-
+
return(loops)
@@ -737,7 +736,7 @@ def initialise():
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.mode_set(mode='EDIT')
bm = bmesh.from_edit_mesh(object.data)
-
+
return(global_undo, object, bm)
@@ -759,7 +758,7 @@ def move_verts(object, bm, mapping, move, influence):
object.data.update()
-# load custom tool settings
+# load custom tool settings
def settings_load(self):
lt = bpy.context.window_manager.looptools
tool = self.name.split()[0].lower()
@@ -795,7 +794,7 @@ def terminate(global_undo):
def bridge_calculate_cubic_spline(bm, coordinates):
result = []
x = [0, 1, 2, 3]
-
+
for j in range(3):
a = []
for i in coordinates:
@@ -830,13 +829,13 @@ def bridge_calculate_cubic_spline(bm, coordinates):
return(spline)
-# return a list with new vertex location vectors, a list with face vertex
+# return a list with new vertex location vectors, a list with face vertex
# integers, and the highest vertex integer in the virtual mesh
def bridge_calculate_geometry(bm, lines, vertex_normals, segments,
interpolation, cubic_strength, min_width, max_vert_index):
new_verts = []
faces = []
-
+
# calculate location based on interpolation method
def get_location(line, segment, splines):
v1 = bm.verts[lines[line][0]].co
@@ -852,7 +851,7 @@ interpolation, cubic_strength, min_width, max_vert_index):
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):
@@ -872,7 +871,7 @@ interpolation, cubic_strength, min_width, max_vert_index):
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
@@ -881,13 +880,13 @@ interpolation, cubic_strength, min_width, 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_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
next_vert_indices = []
-
+
for i, line in enumerate(lines):
if i < len(lines)-1:
v1 = line[0]
@@ -924,12 +923,12 @@ interpolation, cubic_strength, min_width, max_vert_index):
next_vert_indices.append(max_vert_index)
if end_face:
faces.append([v1, v2, lines[i+1][1], line[1]])
-
+
prev_verts = next_verts[:]
prev_vert_indices = next_vert_indices[:]
next_verts = []
next_vert_indices = []
-
+
return(new_verts, faces, max_vert_index)
@@ -940,7 +939,7 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse):
loop1_circular, loop2_circular = [i[1] for i in loops]
circular = loop1_circular or loop2_circular
circle_full = False
-
+
# calculate loop centers
centers = []
for loop in [loop1, loop2]:
@@ -956,7 +955,7 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse):
centers[i] += mathutils.Vector((0.01, 0, 0))
break
center1, center2 = centers
-
+
# calculate the normals of the virtual planes that the loops are on
normals = []
normal_plurity = False
@@ -1012,7 +1011,7 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse):
if ((center2 + normals[1]) - center1).length > \
((center2 - normals[1]) - center1).length:
normals[1].negate()
-
+
# rotation matrix, representing the difference between the plane normals
axis = normals[0].cross(normals[1])
axis = mathutils.Vector([loc if abs(loc) > 1e-8 else 0 for loc in axis])
@@ -1020,14 +1019,14 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse):
axis.negate()
angle = normals[0].dot(normals[1])
rotation_matrix = mathutils.Matrix.Rotation(angle, 4, axis)
-
+
# if circular, rotate loops so they are aligned
if circular:
# make sure loop1 is the circular one (or both are circular)
if loop2_circular and not loop1_circular:
loop1_circular, loop2_circular = True, False
loop1, loop2 = loop2, loop1
-
+
# match start vertex of loop1 with loop2
target_vector = bm.verts[loop2[0]].co - center2
dif_angles = [[(rotation_matrix * (bm.verts[vertex].co - center1)
@@ -1041,7 +1040,7 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse):
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 = \
@@ -1071,7 +1070,7 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse):
loop1.reverse()
if circular:
loop1 = [loop1[-1]] + loop1[:-1]
-
+
# both loops have the same length
if len(loop1) == len(loop2):
# manual override
@@ -1080,25 +1079,25 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse):
loop1 = loop1[twist:]+loop1[:twist]
if reverse:
loop1.reverse()
-
+
lines.append([loop1[0], loop2[0]])
for i in range(1, len(loop1)):
lines.append([loop1[i], loop2[i]])
-
+
# loops of different lengths
else:
# make loop1 longest loop
if len(loop2) > len(loop1):
loop1, loop2 = loop2, loop1
loop1_circular, loop2_circular = loop2_circular, loop1_circular
-
+
# manual override
if twist:
if abs(twist) < len(loop1):
loop1 = loop1[twist:]+loop1[:twist]
if reverse:
loop1.reverse()
-
+
# shortest angle difference doesn't always give correct start vertex
if loop1_circular and not loop2_circular:
shifting = 1
@@ -1115,7 +1114,7 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse):
else:
shifting = False
break
-
+
# basic shortest side first
if mode == 'basic':
lines.append([loop1[0], loop2[0]])
@@ -1126,7 +1125,7 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse):
else:
# quads
lines.append([loop1[i], loop2[i]])
-
+
# shortest edge algorithm
else: # mode == 'shortest'
lines.append([loop1[0], loop2[0]])
@@ -1151,7 +1150,7 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse):
tri, quad = [(bm.verts[loop1[i+1]].co -
bm.verts[loop2[j]].co).length
for j in range(prev_vert2, prev_vert2+2)]
-
+
# triangle
if tri < quad:
lines.append([loop1[i+1], loop2[prev_vert2]])
@@ -1166,11 +1165,11 @@ def bridge_calculate_lines(bm, loops, mode, twist, reverse):
lines.append([loop1[i+1], loop2[0]])
prev_vert2 = 0
circle_full = True
-
+
# final face for circular loops
if loop1_circular and loop2_circular:
lines.append([loop1[0], loop2[0]])
-
+
return(lines)
@@ -1179,22 +1178,22 @@ def bridge_calculate_segments(bm, lines, loops, segments):
# return if amount of segments is set by user
if segments != 0:
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])]
# 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]]
-
+ 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)
-
+
segments = max(1, round(average_bridge_length / average_edge_length))
-
+
return(segments)
@@ -1203,7 +1202,7 @@ def bridge_calculate_virtual_vertex_normals(bm, lines, loops, edge_faces,
edgekey_to_edge):
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():
@@ -1215,7 +1214,7 @@ edgekey_to_edge):
average /= len(vectors)
dic[key] = [average]
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 \
@@ -1225,17 +1224,17 @@ edgekey_to_edge):
if loops[j][1]: # circular
edges.append(edgekey_to_edge[tuple(sorted([loops[j][0][0],
loops[j][0][-1]]))])
-
+
"""
calculation based on face topology (assign edge-normals to vertices)
-
+
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]])
for edge in edges:
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]]
@@ -1244,7 +1243,7 @@ edgekey_to_edge):
# zero-length edge, vertices at same location
continue
edge_center = (v1 + v2) / 2
-
+
# average face coordinates, if connected to more than 1 valid face
if len(faces) > 1:
face_normal = mathutils.Vector()
@@ -1260,7 +1259,7 @@ edgekey_to_edge):
if face_normal.length < 1e-4:
# faces with a surface of 0 have no face normal
continue
-
+
# calculate virtual edge normal
edge_normal = edge_vector.cross(face_normal)
edge_normal.length = 0.01
@@ -1272,17 +1271,17 @@ edgekey_to_edge):
# add virtual edge normal as entry for both vertices it connects
for vertex in edgekey(edge):
vertex_normals[vertex].append(edge_normal)
-
- """
- calculation based on connection with other loop (vertex focused method)
+
+ """
+ calculation based on connection with other loop (vertex focused method)
- used for vertices that aren't connected to any valid faces
-
+
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]
-
+
if vertices:
# edge vectors connected to vertices
edge_vectors = dict([[vertex, []] for vertex in vertices])
@@ -1295,7 +1294,7 @@ edgekey_to_edge):
# zero-length edge, vertices at same location
continue
edge_vectors[v].append(edge_vector)
-
+
# connection vectors between vertices of both loops
connection_vectors = dict([[vertex, []] for vertex in vertices])
connections = dict([[vertex, []] for vertex in vertices])
@@ -1315,14 +1314,14 @@ edgekey_to_edge):
connection_vectors = average_vector_dictionary(connection_vectors)
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
if not connection_vectors[vertex]:
vertex_normals[vertex] = [mathutils.Vector((1, 0, 0))]
continue
-
- # calculate to what location the vertex is connected,
+
+ # calculate to what location the vertex is connected,
# used to determine what way to flip the normal
connected_center = mathutils.Vector()
for v in connections[vertex]:
@@ -1333,7 +1332,7 @@ edgekey_to_edge):
# shouldn't be possible, but better safe than sorry
vertex_normals[vertex] = [mathutils.Vector((1, 0, 0))]
continue
-
+
# can't do proper calculations, because of zero-length vector
if not values:
if (connected_center - (bm.verts[vertex].co + \
@@ -1344,7 +1343,7 @@ edgekey_to_edge):
vertex_normals[vertex] = [connection_vectors[vertex].\
normalized()]
continue
-
+
# calculate vertex normals using edge-vectors,
# connection-vectors and the derived plane normal
for edge_vector in values:
@@ -1358,12 +1357,12 @@ edgekey_to_edge):
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()])
-
+
return(vertex_normals)
@@ -1380,7 +1379,7 @@ def bridge_create_faces(object, bm, faces, twist):
[face.reverse() 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)):
if not faces[i][-1]:
@@ -1391,7 +1390,7 @@ def bridge_create_faces(object, bm, faces, twist):
# result of converting from pre-bmesh period
if faces[i][-1] == faces[i][-2]:
faces[i] = faces[i][:-1]
-
+
new_faces = []
for i in range(len(faces)):
new_faces.append(bm.faces.new([bm.verts[v] for v in faces[i]]))
@@ -1413,12 +1412,12 @@ def bridge_get_input(bm):
else:
edge_count[ek] = 1
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]
loops = get_connected_selections(selected_edges)
-
+
return(loops)
@@ -1441,18 +1440,18 @@ def bridge_initialise(bm, interpolation):
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]
-
+
# 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:
smooth = True
-
+
return(edge_faces, edgekey_to_edge, old_selected_faces, smooth)
@@ -1466,7 +1465,7 @@ def bridge_input_method(loft, loft_loop):
method = "Loft no-loop"
else:
method = "Bridge"
-
+
return(method)
@@ -1483,7 +1482,7 @@ def bridge_match_loops(bm, loops):
center += bm.verts[vertex].co
normals.append(normal / len(vertices) / 10)
centers.append(center / len(vertices))
-
+
# possible matches if loop normals are faced towards the center
# of the other loop
matches = dict([[i, []] for i in range(len(loops))])
@@ -1504,7 +1503,7 @@ def bridge_match_loops(bm, loops):
matches[j].append([(centers[i] - centers[j]).length, j, i])
for key, value in matches.items():
value.sort()
-
+
# matches based on distance between centers and number of vertices in loops
new_order = []
for loop_index in range(len(loops)):
@@ -1523,11 +1522,11 @@ def bridge_match_loops(bm, loops):
if match[3] not in new_order:
new_order += [loop_index, match[3]]
break
-
+
# reorder loops based on matches
if len(new_order) >= 2:
loops = [loops[i] for i in new_order]
-
+
return(loops)
@@ -1538,7 +1537,7 @@ def bridge_remove_internal_faces(bm, old_selected_faces):
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
for face in remove_faces:
bm.faces.remove(face)
@@ -1552,7 +1551,7 @@ def bridge_save_unused_faces(bm, old_selected_faces, loops):
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]
-
+
# group selected faces that are connected
groups = []
grouped_faces = []
@@ -1572,13 +1571,13 @@ def bridge_save_unused_faces(bm, old_selected_faces, loops):
group += vertex_face_group
new_faces.pop(0)
groups.append(group)
-
+
# key: vertex index, value: True/False (is it in a loop that is used)
used_vertices = dict([[i, 0] for i in range(len(bm.verts))])
for loop in loops:
for vertex in loop[0]:
used_vertices[vertex] = True
-
+
# check if group is bridged, if not remove faces from internal faces list
for group in groups:
used = False
@@ -1607,7 +1606,7 @@ def bridge_sort_loops(bm, loops, loft_loop):
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]]
@@ -1624,13 +1623,13 @@ def bridge_sort_loops(bm, loops, loft_loop):
path.reverse()
path = path[:-i] + temp
break
-
+
# reorder loops
loops = [loops[i[0]] for i in path]
# if requested, duplicate first loop at last position, so loft can loop
if loft_loop:
loops = loops + [loops[0]]
-
+
return(loops)
@@ -1641,10 +1640,10 @@ def bridge_update_old_selection(bm, 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)
@@ -1666,7 +1665,7 @@ def circle_3d_to_2d(bm_mod, loop, com, normal):
m = mathutils.Vector((normal[0], normal[1] + 1.0, normal[2]))
p = m - (m.dot(normal) * normal)
q = p.cross(normal)
-
+
# change to 2d coordinates using perpendicular projection
locs_2d = []
for loc, vert in verts_projected:
@@ -1674,7 +1673,7 @@ def circle_3d_to_2d(bm_mod, loop, com, normal):
x = p.dot(vloc) / p.dot(p)
y = q.dot(vloc) / q.dot(q)
locs_2d.append([x, y, vert])
-
+
return(locs_2d, p, q)
@@ -1684,7 +1683,7 @@ def circle_calculate_best_fit(locs_2d):
x0 = 0.0
y0 = 0.0
r = 1.0
-
+
# calculate center and radius (non-linear least squares solution)
for iter in range(500):
jmat = []
@@ -1720,7 +1719,7 @@ def circle_calculate_best_fit(locs_2d):
# 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:
break
-
+
# return center of circle and radius
return(x0, y0, r)
@@ -1733,7 +1732,7 @@ def circle_calculate_min_fit(locs_2d):
center = mathutils.Vector([x0, y0])
# radius of circle
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)
@@ -1744,10 +1743,10 @@ def circle_calculate_verts(flatten, bm_mod, locs_2d, com, p, q, normal):
locs_3d = []
for loc in locs_2d:
locs_3d.append([loc[2], loc[0]*p + loc[1]*q + com])
-
+
if flatten: # flat circle
return(locs_3d)
-
+
else: # project the locations on the existing mesh
vert_edges = dict_vert_edges(bm_mod)
vert_faces = dict_vert_faces(bm_mod)
@@ -1828,7 +1827,7 @@ def circle_calculate_verts(flatten, bm_mod, locs_2d, com, p, q, normal):
# nothing to project on, remain at flat location
projection = loc[1]
new_locs.append([loc[0], projection])
-
+
# return new positions of projected circle
return(new_locs)
@@ -1873,7 +1872,7 @@ def circle_check_loops(single_loops, loops, mapping, bm_mod):
# passed all tests, loop is valid
valid_loops.append([loop, circular])
valid_single_loops[len(valid_loops)-1] = single_loops[i]
-
+
return(valid_single_loops, valid_loops)
@@ -1883,7 +1882,7 @@ def circle_flatten_singles(bm_mod, com, p, q, normal, single_loop):
for vert in single_loop:
loc = mathutils.Vector(bm_mod.verts[vert].co[:])
new_locs.append([vert, loc - (loc-com).dot(normal)*normal])
-
+
return(new_locs)
@@ -1891,7 +1890,7 @@ def circle_flatten_singles(bm_mod, com, p, q, normal, single_loop):
def circle_get_input(object, bm, scene):
# get mesh with modifiers applied
derived, bm_mod = get_derived_bmesh(object, bm, scene)
-
+
# create list of edge-keys based on selection state
faces = False
for face in bm.faces:
@@ -1914,7 +1913,7 @@ def circle_get_input(object, bm, scene):
# no faces, so no internal edges either
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 \
@@ -1922,7 +1921,7 @@ def circle_get_input(object, bm, scene):
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])
@@ -1939,10 +1938,10 @@ def circle_get_input(object, bm, scene):
if vert not in vert_to_single[ek[1]]:
vert_to_single[ek[1]].append(vert)
break
-
+
# sort edge-keys into loops
loops = get_connected_selections(edge_keys)
-
+
# 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:
@@ -1952,7 +1951,7 @@ def circle_get_input(object, bm, scene):
for single in vert_to_single[vert]:
if single not in single_loops[i]:
single_loops[i].append(single)
-
+
return(derived, bm_mod, single_vertices, single_loops, loops)
@@ -1964,7 +1963,7 @@ def circle_influence_locs(locs_2d, new_locs_2d, influence):
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)
@@ -1975,7 +1974,7 @@ def circle_project_non_regular(locs_2d, x0, y0, r):
loc = mathutils.Vector([x-x0, y-y0])
loc.length = r
locs_2d[i] = [loc[0], loc[1], j]
-
+
return(locs_2d)
@@ -2001,7 +2000,7 @@ def circle_project_regular(locs_2d, x0, y0, r):
x = math.cos(t) * r
y = math.sin(t) * r
locs_2d[i] = [x, y, locs_2d[i][2]]
-
+
return(locs_2d)
@@ -2013,7 +2012,7 @@ def circle_shift_loop(bm_mod, loop, com):
distances.sort()
shift = distances[0][1]
loop = [verts[shift:] + verts[:shift], circular]
-
+
return(loop)
@@ -2094,7 +2093,7 @@ def curve_calculate_knots(loop, verts_selected):
knots.insert(0, loop[0][0])
if loop[0][-1] not in knots:
knots.append(loop[0][-1])
-
+
return(knots, points)
@@ -2103,7 +2102,7 @@ def curve_calculate_t(bm_mod, knots, points, pknots, regular, circular):
tpoints = []
loc_prev = False
len_total = 0
-
+
for p in points:
if p in knots:
loc = pknots[knots.index(p)] # use projected knot location
@@ -2120,7 +2119,7 @@ def curve_calculate_t(bm_mod, knots, points, pknots, regular, circular):
tknots.append(tpoints[points.index(p)])
if circular:
tknots[-1] = tpoints[-1]
-
+
# regular option
if regular:
tpoints_average = tpoints[-1] / (len(tpoints) - 1)
@@ -2130,8 +2129,8 @@ def curve_calculate_t(bm_mod, knots, points, pknots, regular, circular):
tknots[i] = tpoints[points.index(knots[i])]
if circular:
tknots[-1] = tpoints[-1]
-
-
+
+
return(tknots, tpoints)
@@ -2140,7 +2139,7 @@ def curve_calculate_vertices(bm_mod, knots, tknots, points, tpoints, splines,
interpolation, restriction):
newlocs = {}
move = []
-
+
for p in points:
if p in knots:
continue
@@ -2156,7 +2155,7 @@ interpolation, restriction):
n = len(splines) - 1
elif n < 0:
n = 0
-
+
if interpolation == 'cubic':
ax, bx, cx, dx, tx = splines[n][0]
x = ax + bx*(m-tx) + cx*(m-tx)**2 + dx*(m-tx)**3
@@ -2173,7 +2172,7 @@ interpolation, restriction):
newlocs[p] = newloc
else: # set the vertex to its new location
move.append([p, newloc])
-
+
if restriction != 'none': # vertex movement is restricted
for p in points:
if p in newlocs:
@@ -2212,7 +2211,7 @@ def curve_cut_boundaries(bm_mod, loops):
cut_loops.append([loop[first:], circular])
else:
cut_loops.append([loop[first:last], circular])
-
+
return(cut_loops)
@@ -2220,7 +2219,7 @@ def curve_cut_boundaries(bm_mod, loops):
def curve_get_input(object, bm, boundaries, scene):
# get mesh with modifiers applied
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]
@@ -2228,13 +2227,13 @@ def curve_get_input(object, bm, boundaries, scene):
vert_edges = dict_vert_edges(bm_mod)
edge_faces = dict_edge_faces(bm_mod)
correct_loops = []
-
+
# find loops through each selected vertex
while len(verts_unsorted) > 0:
loops = curve_vertex_loops(bm_mod, verts_unsorted[0], vert_edges,
edge_faces)
verts_unsorted.pop(0)
-
+
# check if loop is fully selected
search_perpendicular = False
i = -1
@@ -2261,11 +2260,11 @@ def curve_get_input(object, bm, boundaries, scene):
else:
for loop, circular in loops:
correct_loops.append([loop, circular])
-
+
# boundaries option
if boundaries:
correct_loops = curve_cut_boundaries(bm_mod, correct_loops)
-
+
return(derived, bm_mod, correct_loops)
@@ -2282,7 +2281,7 @@ def curve_perpendicular_loops(bm_mod, start_loop, vert_edges, edge_faces):
continue
else:
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]]
@@ -2320,7 +2319,7 @@ def curve_perpendicular_loops(bm_mod, start_loop, vert_edges, edge_faces):
start = max(0, loop[2] - before_start)
end = min(len(loop[0]), loop[2] + after_start + 1)
trimmed_loops.append([loop[0][start:end], False])
-
+
return(trimmed_loops)
@@ -2334,7 +2333,7 @@ def curve_project_knots(bm_mod, verts_selected, knots, points, circular):
v3 -= v1
p = v3.project(v2)
return(p + v1)
-
+
if circular: # project all knots
start = 0
end = len(knots)
@@ -2369,7 +2368,7 @@ def curve_project_knots(bm_mod, verts_selected, knots, points, circular):
pknots.append(mathutils.Vector(bm_mod.verts[knot].co[:]))
if not circular:
pknots.append(mathutils.Vector(bm_mod.verts[knots[-1]].co[:]))
-
+
return(pknots)
@@ -2377,7 +2376,7 @@ def curve_project_knots(bm_mod, verts_selected, knots, points, circular):
def curve_vertex_loops(bm_mod, start_vert, vert_edges, edge_faces):
edges_used = []
loops = []
-
+
for edge in vert_edges[start_vert]:
if edge in edges_used:
continue
@@ -2424,7 +2423,7 @@ def curve_vertex_loops(bm_mod, start_vert, vert_edges, edge_faces):
break
loop.reverse()
loops.append([loop, circular])
-
+
return(loops)
@@ -2437,11 +2436,11 @@ def flatten_get_input(bm):
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
if not vert_verts:
return([[verts, False]])
-
+
loops = []
while len(verts) > 0:
# start of loop
@@ -2462,7 +2461,7 @@ def flatten_get_input(bm):
to_grow += vert_verts[new_vert]
# add loop to loops
loops.append([loop, False])
-
+
return(loops)
@@ -2471,7 +2470,7 @@ 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]
-
+
return(verts_projected)
@@ -2492,32 +2491,32 @@ class gstretch_fake_stroke_point():
# flips loops, if necessary, to obtain maximum alignment to stroke
-def gstretch_align_pairs(ls_pairs, object, bm_mod, method):
+def gstretch_align_pairs(ls_pairs, object, bm_mod, method):
# returns total distance between all verts in loop and corresponding stroke
def distance_loop_stroke(loop, stroke, object, bm_mod, method):
stroke_lengths_cache = False
loop_length = len(loop[0])
total_distance = 0
-
+
if method != 'regular':
relative_lengths = gstretch_relative_lengths(loop, bm_mod)
-
+
for i, v_index in enumerate(loop[0]):
if method == 'regular':
relative_distance = i / (loop_length - 1)
else:
relative_distance = relative_lengths[i]
-
+
loc1 = object.matrix_world * bm_mod.verts[v_index].co
loc2, stroke_lengths_cache = gstretch_eval_stroke(stroke,
relative_distance, stroke_lengths_cache)
total_distance += (loc2 - loc1).length
-
+
return(total_distance)
-
+
if ls_pairs:
for (loop, stroke) in ls_pairs:
- distance_loop_stroke
+ distance_loop_stroke
total_dist = distance_loop_stroke(loop, stroke, object, bm_mod,
method)
loop[0].reverse()
@@ -2525,7 +2524,7 @@ def gstretch_align_pairs(ls_pairs, object, bm_mod, method):
method)
if total_dist_rev > total_dist:
loop[0].reverse()
-
+
return(ls_pairs)
@@ -2535,7 +2534,7 @@ def gstretch_calculate_verts(loop, stroke, object, bm_mod, method):
stroke_lengths_cache = False
loop_length = len(loop[0])
matrix_inverse = object.matrix_world.inverted()
-
+
# return intersection of line with stroke, or None
def intersect_line_stroke(vec1, vec2, stroke):
for i, p in enumerate(stroke.points[1:]):
@@ -2548,11 +2547,11 @@ def gstretch_calculate_verts(loop, stroke, object, bm_mod, method):
if -1 < dist < 1:
return(intersections[0])
return(None)
-
+
if method == 'project':
projection_vectors = []
vert_edges = dict_vert_edges(bm_mod)
-
+
for v_index in loop[0]:
for ek in vert_edges[v_index]:
v1, v2 = ek
@@ -2570,11 +2569,11 @@ def gstretch_calculate_verts(loop, stroke, object, bm_mod, method):
stroke)
if intersection:
move.append([v_index, matrix_inverse * intersection])
-
+
else:
if method == 'irregular':
relative_lengths = gstretch_relative_lengths(loop, bm_mod)
-
+
for i, v_index in enumerate(loop[0]):
if method == 'regular':
relative_distance = i / (loop_length - 1)
@@ -2584,7 +2583,7 @@ def gstretch_calculate_verts(loop, stroke, object, bm_mod, method):
relative_distance, stroke_lengths_cache)
loc = matrix_inverse * loc
move.append([v_index, loc])
-
+
return(move)
@@ -2595,7 +2594,7 @@ conversion_distance, conversion_max, conversion_min, conversion_vertices):
stroke_verts = []
mat_world = object.matrix_world.inverted()
singles = gstretch_match_single_verts(bm_mod, strokes, mat_world)
-
+
for stroke in strokes:
stroke_verts.append([stroke, []])
min_end_point = 0
@@ -2662,7 +2661,7 @@ conversion_distance, conversion_max, conversion_min, conversion_vertices):
if m_stroke != stroke:
continue
bm_mod.edges.new((vert, verts_seq[point]))
-
+
bmesh.update_edit_mesh(object.data)
return(move)
@@ -2704,7 +2703,7 @@ def gstretch_eval_stroke(stroke, distance, stroke_lengths_cache=False):
stroke_lengths_cache = [length / total_length for length in
lengths]
stroke_lengths = stroke_lengths_cache[:]
-
+
if distance in stroke_lengths:
loc = stroke.points[stroke_lengths.index(distance)].co
elif distance > stroke_lengths[-1]:
@@ -2722,7 +2721,7 @@ def gstretch_eval_stroke(stroke, distance, stroke_lengths_cache=False):
stroke.points[stroke_index-1].co
loc = stroke.points[stroke_index-1].co + \
distance_relative * interval_vector
-
+
return(loc, stroke_lengths_cache)
@@ -2751,7 +2750,7 @@ def gstretch_get_strokes(object):
strokes = frame.strokes
if len(strokes) < 1:
return(None)
-
+
return(strokes)
@@ -2759,7 +2758,7 @@ def gstretch_get_strokes(object):
def gstretch_match_loops_strokes(loops, strokes, object, bm_mod):
if not loops or not strokes:
return(None)
-
+
# calculate loop centers
loop_centers = []
for loop in loops:
@@ -2769,7 +2768,7 @@ def gstretch_match_loops_strokes(loops, strokes, object, bm_mod):
center /= len(loop[0])
center = object.matrix_world * center
loop_centers.append([center, loop])
-
+
# calculate stroke centers
stroke_centers = []
for stroke in strokes:
@@ -2778,7 +2777,7 @@ def gstretch_match_loops_strokes(loops, strokes, object, bm_mod):
center += p.co
center /= len(stroke.points)
stroke_centers.append([center, stroke, 0])
-
+
# match, first by stroke use count, then by distance
ls_pairs = []
for lc in loop_centers:
@@ -2789,7 +2788,7 @@ def gstretch_match_loops_strokes(loops, strokes, object, bm_mod):
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
-
+
return(ls_pairs)
@@ -2801,7 +2800,7 @@ def gstretch_match_single_verts(bm_mod, strokes, mat_world):
for stroke in strokes:
endpoints.append((mat_world * stroke.points[0].co, stroke, 0))
endpoints.append((mat_world * stroke.points[-1].co, stroke, -1))
-
+
distances = []
# find single vertices (not connected to other selected verts)
for vert in bm_mod.verts:
@@ -2816,11 +2815,11 @@ def gstretch_match_single_verts(bm_mod, strokes, mat_world):
continue
# calculate distances from vertex to endpoints
distance = [((vert.co - loc).length, vert, stroke, stroke_point,
- endpoint_index) for endpoint_index, (loc, stroke, stroke_point) in
+ endpoint_index) for endpoint_index, (loc, stroke, stroke_point) in
enumerate(endpoints)]
distance.sort()
distances.append(distance[0])
-
+
# create matches, based on shortest distance first
singles = []
while distances:
@@ -2836,7 +2835,7 @@ def gstretch_match_single_verts(bm_mod, strokes, mat_world):
distance_new.sort()
distances_new.append(distance_new[0])
distances = distances_new
-
+
return(singles)
@@ -2849,7 +2848,7 @@ def gstretch_relative_lengths(loop, bm_mod):
total_length = max(lengths[-1], 1e-7)
relative_lengths = [length / total_length for length in
lengths]
-
+
return(relative_lengths)
@@ -2858,7 +2857,7 @@ def gstretch_safe_to_true_strokes(safe_strokes):
strokes = []
for safe_stroke in safe_strokes:
strokes.append(gstretch_fake_stroke(safe_stroke))
-
+
return(strokes)
@@ -2867,7 +2866,7 @@ def gstretch_true_to_safe_strokes(strokes):
safe_strokes = []
for stroke in strokes:
safe_strokes.append([p.co.copy() for p in stroke.points])
-
+
return(safe_strokes)
@@ -2940,7 +2939,7 @@ def relax_calculate_knots(loops):
all_knots.append(k)
for p in points:
all_points.append(p)
-
+
return(all_knots, all_points)
@@ -2978,7 +2977,7 @@ def relax_calculate_t(bm_mod, knots, points, regular):
tpoints.append((tknots[p] + tknots[p+1]) / 2)
all_tknots.append(tknots)
all_tpoints.append(tpoints)
-
+
return(all_tknots, all_tpoints)
@@ -3001,7 +3000,7 @@ points, splines):
n = len(splines[i]) - 1
elif n < 0:
n = 0
-
+
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
@@ -3017,7 +3016,7 @@ points, splines):
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)
@@ -3040,7 +3039,7 @@ def space_calculate_t(bm_mod, knots):
amount = len(knots)
t_per_segment = len_total / (amount - 1)
tpoints = [i * t_per_segment for i in range(amount)]
-
+
return(tknots, tpoints)
@@ -3061,7 +3060,7 @@ splines):
n = len(splines) - 1
elif n < 0:
n = 0
-
+
if interpolation == 'cubic':
ax, bx, cx, dx, tx = splines[n][0]
x = ax + bx*(m-tx) + cx*(m-tx)**2 + dx*(m-tx)**3
@@ -3073,7 +3072,7 @@ splines):
else: # interpolation == 'linear'
a, d, t, u = splines[n]
move.append([p, ((m-t)/u)*d + a])
-
+
return(move)
@@ -3087,7 +3086,7 @@ class Bridge(bpy.types.Operator):
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,
@@ -3135,16 +3134,16 @@ class Bridge(bpy.types.Operator):
twist = bpy.props.IntProperty(name = "Twist",
description = "Twist what vertices are connected to each other",
default = 0)
-
+
@classmethod
def poll(cls, context):
ob = context.active_object
return (ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
-
+
def draw(self, context):
layout = self.layout
#layout.prop(self, "mode") # no cases yet where 'basic' mode is needed
-
+
# top row
col_top = layout.column(align=True)
row = col_top.row(align=True)
@@ -3164,26 +3163,26 @@ class Bridge(bpy.types.Operator):
col_top.prop(self, "remove_faces")
if self.loft:
col_top.prop(self, "loft_loop")
-
+
# override properties
col_top.separator()
row = layout.row(align = True)
row.prop(self, "twist")
row.prop(self, "reverse")
-
+
def invoke(self, context, event):
# load custom settings
context.window_manager.looptools.bridge_loft = self.loft
settings_load(self)
return self.execute(context)
-
+
def execute(self, context):
# initialise
global_undo, object, bm = initialise()
edge_faces, edgekey_to_edge, old_selected_faces, smooth = \
bridge_initialise(bm, self.interpolation)
settings_write(self)
-
+
# check cache to see if we can save time
input_method = bridge_input_method(self.loft, self.loft_loop)
cached, single_loops, loops, derived, mapping = cache_read("Bridge",
@@ -3198,7 +3197,7 @@ class Bridge(bpy.types.Operator):
loops = bridge_sort_loops(bm, loops, self.loft_loop)
else:
loops = bridge_match_loops(bm, loops)
-
+
# saving cache for faster execution next time
if not cached:
cache_write("Bridge", object, bm, input_method, False, False,
@@ -3248,10 +3247,10 @@ class Bridge(bpy.types.Operator):
bmesh.update_edit_mesh(object.data, tessface=False,
destructive=True)
bpy.ops.mesh.normals_make_consistent()
-
+
# cleaning up
terminate(global_undo)
-
+
return{'FINISHED'}
@@ -3261,7 +3260,7 @@ class Circle(bpy.types.Operator):
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)
@@ -3290,19 +3289,19 @@ class Circle(bpy.types.Operator):
description = "Distribute vertices at constant distances along the " \
"circle",
default = True)
-
+
@classmethod
def poll(cls, context):
ob = context.active_object
return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
-
+
def draw(self, context):
layout = self.layout
col = layout.column()
-
+
col.prop(self, "fit")
col.separator()
-
+
col.prop(self, "flatten")
row = col.row(align=True)
row.prop(self, "custom_radius")
@@ -3311,14 +3310,14 @@ class Circle(bpy.types.Operator):
row_right.prop(self, "radius", text="")
col.prop(self, "regular")
col.separator()
-
+
col.prop(self, "influence")
-
+
def invoke(self, context, event):
# load custom settings
settings_load(self)
return self.execute(context)
-
+
def execute(self, context):
# initialise
global_undo, object, bm = initialise()
@@ -3336,12 +3335,12 @@ class Circle(bpy.types.Operator):
False, loops)
single_loops, loops = circle_check_loops(single_loops, loops,
mapping, bm_mod)
-
+
# saving cache for faster execution next time
if not cached:
cache_write("Circle", object, bm, False, False, single_loops,
loops, derived, mapping)
-
+
move = []
for i, loop in enumerate(loops):
# best fitting flat plane
@@ -3374,15 +3373,15 @@ class Circle(bpy.types.Operator):
if self.flatten and single_loops:
move.append(circle_flatten_singles(bm_mod, com, p, q,
normal, single_loops[i]))
-
+
# move vertices to new locations
move_verts(object, bm, mapping, move, -1)
-
+
# cleaning up
if derived:
bm_mod.free()
terminate(global_undo)
-
+
return{'FINISHED'}
@@ -3392,7 +3391,7 @@ class Curve(bpy.types.Operator):
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",
@@ -3421,29 +3420,29 @@ class Curve(bpy.types.Operator):
"extrusions)")),
description = "Restrictions on how the vertices can be moved",
default = 'none')
-
+
@classmethod
def poll(cls, context):
ob = context.active_object
return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
-
+
def draw(self, context):
layout = self.layout
col = layout.column()
-
+
col.prop(self, "interpolation")
col.prop(self, "restriction")
col.prop(self, "boundaries")
col.prop(self, "regular")
col.separator()
-
+
col.prop(self, "influence")
-
+
def invoke(self, context, event):
# load custom settings
settings_load(self)
return self.execute(context)
-
+
def execute(self, context):
# initialise
global_undo, object, bm = initialise()
@@ -3461,12 +3460,12 @@ class Curve(bpy.types.Operator):
loops = check_loops(loops, mapping, bm_mod)
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:
cache_write("Curve", object, bm, False, self.boundaries, False,
loops, derived, mapping)
-
+
move = []
for loop in loops:
knots, points = curve_calculate_knots(loop, verts_selected)
@@ -3479,11 +3478,11 @@ class Curve(bpy.types.Operator):
move.append(curve_calculate_vertices(bm_mod, knots, tknots,
points, tpoints, splines, self.interpolation,
self.restriction))
-
+
# move vertices to new locations
move_verts(object, bm, mapping, move, self.influence)
-
- # cleaning up
+
+ # cleaning up
if derived:
bm_mod.free()
terminate(global_undo)
@@ -3497,7 +3496,7 @@ class Flatten(bpy.types.Operator):
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,
@@ -3519,27 +3518,27 @@ class Flatten(bpy.types.Operator):
"movement inside the bounding box of the selection")),
description = "Restrictions on how the vertices can be moved",
default = 'none')
-
+
@classmethod
def poll(cls, context):
ob = context.active_object
return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
-
+
def draw(self, context):
layout = self.layout
col = layout.column()
-
+
col.prop(self, "plane")
#col.prop(self, "restriction")
col.separator()
-
+
col.prop(self, "influence")
-
+
def invoke(self, context, event):
# load custom settings
settings_load(self)
return self.execute(context)
-
+
def execute(self, context):
# initialise
global_undo, object, bm = initialise()
@@ -3551,12 +3550,12 @@ class Flatten(bpy.types.Operator):
# order input into virtual loops
loops = flatten_get_input(bm)
loops = check_loops(loops, mapping, bm)
-
+
# saving cache for faster execution next time
if not cached:
cache_write("Flatten", object, bm, False, False, False, loops,
False, False)
-
+
move = []
for loop in loops:
# calculate plane and position of vertices on them
@@ -3568,10 +3567,10 @@ class Flatten(bpy.types.Operator):
else:
move.append(to_move)
move_verts(object, bm, False, move, self.influence)
-
+
# cleaning up
terminate(global_undo)
-
+
return{'FINISHED'}
@@ -3581,7 +3580,7 @@ class GStretch(bpy.types.Operator):
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"),
@@ -3626,7 +3625,7 @@ class GStretch(bpy.types.Operator):
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)
+ default = False)
influence = bpy.props.FloatProperty(name = "Influence",
description = "Force of the tool",
default = 100.0,
@@ -3644,20 +3643,20 @@ class GStretch(bpy.types.Operator):
description = "Method of distributing the vertices over the Grease "\
"Pencil stroke",
default = 'regular')
-
+
@classmethod
def poll(cls, context):
ob = context.active_object
return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
-
+
def draw(self, context):
layout = self.layout
col = layout.column()
-
+
col.prop(self, "method")
col.prop(self, "delete_strokes")
col.separator()
-
+
col_conv = col.column(align=True)
col_conv.prop(self, "conversion", text="")
if self.conversion == 'distance':
@@ -3669,9 +3668,9 @@ class GStretch(bpy.types.Operator):
elif self.conversion == 'vertices':
col_conv.prop(self, "conversion_vertices")
col.separator()
-
+
col.prop(self, "influence")
-
+
def invoke(self, context, event):
# flush cached strokes
if 'Gstretch' in looptools_cache:
@@ -3679,12 +3678,12 @@ class GStretch(bpy.types.Operator):
# load custom settings
settings_load(self)
return self.execute(context)
-
+
def execute(self, context):
# initialise
global_undo, object, bm = initialise()
settings_write(self)
-
+
# check cache to see if we can save time
cached, safe_strokes, loops, derived, mapping = cache_read("Gstretch",
object, bm, False, False)
@@ -3709,7 +3708,7 @@ class GStretch(bpy.types.Operator):
strokes = gstretch_get_strokes(object)
else:
strokes = gstretch_get_fake_strokes(object, bm_mod, loops)
-
+
# saving cache for faster execution next time
if not cached:
if strokes:
@@ -3722,7 +3721,7 @@ class GStretch(bpy.types.Operator):
# pair loops and strokes
ls_pairs = gstretch_match_loops_strokes(loops, strokes, object, bm_mod)
ls_pairs = gstretch_align_pairs(ls_pairs, object, bm_mod, self.method)
-
+
move = []
if not loops:
# no selected geometry, convert GP to verts
@@ -3756,12 +3755,12 @@ class GStretch(bpy.types.Operator):
bmesh.update_edit_mesh(object.data, tessface=True,
destructive=True)
move_verts(object, bm, mapping, move, self.influence)
-
- # cleaning up
+
+ # cleaning up
if derived:
bm_mod.free()
terminate(global_undo)
-
+
return{'FINISHED'}
@@ -3771,7 +3770,7 @@ class Relax(bpy.types.Operator):
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"),
@@ -3795,26 +3794,26 @@ class Relax(bpy.types.Operator):
description = "Distribute vertices at constant distances along the" \
"loop",
default = True)
-
+
@classmethod
def poll(cls, context):
ob = context.active_object
return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
-
+
def draw(self, context):
layout = self.layout
col = layout.column()
-
+
col.prop(self, "interpolation")
col.prop(self, "input")
col.prop(self, "iterations")
col.prop(self, "regular")
-
+
def invoke(self, context, event):
# load custom settings
settings_load(self)
return self.execute(context)
-
+
def execute(self, context):
# initialise
global_undo, object, bm = initialise()
@@ -3831,12 +3830,12 @@ class Relax(bpy.types.Operator):
mapping = get_mapping(derived, bm, bm_mod, False, False, loops)
loops = check_loops(loops, mapping, bm_mod)
knots, points = relax_calculate_knots(loops)
-
+
# saving cache for faster execution next time
if not cached:
cache_write("Relax", object, bm, self.input, False, False, loops,
derived, mapping)
-
+
for iteration in range(int(self.iterations)):
# calculate splines and new positions
tknots, tpoints = relax_calculate_t(bm_mod, knots, points,
@@ -3848,12 +3847,12 @@ class Relax(bpy.types.Operator):
move = [relax_calculate_verts(bm_mod, self.interpolation,
tknots, knots, tpoints, points, splines)]
move_verts(object, bm, mapping, move, -1)
-
+
# cleaning up
if derived:
bm_mod.free()
terminate(global_undo)
-
+
return{'FINISHED'}
@@ -3863,7 +3862,7 @@ class Space(bpy.types.Operator):
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,
@@ -3882,27 +3881,27 @@ class Space(bpy.types.Operator):
("linear", "Linear", "Vertices are projected on existing edges")),
description = "Algorithm used for interpolation",
default = 'cubic')
-
+
@classmethod
def poll(cls, context):
ob = context.active_object
return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
-
+
def draw(self, context):
layout = self.layout
col = layout.column()
-
+
col.prop(self, "interpolation")
col.prop(self, "input")
col.separator()
-
+
col.prop(self, "influence")
-
+
def invoke(self, context, event):
# load custom settings
settings_load(self)
return self.execute(context)
-
+
def execute(self, context):
# initialise
global_undo, object, bm = initialise()
@@ -3918,12 +3917,12 @@ class Space(bpy.types.Operator):
context.scene, self.input)
mapping = get_mapping(derived, bm, bm_mod, False, False, loops)
loops = check_loops(loops, mapping, bm_mod)
-
+
# saving cache for faster execution next time
if not cached:
cache_write("Space", object, bm, self.input, False, False, loops,
derived, mapping)
-
+
move = []
for loop in loops:
# calculate splines and new positions
@@ -3936,12 +3935,12 @@ class Space(bpy.types.Operator):
tknots, tpoints, loop[0][:-1], splines))
# move vertices to new locations
move_verts(object, bm, mapping, move, self.influence)
-
+
# cleaning up
if derived:
bm_mod.free()
terminate(global_undo)
-
+
return{'FINISHED'}
@@ -3952,10 +3951,10 @@ class Space(bpy.types.Operator):
# menu containing all tools
class VIEW3D_MT_edit_mesh_looptools(bpy.types.Menu):
bl_label = "LoopTools"
-
+
def draw(self, context):
layout = self.layout
-
+
layout.operator("mesh.looptools_bridge", text="Bridge").loft = False
layout.operator("mesh.looptools_circle")
layout.operator("mesh.looptools_curve")
@@ -3979,7 +3978,7 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel):
layout = self.layout
col = layout.column(align=True)
lt = context.window_manager.looptools
-
+
# bridge - first line
split = col.split(percentage=0.15, align=True)
if lt.display_bridge:
@@ -3991,7 +3990,7 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel):
if lt.display_bridge:
box = col.column(align=True).box().column()
#box.prop(self, "mode")
-
+
# top row
col_top = box.column(align=True)
row = col_top.row(align=True)
@@ -4009,13 +4008,13 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel):
bottom_right.prop(lt, "bridge_cubic_strength")
# boolean properties
col_top.prop(lt, "bridge_remove_faces")
-
+
# override properties
col_top.separator()
row = box.row(align = True)
row.prop(lt, "bridge_twist")
row.prop(lt, "bridge_reverse")
-
+
# circle - first line
split = col.split(percentage=0.15, align=True)
if lt.display_circle:
@@ -4028,7 +4027,7 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel):
box = col.column(align=True).box().column()
box.prop(lt, "circle_fit")
box.separator()
-
+
box.prop(lt, "circle_flatten")
row = box.row(align=True)
row.prop(lt, "circle_custom_radius")
@@ -4037,9 +4036,9 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel):
row_right.prop(lt, "circle_radius", text="")
box.prop(lt, "circle_regular")
box.separator()
-
+
box.prop(lt, "circle_influence")
-
+
# curve - first line
split = col.split(percentage=0.15, align=True)
if lt.display_curve:
@@ -4055,9 +4054,9 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel):
box.prop(lt, "curve_boundaries")
box.prop(lt, "curve_regular")
box.separator()
-
+
box.prop(lt, "curve_influence")
-
+
# flatten - first line
split = col.split(percentage=0.15, align=True)
if lt.display_flatten:
@@ -4071,9 +4070,9 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel):
box.prop(lt, "flatten_plane")
#box.prop(lt, "flatten_restriction")
box.separator()
-
+
box.prop(lt, "flatten_influence")
-
+
# gstretch - first line
split = col.split(percentage=0.15, align=True)
if lt.display_gstretch:
@@ -4087,7 +4086,7 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel):
box.prop(lt, "gstretch_method")
box.prop(lt, "gstretch_delete_strokes")
box.separator()
-
+
col_conv = box.column(align=True)
col_conv.prop(lt, "gstretch_conversion", text="")
if lt.gstretch_conversion == 'distance':
@@ -4099,9 +4098,9 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel):
elif lt.gstretch_conversion == 'vertices':
col_conv.prop(lt, "gstretch_conversion_vertices")
box.separator()
-
+
box.prop(lt, "gstretch_influence")
-
+
# loft - first line
split = col.split(percentage=0.15, align=True)
if lt.display_loft:
@@ -4113,7 +4112,7 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel):
if lt.display_loft:
box = col.column(align=True).box().column()
#box.prop(self, "mode")
-
+
# top row
col_top = box.column(align=True)
row = col_top.row(align=True)
@@ -4132,13 +4131,13 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel):
# boolean properties
col_top.prop(lt, "bridge_remove_faces")
col_top.prop(lt, "bridge_loft_loop")
-
+
# override properties
col_top.separator()
row = box.row(align = True)
row.prop(lt, "bridge_twist")
row.prop(lt, "bridge_reverse")
-
+
# relax - first line
split = col.split(percentage=0.15, align=True)
if lt.display_relax:
@@ -4153,7 +4152,7 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel):
box.prop(lt, "relax_input")
box.prop(lt, "relax_iterations")
box.prop(lt, "relax_regular")
-
+
# space - first line
split = col.split(percentage=0.15, align=True)
if lt.display_space:
@@ -4167,7 +4166,7 @@ class VIEW3D_PT_tools_looptools(bpy.types.Panel):
box.prop(lt, "space_interpolation")
box.prop(lt, "space_input")
box.separator()
-
+
box.prop(lt, "space_influence")
@@ -4177,7 +4176,7 @@ class LoopToolsProps(bpy.types.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",
@@ -4203,7 +4202,7 @@ class LoopToolsProps(bpy.types.PropertyGroup):
display_space = bpy.props.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",
@@ -4253,7 +4252,7 @@ class LoopToolsProps(bpy.types.PropertyGroup):
bridge_twist = bpy.props.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",
@@ -4283,7 +4282,7 @@ class LoopToolsProps(bpy.types.PropertyGroup):
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 "\
@@ -4313,7 +4312,7 @@ class LoopToolsProps(bpy.types.PropertyGroup):
"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",
@@ -4336,7 +4335,7 @@ class LoopToolsProps(bpy.types.PropertyGroup):
"movement inside the bounding box of the selection")),
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 "\
@@ -4382,7 +4381,7 @@ class LoopToolsProps(bpy.types.PropertyGroup):
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)
+ default = False)
gstretch_influence = bpy.props.FloatProperty(name = "Influence",
description = "Force of the tool",
default = 100.0,
@@ -4400,7 +4399,7 @@ class LoopToolsProps(bpy.types.PropertyGroup):
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 "\
@@ -4425,7 +4424,7 @@ class LoopToolsProps(bpy.types.PropertyGroup):
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",