2
$\begingroup$

I am working on a scrip that makes tiles (like for a kitchen floor). I have a script that works perfect on planes for 4 sided tiles. However, I am running into an issues on non-4 sided tiles.

I chose to use cylinders which almost exactly works as needed. But I am hitting an issue on "beveling" (which is just scaling 1 of the faces) the top face of the cylinder.

My code looks at all the faces on the mesh, and

-if it does equal 4 sides, then the script deselects them.

-If it does not equal 4 sides, then runs another check

-If we haven't deleted any faces yet, then it deletes 1 face

-If we have deleted 1 face, then we are on the top face Up to this point works perfectly together

-Then the script scales in the selected face This is where it causes issues

The scaling code works fine on its own if I manually select a face first, and also works fine by hand. I'm not sure if it's a bug or something I'm missing but I could use any help my fellow blender scripters can offer.

I'm including just the face adjusting part of the script, so to test my code you'll have to Add > Mesh > Cylinder to test it out.

Thank you! (see code and images below) Selecting A Cylinder Face in Python Scaling A Cylinder Face in Python Selecting A Cylinder Vertices in Python Scaling A Cylinder Vertices in Python

#Import Python
import bpy
#Import mesh handling
import bmesh
#Import vector
from mathutils import Vector


tileBevelZ = 0.85

#_________________________________________Delete the bottom face

#edit mode
bpy.ops.object.mode_set( mode   = 'EDIT'   )

#number of faces we've deleted
facesDeleted = 0
#our current object
obj = bpy.context.object
#ref to mesh
bm = bmesh.from_edit_mesh(obj.data)


#for each face in mesh
for f in bm.faces:
    #if it's selected
    if f.select == True:
        #if it does not have the number of sides we want
        if len(f.verts) == 4:
            #we dont want to select it
            f.select = False
        # else if it does have the same number of sides    
        else:
            #if we have not deleted a side yet
            if facesDeleted == 0:
                #deslect 1 of the faces
                f.select = False
                #increase faces deleted
                facesDeleted += 1
            #else if we increased our counter    
            else:
                #make sure it's selected
                f.select = True
                #Delete the face
                bpy.ops.mesh.delete(type='FACE')

#set mode to faces
bpy.ops.mesh.select_mode( type  = 'FACE'   )
#select all                
bpy.ops.mesh.select_all(action='SELECT')

#_________________________________________Adjust scale of top face for bevel            
#for each face in mesh
for f in bm.faces:
    #if it's selected
    if f.select == True:
        #if it does not have the number of sides we want
        if len(f.verts) == 4:
            #we dont want to select it
            f.select = False
        # else if it does have the same number of sides    
        else:
            #make sure it's selected
            f.select = True
            #set mode to vertices
            bpy.ops.mesh.select_mode( type  = 'FACE'   )
            #Scale the face in
            bpy.ops.transform.resize(value=(tileBevelZ , tileBevelZ , tileBevelZ), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)
$\endgroup$
0
$\begingroup$

Another take on getting the result.

Rather than trying to debug what is happening with your question script here is another take to achieve what I believe is the desired result.

Test script, object mode, adds a cylinder, removes bottom face, scales top face.

Bottom and top faces have z component to their normals. This is how they are selected below.

Once selected, the bottom face is removed from the faces collection.

Scaling top face similar to related question Using python and bmesh to scale/resize a face in place , in this case, using bmesh.ops.scale

Notice apart from adding the cylinder to test, there is no bpy.ops used.

import bpy
import bmesh
from mathutils import Matrix

scale_factor = 0.85

# add a cylinder to test

bpy.ops.mesh.primitive_cylinder_add()
ob = bpy.context.object
me = ob.data
bm = bmesh.new()
bm.from_mesh(me)

top_face = [f for f in bm.faces if f.normal.z > 0.5].pop()

bottom_face = [f for f in bm.faces if f.normal.z < 0.5].pop()

# remove bottom face

bm.faces.remove(bottom_face)

# scale top face 

bmesh.ops.scale(bm,
        vec = (scale_factor,) * 3,
        space = Matrix.Translation(-top_face.calc_center_median()),
        verts = top_face.verts)

bm.to_mesh(me)
me.update()

Note bmesh.ops.create_cone(diameter1=1, diameter2=0.85) could be used to create the shape, removing the need to scale.

Suggestion, don't over comment code, and consider using if f.select: instead of if f.select == True:

$\endgroup$
  • $\begingroup$ Thank you. This is a very elegant solution. I just tested the script and it worked as needed. I did do a workaround in the meantime where I do an inset and just raise that a little. Having the extra edge loop doesn't affect my needs, though I will be using this. Can I ask, I'm new to python and blender and comment code so I can easily go back and reference things I'm unfamiliar with. Is there a reason you suggest to limit that/ Performance perhaps? $\endgroup$ – WillSmithsRobot Nov 8 at 13:18
  • $\begingroup$ my understanding is DRY vs WET code is about personal preference. Since I work with a team of people of various skill levels, I feel it would be more appropriate to have even small explanations for those who either forgot or have never touched python before. And I haven't found anything on performance impacts.. I know other engines remove comments during runtime, but I could see that as a potential downside. Please let me know if there are others or this is just your preference. $\endgroup$ – WillSmithsRobot Nov 12 at 12:57
  • 1
    $\begingroup$ IMHO Commenting every line is a wee bit OTT. Drowning in comments to a point where one can't see the wood for the trees, eg In question code the # set mode to vertices comment is followed by setting selection mode to face. Have some faith in your team members, that even the least experienced can decipher a simple import or increment a counter code line without it requiring comment. Anyway each to there own and sure it is a matter of personal preference. Remember it is only a suggestion. $\endgroup$ – batFINGER Nov 12 at 14:12
  • $\begingroup$ Thanks! That was well explained $\endgroup$ – WillSmithsRobot Nov 12 at 15:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.