[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