5
$\begingroup$

I have a mesh made of 94 groups of 4 vertices: mesh

I need to scale each group of vertices by a decreasing factor (from 100% to 0%).

I tried two different ways to do that, the first is based on the same function called when using the "scale command" via user interface:

bpy.ops.transform.resize(
value=(0.2, 0.2, 0.2), 
constraint_axis=(False, False, False), 
constraint_orientation='GLOBAL', 
mirror=False, 
proportional='DISABLED', 
proportional_edit_falloff='SMOOTH', 
proportional_size=1)

to begin, I tried to scale the first four vertices:

import bpy

obj = bpy.context.active_object
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action="DESELECT")

verts = obj.data.vertices
obj.data.vertices[0].select=True
obj.data.vertices[1].select=True
obj.data.vertices[2].select=True
obj.data.vertices[3].select=True


bpy.ops.transform.resize(
    value=(0.2, 0.2, 0.2), 
    constraint_axis=(False, False, False), 
    constraint_orientation='GLOBAL', 
    mirror=False, 
    proportional='DISABLED', 
    proportional_edit_falloff='SMOOTH', 
    proportional_size=1)

running the code in object mode with my mesh selected, just switches the enviroment to edit mode and nothing else, however if I manually select four vertices and run this script, it scales the four selected vertices correctly. so.. why is obj.data.vertices[i].select=True completely useless? and how can I correctly select the vertices in order to get bpy.ops.transform.resize() correctly work on them?

I also tried to use bmesh library as it was recommended in most of the forums and guides I visited.

import bpy
import bmesh
import math
import mathutils

# Get the active mesh
obj = bpy.context.edit_object
me = obj.data


# Get a BMesh representation
bm = bmesh.from_edit_mesh(me)
face = bm.faces.active

vertices=[bm.verts[0],bm.verts[1],bm.verts[2],bm.verts[3]]

# Scale
scale = mathutils.Vector((0.2,0.2,0.2))
bmesh.ops.scale(
            bm,
            vec=scale,
            verts=vertices
            )

# Show the updates in the viewport
bmesh.update_edit_mesh(me, True)

this script actually does something: it scales the coordinates of the first four vertices. this means that for each vertex I pass to that function, its position is scaled respect to the origin of the scene ( the point (0,0,0) ). But I want it to be scaled respect to the center of the four vertices, as it would be if I manually select the vertices and apply the bpy.ops.transform.resize() function.

So, in the first case, how can I tell to "Blender" to select vertices in order to apply the bpy.ops.transform.resize() function?

And in the latter case, how can I make it to scale respect to the center of the four vertices instead of the center of the image?

$\endgroup$

migrated from stackoverflow.com Apr 22 '15 at 15:13

This question came from our site for professional and enthusiast programmers.

  • $\begingroup$ Do you have the pivot point set to 3dcursor? Try bpy.context.space_data.pivot_point = 'MEDIAN_POINT' $\endgroup$ – sambler Apr 22 '15 at 8:08
4
$\begingroup$

I prefer a more low level solution. This particular approach requires the object to be in Object Mode, but a small change using Bmesh would let you do it in edit mode. This will give you some insight I hope.

enter image description here

import bpy
from mathutils import Vector

# replace this with reference to your object.
obj = bpy.data.objects['BezierCurve']

def get_average(vert_range):
    med = Vector()
    for vert in vert_range:
        vec = vert.co
        med = med + vec
    return med / len(vert_range)

verts = obj.data.vertices
num_verts = len(verts)
scale = 1 / (num_verts / 4)
j = 0
for i in range(0, num_verts, 4):
    mid = get_average(verts[i:i+4])
    for k in range(i, i+4):
        verts[k].co = verts[k].co.lerp(mid, 1-(j*scale))
    j += 1

enter image description here

For every 4 verts, it finds their average, and scales those verts towards that average by an increasing value every 4 verts. I may have reversed the direction so instead of 1-(j*scale) you might want (j*scale)


for completeness A Bmesh solution, this works in edit mode

import bpy
import bmesh
from mathutils import Vector

def get_average(vert_range):
    med = Vector()
    for vert in vert_range:
        vec = vert.co
        med = med + vec
    return med / len(vert_range)

# Get the active mesh
obj = bpy.context.edit_object
me = obj.data

# Get a BMesh representation
bm = bmesh.from_edit_mesh(me)

verts = bm.verts
num_verts = len(verts)
scale = 1 / (num_verts / 4)
j = 0
for i in range(0, num_verts, 4):
    mid = get_average(verts[i:i+4])
    for k in range(i, i+4):
        verts[k].co = verts[k].co.lerp(mid, 1-(j*scale))
    j += 1

bmesh.update_edit_mesh(me, True)

As you can see you were going in the right direction.

$\endgroup$
  • $\begingroup$ In the end I made it work moving the four vertices along the vector pointing from the middle of the four vertices to coordinates (0,0,0), then scaling them and moving them back along the same vector (with opposite direction). But your solution is exactly what I was looking for to scale the four vertices in place, and in fact it is more correct and it works independently of the approach being with or without bmesh, so thanks a lot! $\endgroup$ – Mirco Nani Apr 25 '15 at 12:38
  • $\begingroup$ Cool, I liked the question, it's useful to have more than one solution to think about. $\endgroup$ – zeffii Apr 25 '15 at 12:41
  • $\begingroup$ As you could see I am a little "newbie" to Blender, so I'd ask you another tip: How do you turn on the vertex enumeration like shown in the second image you posted? It could turn very useful anytime I need to quickly understend how to apply scripts to my meshes $\endgroup$ – Mirco Nani Apr 25 '15 at 12:43
  • $\begingroup$ There's a default bpy.app.debug = True (but I find it hard to see wtthout a polygon background on the index, so there's an addon for it: blender.stackexchange.com/questions/3249/show-mesh-vertices-id/… $\endgroup$ – zeffii Apr 25 '15 at 12:51
2
$\begingroup$

vertex selection using obj.data.vertices[i].select=Trueshould be done in OBJECT mode

import bpy

obj = bpy.context.active_object
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action="DESELECT")

verts = obj.data.vertices
l = int(len(verts)/4)
for i in range(l) :

    bpy.ops.object.mode_set(mode='OBJECT')
    obj.data.vertices[i*4].select=True
    obj.data.vertices[i*4+1].select=True
    obj.data.vertices[i*4+2].select=True
    obj.data.vertices[i*4+3].select=True
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.transform.resize(
        value=(0.2, 0.2, 0.2), 
        constraint_axis=(False, False, False), 
        constraint_orientation='GLOBAL', 
        mirror=False, 
        proportional='DISABLED', 
        proportional_edit_falloff='SMOOTH', 
        proportional_size=1)
    bpy.ops.mesh.select_all(action="DESELECT")    


bpy.ops.object.mode_set(mode='OBJECT')
$\endgroup$
  • $\begingroup$ Thanks for the tip! selecting vertices in edit mode was actually the problem that prevented my script to correctly apply the resize $\endgroup$ – Mirco Nani Apr 25 '15 at 12:46
  • $\begingroup$ @MircoNani you are welcome $\endgroup$ – Chebhou Apr 25 '15 at 12:50

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.