# Tetrahedron isn't proportional even though math seems to be correct

I am creating an object from vertices and faces. I have written two functions that can convert from spherical coordinates to cartesian and another one for the other way around. I am generating the tetrahedron so that if a bounding sphere touches the vertices the radius is 1. The problem is that the even though the radius is equal to 1 for all four vertices the tetrahedron shape isn't right but then looks correct when the top vertex radius is 3/4. Weird. I am running this with Blender 2.9. What do you suppose I did wrong? Or is there something weird about Blender (which I doubt).

import bpy
import numpy as np

def sphere_to_cart(r,theta, phi):
return x, y, z

v_tetra = []
# x,y,z = sphere_to_cart(1,0,0)
# v_tetra.append([round(x,7),round(y,7),round(z,7)])
v_tetra.append([0,0,0.75])
x,y,z = sphere_to_cart(1,120,0)
v_tetra.append([round(x,7),round(y,7),round(z,7)])
x,y,z = sphere_to_cart(1,120,120)
v_tetra.append([round(x,7),round(y,7),round(z,7)])
x,y,z = sphere_to_cart(1,120,240)
v_tetra.append([round(x,7),round(y,7),round(z,7)])
f_tetra = [[0,1,2],[0,2,3],[0,3,1],[1,2,3]]
name = "Tetrahedron"
mesh = bpy.data.meshes.new(name)
obj = bpy.data.objects.new(name, mesh)
col = bpy.data.collections.get("Objects")
bpy.context.view_layer.objects.active = obj
mesh.from_pydata(v_tetra, [], f_tetra)

so = bpy.context.active_object
verts = so.data.vertices
print("Vertex info:")
for v in verts:
print(v.co)


To illustrate I have checked the math:

print(v_tetra)
r,theta,phi = cart_to_sphere(0.000000001,0,1)
print(r,theta,phi)
r,theta,phi = cart_to_sphere(0.866,0,-0.5)
print(r,theta,phi)
r,theta,phi = cart_to_sphere(-0.433,0.75,-0.5)
print(r,theta,phi)
r,theta,phi = cart_to_sphere(-0.433,-0.75,-0.5)
print(r,theta,phi)


Which give the result:

[[0.0, 0.0, 1.0], [0.866, 0.0, -0.5], [-0.433, 0.75, -0.5], [-0.433, -0.75, -0.5]]
1.0 1e-09 0.0
0.9999779997579946 -1.0471848490249271 0.0
0.9999944999848749 -1.047194375741009 -1.0472102531819656
0.9999944999848749 -1.047194375741009 1.0472102531819656


Input is not correct

Diagram illustrating only the top view looks directly onto a tri normal. Hence in top view the verts are 120 degrees apart. It appears you have also used this for front (for example) and wrongly assumed the other verts would be sitting on 30 degrees south (120 degrees from north). In front view the triangle we see has an edge angle (The tetrahedron dihedral angle of 70 degrees) from the base, as seen in right ortho view (LHS)

In an answer to this question have hard-coded a regular tetrahedrons coordinate points.

https://blender.stackexchange.com/a/143372/15543

seen as coords in code below. Note these coords are for a unit edge tetra containing verts at (0, 0, 0) and (0, 0, 1) and aligned such that an edge is parallel to x axis.

Coordinates for a regular tetrahedron

Expressed symmetrically as 4 points on the unit sphere, centroid at the origin, with lower face level, the vertices are:

$$v_1 = \left(\sqrt{\frac{8}{9}},0,-\frac{1}{3}\right)$$

$$v_2 = > \left(-\sqrt{\frac{2}{9}},\sqrt{\frac{2}{3}},-\frac{1}{3}\right)$$

$$v_3 = > \left(-\sqrt{\frac{2}{9}},-\sqrt{\frac{2}{3}},-\frac{1}{3}\right)$$

$$v_4 = (0,0,1)$$

with the edge length of $$\sqrt{\frac{8}{3}}$$.

Could have changed to match these coords, instead for example sake have:

To make the origin at the center of geometry averaged coords, and set it as the origin (0, 0, 0).

Made all the distances from origin to point unit, scaled by inverse of length. All edges have same length..

To skin quickly used the bmesh convex hull operator.

.. and finally reported the distance and latitude and longitude from the origin point.

import bpy
from mathutils import Vector, Matrix
import bmesh
from bpy import context
from math import degrees, pi

coords = [Vector(c) for c in (
(0, 0, 0),
(1, 0, 0),
(.5, 3**.5 / 2, 0),
(.5, 1 / 3 * 3**.5 / 2, ((3**.5 / 2)**2 - (1 / 3 * 3**.5 / 2)**2)**.5))]

o = sum(coords, Vector()) / 4

print("origin", o)

me = bpy.data.meshes.new("Tetra")
me.from_pydata(coords, [], [])
me.transform(Matrix.Translation(-o))
me.update()

# all have the same distance, not unit
for v in me.vertices:
print(v.co.length)
# scale to unit.
me.transform(Matrix.Diagonal((1 / v.co.length, ) * 3).to_4x4())
bm = bmesh.new()
bm.from_mesh(me)
bmesh.ops.convex_hull(
bm,
input=bm.verts,
)
bm.to_mesh(me)
me.update()

# make the object
ob = bpy.data.objects.new("Tetra", me)

# calculate and report radius / lat / lon (local space)
z_axis = Vector((0, 0, 1)) # north
y_axis = Vector((0, -1, 0)) # 0 longitude
for v in bm.verts:
lat = pi / 2 - z_axis.angle(v.co)
if v.co.xy.length < 1e-7:
lon = 0
else:
lon =  y_axis.xy.angle_signed(v.co.xy)
print(v.co.length, degrees(lat), degrees(lon))


Output, spherical coordinates (in degrees)

Radius, Latitude, Longitude
----------------------------

0.9999999701976772 -19.471218922405377 60.000001669652114
0.9999999701976772 -19.471218922405377 -60.000001669652114
0.9999999478459345 -19.47122575259455 -180.00000500895632
1.0 90.0 0.0   # north pole.


In both this version and yours the distance from the origin to vert is 1, however your input latitudes are not consistent with keeping equilateral triangle faces.

Result above suggest that for a regular tetrahedron with apex at north pole, the three other points are distributed 120 degrees of longitude apart on latitude 19.47 degrees south.

• I see what I did wrong. Awesome, thank you! Is there a good link for more information on Bmesh? it seems like a better approach to making the meshes from scripting. Sep 22, 2020 at 18:42
• docs.blender.org/api/current/bmesh.html#module-bmesh Cheers. Kinda threw a bit of everything in by way of example. Using Mesh.from_pydata(...) is generally the quickest. For example the bmesh convex hull op from tetra points is a cute way to skin the points. If coding an add unit sphere tetra primitive operator would hardcode in the points and faces. Here is a helper for doing this blender.stackexchange.com/questions/65129/… Sep 22, 2020 at 18:59