[Bf-extensions-cvs] [e1f331e0] master: Rigify: add more utility code for the upcoming face rig.

Alexander Gavrilov noreply at git.blender.org
Sun Jul 11 18:23:01 CEST 2021


Commit: e1f331e0af4461dbf80e03e76d9576fa748a0460
Author: Alexander Gavrilov
Date:   Sun Jul 11 16:13:28 2021 +0300
Branches: master
https://developer.blender.org/rBAe1f331e0af4461dbf80e03e76d9576fa748a0460

Rigify: add more utility code for the upcoming face rig.

Move widget and driver utilities from the feature set and rewrite
create_circle_widget. Also increase the line length for autopep8.

===================================================================

A	rigify/.pep8
M	rigify/utils/mechanism.py
M	rigify/utils/misc.py
M	rigify/utils/widgets.py
M	rigify/utils/widgets_basic.py

===================================================================

diff --git a/rigify/.pep8 b/rigify/.pep8
new file mode 100644
index 00000000..a2d556f4
--- /dev/null
+++ b/rigify/.pep8
@@ -0,0 +1,2 @@
+[pycodestyle]
+max_line_length = 99
diff --git a/rigify/utils/mechanism.py b/rigify/utils/mechanism.py
index b4b2d389..413d8a00 100644
--- a/rigify/utils/mechanism.py
+++ b/rigify/utils/mechanism.py
@@ -312,6 +312,11 @@ def make_driver(owner, prop, *, index=-1, type='SUM', expression=None, variables
     return fcu
 
 
+#=============================================
+# Driver variable utilities
+#=============================================
+
+
 def driver_var_transform(target, bone=None, *, type='LOC_X', space='WORLD', rotation_mode='AUTO'):
     """
     Create a Transform Channel driver variable specification.
@@ -337,6 +342,38 @@ def driver_var_transform(target, bone=None, *, type='LOC_X', space='WORLD', rota
     return { 'type': 'TRANSFORMS', 'targets': [ target_map ] }
 
 
+def driver_var_distance(target, *, bone1=None, target2=None, bone2=None, space1='WORLD', space2='WORLD'):
+    """
+    Create a Distance driver variable specification.
+
+    Usage:
+        make_driver(..., variables=[driver_var_distance(...)])
+
+    Target bone name can be provided via a 'lazy' callable closure without arguments.
+    """
+
+    assert space1 in {'WORLD', 'TRANSFORM', 'LOCAL'}
+    assert space2 in {'WORLD', 'TRANSFORM', 'LOCAL'}
+
+    target1_map = {
+        'id': target,
+        'transform_space': space1 + '_SPACE',
+    }
+
+    if bone1 is not None:
+        target1_map['bone_target'] = bone1
+
+    target2_map = {
+        'id': target2 or target,
+        'transform_space': space2 + '_SPACE',
+    }
+
+    if bone2 is not None:
+        target2_map['bone_target'] = bone2
+
+    return {'type': 'LOC_DIFF', 'targets': [target1_map, target2_map]}
+
+
 #=============================================
 # Constraint management
 #=============================================
diff --git a/rigify/utils/misc.py b/rigify/utils/misc.py
index e4ba55f2..9d4307c0 100644
--- a/rigify/utils/misc.py
+++ b/rigify/utils/misc.py
@@ -22,7 +22,7 @@ import bpy
 import math
 import collections
 
-from itertools import tee, chain, islice, repeat
+from itertools import tee, chain, islice, repeat, permutations
 from mathutils import Vector, Matrix, Color
 from rna_prop_ui import rna_idprop_value_to_python
 
@@ -32,6 +32,28 @@ from rna_prop_ui import rna_idprop_value_to_python
 #=============================================
 
 
+axis_vectors = {
+    'x': (1,0,0),
+    'y': (0,1,0),
+    'z': (0,0,1),
+    '-x': (-1,0,0),
+    '-y': (0,-1,0),
+    '-z': (0,0,-1),
+}
+
+
+# Matrices that reshuffle axis order and/or invert them
+shuffle_matrix = {
+    sx+x+sy+y+sz+z: Matrix((
+        axis_vectors[sx+x], axis_vectors[sy+y], axis_vectors[sz+z]
+        )).transposed().freeze()
+    for x, y, z in permutations(['x', 'y', 'z'])
+    for sx in ('', '-')
+    for sy in ('', '-')
+    for sz in ('', '-')
+}
+
+
 def angle_on_plane(plane, vec1, vec2):
     """ Return the angle between two vectors projected onto a plane.
     """
diff --git a/rigify/utils/widgets.py b/rigify/utils/widgets.py
index 970904c1..a8691349 100644
--- a/rigify/utils/widgets.py
+++ b/rigify/utils/widgets.py
@@ -24,6 +24,7 @@ import inspect
 import functools
 
 from mathutils import Matrix, Vector, Euler
+from itertools import count
 
 from .errors import MetarigError
 from .collections import ensure_widget_collection
@@ -210,6 +211,113 @@ def widget_generator(generate_func=None, *, register=None, subsurf=0):
     return wrapper
 
 
+def generate_lines_geometry(geom, points, *, matrix=None, closed_loop=False):
+    """
+    Generates a polyline using given points, optionally closing the loop.
+    """
+    assert len(points) >= 2
+
+    base = len(geom.verts)
+
+    for i, raw_point in enumerate(points):
+        point = Vector(raw_point).to_3d()
+
+        if matrix:
+            point = matrix @ point
+
+        geom.verts.append(point)
+
+        if i > 0:
+            geom.edges.append((base + i - 1, base + i))
+
+    if closed_loop:
+        geom.edges.append((len(geom.verts) - 1, base))
+
+
+def generate_circle_geometry(geom, center, radius, *, matrix=None, angle_range=None,
+                             steps=24, radius_x=None, depth_x=0):
+    """
+    Generates a circle, adding vertices and edges to the lists.
+    center, radius: parameters of the circle
+    matrix: transformation matrix (by default the circle is in the XY plane)
+    angle_range: pair of angles to generate an arc of the circle
+    steps: number of edges to cover the whole circle (reduced for arcs)
+    """
+    assert steps >= 3
+
+    start = 0
+    delta = math.pi * 2 / steps
+
+    if angle_range:
+        start, end = angle_range
+        if start == end:
+            steps = 1
+        else:
+            steps = max(3, math.ceil(abs(end - start) / delta) + 1)
+            delta = (end - start) / (steps - 1)
+
+    if radius_x is None:
+        radius_x = radius
+
+    center = Vector(center).to_3d()  # allow 2d center
+    points = []
+
+    for i in range(steps):
+        angle = start + delta * i
+        x = math.cos(angle)
+        y = math.sin(angle)
+        points.append(center + Vector((x * radius_x, y * radius, x * x * depth_x)))
+
+    generate_lines_geometry(geom, points, matrix=matrix, closed_loop=not angle_range)
+
+
+def generate_circle_hull_geometry(geom, points, radius, gap, *, matrix=None, steps=24):
+    """
+    Given a list of 2D points forming a convex hull, generate a contour around
+    it, with each point being circumscribed with a circle arc of given radius,
+    and keeping the given distance gap from the lines connecting the circles.
+    """
+    assert radius >= gap
+
+    if len(points) <= 1:
+        if points:
+            generate_circle_geometry(
+                geom, points[0], radius,
+                matrix=matrix, steps=steps
+            )
+        return
+
+    base = len(geom.verts)
+    points_ex = [points[-1], *points, points[0]]
+    agap = math.asin(gap / radius)
+
+    for i, pprev, pcur, pnext in zip(count(0), points_ex[0:], points_ex[1:], points_ex[2:]):
+        vprev = pprev - pcur
+        vnext = pnext - pcur
+
+        # Compute bearings to adjacent points
+        aprev = math.atan2(vprev.y, vprev.x)
+        anext = math.atan2(vnext.y, vnext.x)
+        if anext <= aprev:
+            anext += math.pi * 2
+
+        # Adjust gap for circles that are too close
+        aprev += max(agap, math.acos(min(1, vprev.length/radius/2)))
+        anext -= max(agap, math.acos(min(1, vnext.length/radius/2)))
+
+        if anext > aprev:
+            if len(geom.verts) > base:
+                geom.edges.append((len(geom.verts)-1, len(geom.verts)))
+
+            generate_circle_geometry(
+                geom, pcur, radius, angle_range=(aprev, anext),
+                matrix=matrix, steps=steps
+            )
+
+    if len(geom.verts) > base:
+        geom.edges.append((len(geom.verts)-1, base))
+
+
 def create_circle_polygon(number_verts, axis, radius=1.0, head_tail=0.0):
     """ Creates a basic circle around of an axis selected.
         number_verts: number of vertices of the polygon
@@ -245,6 +353,10 @@ def create_circle_polygon(number_verts, axis, radius=1.0, head_tail=0.0):
     return verts, edges
 
 
+#=============================================
+# Widget transformation
+#=============================================
+
 def adjust_widget_axis(obj, axis='y', offset=0.0):
     mesh = obj.data
 
diff --git a/rigify/utils/widgets_basic.py b/rigify/utils/widgets_basic.py
index abc27263..543457eb 100644
--- a/rigify/utils/widgets_basic.py
+++ b/rigify/utils/widgets_basic.py
@@ -18,7 +18,9 @@
 
 # <pep8 compliant>
 
-from .widgets import create_widget, widget_generator, register_widget
+from .misc import shuffle_matrix
+from .widgets import (create_widget, widget_generator, register_widget,
+                      generate_circle_geometry)
 
 # Common Widgets
 
@@ -30,19 +32,23 @@ def create_line_widget(geom):
 
 
 @widget_generator
-def create_circle_widget(geom, *, radius=1.0, head_tail=0.0, head_tail_x=None, with_line=False):
+def create_circle_widget(geom, *, radius=1.0, head_tail=0.0, radius_x=None, head_tail_x=None, with_line=False):
     """ Creates a basic circle widget, a circle around the y-axis.
         radius: the radius of the circle
         head_tail: where along the length of the bone the circle is (0.0=head, 1.0=tail)
-        head_tail_x: if not None, specifies a different value along the X axis to create a deformed circle
+        radius_x: if not None, specifies a different radius along the X axis, creating an ellipse
+        head_tail_x: if not None, specifies a different value along the X axis to create
+                     a circle deformed in the Y direction.
     """
-    v = [(0.7071068286895752, 2.980232238769531e-07, -0.7071065306663513), (0.8314696550369263, 2.980232238769531e-07, -0.5555699467658997), (0.9238795042037964, 2.682209014892578e-07, -0.3826831877231598), (0.9807852506637573, 2.5331974029541016e-07, -0.19509011507034302), (1.0, 2.365559055306221e-07, 1.6105803979371558e-07), (0.9807853698730469, 2.2351741790771484e-07, 0.19509044289588928), (0.9238796234130859, 2.086162567138672e-07, 0.38268351554870605), (0.8314696550369263, 1.7881393 [...]

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list