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: torus

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

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): photoshopped

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: XYview

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. workinprogress

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!

  • $\begingroup$ 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. $\endgroup$ – Robin Betts Aug 5 '20 at 10:47
  • $\begingroup$ @RobinBetts just posted and noticed comment... pretty much where comments were leading the other day. Think I might have stuffed up the maths somewhere, $\endgroup$ – batFINGER Aug 5 '20 at 18:03
  • $\begingroup$ @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. $\endgroup$ – batFINGER Aug 5 '20 at 18:10

You can use the Proportional Editing mode : enter image description here

  • 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.

enter image description here

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.

enter image description here

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

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

enter image description here

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.

  • $\begingroup$ Very nice... definite UV .. but does @Fluffy want all the profiles to be elliptical in the result? $\endgroup$ – Robin Betts Aug 5 '20 at 11:24
  • $\begingroup$ @RobinBetts, I don't know... we'll see.The images in the question are not so clear about it effectively. $\endgroup$ – lemon Aug 5 '20 at 11:28
  • $\begingroup$ 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. $\endgroup$ – Smashing Fulffy Aug 5 '20 at 17:34
  • $\begingroup$ @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. $\endgroup$ – lemon Aug 5 '20 at 17:43
  • $\begingroup$ 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! $\endgroup$ – Smashing Fulffy 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:

    rt = radians(theta)
    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(
    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")

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.

enter image description here

  • 1
    $\begingroup$ 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 $\endgroup$ – Smashing Fulffy Aug 5 '20 at 23:20

BMesh script

enter image description here

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
from math import asin, radians

inner_radius = 0.5
outer_radius = 1.5 # maximum
height = 0.5
eccentricity_angle = radians(45)
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(
            matrix= Matrix.Rotation(radians(-90), 4, 'X') @ Matrix.Translation((1, 0, 0)),
    Q = Matrix.Rotation(
            (p2 - p1).xy.angle_signed(x), 
            4, 'Z') 
            matrix = Q,
            matrix = (0.5 * (p2 - p1).length * Matrix()),
            matrix=Matrix.Scale(height /(p2 - p1).length, 3, (0, 0, 1)),
            matrix= Matrix.Translation(p1),
    return bm.edges[-number_segments:]

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


while len(ribs) > 1:
    rib = ribs.pop()
            edges=rib + ribs[-1],

ob = bpy.data.objects.new("Ell_Toro", me)
  • $\begingroup$ Your answer is very nice indeed @RobinBetts Caught up with you and Fixed the error mentioned which was giving me the oval shape instead of ellipse due rotating rib to circle radial instead of via circle -> ellipse. Made some wonderfully weird crumpet shaped torii whilst getting it wrong. $\endgroup$ – batFINGER Aug 6 '20 at 8:16
  • $\begingroup$ .. So that's what it was! I was trying to work out where the difference came from, but ran out of fag-packet, as it were, before I had to go to bed. I'm so slow at these things..:( $\endgroup$ – Robin Betts Aug 6 '20 at 8:21
  • 1
    $\begingroup$ Ditto. Bit the 99% rule with this one. Nothing worse than running out of smokes. They're as dear as poison here. As is think we have pretty muchly matched result. Do you think a scaled circle matches ellipse (have lazilly scaled the rib circles, rather than rotate project)? $\endgroup$ – batFINGER Aug 6 '20 at 8:26

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.