[Bf-extensions-cvs] [0b2d5112] master: Update mesh_inset (alternate inset using straight skeleton) for 2.8.

Howard Trickey noreply at git.blender.org
Tue Feb 19 02:58:23 CET 2019


Commit: 0b2d51126c5fbeebb313ebd791fbbbf79b927f8d
Author: Howard Trickey
Date:   Mon Feb 18 20:50:21 2019 -0500
Branches: master
https://developer.blender.org/rBA0b2d51126c5fbeebb313ebd791fbbbf79b927f8d

Update mesh_inset (alternate inset using straight skeleton) for 2.8.

Update also made it modal, with interaction mode as in built-in inset,
where mouse movement adjusts amount and holding control makes mouse
movement affext height. Also renamed from "Inset Polygon" to
"Inset Straight Skeleton" to lessen confusion with built-in one and
emphasizing why this one is different.
Recommend binding mesh.insetstraightskeleton to a key, like Alt-i

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

M	mesh_inset/__init__.py

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

diff --git a/mesh_inset/__init__.py b/mesh_inset/__init__.py
index e7c7d587..781b6962 100644
--- a/mesh_inset/__init__.py
+++ b/mesh_inset/__init__.py
@@ -19,12 +19,12 @@
 # <pep8 compliant>
 
 bl_info = {
-    "name": "Inset Polygon",
+    "name": "Inset Straight Skeleton",
     "author": "Howard Trickey",
-    "version": (1, 0, 1),
-    "blender": (2, 73, 0),
-    "location": "View3D > Tools",
-    "description": "Make an inset polygon inside selection.",
+    "version": (1, 1),
+    "blender": (2, 80, 0),
+    "location": "3DView Operator",
+    "description": "Make an inset inside selection using straight skeleton algorithm.",
     "warning": "",
     "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
                 "Scripts/Modeling/Inset-Polygon",
@@ -45,22 +45,35 @@ import math
 import bpy
 import bmesh
 import mathutils
+from mathutils import Vector
+from bpy_extras import view3d_utils
+import gpu
+from gpu_extras.batch import batch_for_shader
+
 from bpy.props import (
         BoolProperty,
         EnumProperty,
         FloatProperty,
         )
 
+SpaceView3D = bpy.types.SpaceView3D
+
+INSET_VALUE = 0
+HEIGHT_VALUE = 1
+NUM_VALUES = 2
+
+# TODO: make a dooted-line shader
+shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
 
-class Inset(bpy.types.Operator):
-    bl_idname = "mesh.insetpoly"
-    bl_label = "Inset Polygon"
-    bl_description = "Make an inset polygon inside selection"
-    bl_options = {'REGISTER', 'UNDO'}
+class MESH_OT_InsetStraightSkeleton(bpy.types.Operator):
+    bl_idname = "mesh.insetstraightskeleton"
+    bl_label = "Inset Straight Skeleton"
+    bl_description = "Make an inset inside selection using straight skeleton algorithm"
+    bl_options = {'UNDO', 'REGISTER', 'GRAB_CURSOR', 'BLOCKING'}
 
     inset_amount: FloatProperty(name="Amount",
         description="Amount to move inset edges",
-        default=5.0,
+        default=0.0,
         min=0.0,
         max=1000.0,
         soft_min=0.0,
@@ -77,15 +90,9 @@ class Inset(bpy.types.Operator):
     region: BoolProperty(name="Region",
         description="Inset selection as one region?",
         default=True)
-    scale: EnumProperty(name="Scale",
-        description="Scale for amount",
-        items=[
-            ('PERCENT', "Percent",
-                "Percentage of maximum inset amount"),
-            ('ABSOLUTE', "Absolute",
-                "Length in blender units")
-            ],
-        default='PERCENT')
+    quadrangulate: BoolProperty(name="Quadrangulate",
+        description="Quadrangulate after inset?",
+        default=True)
 
     @classmethod
     def poll(cls, context):
@@ -96,32 +103,152 @@ class Inset(bpy.types.Operator):
         layout = self.layout
         box = layout.box()
         box.label(text="Inset Options:")
-        box.prop(self, "scale")
         box.prop(self, "inset_amount")
         box.prop(self, "inset_height")
         box.prop(self, "region")
+        box.prop(self, "quadrangulate")
 
     def invoke(self, context, event):
+        self.modal = True
+        # make backup bmesh from current mesh, after flushing editmode to mesh
+        bpy.context.object.update_from_editmode()
+        self.backup = bmesh.new()
+        self.backup.from_mesh(bpy.context.object.data)
+        self.inset_amount = 0.0
+        self.inset_height = 0.0
+        self.center, self.center3d = calc_select_center(context)
+        self.center_pixel_size = calc_pixel_size(context, self.center3d)
+        udpi = context.preferences.system.dpi
+        upixelsize = context.preferences.system.pixel_size
+        self.pixels_per_inch = udpi * upixelsize
+        self.value_mode = INSET_VALUE
+        self.initial_length = [-1.0, -1.0]
+        self.scale = [self.center_pixel_size] * NUM_VALUES
+        self.calc_initial_length(event, True)
+        self.mouse_cur = Vector((event.mouse_region_x, event.mouse_region_y))
+        col = context.preferences.themes["Default"].view_3d.view_overlay
+        self.line_color = (col.r, col.g, col.b, 1.0)
+
         self.action(context)
-        return {'FINISHED'}
+
+        context.window_manager.modal_handler_add(self)
+        self.draw_handle = SpaceView3D.draw_handler_add(draw_callback,
+            (self,), 'WINDOW', 'POST_PIXEL')
+
+        return {'RUNNING_MODAL'}
+
+    def calc_initial_length(self, event, mode_changed):
+        mdiff = self.center - Vector((event.mouse_region_x, event.mouse_region_y))
+        mlen = mdiff.length;
+        vmode = self.value_mode
+        if mode_changed or self.initial_length[vmode] == -1:
+            if vmode == INSET_VALUE:
+                value = self.inset_amount
+            else:
+               value = self.inset_height
+            sc = self.scale[vmode]
+            if value != 0.0:
+                mlen = mlen - value / sc
+        self.initial_length[vmode] = mlen
+
+    def modal(self, context, event):
+        if event.type in ['LEFTMOUSE', 'RIGHTMOUSE', 'ESC']:
+            if self.modal:
+                self.backup.free()
+            if self.draw_handle:
+                SpaceView3D.draw_handler_remove(self.draw_handle, 'WINDOW')
+                context.area.tag_redraw()
+            if event.type == 'LEFTMOUSE':  # Confirm
+                return {'FINISHED'}
+            else:  # Cancel
+                return {'CANCELLED'}
+        else:
+            # restore mesh to original state
+            bpy.ops.object.editmode_toggle()
+            self.backup.to_mesh(bpy.context.object.data)
+            bpy.ops.object.editmode_toggle()
+            if event.type == 'MOUSEMOVE':
+                if self.value_mode == INSET_VALUE and event.ctrl:
+                    self.value_mode = HEIGHT_VALUE
+                    self.calc_initial_length(event, True)
+                elif self.value_mode == HEIGHT_VALUE and not event.ctrl:
+                    self.value_mode = INSET_VALUE
+                    self.calc_initial_length(event, True)
+                self.mouse_cur = Vector((event.mouse_region_x, event.mouse_region_y))
+                vmode = self.value_mode
+                mdiff = self.center - self.mouse_cur
+                value = (mdiff.length - self.initial_length[vmode]) * self.scale[vmode]
+                if vmode == INSET_VALUE:
+                    self.inset_amount = value
+                else:
+                    self.inset_height = value
+            elif event.type == 'R' and event.value == 'PRESS':
+                self.region = not self.region
+            elif event.type == 'Q' and event.value == 'PRESS':
+                self.quadrangulate = not self.quadrangulate
+            self.action(context)
+
+        return {'RUNNING_MODAL'}
 
     def execute(self, context):
+        self.modal = False
         self.action(context)
         return {'FINISHED'}
 
     def action(self, context):
-        save_global_undo = bpy.context.preferences.edit.use_global_undo
-        bpy.context.preferences.edit.use_global_undo = False
         obj = bpy.context.active_object
         mesh = obj.data
         do_inset(mesh, self.inset_amount, self.inset_height, self.region,
-            self.scale == 'PERCENT')
-        bpy.context.preferences.edit.use_global_undo = save_global_undo
+		        self.quadrangulate)
         bpy.ops.object.editmode_toggle()
         bpy.ops.object.editmode_toggle()
 
 
-def do_inset(mesh, amount, height, region, as_percent):
+def draw_callback(op):
+    startpos = op.mouse_cur
+    endpos = op.center
+    coords = [startpos.to_tuple(), endpos.to_tuple()]
+    batch = batch_for_shader(shader, 'LINES', {"pos": coords})
+
+    try:
+        shader.bind()
+        shader.uniform_float("color", op.line_color)
+        batch.draw(shader)
+    except:
+        pass
+
+def calc_pixel_size(context, co):
+    # returns size in blender units of a pixel at 3d coord co
+    # see C code in ED_view3d_pixel_size and ED_view3d_update_viewmat
+    m = context.region_data.perspective_matrix
+    v1 = m[0].to_3d()
+    v2 = m[1].to_3d()
+    ll = min(v1.length_squared, v2.length_squared)
+    len_pz = 2.0 / math.sqrt(ll)
+    len_sz = max(context.region.width, context.region.height)
+    rv3dpixsize = len_pz / len_sz
+    proj = m[3][0] * co[0] + m[3][1] * co[1] + m[3][2] * co[2] + m[3][3]
+    ups = context.preferences.system.pixel_size
+    return proj * rv3dpixsize * ups
+
+def calc_select_center(context):
+    # returns region 2d coord and global 3d coord of selection center
+    ob = bpy.context.active_object
+    mesh = ob.data
+    center = Vector((0.0, 0.0, 0.0))
+    n = 0
+    for v in mesh.vertices:
+        if v.select:
+            center = center + Vector(v.co)
+            n += 1
+    if n > 0:
+        center = center / n
+    world_center = ob.matrix_world @ center
+    world_center_2d = view3d_utils.location_3d_to_region_2d( \
+    	context.region, context.region_data, world_center)
+    return (world_center_2d, world_center)
+
+def do_inset(mesh, amount, height, region, quadrangulate):
     if amount <= 0.0:
         return
     pitch = math.atan(height / amount)
@@ -142,7 +269,7 @@ def do_inset(mesh, amount, height, region, as_percent):
         m.face_data.append(f.index)
     orig_numv = len(m.points.pos)
     orig_numf = len(m.faces)
-    model.BevelSelectionInModel(m, amount, pitch, True, region, as_percent)
+    model.BevelSelectionInModel(m, amount, pitch, quadrangulate, region, False)
     if len(m.faces) == orig_numf:
         # something went wrong with Bevel - just treat as no-op
         return
@@ -160,41 +287,39 @@ def do_inset(mesh, amount, height, region, as_percent):
             continue
         # copy face attributes from old face that it was derived from
         bfi = blender_old_face_index[i]
-        if bfi and 0 <= bfi < start_faces:
-            bm.faces.ensure_lookup_table()
-            oldface = bm.faces[bfi]
-            bfacenew = bm.faces.new(vs, oldface)
-            # bfacenew.copy_from_face_interp(oldface)
-        else:
-            bfacenew = bm.faces.new(vs)
-        new_faces.append(bfacenew)
+        # sometimes, not sure why, this face already exists
+        # bmesh will give a value error in bm.faces.new() in that case
+        try:
+        

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list