1
$\begingroup$

I am having difficulty programmatically beveling an edge when there are (unselected) vertices just inside of that edge. The selected edges are beveled, but the nearby vertices produce undesirable artifacts that get worse as the degree of bevel increases.

Is there a way to prevent or remove those artifacts?

Reproduction steps:

# create a sphere with a hole
bpy.ops.mesh.primitive_cylinder_add(radius=1,depth=6)
bar = bpy.context.object
bpy.ops.mesh.primitive_ico_sphere_add(radius=2, subdivisions=4)
sphere = bpy.context.object
mod = sphere.modifiers.new("foo", type='BOOLEAN')
mod.operation = 'DIFFERENCE'
mod.object = bar
mod.use_hole_tolerant = True
mod.solver = 'EXACT'
bpy.ops.object.modifier_apply(modifier=mod.name)
bpy.data.objects.remove(bar, do_unlink=True)

# select sharp vertices
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.select_mode(type="EDGE")
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.mesh.edges_select_sharp()

# ???
bpy.ops.transform.edge_bevelweight(value=1, use_accurate=True)
bpy.ops.mesh.bevel(offset_type='WIDTH',
                   offset=0.3,
                   segments=3,
                   mark_seam=True)
$\endgroup$

1 Answer 1

1
$\begingroup$

You can fix this by first restructuring your icosphere such that ther're long connective edges to the sharp edges. Bevels can become distorted when interruptive vertices are within the bevel width range, so lengthy edges with no in-between vertices helps eliminate the distortion.

enter image description here

Doing all of this programmatically was a challenge for me, but I got it to work and here's what the bevel looks like:

enter image description here

The main logic was to:

  1. create a sphere with a hole (I first cut out a hole of radius 1.268 using the Boolean modifier and deleted the inner faces created by this hole. I start with a larger hole because I can easily extrude the vertices inwards to create the inner hole. Going the other direction is more difficult)

  2. extrude the hole boundary ring selections and then reposition these selections such that their vertices land at proper coordinates sphere

  3. fill in the edges and faces (had to sort the hole rings by planar angle to pair them correctly)

  4. bevel the sharp edges

--- I just used trial and error to get a value of 1.268 for initial hole radius. This is approximately the number such that a bevel width of .3 meets the outer rings of larger radius. (I think there could be a formula to derive for this relation such that you can get it to be exact and then you can merge verts by distance, but I haven't put much thought into it) ---

import bpy, bmesh
from math import atan2, sqrt, pi
from mathutils import Vector


# create a sphere with a hole
r = 1.268
bpy.ops.mesh.primitive_cylinder_add(vertices = 100, radius = r, depth = 6)
bar = bpy.context.object
bpy.ops.mesh.primitive_ico_sphere_add(radius = 2, subdivisions = 6)
sphere = bpy.context.object
mod = sphere.modifiers.new("foo", type = 'BOOLEAN')
mod.operation = 'DIFFERENCE'
mod.object = bar
mod.use_hole_tolerant = True
mod.solver = 'EXACT'
bpy.ops.object.modifier_apply(modifier = mod.name)
bpy.data.objects.remove(bar,do_unlink = True)

# deselect and change to edge select
bpy.ops.object.mode_set(mode = 'EDIT')
bpy.ops.mesh.select_all(action = 'DESELECT')
bpy.ops.mesh.select_mode(type = 'EDGE')

# create sphere bmesh
me = sphere.data
bm = bmesh.from_edit_mesh(me)

# select and delete edges created by boolean hole (indentified by their long length)
for edge in bm.edges:
    verts = edge.verts
    if (verts[1].co - verts[0].co).length > 1:
        edge.select = True
        
# get selected vertices
selected = [v for v in bm.verts if v.select == True]

# delete selected edges        
bpy.ops.mesh.delete(type = 'EDGE')

# select top and bottom loop vertices
bpy.ops.mesh.select_mode(type = 'VERT')
for v in bm.verts:
    if v in selected:
        v.select = True
 
# extrude selection nowhere
bpy.ops.mesh.extrude_vertices_move()

# get selected vertices
selected = [v for v in bm.verts if v.select == True]

# distinguish top and bottom ring extrusions
top = [v for v in selected if v.co.z > 0]
bottom = [v for v in selected if v.co.z < 0]

# function to return xy-planar angle (angle CCW from +x axis)
def get_angle(v):
    x = v.co.x
    y = v.co.y
    theta = atan2(y, x)
    return theta

# sort top and bottom verts by their xy-planar angle
top = sorted(top, key = get_angle)
bottom = sorted(bottom, key = get_angle)

# deselect all
bpy.ops.mesh.select_all(action = 'DESELECT') 

# reposition vertices of top and bottom extrusion
# also fill in edge between corresponding verts
for v1, v2 in zip(top, bottom):
    v1.co = (v1.co.x/r, v1.co.y/r, sqrt(3))
    v2.co = (v2.co.x/r, v2.co.y/r, -sqrt(3))
    v1.select = True
    v2.select = True
    bpy.ops.mesh.edge_face_add()
    bpy.ops.mesh.select_all(action = 'DESELECT')

# select top
for v in top:
    v.select = True

# fill and delete face (creates top ring edges)
bpy.ops.mesh.edge_face_add()
bpy.ops.mesh.delete(type = 'ONLY_FACE')

# select bottom 
for v in bottom:
    v.select = True 

# fill and delete face (creates bottom ring edges)        
bpy.ops.mesh.edge_face_add()
bpy.ops.mesh.delete(type = 'ONLY_FACE')  

# select all and fill in all faces
bpy.ops.mesh.select_all(action = 'SELECT')
bpy.ops.mesh.edge_face_add()

# select only the sharp edges
bpy.ops.mesh.select_mode(type = 'EDGE')
bpy.ops.mesh.select_all(action = 'DESELECT')
bpy.ops.mesh.edges_select_sharp()

# bevel sharp edge selection
bpy.ops.transform.edge_bevelweight(value = 1, use_accurate = True)
bpy.ops.mesh.bevel(offset_type = 'WIDTH',
                   offset = 0.3,
                   segments = 20,)

# go to object mode                   
bpy.ops.object.mode_set(mode = 'OBJECT')
$\endgroup$
6
  • $\begingroup$ For me, running the code you pasted did not produce the pictures you included (I cannot figure out how to post the picture, but its basically an extremely extruded top/bottom circle). $\endgroup$ Mar 14 at 2:16
  • $\begingroup$ @BlenderNube can you share an imgur pic and also, which version are you in? $\endgroup$ Mar 14 at 2:20
  • $\begingroup$ I'll be thinking. "cannot figure out" - I'd still like to see if possible. Just go to imgur, create an account and upload an image and then paste the link to the image in a comment $\endgroup$ Mar 14 at 2:26
  • $\begingroup$ imgur.com/a/SkcWy32 <--- This is what my extrusion looks like. I'm not too sure how it can be extreme when I don't input any translation value into the extrusion function and then map the extrusions to their proper positions. Could you check if a vertex of your extrusion has the same gobal and local coordinates? $\endgroup$ Mar 14 at 6:28
  • 1
    $\begingroup$ The problem was that Blender was stuck in edit mode after running the script, so it fails the second time. I guess a universal solution to sharp edges is not really possible using B-REP: I'll probably look at dilation and erosion in a different software package to solve this. Thanks again, and I will figure out how to post images before my next question :) $\endgroup$ Mar 17 at 20:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .