Newer
Older
return(strokes)
# convert a GP stroke into a list of points which can be stored in cache
def gstretch_true_to_safe_strokes(strokes):
safe_strokes = []
for stroke in strokes:
safe_strokes.append([p.co.copy() for p in stroke.points])
CoDEmanX
committed
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
return(safe_strokes)
# force consistency in GUI, max value can never be lower than min value
def gstretch_update_max(self, context):
# called from operator settings (after execution)
if 'conversion_min' in self.keys():
if self.conversion_min > self.conversion_max:
self.conversion_max = self.conversion_min
# called from toolbar
else:
lt = context.window_manager.looptools
if lt.gstretch_conversion_min > lt.gstretch_conversion_max:
lt.gstretch_conversion_max = lt.gstretch_conversion_min
# force consistency in GUI, min value can never be higher than max value
def gstretch_update_min(self, context):
# called from operator settings (after execution)
if 'conversion_max' in self.keys():
if self.conversion_max < self.conversion_min:
self.conversion_min = self.conversion_max
# called from toolbar
else:
lt = context.window_manager.looptools
if lt.gstretch_conversion_max < lt.gstretch_conversion_min:
lt.gstretch_conversion_min = lt.gstretch_conversion_max
# ########################################
# ##### Relax functions ##################
# ########################################
# create lists with knots and points, all correctly sorted
def relax_calculate_knots(loops):
all_knots = []
all_points = []
for loop, circular in loops:
knots = [[], []]
points = [[], []]
if circular:
extend = [False, True, 0, 1, 0, 1]
extend = [True, False, 0, 1, 1, 2]
else:
extend = [False, False, 0, 1, 1, 2]
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):
knots[j].append(loop[i])
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:
points[j].append(loop[i])
elif loop[i] != points[j][0]:
points[j].append(loop[i])
if circular:
if knots[j][0] != knots[j][-1]:
knots[j].append(knots[j][0])
if len(points[1]) == 0:
knots.pop(1)
points.pop(1)
for k in knots:
all_knots.append(k)
for p in points:
all_points.append(p)
CoDEmanX
committed
return(all_knots, all_points)
# calculate relative positions compared to first knot
def relax_calculate_t(bm_mod, knots, points, regular):
all_tknots = []
all_tpoints = []
for i in range(len(knots)):
amount = len(knots[i]) + len(points[i])
for j in range(amount):
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)]])
len_total = 0
loc_prev = False
tknots = []
tpoints = []
for m in mix:
loc = mathutils.Vector(bm_mod.verts[m[1]].co[:])
if not loc_prev:
loc_prev = loc
len_total += (loc - loc_prev).length
if m[0]:
tknots.append(len_total)
else:
tpoints.append(len_total)
loc_prev = loc
if regular:
tpoints = []
for p in range(len(points[i])):
tpoints.append((tknots[p] + tknots[p + 1]) / 2)
all_tknots.append(tknots)
all_tpoints.append(tpoints)
CoDEmanX
committed
return(all_tknots, all_tpoints)
# change the location of the points to their place on the spline
def relax_calculate_verts(bm_mod, interpolation, tknots, knots, tpoints,
points, splines):
change = []
move = []
for i in range(len(knots)):
for p in points[i]:
m = tpoints[i][points[i].index(p)]
if m in tknots[i]:
n = tknots[i].index(m)
else:
t = tknots[i][:]
t.append(m)
t.sort()
if n > len(splines[i]) - 1:
n = len(splines[i]) - 1
elif n < 0:
n = 0
CoDEmanX
committed
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
ay, by, cy, dy, ty = splines[i][n][1]
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'
a, d, t, u = splines[i][n]
if u == 0:
u = 1e-8
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])
CoDEmanX
committed
return(move)
# ########################################
# ##### Space functions ##################
# ########################################
# calculate relative positions compared to first knot
def space_calculate_t(bm_mod, knots):
tknots = []
loc_prev = False
len_total = 0
for k in knots:
loc = mathutils.Vector(bm_mod.verts[k].co[:])
if not loc_prev:
loc_prev = loc
len_total += (loc - loc_prev).length
tknots.append(len_total)
loc_prev = loc
amount = len(knots)
t_per_segment = len_total / (amount - 1)
tpoints = [i * t_per_segment for i in range(amount)]
CoDEmanX
committed
return(tknots, tpoints)
# change the location of the points to their place on the spline
def space_calculate_verts(bm_mod, interpolation, tknots, tpoints, points,
splines):
move = []
for p in points:
m = tpoints[points.index(p)]
if m in tknots:
n = tknots.index(m)
else:
t = tknots[:]
t.append(m)
t.sort()
n = t.index(m) - 1
if n > len(splines) - 1:
n = len(splines) - 1
elif n < 0:
n = 0
CoDEmanX
committed
if interpolation == 'cubic':
ax, bx, cx, dx, tx = splines[n][0]
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
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'
a, d, t, u = splines[n]
move.append([p, ((m - t) / u) * d + a])
CoDEmanX
committed
return(move)
# ########################################
# ##### Operators ########################
# ########################################
# 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'}
CoDEmanX
committed
name="Strength",
description="Higher strength results in more fluid curves",
default=1.0,
soft_min=-3.0,
soft_max=3.0
)
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'
)
name="Loft",
description="Loft multiple loops, instead of considering them as "
"a multi-input for bridging",
default=False
)
name="Loop",
description="Connect the first and the last loop with each other",
default=False
)
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'
)
name="Mode",
items=(('basic', "Basic", "Fast algorithm"),
('shortest', "Shortest edge", "Slower algorithm with better vertex matching")),
description="Algorithm used for bridging",
default='shortest'
)
name="Remove faces",
description="Remove faces that are internal after bridging",
default=True
)
name="Reverse",
description="Manually override the direction in which the loops "
"are bridged. Only use if the tool gives the wrong result",
default=False
)
name="Segments",
description="Number of segments used to bridge the gap (0=automatic)",
default=1,
min=0,
soft_max=20
)
name="Twist",
description="Twist what vertices are connected to each other",
default=0
)
CoDEmanX
committed
@classmethod
def poll(cls, context):
ob = context.active_object
return (ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
CoDEmanX
committed
def draw(self, context):
layout = self.layout
# layout.prop(self, "mode") # no cases yet where 'basic' mode is needed
CoDEmanX
committed
# top row
col_top = layout.column(align=True)
row = col_top.row(align=True)
col_left = row.column(align=True)
col_right = row.column(align=True)
col_right.active = self.segments != 1
col_left.prop(self, "segments")
col_right.prop(self, "min_width", text="")
# bottom row
bottom_left = col_left.row()
bottom_left.active = self.segments != 1
bottom_left.prop(self, "interpolation", text="")
bottom_right = col_right.row()
bottom_right.active = self.interpolation == 'cubic'
bottom_right.prop(self, "cubic_strength")
# boolean properties
col_top.prop(self, "remove_faces")
if self.loft:
col_top.prop(self, "loft_loop")
CoDEmanX
committed
# override properties
col_top.separator()
row.prop(self, "twist")
row.prop(self, "reverse")
CoDEmanX
committed
def invoke(self, context, event):
# load custom settings
context.window_manager.looptools.bridge_loft = self.loft
settings_load(self)
return self.execute(context)
CoDEmanX
committed
def execute(self, context):
# initialise
object, bm = initialise()
edge_faces, edgekey_to_edge, old_selected_faces, smooth = \
bridge_initialise(bm, self.interpolation)
settings_write(self)
CoDEmanX
committed
# 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",
object, bm, input_method, False)
if not cached:
# get loops
loops = bridge_get_input(bm)
if loops:
# reorder loops if there are more than 2
if len(loops) > 2:
if self.loft:
loops = bridge_sort_loops(bm, loops, self.loft_loop)
else:
loops = bridge_match_loops(bm, loops)
CoDEmanX
committed
# saving cache for faster execution next time
if not cached:
cache_write("Bridge", object, bm, input_method, False, False,
loops, False, False)
if loops:
# calculate new geometry
vertices = []
faces = []
max_vert_index = len(bm.verts) - 1
for i in range(1, len(loops)):
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)
segments = bridge_calculate_segments(bm, lines,
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
)
if new_verts:
vertices += new_verts
if new_faces:
faces += new_faces
# make sure faces in loops that aren't used, aren't removed
if self.remove_faces and old_selected_faces:
bridge_save_unused_faces(bm, old_selected_faces, loops)
# create vertices
if vertices:
bridge_create_vertices(bm, vertices)
# 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
bridge_select_new_faces(new_faces, smooth)
# edge-data could have changed, can't use cache next run
if faces and not vertices:
cache_delete("Bridge")
# delete internal faces
if self.remove_faces and old_selected_faces:
bridge_remove_internal_faces(bm, old_selected_faces)
# make sure normals are facing outside
bmesh.update_edit_mesh(object.data, loop_triangles=False,
bpy.ops.mesh.normals_make_consistent()
CoDEmanX
committed
CoDEmanX
committed
return{'FINISHED'}
# circle operator
bl_idname = "mesh.looptools_circle"
bl_label = "Circle"
bl_description = "Move selected vertices into a circle shape"
bl_options = {'REGISTER', 'UNDO'}
CoDEmanX
committed
name="Radius",
description="Force a custom radius",
default=False
)
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'
)
name="Flatten",
description="Flatten the circle, instead of projecting it on the mesh",
default=True
)
name="Influence",
description="Force of the tool",
default=100.0,
min=0.0,
max=100.0,
precision=1,
subtype='PERCENTAGE'
)
name="Lock X",
description="Lock editing of the x-coordinate",
default=False
)
name="Lock Y",
description="Lock editing of the y-coordinate",
default=False
)
description="Lock editing of the z-coordinate",
default=False
)
name="Radius",
description="Custom radius for circle",
default=1.0,
min=0.0,
soft_max=1000.0
)
name="Regular",
description="Distribute vertices at constant distances along the circle",
default=True
)
CoDEmanX
committed
@classmethod
def poll(cls, context):
ob = context.active_object
return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
CoDEmanX
committed
def draw(self, context):
layout = self.layout
col = layout.column()
CoDEmanX
committed
col.prop(self, "fit")
col.separator()
CoDEmanX
committed
col.prop(self, "flatten")
row = col.row(align=True)
row.prop(self, "custom_radius")
row_right = row.row(align=True)
row_right.active = self.custom_radius
row_right.prop(self, "radius", text="")
col.prop(self, "regular")
col.separator()
CoDEmanX
committed
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='UNLOCKED')
row.prop(self, "lock_y", text="Y", icon='LOCKED')
row.prop(self, "lock_y", text="Y", icon='UNLOCKED')
row.prop(self, "lock_z", text="Z", icon='LOCKED')
row.prop(self, "lock_z", text="Z", icon='UNLOCKED')
CoDEmanX
committed
def invoke(self, context, event):
# load custom settings
settings_load(self)
return self.execute(context)
CoDEmanX
committed
def execute(self, context):
# initialise
object, bm = initialise()
settings_write(self)
# check cache to see if we can save time
cached, single_loops, loops, derived, mapping = cache_read("Circle",
object, bm, False, False)
if cached:
Vladimir Spivak(cwolf3d)
committed
derived, bm_mod = get_derived_bmesh(object, bm, False)
else:
# find loops
derived, bm_mod, single_vertices, single_loops, loops = \
circle_get_input(object, bm)
mapping = get_mapping(derived, bm, bm_mod, single_vertices,
False, loops)
single_loops, loops = circle_check_loops(single_loops, loops,
mapping, bm_mod)
CoDEmanX
committed
# saving cache for faster execution next time
if not cached:
cache_write("Circle", object, bm, False, False, single_loops,
loops, derived, mapping)
CoDEmanX
committed
move = []
for i, loop in enumerate(loops):
# best fitting flat plane
com, normal = calculate_plane(bm_mod, loop)
# if circular, shift loop so we get a good starting vertex
if loop[1]:
loop = circle_shift_loop(bm_mod, loop, com)
# flatten vertices on plane
locs_2d, p, q = circle_3d_to_2d(bm_mod, loop, com, normal)
# calculate circle
if self.fit == 'best':
x0, y0, r = circle_calculate_best_fit(locs_2d)
x0, y0, r = circle_calculate_min_fit(locs_2d)
# radius override
if self.custom_radius:
r = self.radius / p.length
# calculate positions on circle
if self.regular:
new_locs_2d = circle_project_regular(locs_2d[:], x0, y0, r)
else:
new_locs_2d = circle_project_non_regular(locs_2d[:], x0, y0, r)
# take influence into account
locs_2d = circle_influence_locs(locs_2d, new_locs_2d,
self.influence)
# calculate 3d positions of the created 2d input
move.append(circle_calculate_verts(self.flatten, bm_mod,
locs_2d, com, p, q, normal))
# flatten single input vertices on plane defined by loop
if self.flatten and single_loops:
move.append(circle_flatten_singles(bm_mod, com, p, q,
normal, single_loops[i]))
CoDEmanX
committed
# move vertices to new locations
if self.lock_x or self.lock_y or self.lock_z:
lock = [self.lock_x, self.lock_y, self.lock_z]
else:
lock = False
move_verts(object, bm, mapping, move, lock, -1)
CoDEmanX
committed
# cleaning up
if derived:
bm_mod.free()
CoDEmanX
committed
return{'FINISHED'}
# curve operator
bl_idname = "mesh.looptools_curve"
bl_label = "Curve"
bl_description = "Turn a loop into a smooth curve"
bl_options = {'REGISTER', 'UNDO'}
CoDEmanX
committed
name="Boundaries",
description="Limit the tool to work within the boundaries of the selected vertices",
default=False
)
name="Influence",
description="Force of the tool",
default=100.0,
min=0.0,
max=100.0,
precision=1,
subtype='PERCENTAGE'
)
name="Interpolation",
items=(("cubic", "Cubic", "Natural cubic spline, smooth results"),
("linear", "Linear", "Simple and fast linear algorithm")),
description="Algorithm used for interpolation",
default='cubic'
)
name="Lock X",
description="Lock editing of the x-coordinate",
default=False
)
name="Lock Y",
description="Lock editing of the y-coordinate",
default=False
)
name="Lock Z",
description="Lock editing of the z-coordinate",
default=False
)
name="Regular",
description="Distribute vertices at constant distances along the curve",
default=True
)
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'
)
CoDEmanX
committed
@classmethod
def poll(cls, context):
ob = context.active_object
return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
CoDEmanX
committed
def draw(self, context):
layout = self.layout
col = layout.column()
CoDEmanX
committed
col.prop(self, "interpolation")
col.prop(self, "restriction")
col.prop(self, "boundaries")
col.prop(self, "regular")
col.separator()
CoDEmanX
committed
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='UNLOCKED')
row.prop(self, "lock_y", text="Y", icon='LOCKED')
row.prop(self, "lock_y", text="Y", icon='UNLOCKED')
row.prop(self, "lock_z", text="Z", icon='LOCKED')
row.prop(self, "lock_z", text="Z", icon='UNLOCKED')
CoDEmanX
committed
def invoke(self, context, event):
# load custom settings
settings_load(self)
return self.execute(context)
CoDEmanX
committed
def execute(self, context):
# initialise
object, bm = initialise()
settings_write(self)
# check cache to see if we can save time
cached, single_loops, loops, derived, mapping = cache_read("Curve",
object, bm, False, self.boundaries)
if cached:
Vladimir Spivak(cwolf3d)
committed
derived, bm_mod = get_derived_bmesh(object, bm, False)
else:
# find loops
derived, bm_mod, loops = curve_get_input(object, bm, self.boundaries)
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
]
CoDEmanX
committed
# saving cache for faster execution next time
if not cached:
cache_write("Curve", object, bm, False, self.boundaries, False,
loops, derived, mapping)
CoDEmanX
committed
move = []
for loop in loops:
knots, points = curve_calculate_knots(loop, verts_selected)
pknots = curve_project_knots(bm_mod, verts_selected, knots,
points, loop[1])
tknots, tpoints = curve_calculate_t(bm_mod, knots, points,
pknots, self.regular, loop[1])
splines = calculate_splines(self.interpolation, bm_mod,
tknots, knots)
move.append(curve_calculate_vertices(bm_mod, knots, tknots,
points, tpoints, splines, self.interpolation,
self.restriction))
CoDEmanX
committed
# move vertices to new locations
if self.lock_x or self.lock_y or self.lock_z:
lock = [self.lock_x, self.lock_y, self.lock_z]
else:
lock = False
move_verts(object, bm, mapping, move, lock, self.influence)
CoDEmanX
committed
# cleaning up
if derived:
bm_mod.free()
return{'FINISHED'}
# flatten operator
bl_idname = "mesh.looptools_flatten"
bl_label = "Flatten"
bl_description = "Flatten vertices on a best-fitting plane"
bl_options = {'REGISTER', 'UNDO'}
CoDEmanX
committed
name="Influence",
description="Force of the tool",
default=100.0,
min=0.0,
max=100.0,
precision=1,
subtype='PERCENTAGE'
)
name="Lock X",
description="Lock editing of the x-coordinate",
default=False
)
name="Lock Y",
description="Lock editing of the y-coordinate",
default=False
)
description="Lock editing of the z-coordinate",
default=False
)
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'
)
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'
)
CoDEmanX
committed
@classmethod
def poll(cls, context):
ob = context.active_object
return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
CoDEmanX
committed
def draw(self, context):
layout = self.layout
col = layout.column()
CoDEmanX
committed
col.prop(self, "plane")
CoDEmanX
committed
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='UNLOCKED')
row.prop(self, "lock_y", text="Y", icon='LOCKED')
row.prop(self, "lock_y", text="Y", icon='UNLOCKED')
row.prop(self, "lock_z", text="Z", icon='LOCKED')
row.prop(self, "lock_z", text="Z", icon='UNLOCKED')
CoDEmanX
committed
def invoke(self, context, event):
# load custom settings
settings_load(self)
return self.execute(context)
CoDEmanX
committed
def execute(self, context):
# initialise
object, bm = initialise()
settings_write(self)
# check cache to see if we can save time
cached, single_loops, loops, derived, mapping = cache_read("Flatten",
object, bm, False, False)
if not cached:
# order input into virtual loops
loops = flatten_get_input(bm)
loops = check_loops(loops, mapping, bm)
CoDEmanX
committed
# saving cache for faster execution next time
if not cached:
cache_write("Flatten", object, bm, False, False, False, loops,
False, False)
CoDEmanX
committed
move = []
for loop in loops:
# calculate plane and position of vertices on them
com, normal = calculate_plane(bm, loop, method=self.plane,
object=object)
to_move = flatten_project(bm, loop, com, normal)
if self.restriction == 'none':
move.append(to_move)
else:
move.append(to_move)
# move vertices to new locations
if self.lock_x or self.lock_y or self.lock_z:
lock = [self.lock_x, self.lock_y, self.lock_z]
else:
lock = False
move_verts(object, bm, False, move, lock, self.influence)
CoDEmanX
committed
CoDEmanX
committed
return{'FINISHED'}
# Annotation operator
class RemoveAnnotation(Operator):
bl_idname = "remove.annotation"
bl_label = "Remove Annotation"
bl_description = "Remove all Annotation Strokes"
try:
bpy.data.grease_pencils[0].layers.active.clear()
except:
self.report({'INFO'}, "No Annotation data to Unlink")
# GPencil operator
class RemoveGPencil(Operator):
bl_idname = "remove.gp"
bl_label = "Remove GPencil"
bl_description = "Remove all GPencil Strokes"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
try:
looptools = context.window_manager.looptools
looptools.gstretch_guide.data.layers.data.clear()
looptools.gstretch_guide.data.update_tag()
except:
self.report({'INFO'}, "No GPencil data to Unlink")
return {'CANCELLED'}
return{'FINISHED'}
bl_idname = "mesh.looptools_gstretch"
bl_label = "Gstretch"
bl_description = "Stretch selected vertices to active stroke"
CoDEmanX
committed
name="Conversion",
items=(("distance", "Distance", "Set the distance between vertices "
"of the converted stroke"),
("limit_vertices", "Limit vertices", "Set the minimum and maximum "
"number of vertices that converted strokes will have"),
("vertices", "Exact vertices", "Set the exact number of vertices "
"that converted strokes will have. Short strokes "
"with few points may contain less vertices than this number."),
("none", "No simplification", "Convert each point "
description="If strokes are converted to geometry, "
"use this simplification method",
default='limit_vertices'
)
name="Distance",
description="Absolute distance between vertices along the converted "
" stroke",
default=0.1,
min=0.000001,
soft_min=0.01,
soft_max=100
)
description="Maximum number of vertices strokes will "
"have, when they are converted to geomtery",
default=32,
min=3,
soft_max=500,
update=gstretch_update_min
)
description="Minimum number of vertices strokes will "
"have, when they are converted to geomtery",
default=8,
min=3,
soft_max=500,
update=gstretch_update_max
)
description="Number of vertices 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
)
description="Remove strokes if they have been used."
"WARNING: DOES NOT SUPPORT UNDO",
name="Influence",
description="Force of the tool",
default=100.0,
min=0.0,
max=100.0,
precision=1,
subtype='PERCENTAGE'
)
name="Lock X",
description="Lock editing of the x-coordinate",
default=False
)
name="Lock Y",
description="Lock editing of the y-coordinate",
default=False
)
name="Lock Z",
description="Lock editing of the z-coordinate",
default=False
)
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 "
"stroke",
CoDEmanX
committed
@classmethod
def poll(cls, context):
ob = context.active_object
return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
CoDEmanX
committed
looptools = context.window_manager.looptools