[Bf-extensions-cvs] SVN commit: /data/svn/bf-extensions [3635] trunk/py/scripts/addons/ mesh_looptools.py: Version 4.2
Bart Crouch
bartius.crouch at gmail.com
Wed Jul 18 21:22:43 CEST 2012
Revision: 3635
http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-extensions&revision=3635
Author: crouch
Date: 2012-07-18 19:22:43 +0000 (Wed, 18 Jul 2012)
Log Message:
-----------
Version 4.2
Bugfixes and added new tool: Gstretch
Modified Paths:
--------------
trunk/py/scripts/addons/mesh_looptools.py
Modified: trunk/py/scripts/addons/mesh_looptools.py
===================================================================
--- trunk/py/scripts/addons/mesh_looptools.py 2012-07-17 18:15:08 UTC (rev 3634)
+++ trunk/py/scripts/addons/mesh_looptools.py 2012-07-18 19:22:43 UTC (rev 3635)
@@ -19,7 +19,7 @@
bl_info = {
'name': "LoopTools",
'author': "Bart Crouch",
- 'version': (4, 0, 1),
+ 'version': (4, 2, 0),
'blender': (2, 6, 3),
'location': "View3D > Toolbar and View3D > Specials (W-key)",
'warning': "",
@@ -36,6 +36,7 @@
import collections
import mathutils
import math
+from bpy_extras import view3d_utils
##########################################
@@ -703,6 +704,10 @@
global_undo = bpy.context.user_preferences.edit.use_global_undo
bpy.context.user_preferences.edit.use_global_undo = False
object = bpy.context.active_object
+ if 'MIRROR' in [mod.type for mod in object.modifiers if mod.show_viewport]:
+ # ensure that selection is synced for the derived mesh
+ 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)
@@ -2420,7 +2425,233 @@
return(verts_projected)
+
+
##########################################
+####### Gstretch functions ###############
+##########################################
+
+# flips loops, if necessary, to obtain maximum alignment to stroke
+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
+ total_dist = distance_loop_stroke(loop, stroke, object, bm_mod,
+ method)
+ loop[0].reverse()
+ total_dist_rev = distance_loop_stroke(loop, stroke, object, bm_mod,
+ method)
+ if total_dist_rev > total_dist:
+ loop[0].reverse()
+
+ return(ls_pairs)
+
+
+# calculate vertex positions on stroke
+def gstretch_calculate_verts(loop, stroke, object, bm_mod, method):
+ move = []
+ 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:]):
+ intersections = mathutils.geometry.intersect_line_line(vec1, vec2,
+ p.co, stroke.points[i].co)
+ if intersections and \
+ (intersections[0] - intersections[1]).length < 1e-2:
+ x, dist = mathutils.geometry.intersect_point_line(
+ intersections[0], p.co, stroke.points[i].co)
+ 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
+ v1 = bm_mod.verts[v1]
+ v2 = bm_mod.verts[v2]
+ if v1.select + v2.select == 1 and not v1.hide and not v2.hide:
+ vec1 = object.matrix_world * v1.co
+ vec2 = object.matrix_world * v2.co
+ intersection = intersect_line_stroke(vec1, vec2, stroke)
+ if intersection:
+ break
+ if not intersection:
+ v = bm_mod.verts[v_index]
+ intersection = intersect_line_stroke(v.co, v.co + v.normal,
+ 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)
+ else: # method == 'irregular'
+ relative_distance = relative_lengths[i]
+ loc, stroke_lengths_cache = gstretch_eval_stroke(stroke,
+ relative_distance, stroke_lengths_cache)
+ loc = matrix_inverse * loc
+ move.append([v_index, loc])
+
+ return(move)
+
+
+# erases the grease pencil stroke
+def gstretch_erase_stroke(stroke, context):
+ # change 3d coordinate into a stroke-point
+ def sp(loc, context):
+ lib = {'name': "",
+ '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)),
+ 'pressure': 1,
+ 'time': 0}
+ return(lib)
+
+ erase_stroke = [sp(p.co, context) for p in stroke.points]
+ if erase_stroke:
+ erase_stroke[0]['is_start'] = True
+ bpy.ops.gpencil.draw(mode='ERASER', stroke=erase_stroke)
+
+
+# get point on stroke, given by relative distance (0.0 - 1.0)
+def gstretch_eval_stroke(stroke, distance, stroke_lengths_cache=False):
+ # use cache if available
+ 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])
+ total_length = max(lengths[-1], 1e-7)
+ 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]:
+ # should be impossible, but better safe than sorry
+ loc = stroke.points[-1].co
+ else:
+ 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
+
+ return(loc, stroke_lengths_cache)
+
+
+# get grease pencil strokes for the active object
+def gstretch_get_strokes(object):
+ gp = object.grease_pencil
+ if not gp:
+ return(None)
+ layer = gp.layers.active
+ if not layer:
+ return(None)
+ frame = layer.active_frame
+ if not frame:
+ return(None)
+ strokes = frame.strokes
+ if len(strokes) < 1:
+ return(None)
+
+ return(strokes)
+
+
+# returns a list with loop-stroke pairs
+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:
+ center = mathutils.Vector()
+ for v_index in loop[0]:
+ center += bm_mod.verts[v_index].co
+ center /= len(loop[0])
+ center = object.matrix_world * center
+ loop_centers.append([center, loop])
+
+ # calculate stroke centers
+ stroke_centers = []
+ for stroke in strokes:
+ center = mathutils.Vector()
+ for p in stroke.points:
+ 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:
+ distances = []
+ for i, sc in enumerate(stroke_centers):
+ distances.append([sc[2], (lc[0] - sc[0]).length, i])
+ 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
+
+ return(ls_pairs)
+
+
+# returns list with a relative distance (0.0 - 1.0) of each vertex on the loop
+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])
+ total_length = max(lengths[-1], 1e-7)
+ relative_lengths = [length / total_length for length in
+ lengths]
+
+ return(relative_lengths)
+
+
+##########################################
####### Relax functions ##################
##########################################
@@ -3094,6 +3325,101 @@
return{'FINISHED'}
+# gstretch operator
+class GStretch(bpy.types.Operator):
+ bl_idname = "mesh.looptools_gstretch"
+ bl_label = "Gstretch"
+ bl_description = "Stretch selected vertices to Grease Pencil stroke"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ delete_strokes = bpy.props.BoolProperty(name="Delete strokes",
+ description = "Remove Grease Pencil strokes if they have been used "\
+ "for Gstretch",
+ 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')
+ method = bpy.props.EnumProperty(name = "Method",
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-extensions-cvs
mailing list