One of our signature moves is to present object-oriented thinking (as in computer science), with polyhedrons as paradigm objects. With the Polyhedron type come its methods for rotating and scaling itself about the origin.
We start with a canonical arrangement of polyhedrons known to students of Synergetics as the concentric and or cosmic hierarchy. The accompanying volumes table centers around the unit edge tetrahedron as unit volume concept, and comes with a consistent vector algebra akin to XYZ's: Quadrays.
Each polyhedron in the hierarchy (Tetrahedron, Cube, Octahedron...) is a subclass of Polyhedron, but with specific faces, tuples of vertices, hard-coded or selected from a database.
Verboten Math (Martian Math) Volumes Table:
Think Kepler
To get the full experience of a database behind a web-based API, we store our Polyhedrons as related SQL tables and serve them out in response to HTTP requests.
Starting with SQLite inside of Python makes it easy, as no separate database engine need be started up or maintained. sqlite3
is already included in the Python Standard Library.
A database is where we keep track of inventory, save stuff, bank.
A typical table structure: a main table has one line per polyhedron, which points one-to-many to its faces, which in turn point one-to-many to the vertex vectors each face has, going around. Edges distill from faces, e.g. if we have face ('A', 'B', 'C') then we have edges 'AB', 'BC', 'AC'. To keep edge names unique, we tend to alphabetize them, such that 'CA' and 'AC' don't register separately. Just use 'AC'.
See my Digital Mathematics from the Silicon Forest for more context (with Supermarket, Martian, Neolithic and Casino Math).
About *
and **
as "splat apart" vs "smash together" operators respectively
f = lambda *x: x # combine as a tuple
arg = ([1, 2], [3,4]) # two lists inside a tuple
f(*arg) # splat apart tuple then smash lists together
([1, 2], [3, 4])
In this section we're doing a closed loop transformation involving what David Koski and I have dubbed the "S factor", meaning the volumetric ratio of the S to E modules.
Another way to compute the same sfactor is to ratio the cuboctahedron with the icosahedron of same edges, the two being related by the Jitterbug Transformation.
The sfactor, applied to the cuboctahedron (CO) of volume 2.5 (half the edges of the edge 2R CO, R = IVM ball radius) may be modeled as the beginning of a twisting action, akin to the Jitterbug, but wherein edges are allowed to lengthen.
The trianglar faces twist in a way that keeps them flush with a containing octahedron of volume 4. Two applications of the sfactor take us to the icosahedron with eight of its faces flush to said Octahedron.
From: RBF, Synergetics, Fig. 988.00 Polyhedral Evolution: S Quanta Module: Comparisons of skew polyhedra.
This relationship of the two polys (octa and icosa) begets the S modules in the first place: 24 of them (12L and 12R) brick in the difference. 24 S modules, applied to the icosahedron, build its containing volume 4 octahedron.
We call this faces-flush icosahedron the IcosaWithin. The 2.5 CO morphs into the IcosaWithin with two applications of the sfactor i.e. one application of the 2nd power of the sfactor.
This IcosaWithin has edges equal to the sfactor. Multiplying by the reciprocal of the sfactor therefore models shrinking the IcosaWithin to one of edges 1R, i.e. 1/8th the volume of the Jitterbug Icosa, the one with edges equal the volume 20 CO's.
But those edges 1 match the those of the 2.5 volumed CO. One more application of the sfactor will therefore bring us full circle back to 2.5. Then we may start the whole cycle again: 2.5 CO times sfactor times sfactor brings us to IcosaWithin, which times 1/sfactor brings us to 1R edged Icosa, times the sfactor again brings us back to the 2.5 volumed CO.
import sympy as sy
from sympy import Symbol, symbols
Fuller wanted to make a fresh start with Synergetics, free of much cultural baggage, or at least that's one explanation for why he expelled $\phi$ from the proceedings. However, bringing $\phi$ back considerably simplifies the symbolic expressions, $\phi$ being a symbol for the golden mean.
$$ \phi:1 :: (1 + \phi) : \phi $$$$ \phi = (\sqrt{5} + 1)/2 $$φ = Symbol('φ')
sy.solve('φ - (1 + φ)/φ')
[1/2 - sqrt(5)/2, 1/2 + sqrt(5)/2]
phi = (sy.sqrt(5)+1)/2
phi.evalf(50)
icosavol = 5 * sy.sqrt(2) * phi**2 # icosa of edges 2R
icosavol.evalf()
sfactor = sy.Integer(20)/icosavol # CO:Icosa ratio = S:E ratio
sfactor = sfactor.simplify()
sfactor
sfactor.evalf()
a = Symbol('a')
a = 2
FE = a * sy.sqrt(7 - 3 * sy.sqrt(5))
FE.evalf() # also sfactor
icosa_xyz = a**3 * (5/12)*(3 + sy.sqrt(5)) # textbook XYZ volume
icosa_xyz.evalf()
S3 = sy.sqrt((9/8)) # Synergetics constant
S3
S3 * icosa_xyz.evalf() # convert to tetravolumes
IcosaWithin = sy.Rational(5,2) * sfactor**2 # 2.5 CO to IcosaWithin
Smod = (4 - IcosaWithin)/24
Smod = Smod.simplify()
Smod
(24 * Smod + IcosaWithin).evalf()
sfactor.evalf()
tinyme = (icosavol/8).evalf(40) # 1/8th icosa of edges 2R, edges R
tinyme
Volume changes as a 3rd power of the scale factor as applied to edges. For example if we apply 1/sfactor then the corresponding volume changes by a 3rd power of 1/sfactor. This is only if we assume the shape is held constant, in terms of surface and central angles.
(8 * IcosaWithin * (1/sfactor**3)).evalf() # check
(IcosaWithin * 1/sfactor**3).evalf(40) # same as tinyme
(tinyme * sfactor).evalf(40) # Jitterbug from icosa to CO, back to 2.5
When we apply the sfactor to the 2.5 CO of edges 1R, we might imagine it going, right there and then, to a CO of edges sfactor and volume 2.5 times the sfactor to the 3rd power.
This very CO may then be shrunk to IcosaWithin with same length edges, and embedded in the Octa 4, by the application of 1/sfactor.
(2.5 * sfactor**3 * 1/sfactor).evalf() # IcosaWithin
David Koski: Too cool Icosawithin is 60S + 20s3
What does he mean? The S module scaled up by phi in linear dimensions, has its volume impacted by phi to the 3rd power. S3 (capital S) means stretch S mod legs by phi, and so scale up volume by phi to the 3rd. s3 (lowercase s) means shrink S mod legs by 1/phi, and so scale down volume by (1/phi) to the 3rd.
(60 * Smod + 20 * Smod * (1/phi**3)).evalf(40)
IcosaWithin
IcosaWithin.evalf(40)
Now it's time to do some actual object oriented programming by defining a poly type that may be hit with either a volume scaler, or an edge scalar. These two have a consistent power relationship meaning to rescale volume by X is to rescale edges by ${X}^{1/3}$. To rescale edges by X is to rescale volume by $X^{3}$.
David Koski:
Fundamental to E->S is phi scaling:
class Poly:
def __init__(self, spine, vol):
"""
spine may be any linear dimension we care about
volume is shape volume
"""
self.edge = spine
self.volume = vol
def __mul__(self, scale_v):
"""
scale_v resizes volume, and edges by a 3rd root of scale_v
"""
return type(self)(self.edge * scale_v**(1/3), self.volume * scale_v)
def __call__(self, scale_e):
"""
scale_e resizes edges, and volume by a 3rd power of scale_e
"""
return type(self)(self.edge * scale_e, self.volume * scale_e**3)
rt2 = sy.sqrt # map sympy sqrt to rt2 for 2nd root
φ = (rt2(5)+1)/2 # golden ratio -- same as phi above
E = Poly(1, (rt2(2) / 8) * (φ**-3)) # home base Emod
e3 = E * φ**-3 # small e, phi down
e6 = e3 * φ**-3 # small e, phi down again
E3 = E * φ**3 # capital E, phi up
E6 = E3 * φ**3 # phi up yet again
S = Poly(sfactor, (φ**-5) / 2) # home base Smod
s3 = S * φ**-3 # small s, phi down
s6 = s3 * φ**-3 # small s, phi down again
S3 = S * φ**3 # capital s, phi up
S6 = S3 * φ**3 # phi up yet again
(4 * e3.volume + e6.volume)
rt2(2)/(8*φ**9) + rt2(2)/(2*φ**6)
(4 * e3.volume + e6.volume).evalf(40)
E.volume.evalf(40)
S.volume/E.volume
(2 * rt2(2))/φ**2
((2 * rt2(2))/φ**2).evalf(40)
The expression below is what we find in the pages of Synergetics, plane net for the S. It's the same number, but purged of $\phi$.
2 * rt2(7 - 3 * rt2(5))
2 * rt2(7 - 3 * rt2(5)).evalf(40)
Yes, we may scale a volume while preserving its shape, giving that simple 3rd power change relationship, between the linear and volumetric. Double the edges, eight-fold the volume. However, we may also grow or shrink a volume by transforming it to another shape. We think of the Jitterbug Transformation, the cuboctahedron (CO) to icosahedron transformation, from volume 20 to 18.51 as "shape shifting". In this case, the edges didn't resize at all, yet the volume shrank by a factor of 1/sfactor.
Application of 1/sfactor in this case is not meant to preserve the CO shape.
What we show below is a closed loop where we start from the 2.5 volumed CO(R) embedded in Octa(D). Two applications of the sfactor (i.e. 2nd power) stretches its legs as the VE twist-opens into Icosa(sfactor), faces embedded in Octa(D) all along the way.
An additional shape shifting application of sfactor turns Icosa(sfactor) back into a CO with sfactor edges (i.e. unchanged -- the usual Jitterbug action).
Now that we're at this larger CO(sfactor) of volume about 3.15, one application of 1/sfactor, strictly as a shape-preserving scaling factor this time, will fast track us back, in one step, back to the CO(R) of 2.5.
It's like an express train versus a local. The local scenic route goes from CO(R) through some other intermediate step along the Octa(D) edges rail line -- what the CO(R)'s vertices follow when opening to IcosaWithin (volume ~2.92).
Yet a 3rd application of sfactor, as a shape shifter, does the classic Icosa --> Cubocta Jitterbug while preserving edge lengths. We're now at our destination: CO(sfactor) i.e. a cuboctahedron with edges (and radii) equal sfactor (S:E).
The express route is CO(sfactor) back to CO(R) direct, with 1/sfactor turning edges of sfactor (~1.08) back to edges of 1R, back to where we started in one step.
In sum: CO(R) -> E2S -> E2S = IcosaWithin -> E2S = CO of edges E2S. CO -> S2E goes right back to CO(R) if S2E is applied as a resizer, not as a reshaper. That's after importing from a namespace developed below (E2S is the S factor, i.e. E * E2S -> S where E2S reshapes E to S).
CO = Poly(1, 2.5)
IcosaWithin = CO * sfactor * sfactor
IcosaWithin.volume.evalf()
COmax = IcosaWithin * sfactor
COmax.volume.evalf()
COmax
<__main__.Poly at 0x7fb4c9b17c40>
(COmax(1/sfactor)).volume.evalf()
(CO * sfactor * sfactor).volume.evalf()
print((CO * sfactor).volume.evalf()) # somewhere along the Octa(D) rail system
2.70090756737726
(8 * (CO.volume * 1/sfactor)).evalf()
Here's a quick primer if you do not remember or have no yet encountered the BEAST modules:
More about BEAST (Synergetics 101) Synergeo listserv Feb 13, 2024
Here's a video review as well (about 30 mins):
from IPython.display import YouTubeVideo
YouTubeVideo("OBuH2eAJT6Q")
We have the shapes themselves, then we have scale factors taking us from one to another. However sometimes just a scale factor is not enough as we have strictly shape preserving, but also shape-shifting operations. The Jitterbug is a good example of shape shifting, whereas scaling an S or E module up and down by phi is strictly shape preserving.
When we shape shift a T module of volume 1/24 into an S module, what does that look like in terms of a scale factor? What is the scale factor between any two shapes? Is that generally a meaningful question?
We've already gotten a lot of mileage out of one such scale factor above, the S factor (S:E volume ratio). The S-factor expresses the edge length preserving yet shape shifting Jitterbug transformation from the Icosahedron to the Cuboctahedron. It also plays a role in a different non-edge-length-preserving cuboctahedron-to-icosahedron transformation, taken up as a topic in the Koski Identities section below.
However another scale factor gets a lot of attention in Synergetics 2 especially and we might call it the T factor or T2E. The T & E modules differ by only a tiny relative volume, by a factor of about 0.9994 (E2T) or 1.0005 (T2E).
A2B = B2A = T2A = A2B = T2B = B2T = 1
T2E = sy.Rational(3,2) ** sy.Rational(1,3) * sy.sqrt(2) / φ
T2E.simplify()
T2E.evalf(51)
E2T = (1/T2E).simplify()
E2T
(2**sy.Rational(5,6) * 3**sy.Rational(2,3) * φ / 6).simplify().evalf(40)
E2T.evalf(40)
David Koski:
T->S is 12(phi**-5) = 1.082039324993691 factor which is 24S Cleans up nicely
T = sy.Rational(1, 24)
S.volume
T2S = 12 * φ ** -5 # no, not the S-factor
T2S.evalf()
(T * T2S).evalf(40)
S.volume.evalf(40)
24*S.volume.evalf(40)
T2S.evalf(40)
E2S = S.volume/E.volume
E2S
E2S.evalf(20)
sfactor.evalf(20)
S2E = 1/E2S
S2E
S2E.evalf(40)
Kirby Urner:
CO(R) -> E2S -> E2S = IcosaWithin -> E2S = CO of edges E2S. CO -> S2E goes right back to CO(R) if S2E is applied as a resizer, not as a reshaper.
IW = sy.Rational(5, 2) * E2S * E2S
IW
(IW * E2S).evalf() # CO edges E2S, same as IW
((IW * E2S) * S2E**3).evalf() # back to CO
IW
David Koski:
IcosaWithin * (phi**4)(1/sFactor) -> Icosa(D) of 18.51...
IcosaWithin2IcosaD = S2E * φ**4
(IW * IcosaWithin2IcosaD).evalf(40)
(20 * S2E).evalf(40)
T2E is applied edge-wise as a shape preserving increment. Volume changes as a 3rd power.
(5 * T2E ** 3)/120
((5 * T2E ** 3)/120).evalf(40)
E.volume.evalf(40)
(120 * E.volume).evalf()
((120 * E.volume) * φ**3).evalf(40) # SuperRT
S3 = sy.sqrt(sy.Rational(9,8)) # S3 as in XYZ <-> IVM conversion constant
S3.evalf(40)
(20 * S3).evalf(40) # SuperRT
The original two volumes, Synergetics and Synergetics 2, do not include phi-scaling the S and E modules, several notches "up" and "down" and then expressing concentric hierarchy volumes using whole number combinations of these.
We go over this information below.
φ = (rt2(5) + 1)/2
S = (φ**-5) / 2
S3 = S * φ ** 3
S6 = S3 * φ ** 3
S9 = S6 * φ ** 3
s3 = S * φ ** -3
s6 = s3 * φ ** -3
s9 = s6 * φ ** -3
E = (rt2(2) / 8) * (φ**-3)
E3 = E * φ ** 3
E6 = E3 * φ ** 3
E9 = E6 * φ ** 3
e3 = E * φ ** -3
e6 = e3 * φ ** -3
e9 = e6 * φ ** -3
The "SkewIcosa" in the table below is a regular icosahedron. The term "skew" in this case reminds us if how it's developed by skewing a cuboctahedron of volume 2.5, such that its edges stretch while its faces remain flush with an octahedron of volume 4.
The resulting icosahedron, faces flush with the octahedron, serves as a basis for developing the S module.
(24 * S + IcosaWithin.volume).evalf(100)
Tetrahedron = S6 + S3 # (volume = 1)
SkewIcosa = 60*S + 20*s3 # (volume = ~2.92)
Cube = 3*S6 + 3*S3 # (volume = 3)
Octahedron = 4*S6 + 4*S3 # (volume = 4)
RhTriac_T = 5*S6 + 5*S3 # (volume = 5)
RhTriac_E =120*E
RhDodeca = 6*S6 + 6*S3 # (volume = 6)
PentDodeca =348*E + 84*e3 # (volume = ~15.35)
Icosahedron =100*E3 + 20*E # (volume = ~18.51)
VE =420*S +100*s3 # (volume = 20)
SuperRT =480*E +120*e3 # (volume = ~21.21)
p = 50 # precision
print(f"""
Tetrahedron = {Tetrahedron.evalf(p):50.45f} = {Tetrahedron.simplify()}
SkewIcosa = {SkewIcosa.evalf(p):50.45f} = {SkewIcosa.simplify()}
Cube = {Cube.evalf(p):50.45f} = {Cube.simplify()}
Octahedron = {Octahedron.evalf(p):50.45f} = {Octahedron.simplify()}
RhTriac_T = {RhTriac_T.evalf(p):50.45f} = {RhTriac_T.simplify()}
RhTriac_E = {RhTriac_E.evalf(p):50.45f} = {RhTriac_E.simplify()}
RhDodeca = {RhDodeca.evalf(p):50.45f} = {RhDodeca.simplify()}
PentDodeca = {PentDodeca.evalf(p):50.45f} = {PentDodeca.simplify()}
Icosahedron = {Icosahedron.evalf(p):50.45f} = {Icosahedron.simplify()}
VE = {VE.evalf(p):50.45f} = {VE.simplify()}
SuperRT = {SuperRT.evalf(p):50.45f} = {SuperRT.simplify()}
""")
Tetrahedron = 1.000000000000000000000000000000000000000000000 = 1 SkewIcosa = 2.917960675006309107724789938061712936781449212 = 70 - 30*sqrt(5) Cube = 3.000000000000000000000000000000000000000000000 = 3 Octahedron = 4.000000000000000000000000000000000000000000000 = 4 RhTriac_T = 5.000000000000000000000000000000000000000000000 = 5 RhTriac_E = 5.007758031332838515932741440199835648703170829 = -30*sqrt(2) + 15*sqrt(10) RhDodeca = 6.000000000000000000000000000000000000000000000 = 6 PentDodeca = 15.350018208050781864011005748221813389851871774 = 3*sqrt(10)/2 + 15*sqrt(2)/2 Icosahedron = 18.512295868219161196009899292654531923571426914 = 5*sqrt(10)/2 + 15*sqrt(2)/2 VE = 20.000000000000000000000000000000000000000000000 = 20 SuperRT = 21.213203435596425732025330863145471178545078131 = 15*sqrt(2)
print((8 * IcosaWithin.volume * (1/sfactor)**3).evalf(50)) # shrink to R-edge Icosa, inflate by 8 for D-edge Icosa
18.512295868219161196009899292654531923571426913640
apex_e = S3 ** sy.Rational(1,3)
(apex_e).evalf()
S3.evalf()
S6.evalf()
(IW/2.5).evalf()
(sfactor**2).evalf()
We start with the Goldberg paper submitting in 1972, published in 1974, before the first volume of Synergetics came out in 1975.
The paper summarizes space-filling tetrahedrons already known, while adding a generalization allowing for an accordian-like expansion / contraction of RITEs and fractional RITEs (1/2 and 1/4) when stacking them in arbitrarily long equilateral prisms.
The Mite and Bite (as they're known in Synergetics) stand outside of the infinite Rite families in this categorization, but may be lumped by other criteria, not taken up in the Goldberg paper, such as their composability from A and B modules (see above).
from sympy import sqrt as rt2
from tetravolume import Tetrahedron
The last two rows in each column (not counting the summation row) need to be swapped to stay compatible with our way of passing lengths to the Tetrahedron API, which expects edges of the base opposite the apex A to be traversed in the order BC, CD, BD, after AB, AC, AD in that order.
The angles subcolumn in the Goldberg Paper, associated with each module, gives dihedral angles, the computation of which is included in the Tetrahedron type (as of May 6, 2024).
I'm getting a mismatch in column 4 (465 vs 420 for the 1/4 Rite in question). Have I made an error somewhere? We agree on 435 in column 2.
column1 = (rt2(3), rt2(3), 2, 2, rt2(3), rt2(3))
column2 = (rt2(3), rt2(2), 2, 1, rt2(2), rt2(3))
column3 = (rt2(3), 2*rt2(2), 2, rt2(3), 2, rt2(3))
column4 = (rt2(3), rt2(3), rt2(5)/2, 2, rt2(5)/2, rt2(5)/2)
column5 = (rt2(3), rt2(3), rt2(11)/2, 2, rt2(3)/2, rt2(11)/2)
rite = Tetrahedron(*column1)
mite = Tetrahedron(*column2)
bite = Tetrahedron(*column3)
qrite = Tetrahedron(*column4)
hrite = Tetrahedron(*column5)
k = rt2(2)/32 # constant of proportionality
rite.ivm_volume() * k
mite.ivm_volume() * k
bite.ivm_volume() * k
qrite.ivm_volume() * k
hrite.ivm_volume() * k
from sympy import Symbol, Rational
k = Symbol('k')
Instead of using a volume changing scalar, lets get that number k's 3rd root and use that as a linear scale factor. This way, we should get all the edge lengths we need for the modules under scrutiny, and consistently with our volume requirements.
s = k**Rational(1,3) # 3rd root
s
Every edge is to be individually rescaled by this 3rd root of k.
column1 = [e*s for e in column1]
column2 = [e*s for e in column2]
column3 = [e*s for e in column3]
column4 = [e*s for e in column4]
column5 = [e*s for e in column5]
column1
[sqrt(3)*k**(1/3), sqrt(3)*k**(1/3), 2*k**(1/3), 2*k**(1/3), sqrt(3)*k**(1/3), sqrt(3)*k**(1/3)]
Now lets substitute in our expression for k and see how the overall lengths simplify.
rite = [e.subs({"k":rt2(2)/32}) for e in column1]
mite = [e.subs({"k":rt2(2)/32}) for e in column2]
bite = [e.subs({"k":rt2(2)/32}) for e in column3]
qrite = [e.subs({"k":rt2(2)/32}) for e in column4]
hrite = [e.subs({"k":rt2(2)/32}) for e in column5]
rite # looking good
[sqrt(6)/4, sqrt(6)/4, sqrt(2)/2, sqrt(2)/2, sqrt(6)/4, sqrt(6)/4]
Now lets examine each module in turn, going left to right per the Goldberg Table (Table 1 in the article).
Rite = Tetrahedron(*rite)
Rite.edges()
{'AB': sqrt(6)/4, 'AC': sqrt(6)/4, 'AD': sqrt(2)/2, 'BC': sqrt(2)/2, 'CD': sqrt(6)/4, 'BD': sqrt(6)/4}
Rite.ivm_volume() # check (2 Mites)
rite_angles = Rite.degrees(True)
sum(rite_angles.values())
rite_angles
{'BAC': 70.5287793655093, 'CAD': 54.7356103172453, 'BAD': 54.7356103172453, 'ABC': 54.7356103172453, 'CBD': 54.7356103172453, 'ABD': 70.5287793655093, 'ACB': 54.7356103172453, 'ACD': 70.5287793655093, 'BCD': 54.7356103172453, 'ADC': 54.7356103172453, 'BDC': 70.5287793655093, 'ADB': 54.7356103172453}
rite_dihedrals = Rite.dihedrals(True, True)
rite_dihedrals
{'AB': 60.0000000000000, 'AC': 60.0000000000000, 'AD': 90.0000000000000, 'BC': 90.0000000000000, 'CD': 60.0000000000000, 'BD': 60.0000000000000}
sum(rite_dihedrals.values())
Mite = Tetrahedron(*mite)
Mite.edges()
{'AB': sqrt(6)/4, 'AC': 1/2, 'AD': sqrt(2)/2, 'BC': sqrt(2)/4, 'CD': 1/2, 'BD': sqrt(6)/4}
Mite.ivm_volume() # check
mite_angles = Mite.degrees(True)
sum(mite_angles.values())
mite_angles
{'BAC': 35.2643896827547, 'CAD': 45.0000000000000, 'BAD': 54.7356103172453, 'ABC': 54.7356103172453, 'CBD': 54.7356103172453, 'ABD': 70.5287793655093, 'ACB': 90.0000000000000, 'ACD': 90.0000000000000, 'BCD': 90.0000000000000, 'ADC': 45.0000000000000, 'BDC': 35.2643896827547, 'ADB': 54.7356103172453}
mite_dihedrals = Mite.dihedrals(True, True)
mite_dihedrals
{'AB': 60.0000000000000, 'AC': 90.0000000000000, 'AD': 45.0000000000000, 'BC': 90.0000000000000, 'CD': 90.0000000000000, 'BD': 60.0000000000000}
sum(mite_dihedrals.values())
Bite = Tetrahedron(*bite)
Bite.edges()
{'AB': sqrt(6)/4, 'AC': 1, 'AD': sqrt(2)/2, 'BC': sqrt(6)/4, 'CD': sqrt(2)/2, 'BD': sqrt(6)/4}
Bite.ivm_volume() # check (2 Mites)
bite_angles = Bite.degrees(True)
sum(bite_angles.values())
bite_angles
{'BAC': 35.2643896827547, 'CAD': 45.0000000000000, 'BAD': 54.7356103172453, 'ABC': 109.471220634491, 'CBD': 70.5287793655093, 'ABD': 70.5287793655093, 'ACB': 35.2643896827547, 'ACD': 45.0000000000000, 'BCD': 54.7356103172453, 'ADC': 90.0000000000000, 'BDC': 54.7356103172453, 'ADB': 54.7356103172453}
bite_dihedrals = Bite.dihedrals(True, True)
bite_dihedrals
{'AB': 60.0000000000000, 'AC': 90.0000000000000, 'AD': 45.0000000000000, 'BC': 60.0000000000000, 'CD': 45.0000000000000, 'BD': 120.000000000000}
sum(bite_dihedrals.values())
Qrite = Tetrahedron(*qrite)
Qrite.edges()
{'AB': sqrt(6)/4, 'AC': sqrt(6)/4, 'AD': sqrt(10)/8, 'BC': sqrt(2)/2, 'CD': sqrt(10)/8, 'BD': sqrt(10)/8}
Qrite.ivm_volume() # Check (1/4 of 1/4)
qrite_angles = Qrite.degrees(True)
sum(qrite_angles.values())
qrite_angles
{'BAC': 70.5287793655093, 'CAD': 39.2315204835923, 'BAD': 39.2315204835923, 'ABC': 54.7356103172453, 'CBD': 26.5650511770780, 'ABD': 39.2315204835923, 'ACB': 54.7356103172453, 'ACD': 39.2315204835923, 'BCD': 26.5650511770780, 'ADC': 101.536959032815, 'BDC': 126.869897645844, 'ADB': 101.536959032815}
qrite_dihedrals = Qrite.dihedrals(True, True)
qrite_dihedrals
{'AB': 30.0000000000000, 'AC': 30.0000000000000, 'AD': 131.810314895779, 'BC': 45.0000000000000, 'CD': 114.094842552111, 'BD': 114.094842552111}
sum(qrite_dihedrals.values()) # mismatch with column 4
Hrite = Tetrahedron(*hrite)
Hrite.edges()
{'AB': sqrt(6)/4, 'AC': sqrt(6)/4, 'AD': sqrt(22)/8, 'BC': sqrt(2)/2, 'CD': sqrt(6)/8, 'BD': sqrt(22)/8}
Hrite.ivm_volume() # Check (1/2 of 1/4)
hrite_angles = Hrite.degrees(True)
sum(hrite_angles.values())
hrite_angles
{'BAC': 70.5287793655093, 'CAD': 29.4962084965664, 'BAD': 58.5178458947061, 'ABC': 54.7356103172453, 'CBD': 25.2394018206789, 'ABD': 58.5178458947061, 'ACB': 54.7356103172453, 'ACD': 70.5287793655093, 'BCD': 54.7356103172453, 'ADC': 79.9750121379243, 'BDC': 100.024987862076, 'ADB': 62.9643082105877}
hite_dihedrals = Hrite.dihedrals(True, True)
hite_dihedrals
{'AB': 30.0000000000000, 'AC': 60.0000000000000, 'AD': 106.778654880960, 'BC': 90.0000000000000, 'CD': 60.0000000000000, 'BD': 73.2213451190396}
sum(hite_dihedrals.values())