mirror of
https://github.com/blender/blender-addons.git
synced 2025-08-16 15:35:05 +00:00
150 lines
4.3 KiB
Python
150 lines
4.3 KiB
Python
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
# <pep8 compliant>
|
|
|
|
bl_info = {
|
|
"name": "Vertex Chamfer",
|
|
"author": "Andrew Hale (TrumanBlending)",
|
|
"version": (0, 1),
|
|
"blender": (2, 63, 0),
|
|
"location": "Spacebar Menu",
|
|
"description": "Chamfer vertex",
|
|
"doc_url": "",
|
|
"category": "Mesh",
|
|
}
|
|
|
|
|
|
import bpy
|
|
import bmesh
|
|
from bpy.types import Operator
|
|
from bpy.props import (
|
|
BoolProperty,
|
|
FloatProperty,
|
|
)
|
|
|
|
|
|
class VertexChamfer(Operator):
|
|
bl_idname = "mesh.vertex_chamfer"
|
|
bl_label = "Chamfer Vertex"
|
|
bl_description = "Tri chamfer selected vertices"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
factor: FloatProperty(
|
|
name="Factor",
|
|
description="Size of the Champfer",
|
|
default=0.1,
|
|
min=0.0,
|
|
soft_max=1.0
|
|
)
|
|
relative: BoolProperty(
|
|
name="Relative",
|
|
description="If Relative, Champfer size is relative to the edge length",
|
|
default=True
|
|
)
|
|
dissolve: BoolProperty(
|
|
name="Remove",
|
|
description="Remove/keep the original selected vertices\n"
|
|
"Remove creates a new triangle face between the Champfer edges,\n"
|
|
"similar to the Dissolve Vertices operator",
|
|
default=True
|
|
)
|
|
displace: FloatProperty(
|
|
name="Displace",
|
|
description="Active only if Remove option is disabled\n"
|
|
"Displaces the original selected vertices along the normals\n"
|
|
"defined by the Champfer edges",
|
|
soft_min=-5.0,
|
|
soft_max=5.0
|
|
)
|
|
|
|
@classmethod
|
|
def poll(self, context):
|
|
return (context.active_object.type == 'MESH' and
|
|
context.mode == 'EDIT_MESH')
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.prop(self, "factor", text="Distance" if self.relative else "Factor")
|
|
sub = layout.row()
|
|
sub.prop(self, "relative")
|
|
sub.prop(self, "dissolve")
|
|
if not self.dissolve:
|
|
layout.prop(self, "displace")
|
|
|
|
def execute(self, context):
|
|
ob = context.active_object
|
|
me = ob.data
|
|
bm = bmesh.from_edit_mesh(me)
|
|
|
|
bm.select_flush(True)
|
|
|
|
fac = self.factor
|
|
rel = self.relative
|
|
dissolve = self.dissolve
|
|
displace = self.displace
|
|
|
|
for v in bm.verts:
|
|
v.tag = False
|
|
|
|
# Loop over edges to find those with both verts selected
|
|
for e in bm.edges[:]:
|
|
e.tag = e.select
|
|
if not e.select:
|
|
continue
|
|
elen = e.calc_length()
|
|
val = fac if rel else fac / elen
|
|
val = min(val, 0.5)
|
|
# Loop over the verts of the edge to split
|
|
for v in e.verts:
|
|
# if val == 0.5 and e.other_vert(v).tag:
|
|
# continue
|
|
en, vn = bmesh.utils.edge_split(e, v, val)
|
|
en.tag = vn.tag = True
|
|
val = 1.0 if val == 1.0 else val / (1.0 - val)
|
|
|
|
# Get all verts which are selected but not created previously
|
|
verts = [v for v in bm.verts if v.select and not v.tag]
|
|
|
|
# Loop over all verts to split their linked edges
|
|
for v in verts:
|
|
for e in v.link_edges[:]:
|
|
if e.tag:
|
|
continue
|
|
elen = e.calc_length()
|
|
val = fac if rel else fac / elen
|
|
bmesh.utils.edge_split(e, v, val)
|
|
|
|
# Loop over all the loops of the vert
|
|
for l in v.link_loops:
|
|
# Split the face
|
|
bmesh.utils.face_split(
|
|
l.face,
|
|
l.link_loop_next.vert,
|
|
l.link_loop_prev.vert
|
|
)
|
|
|
|
# Remove the vert or displace otherwise
|
|
if dissolve:
|
|
bmesh.utils.vert_dissolve(v)
|
|
else:
|
|
v.co += displace * v.normal
|
|
|
|
me.calc_loop_triangles()
|
|
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
def register():
|
|
bpy.utils.register_class(VertexChamfer)
|
|
|
|
|
|
def unregister():
|
|
bpy.utils.unregister_class(VertexChamfer)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
register()
|