# How to deform the outer part of a torus along one axis?

Thanks so much to those who answered my original question, but I don't think I was clear enough about what I was needing. So here's a (hopefully) clearer explanation:

So here's a standard (high res) torus, can see the specs on the pic:

And so I want to scale it wider by 30% just with the default scaler, like this:

However I don't want the inner circle (the hole) to be scaled, just the outer diameter. The result should look something like this (except with all rounded surfaces):

And with a constant Z height at the highest and lowest points of the curve, so the side (X/Y) view should look like this:

I made this which is closest I can get, by beveling the corners of a cubic shape, but the top and bottom planes are still flat. I need it rounded. I want it so that if you were cutting the shape radially you'd get a perfect oval cross-section shape.

Hopefully this is clear. This should be an easy shape to make by an experienced Blender user but I just started playing with it not long ago.

Thanks again!

• It's trickier than it looks, .. I'm a slow scripter .. maybe someone else will jump in? :D But I think one approach to a solution is as follows: There are 2 ellipses to be considered at any angle theta around the center of the torus. 1. The plan view outer ellipse. We need its radius at theta around its center, from that ellipse's Polar Form. From that, we can derive the major axis of 2. the profile ellipse at that angle, which could just be the scaling and offset of a circular profile. Aug 5 '20 at 10:47
• @RobinBetts just posted and noticed comment... pretty much where comments were leading the other day. Think I might have stuffed up the maths somewhere, Aug 5 '20 at 18:03
• @RobinBetts yep, figured it out need to rotate the rib by the angle defined by p2 - p1, not the one that determines them. Which is giving the oval look. Will fix later. Aug 5 '20 at 18:10

You can use the Proportional Editing mode :

• Select only the outer ring of vertices
• Activate the PET with O or with the button (blue arrow in the screenshot)
• initiate the scale on the desired axis THEN control the radius of the PET with your mouse wheel

You can try different fallof profiles (the "curve" button right to the PET). "Sphere" seems a good choice.

A way to do it is to use some modifiers (subdivision surface, screw and surface deform).

At the end, we'll use the surface deform modifier, and its vertex group option. The idea is to set the vertex group weights from 0 to 1 depending on the proximity to the inner radius.

As we want to set the vertex weights simply (and this is the reason why we use subdivision surface and screw modifiers), we start with a square, so with 4 vertices to make the torus ring and set the weights manually: 0 for the inner, 0.5 for the two in the middle, 1 for the outer.

To make this square round, we add a subdivision surface modifier.

And to make the torus, we add a screw modifier.

Finally, the surface deform modifier will trigger when another object is deformed in edit mode (the object is named plane.001 in the capture above).

In the example here, a square is deformed (scaled) along y.

• Very nice... definite UV .. but does @Fluffy want all the profiles to be elliptical in the result? Aug 5 '20 at 11:24
• @RobinBetts, I don't know... we'll see.The images in the question are not so clear about it effectively. Aug 5 '20 at 11:28
• I believe this is exactly what I'm looking for! @lemon Thank you! But which value do I modify in your .blend to do the deforming like in the gif? Tried changing the strength value in the surface deform modifier but didn't work. Aug 5 '20 at 17:34
• @SmashingFulffy, you have to enter edit mode for the plane next to the torus. Then you can scale it along Y (in the config of the joined file). This plane is used in the surface deform modifier as parameter to influence the torus. Aug 5 '20 at 17:43
• Got it! Thanks @lemon again. And thanks for sharing the .blend file that helps tremendously. It seems like such a simple shape in my mind but so hard to explain in words. All you folks here rock! Aug 5 '20 at 23:16

This script takes the approach commented, calculating the radius of the outer ellipse in plan view for any angle theta around the torus center, by using the ellipse's representation in polar coordinates.

From the outer radius of the torus, and its inner radius of 1, the major radius of an elliptical profile at any theta can be calculated, and a circle scaled, rotated, and translated into place for each radial segment.

import bpy
import bmesh
from mathutils import *
from math import *

a = 1.29    # minor outer radius
b = 1.64    # major outer radius
u = 72      # u segments
v = 24      # v segments

def r_at(theta):
den2 = ((b * cos(theta)) ** 2.0) + ((a * sin(theta)) ** 2.0)
return a * b / sqrt(den2)

