6
$\begingroup$

This question is related to this one: How to distribute the objects on a sphere evenly? I wonder if there's a way to create a sphere, in which every vertex loop's count is decreased according to its vertical position by scripting (let's say every ring count starting from the top of the sphere to the midlle of it is decreased by 6 vertices- as pictured below)? Is there a way to create a sphere and define it's individual ring's count by scripting?

P.s. I don't know if a tittle of my question is right. If not feel free to change it. enter image description here

$\endgroup$
5
$\begingroup$

enter image description here

I think, there is no perfect solution to this problem, though:

enter image description here

What is the problem:

  • Chords of each circle need to be regular in both directions (latitude, longitude)
  • But this chords length need to correspond to a latitude of this sphere
  • And this latitude may not correspond to an entire amount of the chord length

So the result is only an approximation.

The code without the UI:

import bpy
from math import pi, cos, sin

#Create a new mesh from a geometry 
def CreateMesh( scene, name, location, vertices, edges, polygons ):
    mesh = bpy.data.meshes.new( name )
    obj = bpy.data.objects.new( name, mesh )
    obj.location = location

    scene.objects.link( obj )
    scene.objects.active = obj
    obj.select = True

    mesh.from_pydata( vertices, edges, polygons )
    mesh.update()    

    return obj

#Merge new vertices (a ring) to previous one
def MergeGeometry( vertices, edges, newVertices ):
    base = len( vertices )
    vertices += newVertices
    newVerticesAmount = len( newVertices )
    if newVerticesAmount > 2:
        edges += [(base + i, base + int((i+1) % newVerticesAmount) ) for i in range( newVerticesAmount )]
    elif newVerticesAmount == 2:
        edges += [(base, base + 1)]
    return vertices, edges    

#Calculate a circle
def Circle( z, r, verticesAmount ):
    baseAngle = 2 * pi / verticesAmount
    return [(r * cos(i * baseAngle), r * sin(i * baseAngle), z) for i in range( verticesAmount )]

#Calculate the sphere
def HomogeneousSphereBySegments( segments, r ):
    vertices, edges = [], []

    if segments % 2 == 0:
        ringAmount = segments // 2
        baseAngle = pi / ringAmount
        chord = 2 * r * sin( pi / segments )
        arc = 2 * pi * r / segments
    else:
        #If odd we have to shift the angle from the 'equator'
        ringAmount = segments // 2
        baseAngle = pi / ringAmount
        chord = 2 * r * cos( baseAngle / 2 ) * sin( pi / segments )
        arc = 2 * pi * r * cos( baseAngle / 2 ) / segments

    #First pole
    vertices += [(0,0,-r)]

    for i in range( 1, ringAmount ):
        angle = (i * baseAngle) - (pi / 2)
        z = r * sin( angle )
        localR = abs( r * cos( angle ) )
        verticesAmount = int( 2 * pi * localR / arc )
        vertices, edges = MergeGeometry( vertices, edges, Circle( z, localR, verticesAmount ) )
        print( ringAmount, baseAngle, angle, z, localR )        

    #Second pole
    vertices += [(0,0,r)]

    return vertices, edges

Blend file with code usable as an operator (add mesh, 'Homogeneous Sphere'):

| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ Thank you for your effort. The fact you can easily edit the sphere's parameters via operator panel make it a perferct answer. Amazing! $\endgroup$ – Paul Gonet May 2 '17 at 10:59
3
$\begingroup$

You can use bmesh.ops.create_circle() to add a circle to a mesh, then use bmesh.ops.translate() to put it into position, unless you create a matrix and pass it to create_circle.

You use the pythagorean theorem to get the size of each ring to get a spherical result.

And if you think the following math is out, it is because the diameter parameter for create_circle appears to actually be the radius.

import bpy
import bmesh
from math import sqrt

ring_verts = [32,32,32,30,24,18,12,6]
radius = 1.0
step_size = radius / len(ring_verts)

def get_diameter(z):
    return sqrt(radius**2 - z**2)

bm = bmesh.new()
for i,v in enumerate(ring_verts):
    z = step_size * i
    diam = get_diameter(z)
    ret = bmesh.ops.create_circle(bm, cap_ends=False, diameter=diam, segments=v)
    bmesh.ops.translate(bm, verts=ret['verts'], vec=(0.0,0.0,z))

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

scene = bpy.context.scene
obj = bpy.data.objects.new("Object", me)
scene.objects.link(obj)
scene.objects.active = obj
obj.select = True
| improve this answer | |
$\endgroup$

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.