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?
I think, there is no perfect solution to this problem, though:
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'):
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