bm = bmesh.new()

segs  =  range (0,360, int(360/u))
for theta in segs:

rr =  r_at(rt)
sf = (rr - 1.0)
xo = 1+(sf/2.0)
tvec = Vector((xo,0.0,0.0))

v_dict = bmesh.ops.create_circle(
bm,
cap_ends=False,
segments=v)
v_list = v_dict['verts']

m_sca_x = Matrix.Scale(sf/2.0, 4, (1.0, 0.0, 0.0))
m_sca_y = Matrix.Scale((a-1)/2.0, 4, (0.0, 1.0, 0.0))
m_rot_x = Matrix.Rotation(pi/2, 4, 'X')
m_trans = Matrix.Translation(tvec)
m_rot_z = Matrix.Rotation(rt, 4, 'Z')
m_xform = m_rot_z @ m_trans @ m_rot_x @ m_sca_y @ m_sca_x

bmesh.ops.transform(bm, verts=v_list, matrix=m_xform)

bmesh.ops.bridge_loops(bm, edges=bm.edges, use_cyclic=True)

me = bpy.data.meshes.new("OvalTorus")
bm.to_mesh(me)
bm.free()

obj = bpy.data.objects.new("OvalTorus", me)


I've assumed the profile at the minor radius of the outer ellipse is supposed to be circular, and that height is maintained throughout. All profiles are elliptical, in the radial cross-section.

• This looks like the correct shape i asked for. Thanks! Even though I got the shape from Lemon's solution I'll try yours as well, hopefully it'll make me a better blenderer. Lol Aug 5 '20 at 23:20

BMesh script

Using methods from https://blender.stackexchange.com/a/132928/15543 re making an ellipse from the eccentricity.

Here is a test script, result image above using values as below. The inner radius is the radius of hole, the outer radius the maximum radius of ellipse, and an angle of eccentricity as explained in link.

Have added a height to keep the "ribs" too, whereas could also use the eccentricity. Am not sure if a scaled circle in 1D, and rotated and projected are same ellipse.

Anyway, the method uses the points created by scribing the hole, and the ellipse as reference points to create ribs and skin them.

Left as a long chain of transforms, instead could make a you-beaut chain matrix, but ...

import bpy
import bmesh
from bpy import context
from mathutils import Matrix, Vector

height = 0.5
number_segments = 32
number_rings = 64

Re = Matrix.Rotation(eccentricity_angle, 3, 'X')
Se = Matrix.Scale(0, 3, (0, 0, 1))

Me = Se @ Re
me = bpy.data.meshes.new("Ell_Toro")
bm = bmesh.new()
Te = Matrix.Translation((1, 0, 0))

def new_rib(angle):
x = Vector((1, 0))
R = Matrix.Rotation(angle, 4, 'Z')
p1 = R @ Vector((inner_radius, 0, 0))
p2 = Me @ (R @ Vector((outer_radius, 0, 0)))

rib = bmesh.ops.create_circle(
bm,
segments=number_segments,
matrix= Matrix.Rotation(radians(-90), 4, 'X') @ Matrix.Translation((1, 0, 0)),
)
Q = Matrix.Rotation(
(p2 - p1).xy.angle_signed(x),
4, 'Z')

bmesh.ops.transform(
bm,
verts=rib["verts"],
matrix = Q,
)

bmesh.ops.transform(
bm,
verts=rib["verts"],
matrix = (0.5 * (p2 - p1).length * Matrix()),
)

bmesh.ops.transform(
bm,
verts=rib["verts"],
matrix=Matrix.Scale(height /(p2 - p1).length, 3, (0, 0, 1)),
)
bmesh.ops.transform(
bm,
verts=rib["verts"],
matrix= Matrix.Translation(p1),
)
return bm.edges[-number_segments:]

ribs = [new_rib(i * angle) for i in range(number_rings)]

ribs.append(ribs[0])

while len(ribs) > 1:
rib = ribs.pop()
bmesh.ops.bridge_loops(
bm,
use_pairs=True,
edges=rib + ribs[-1],
)

ob = bpy.data.objects.new("Ell_Toro", me)
bm.to_mesh(me)