Hi,
I wrote a small script which allows to apply a variable scale
to the x and y coordinates of an object.
This is handy if you have a cylinder which is 1 unit in diameter
on one side and should be 2 units on the other. (OK, in this case,
other methods work as well, but just think of othe objects without
rotational symmetry.)
The scaling is given for both ends and works on the x and y coordinates
only (hardcoded ), and which factor applies, depends on the z coordinate.
A variable offset may be added as well.
Maybe this or similar functionality is available with built-in blender
tools, in this case I would welcome feedback, then I’ll have to
read the manual better.
Tested in blender 2.42a / Python 2.4.
Best regards,
Michael
#!BPY
"""
Name: 'Variable Scale'
Blender: 242
Group: 'Mesh'
Tooltip: 'Perform a x/y scaling depending on z coordinate.'
"""
__author__ = ["Michael Bischoff"]
__url__ = ("blender", "eldorado", "http://none.existing.de/blender/")
__version__ = "0.1"
__bpydoc__ = """\
Variable Scale
Perform a x/y scaling depending on z coordinate.
"""
#
# This script scales the x and y portion of the selected vertices
# depending on their z coordinate.
# scaling happens relatively to the 3D cursor (x and y components)
# the z component of the 3D cursor is irrelevant to this script
#
# ATTENTION!
# This script does work only as expected, if the object transformation
# is identity.
# This can be achieved by pressing ctrl-A (apply scale and rotation)
# for the object in "Object mode".
#
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Script copyright (C) Michael Bischoff
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
import math
import Blender
from Blender import Mathutils, Window, NMesh, Draw, Scene, Noise
from Blender.Mathutils import *
import BPyMesh
def main():
scn = Scene.GetCurrent()
ob = scn.getActiveObject()
is_editmode= Window.EditMode()
if not is_editmode:
Draw.PupMenu('Error! Must be in Edit mode%t|Please switch to edit mode, then call this script again.')
return
if ob == None or ob.getType() != 'Mesh':
Draw.PupMenu('Error! No active mesh selected%t|You must have a mesh object selected.')
return
#=================================#
# Popup menu to obtain parameters #
#=================================#
mode = Draw.Create(0)
maxz_dx = Draw.Create(0.0)
maxz_dy = Draw.Create(0.0)
maxz_dz = Draw.Create(0.0)
minz_fx = Draw.Create(1.0)
minz_fy = Draw.Create(1.0)
maxz_fx = Draw.Create(2.0)
maxz_fy = Draw.Create(1.0)
# Get parameters from user
pup_block= [\
'Transformation mode:',
('mode', mode, 0, 7, '0=exp, 1=linear, 2=quadratic, 3=cubic, 4=half sinus, 5=full sinus, 6=circle segment, 7 = alternate circle.'),\
'max z translation:',
('dx', maxz_dx, -100.0, 100.0, 'dx at maximum z.'),\
('dy', maxz_dy, -100.0, 100.0, 'dy at maximum z.'),\
('dz', maxz_dz, -100.0, 100.0, 'dz at maximum z.'),\
'scale at min z:',
('x scale', minz_fx, 0.0, 10.0, 'x scale at smallest z value.'),\
('y scale', minz_fy, 0.0, 10.0, 'y scale at smallest z value.'),\
'scale at max z:',
('x scale', maxz_fx, 0.0, 10.0, 'x scale at biggest z value.'),\
('y scale', maxz_fy, 0.0, 10.0, 'y scale at biggest z value.'),\
'Remember ctrl-A!!!',
]
if not Draw.PupBlock('Define z-dependend scale / translation', pup_block):
return
mode = mode.val
maxz_dx = maxz_dx.val
maxz_dy = maxz_dy.val
maxz_dz = maxz_dz.val
minz_fx = minz_fx.val
minz_fy = minz_fy.val
maxz_fx = maxz_fx.val
maxz_fy = maxz_fy.val
# leave edit mode before getting mesh (suggested by Python reference)
Window.EditMode(0)
me = ob.getData(mesh=1)
cursor_pos = Vector(Window.GetCursorPos())
count = 0
minz = 0.0
maxz = 0.0
for v in me.verts:
if v.sel:
z = v.co[2]
if count == 1:
minz = z
maxz = z
else:
if minz > z:
minz = z
if maxz < z:
maxz = z
count += 1
if minz == maxz:
Draw.PupMenu('Error! All vertices (if any) have same z coordinate.')
return
dz = maxz - minz
print 'minz = %f|' % minz
print 'maxz = %f|' % maxz
for v in me.verts:
if v.sel:
# default mode: linear
alpha = (v.co[2] - minz) / dz
# other modes might change the mapping, however, it's
# always in the range [0,1]
if mode == 0:
# apply exponential transformation
alpha = (math.exp(alpha) - 1.0) / (math.e - 1.0)
if mode == 2:
# apply quadratic transformation
alpha *= alpha
if mode == 3:
# apply cubic transformation
alpha *= alpha * alpha
if mode == 4:
# apply sinus transformation (half periode)
alpha = 0.5 * (1.0 - math.cos(math.pi * alpha))
if mode == 5:
# apply sinus transformation (full periode)
alpha = 0.5 * (1.0 - math.cos(2.0 * math.pi * alpha))
if mode == 6:
# apply circle segment transformation
if alpha > 1.0:
alpha = 1.0 # paranoia to avoid negative root exceptions
alpha = 1.0 - alpha
alpha = math.sqrt(1.0 - alpha * alpha)
if mode == 7:
# apply circle segment transformation
if alpha > 1.0:
alpha = 1.0 # paranoia to avoid negative root exceptions
alpha = 1.0 - math.sqrt(1.0 - alpha * alpha)
scalex = alpha * maxz_fx + (1.0 - alpha) * minz_fx - 1.0
scaley = alpha * maxz_fy + (1.0 - alpha) * minz_fy - 1.0
v.co[0] += alpha * maxz_dx + scalex * (v.co[0] - cursor_pos[0])
v.co[1] += alpha * maxz_dy + scaley * (v.co[1] - cursor_pos[1])
v.co[2] += alpha * maxz_dz
# update mesh data
me.update()
# TODO: recalc normals. according to doc, a suitable parameter to
# me.update() should be able to do this, but this did not work in 2.42a
# return to edit mode
Window.EditMode(1)
statmsg = 'Processed %i vertices|' % count
Draw.PupMenu(statmsg)
if __name__ == '__main__':
main()