Creating a structure of linked tetrahedra

Hi there. I am trying to generate a 3D landscape of tetrahedra, so to say, and I’m having some trouble with the mathematics and coding of it. I’m also still quite new to Processing! If I am not being clear enough about something, please let me know and I’ll do my best to provide more info.

Drawing just one tetrahedron (let’s refer to it as ABCD) is not so hard, using:
vertex(1,1,1)
vertex(1,-1,-1)
vertex(-1,1,-1)
vertex(-1,-1,1)

My next step is to choose one of the faces of my tetrahedron (ABD for example), and generate a new vertex E which, when connected to the A, B and D, makes a new tetrahedron ABDE. Pretty much like the image below!!
tetras|507x142

Does anyone have any tips or knowledge on how to do this? Thanks!

2 Likes

i would use:

for 3D view


good, show us that part you have already
( as a running code formatted and pasted into

</> code tag 

1 Like

Ah, I had already been using peasycam and createshape, but I’ll check out PVector, thanks! Below is what I have right now:

1 Like

ok, next

  • try to fill it and you will see that you need 4 times triangles ( each 3 vertex )

but for the real job
4 sides can have a vertex “E”
how you choose side and point

Hi @dauwpunt Welcome to the forum.

If you really want structures of tetrahedrons you will eventually run into problems. Linking them together will force you to use the quadray coordinate system instead of the cartesian. And tetrahedrons are not space filling. The best computer simulations last year gave about 85 % space filling. In other words you can’t always link/attach them.
The cube is the only Platonic solid which does fill space, as well as some combinations of tetrahedra and octahedra do.
Below a not question related code, but if you plan to run on android you can’t use the peasycam lib mentioned by kll, but this code does rotate without going to the first begin position every time you touch the screen, rather starting at last position. It also shows the quadray axes

float rotX, rotY, s;

void setup(){
  size(720, 480, P3D);
  s = 60;  
  noFill();
  stroke(255);
}

void draw(){
  background(0);
  translate(width/2, height/2);
  rotateX(rotX); 
  rotateY(-rotY); 
  beginShape(TRIANGLE_STRIP);
  stroke(255, 0, 0);
  strokeWeight(3);
  vertex( s,  s,  s); // red v1
  stroke(0, 255, 0);
  vertex(-s, -s,  s); // green v2
  stroke(0, 0, 255);
  vertex(-s,  s, -s); // blue v3
  stroke(255, 255, 0);
  vertex( s, -s, -s); // yellow v4
  stroke(255, 0, 0);
  vertex( s,  s,  s); // red v1 again to close tetrahedron
  endShape();
  stroke(200, 0, 0);
  line(0, 0, 0, -4*s, -4*s, -4*s);
  stroke(255, 50, 50);
  line(0, 0, 0,  4*s,  4*s,  4*s);
  stroke(0, 200, 0);
  line(0, 0, 0, -4*s, -4*s,  4*s);
  stroke(50, 255, 50);
  line(0, 0, 0,  4*s,  4*s, -4*s);
  stroke(0, 0, 200);
  line(0, 0, 0, -4*s,  4*s, -4*s);
  stroke(50, 50, 255);
  line(0, 0, 0,  4*s, -4*s,  4*s);
  stroke(255);
  line(0, 0, 0,  4*s, -4*s, -4*s);
  stroke(150);
  line(0, 0, 0, -4*s,  4*s,  4*s);
}

void mouseDragged() { 
  rotY -= (mouseX - pmouseX) * 0.01; 
  rotX -= (mouseY - pmouseY) * 0.01;
}
1 Like

here is why i think PVector
now even PVector[] could make sense:

// https://discourse.processing.org/t/creating-a-structure-of-linked-tetrahedra/16955

//Drawing just one tetrahedron (let’s refer to it as ABCD) is not so hard, using:
//vertex(1,1,1)
//vertex(1,-1,-1)
//vertex(-1,1,-1)
//vertex(-1,-1,1)

// https://en.wikipedia.org/wiki/Tetrahedron
// http://learningprocessing.com/examples/chp14/example-14-04-pyramid
// https://processing.org/tutorials/p3d/

import peasy.*;
PeasyCam cam;
PShape thd;

int big   = 100;
int alpha = 255;

PVector a = new PVector(big, big, big);
PVector b = new PVector(big, -big, -big);
PVector c = new PVector(-big, big, -big);
PVector d = new PVector(-big, -big, big);

PVector[] t4 = {a,b,c,d};//new PVector[4];

void setup() {
  size(500, 500, P3D);
  make_thd(t4);
  cam = new PeasyCam(this, 400);
}

void make_thd(PVector[] t) {
  thd = createShape();
  thd.beginShape(TRIANGLES);

  thd.stroke(0,200,200);
  thd.strokeWeight(2);
  
  thd.fill(100,0,0,alpha);
  thd.vertex(t[0].x,t[0].y,t[0].z);
  thd.vertex(t[1].x,t[1].y,t[1].z);
  thd.vertex(t[2].x,t[2].y,t[2].z);
  
  thd.fill(0,100,0,alpha);
  thd.vertex(t[0].x,t[0].y,t[0].z);
  thd.vertex(t[1].x,t[1].y,t[1].z);
  thd.vertex(t[3].x,t[3].y,t[3].z);

  thd.fill(0,0,100,alpha);
  thd.vertex(t[2].x,t[2].y,t[2].z);
  thd.vertex(t[1].x,t[1].y,t[1].z);
  thd.vertex(t[3].x,t[3].y,t[3].z);

  thd.fill(0,0,0,alpha);
  thd.vertex(t[0].x,t[0].y,t[0].z);
  thd.vertex(t[3].x,t[3].y,t[3].z);
  thd.vertex(t[2].x,t[2].y,t[2].z);

  thd.endShape();
}

void show_points() {
  fill(200,0,0);
  textSize(20);
  push();
  translate(a.x,a.y,a.z);
  text("A",0,0);
  pop();
  push();
  translate(b.x,b.y,b.z);
  text("B",0,0);
  pop();
  push();
  translate(c.x,c.y,c.z);
  text("C",0,0);
  pop();
  push();
  translate(d.x,d.y,d.z);
  text("D",0,0);
  pop();
}

void draw() {
  background(200, 200, 0);
  shape(thd, 0, 0);
  show_points();
}

1 Like

Thank you very much for this in-depth reply Noel! I will look into the quadray coordinate system you mentioned. It’s been a while since I’ve been doing something so math-heavy haha.
I am aware that they are not space-filling! It’s not that I necessarily want a space that is filled to the brim with tetrahedra, but it would be perfect if I could have one centered tetrahedron, and then pick one of its faces and attach a new tetrahedron to it. I’m starting to find out this sounds a lot easier than it will probably be.

I saw some posts about using the translate() and rotatex/y/z functions to move the grid. Could that be something useful?

I guess not, because translation will occur in Cartesian space.
You can still build from the center using two points of a face and distance the other along the quadray axis.
Below a code with one tetrahedron attached forming a triangular bipyramid, rotating around centroid of the center tetrahedron. I did not close all faces just to see through.
Maybe you can build something using a PVector array like @kll suggested. Let us know if you succeed.



float rotX, rotY, s, t;

void setup(){
  size(720, 480, P3D);
  s = 60;  
  t = 2*sqrt(6)/3;
  noFill();
  stroke(255);
  hint ( DISABLE_DEPTH_TEST);
} 

void draw(){
  background(0);
  translate(width/2, height/2);
  rotateX(rotX); 
  rotateY(-rotY); 
  
  fill(255, 0, 0, 200); // first red face
  beginShape(TRIANGLES);
  strokeWeight(3); 
  vertex( s,  s,  s); 
  vertex(-s, -s,  s);
  vertex(-s,  s, -s); 
  endShape();
  
  fill(0, 255, 0, 200); // second green face
  beginShape(TRIANGLES);
  //stroke(255, 255, 0);
  vertex( s, -s, -s); 
  //stroke(255, 0, 0);
  vertex( s,  s,  s); 
  //stroke(0, 255, 0);
  vertex(-s, -s, s); 
  endShape();
  
  fill(255, 255, 0, 200); // third yellow face
  beginShape(TRIANGLES);
  vertex( -s, s, -s); 
  vertex( s,  s,  s); 
  vertex(s, -s, -s);
  endShape();
  
  // Using two points of first red face and distance the other
  fill(255, 0, 0, 200); 
  beginShape(TRIANGLES);
  strokeWeight(3);
  vertex( s,  s,  s); 
  vertex(-s, -s,  s); 
  vertex(-t*s,  t*s, t*s); 
  endShape();
  
  fill(255, 0, 0, 200); 
  beginShape(TRIANGLES);
  strokeWeight(3);
  vertex( s,  s,  s); 
  vertex(-t*s, t*s,  t*s); 
  vertex(-s,  s, -s); 
  endShape();
 
  // The axes
  stroke(200, 0, 0, 100);
  line(0, 0, 0, -4*s, -4*s, -4*s);
  stroke(255, 50, 50, 100);
  line(0, 0, 0,  4*s,  4*s,  4*s);
  stroke(0, 200, 0, 100);
  line(0, 0, 0, -4*s, -4*s,  4*s);
  stroke(50, 255, 50, 100);
  line(0, 0, 0,  4*s,  4*s, -4*s);
  stroke(0, 0, 200, 100);
  line(0, 0, 0, -4*s,  4*s, -4*s);
  stroke(50, 50, 255, 100);
  line(0, 0, 0,  4*s, -4*s,  4*s);
  stroke(255, 100);
  line(0, 0, 0,  4*s, -4*s, -4*s);
  stroke(150, 100);
  line(0, 0, 0, -4*s,  4*s,  4*s);
}

void mouseDragged() { 
  rotY -= (mouseX - pmouseX) * 0.01; 
  rotX -= (mouseY - pmouseY) * 0.01;
}

@noel this was a tremendous help, thank you. I’ve made some progress and am looking next to apply the pVector idea that @kll suggested.

Right now, this is my goal:

  • One visual configuration that shows the center tetrahedron, plus its 4 companions that are attached via the triangular faces. This was fairly easy to achieve with the help of your code.
  • One visual configuration that shows the center tetrahedron, plus its 12(?) companions that are attached via the sides only. This one is more tricky, but I have managed to create some of these companions (the green tetrahedra).

any ideas on how to make my code more efficient are of course very welcome haha.

float s, t;
import peasy.*;
PeasyCam cam;

void setup(){
  size(720, 480, P3D);
  s = 60;  
  t = 2*sqrt(6)/3;
  noFill();
  stroke(255);
  cam = new PeasyCam(this, 100);
  cam.setMinimumDistance(500);
  cam.setMaximumDistance(2000);
} 

void draw(){
  background(0);
  //translate(width/2, height/2);
  
  // TETRA 1: the base
  fill(255, 0, 0); // first red face
  beginShape(TRIANGLES);
  strokeWeight(3); 
  vertex( s,  s,  s); 
  vertex(-s, -s,  s);
  vertex(-s,  s, -s); 
  endShape();
  
  fill(255, 0, 0); // second red face
  beginShape(TRIANGLES);
  vertex( s, -s, -s); 
  vertex( s,  s,  s); 
  vertex(-s, -s, s); 
  endShape();
  
  fill(255, 0, 0); // third red face
  beginShape(TRIANGLES);
  vertex( -s, s, -s); 
  vertex( s,  s,  s); 
  vertex(s, -s, -s);
  endShape();
  
  fill(255, 0, 0); // fourth red face
  beginShape(TRIANGLES);
  vertex(-s, -s,  s);
  vertex(-s,  s, -s);
  vertex( s, -s, -s);
  endShape();
  
        // TETRA 2.1: Using two points of first red face and distance the other using t
        fill(0, 85, 0, 200); 
        beginShape(TRIANGLES);
        strokeWeight(3);
        vertex( s,  s,  s); 
        vertex(-s, -s,  s); 
        vertex(-t*s,  t*s, t*s); 
        endShape();
        
        fill(0, 170, 0, 200);
        beginShape(TRIANGLES);
        strokeWeight(3);
        vertex( s,  s,  s);  
        vertex(-s,  s, -s); 
        vertex(-t*s, t*s,  t*s);
        endShape();
        
        fill(0, 255, 0, 200);
        beginShape(TRIANGLES);
        strokeWeight(3);
        vertex(-s, -s,  s);  
        vertex(-s,  s, -s); 
        vertex(-t*s, t*s,  t*s);
        endShape();
        
              // TETRA 2.1.1: Using two points of third 2.1 and distance the other using t
              fill(0, 255, 0, 200); 
              beginShape(TRIANGLES);
              strokeWeight(3);
              vertex(-s, -s,  s); 
              vertex(-t*s,  t*s, t*s); 
              vertex(-2*t*s,  0, 0);  
              endShape();
              
              beginShape(TRIANGLES);
              strokeWeight(3);
              vertex(-s,  s, -s); 
              vertex(-t*s, t*s,  t*s); 
              vertex(-2*t*s,  0, 0);
              endShape();
              
              beginShape(TRIANGLES);
              strokeWeight(3);
              vertex(-s, -s,  s); 
              vertex(-s,  s, -s); 
              vertex(-2*t*s,  0, 0);
              endShape();
              
              // TETRA 2.1.2: Using two points of first 2.1 and distance the other using t
              fill(0, 85, 0, 200); 
              beginShape(TRIANGLES);
              strokeWeight(3);
              vertex( s,  s,  s); 
              vertex(-s, -s,  s); 
              vertex(0, 0, 2*t*s);  
              endShape();
              
              beginShape(TRIANGLES);
              strokeWeight(3);
              vertex(-s, -s,  s); 
              vertex(-t*s,  t*s, t*s); 
              vertex(0, 0, 2*t*s);
              endShape();
              
              beginShape(TRIANGLES);
              strokeWeight(3);
              vertex(-s, -s,  s); 
              vertex(-t*s,  t*s, t*s); 
              vertex(0, 0, 2*t*s);
              endShape();
        
        // TETRA 2.2: Using two points of second red face and distance the other using t      
        fill(0, 0, 255, 150); 
        beginShape(TRIANGLES);
        strokeWeight(3);
        vertex( s, -s, -s);   
        vertex( s,  s,  s);
        vertex(t*s,  -t*s, t*s);
        endShape();
        
        beginShape(TRIANGLES);
        strokeWeight(3);
        vertex( s, -s, -s);   
        vertex(-s, -s, s);
        vertex(t*s,  -t*s, t*s);
        endShape();
        
        beginShape(TRIANGLES);
        strokeWeight(3);
        vertex( s,  s,  s);   
        vertex(-s, -s, s);
        vertex(t*s,  -t*s, t*s);
        endShape();
        
              //// TETRA 2.2.1: Using two points of 2.2 and distance the other using t      
              //fill(0, 255, 255, 150); 
              //beginShape(TRIANGLES);
              //strokeWeight(3);
              //vertex( s, -s, -s);   
              //vertex(-s, -s, s);
              //vertex(0, -2*t*s, 0);
              //endShape();
              
              //beginShape(TRIANGLES);
              //strokeWeight(3);
              //vertex( s, -s, -s);   
              //vertex(t*s,  -t*s, t*s);
              //vertex(0, -2*t*s, 0);
              //endShape();
              
              //beginShape(TRIANGLES);
              //strokeWeight(3);
              //vertex(-s, -s, s);   
              //vertex(t*s,  -t*s, t*s);
              //vertex(0, -2*t*s, 0);
              //endShape();
        
        // TETRA 2.3: Using two points of third red face and distance the other using t  
        fill(255, 255, 0, 150); 
        beginShape(TRIANGLES);
        strokeWeight(3);
        vertex( -s, s, -s); 
        vertex( s,  s,  s);
        vertex(t*s,  t*s, -t*s);
        endShape();
        
        beginShape(TRIANGLES);
        strokeWeight(3);
        vertex( -s, s, -s);   
        vertex(s, -s, -s);
        vertex(t*s,  t*s, -t*s);
        endShape();
        
        beginShape(TRIANGLES);
        strokeWeight(3);
        vertex( s,  s,  s);   
        vertex(s, -s, -s);
        vertex(t*s,  t*s, -t*s);
        endShape();
        
        // TETRA 2.4: Using two points of fourth red face and distance the other using t  
        fill(255, 0, 255, 150); 
        beginShape(TRIANGLES);
        strokeWeight(3);
        vertex(-s, -s,  s);
        vertex(-s,  s, -s);
        vertex(-t*s, -t*s, -t*s);
        endShape();
        
        beginShape(TRIANGLES);
        strokeWeight(3);
        vertex(-s, -s,  s);   
        vertex( s, -s, -s);
        vertex(-t*s, -t*s, -t*s);
        endShape();
        
        beginShape(TRIANGLES);
        strokeWeight(3);
        vertex(-s,  s, -s);   
        vertex( s, -s, -s);
        vertex(-t*s, -t*s, -t*s);
        endShape();
  
  // The axes
  stroke(200, 0, 0, 100);
  line(0, 0, 0, -4*s, -4*s, -4*s);
  stroke(255, 50, 50, 100);
  line(0, 0, 0,  4*s,  4*s,  4*s);
  stroke(0, 200, 0, 100);
  line(0, 0, 0, -4*s, -4*s,  4*s);
  stroke(50, 255, 50, 100);
  line(0, 0, 0,  4*s,  4*s, -4*s);
  stroke(0, 0, 200, 100);
  line(0, 0, 0, -4*s,  4*s, -4*s);
  stroke(50, 50, 255, 100);
  line(0, 0, 0,  4*s, -4*s,  4*s);
  stroke(255, 100);
  line(0, 0, 0,  4*s, -4*s, -4*s);
  stroke(150, 100);
  line(0, 0, 0, -4*s,  4*s,  4*s);
}
2 Likes

I‘d go for a class Tetrahedron and then make an Arraylist of this.

Then in setup you do the calculations and fill the arraylist

In draw you only do the display bit

Animation :

Remember the rubicks cube?

Similarly there is

A snake which is turnable at each element (I programmed this actually)

Maybe you could apply this to your program.

Then each element is attached by a rotating joint to the next element

1 Like

Hi there, been a while! I’ve made good progress with this but now am in need of some more mathematical wisdom. Code below!

There is a coordinate I need for the black tetrahedron that will sit against the green tetrahedron (0,1,4,9). For the life of me I can’t figure out what the coordinates should be… Any ideas how to find them?

import peasy.*;
PeasyCam cam;
PShape thd;
float s = 60;
float t = 2*sqrt(6)/3;
int alpha = 200;

PVector a = new PVector(s, s, s);
PVector b = new PVector(-s, -s, s);
PVector c = new PVector(-s, s, -s);
PVector d = new PVector(s, -s, -s);

PVector e = new PVector(-t*s,  t*s, t*s);
PVector f = new PVector(t*s,  -t*s, t*s);
PVector g = new PVector(-t*s,  -t*s, -t*s);
PVector h = new PVector(t*s,  t*s, -t*s);

PVector i = new PVector(-2*t*s,  0, 0); // e+g
PVector j = new PVector(0, 0, 2*t*s); // e+f
PVector k = new PVector(0, 2*t*s, 0); // e+h // ook voor geel
PVector l = new PVector(2*t*s,  0, 0); // f+h // ook voor geel
PVector m = new PVector(0, -2*t*s, 0); // f+g
PVector n = new PVector(0, 0, -2*t*s); // g+h // ook voor geel, wordt 13

PVector o = new PVector(-2*t*s, 0, 2*t*s); //b+e

// geel = 10,11,15

PVector[] t15 = {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o};

void setup(){
  size(720, 480, P3D);   
  make_thd(t15);
  cam = new PeasyCam(this, 100);
  cam.setMinimumDistance(500);
  cam.setMaximumDistance(2000);
} 

void make_thd(PVector[] t) {
  thd = createShape();
  thd.beginShape(TRIANGLES);
  
  thd.stroke(200,200,200);
  thd.strokeWeight(3);
  
thd.fill(255,255,255,alpha);        // base 0,1,2,3
thd.vertex(t[0].x,t[0].y,t[0].z);
thd.vertex(t[1].x,t[1].y,t[1].z);
thd.vertex(t[2].x,t[2].y,t[2].z);
  
thd.vertex(t[0].x,t[0].y,t[0].z);
thd.vertex(t[1].x,t[1].y,t[1].z);
thd.vertex(t[3].x,t[3].y,t[3].z);

thd.vertex(t[2].x,t[2].y,t[2].z);
thd.vertex(t[1].x,t[1].y,t[1].z);
thd.vertex(t[3].x,t[3].y,t[3].z);

thd.vertex(t[0].x,t[0].y,t[0].z);
thd.vertex(t[3].x,t[3].y,t[3].z);
thd.vertex(t[2].x,t[2].y,t[2].z);
  
  thd.fill(0,255,0,alpha);            // base 0,1,2,4
  thd.vertex(t[0].x,t[0].y,t[0].z);
  thd.vertex(t[1].x,t[1].y,t[1].z);
  thd.vertex(t[2].x,t[2].y,t[2].z);
  
  thd.vertex(t[0].x,t[0].y,t[0].z);
  thd.vertex(t[1].x,t[1].y,t[1].z);
  thd.vertex(t[4].x,t[4].y,t[4].z);

  thd.vertex(t[2].x,t[2].y,t[2].z);
  thd.vertex(t[1].x,t[1].y,t[1].z);
  thd.vertex(t[4].x,t[4].y,t[4].z);

  thd.vertex(t[0].x,t[0].y,t[0].z);
  thd.vertex(t[4].x,t[4].y,t[4].z);
  thd.vertex(t[2].x,t[2].y,t[2].z);
        
        thd.fill(0,125,0,alpha);            // base 0,1,4,9
        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[4].x,t[4].y,t[4].z);
  
        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[9].x,t[9].y,t[9].z);

        thd.vertex(t[4].x,t[4].y,t[4].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[9].x,t[9].y,t[9].z);

        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[9].x,t[9].y,t[9].z);
        thd.vertex(t[4].x,t[4].y,t[4].z);
        
              thd.fill(0,0,0,alpha);            // base 4,1,9,14
              thd.vertex(t[4].x,t[4].y,t[4].z);
              thd.vertex(t[1].x,t[1].y,t[1].z);
              thd.vertex(t[9].x,t[9].y,t[9].z);
  
              thd.vertex(t[4].x,t[4].y,t[4].z);
              thd.vertex(t[1].x,t[1].y,t[1].z);
              thd.vertex(t[14].x,t[14].y,t[14].z);

              thd.vertex(t[9].x,t[9].y,t[9].z);
              thd.vertex(t[1].x,t[1].y,t[1].z);
              thd.vertex(t[14].x,t[14].y,t[14].z);

              thd.vertex(t[4].x,t[4].y,t[4].z);
              thd.vertex(t[14].x,t[14].y,t[14].z);
              thd.vertex(t[9].x,t[9].y,t[9].z);
        
        thd.fill(0,125,0,alpha);            // base 2,1,4,8
        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[4].x,t[4].y,t[4].z);
  
        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[8].x,t[8].y,t[8].z);

        thd.vertex(t[4].x,t[4].y,t[4].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[8].x,t[8].y,t[8].z);

        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[8].x,t[8].y,t[8].z);
        thd.vertex(t[4].x,t[4].y,t[4].z);
        
        thd.fill(0,125,0,alpha);            // base 0,4,2,10
        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[4].x,t[4].y,t[4].z);
        thd.vertex(t[2].x,t[2].y,t[2].z);
  
        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[4].x,t[4].y,t[4].z);
        thd.vertex(t[10].x,t[10].y,t[10].z);

        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[4].x,t[4].y,t[4].z);
        thd.vertex(t[10].x,t[10].y,t[10].z);

        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[10].x,t[10].y,t[10].z);
        thd.vertex(t[2].x,t[2].y,t[2].z);
  
  thd.fill(255,0,0,alpha);            // base 0,1,3,5
  thd.vertex(t[0].x,t[0].y,t[0].z);
  thd.vertex(t[1].x,t[1].y,t[1].z);
  thd.vertex(t[3].x,t[3].y,t[3].z);
  
  thd.vertex(t[0].x,t[0].y,t[0].z);
  thd.vertex(t[1].x,t[1].y,t[1].z);
  thd.vertex(t[5].x,t[5].y,t[5].z);

  thd.vertex(t[3].x,t[3].y,t[3].z);
  thd.vertex(t[1].x,t[1].y,t[1].z);
  thd.vertex(t[5].x,t[5].y,t[5].z);

  thd.vertex(t[0].x,t[0].y,t[0].z);
  thd.vertex(t[5].x,t[5].y,t[5].z);
  thd.vertex(t[3].x,t[3].y,t[3].z);
  
        thd.fill(125,0,0,alpha);            // 0,5,3,11
        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[5].x,t[5].y,t[5].z);
        thd.vertex(t[3].x,t[3].y,t[3].z);
  
        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[5].x,t[5].y,t[5].z);
        thd.vertex(t[11].x,t[11].y,t[11].z);

        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[5].x,t[5].y,t[5].z);
        thd.vertex(t[11].x,t[11].y,t[11].z);

        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[11].x,t[11].y,t[11].z);
        thd.vertex(t[3].x,t[3].y,t[3].z);
  
        thd.fill(125,0,0,alpha);            // 3,1,5,12
        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[5].x,t[5].y,t[5].z);
  
        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[12].x,t[12].y,t[12].z);

        thd.vertex(t[5].x,t[5].y,t[5].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[12].x,t[12].y,t[12].z);

        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[12].x,t[12].y,t[12].z);
        thd.vertex(t[5].x,t[5].y,t[5].z);
  
        thd.fill(125,0,0,alpha);            // 0,1,5,9
        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[5].x,t[5].y,t[5].z);
  
        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[9].x,t[9].y,t[9].z);

        thd.vertex(t[5].x,t[5].y,t[5].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[9].x,t[9].y,t[9].z);

        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[9].x,t[9].y,t[9].z);
        thd.vertex(t[5].x,t[5].y,t[5].z);
  
  thd.fill(0,0,255,alpha);            // base 2,1,3,6
  thd.vertex(t[2].x,t[2].y,t[2].z);
  thd.vertex(t[1].x,t[1].y,t[1].z);
  thd.vertex(t[3].x,t[3].y,t[3].z);
  
  thd.vertex(t[2].x,t[2].y,t[2].z);
  thd.vertex(t[1].x,t[1].y,t[1].z);
  thd.vertex(t[6].x,t[6].y,t[6].z);

  thd.vertex(t[3].x,t[3].y,t[3].z);
  thd.vertex(t[1].x,t[1].y,t[1].z);
  thd.vertex(t[6].x,t[6].y,t[6].z);

  thd.vertex(t[2].x,t[2].y,t[2].z);
  thd.vertex(t[6].x,t[6].y,t[6].z);
  thd.vertex(t[3].x,t[3].y,t[3].z);
  
        thd.fill(0,0,125,alpha);            // 2,1,6,8
        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[6].x,t[6].y,t[6].z);
  
        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[8].x,t[8].y,t[8].z);

        thd.vertex(t[6].x,t[6].y,t[6].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[8].x,t[8].y,t[8].z);

        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[8].x,t[8].y,t[8].z);
        thd.vertex(t[6].x,t[6].y,t[6].z);
        
        thd.fill(0,0,125,alpha);            // 2,6,3,13
        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[6].x,t[6].y,t[6].z);
        thd.vertex(t[3].x,t[3].y,t[3].z);
  
        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[6].x,t[6].y,t[6].z);
        thd.vertex(t[13].x,t[13].y,t[13].z);

        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[6].x,t[6].y,t[6].z);
        thd.vertex(t[13].x,t[13].y,t[13].z);

        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[13].x,t[13].y,t[13].z);
        thd.vertex(t[3].x,t[3].y,t[3].z);

        thd.fill(0,0,125,alpha);            // 3,1,6,12
        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[6].x,t[6].y,t[6].z);
  
        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[12].x,t[12].y,t[12].z);

        thd.vertex(t[6].x,t[6].y,t[6].z);
        thd.vertex(t[1].x,t[1].y,t[1].z);
        thd.vertex(t[12].x,t[12].y,t[12].z);

        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[12].x,t[12].y,t[12].z);
        thd.vertex(t[6].x,t[6].y,t[6].z);

  thd.fill(255,255,0,alpha);          // base 0,3,2,7
  thd.vertex(t[0].x,t[0].y,t[0].z);
  thd.vertex(t[3].x,t[3].y,t[3].z);
  thd.vertex(t[2].x,t[2].y,t[2].z);
  
  thd.vertex(t[0].x,t[0].y,t[0].z);
  thd.vertex(t[3].x,t[3].y,t[3].z);
  thd.vertex(t[7].x,t[7].y,t[7].z);

  thd.vertex(t[2].x,t[2].y,t[2].z);
  thd.vertex(t[3].x,t[3].y,t[3].z);
  thd.vertex(t[7].x,t[7].y,t[7].z);

  thd.vertex(t[0].x,t[0].y,t[0].z);
  thd.vertex(t[7].x,t[7].y,t[7].z);
  thd.vertex(t[2].x,t[2].y,t[2].z);
  
        thd.fill(125,125,0,alpha);            // 2,3,7,13
        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[7].x,t[7].y,t[7].z);
  
        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[13].x,t[13].y,t[13].z);

        thd.vertex(t[7].x,t[7].y,t[7].z);
        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[13].x,t[13].y,t[13].z);

        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[13].x,t[13].y,t[13].z);
        thd.vertex(t[7].x,t[7].y,t[7].z);
  
        thd.fill(125,125,0,alpha);            // 0,3,7,11
        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[7].x,t[7].y,t[7].z);
  
        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[11].x,t[11].y,t[11].z);

        thd.vertex(t[7].x,t[7].y,t[7].z);
        thd.vertex(t[3].x,t[3].y,t[3].z);
        thd.vertex(t[11].x,t[11].y,t[11].z);

        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[11].x,t[11].y,t[11].z);
        thd.vertex(t[7].x,t[7].y,t[7].z);
        
        thd.fill(125,125,0,alpha);            // 0,2,7,10
        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[7].x,t[7].y,t[7].z);
  
        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[10].x,t[10].y,t[10].z);

        thd.vertex(t[7].x,t[7].y,t[7].z);
        thd.vertex(t[2].x,t[2].y,t[2].z);
        thd.vertex(t[10].x,t[10].y,t[10].z);

        thd.vertex(t[0].x,t[0].y,t[0].z);
        thd.vertex(t[10].x,t[10].y,t[10].z);
        thd.vertex(t[7].x,t[7].y,t[7].z);
  
  thd.endShape();
}

void show_points() {
  textAlign(CENTER, CENTER);
  
  fill(200,200,200);
  textSize(45);
  push();
  translate(a.x*1.2,a.y*1.2,a.z*1.2);
  text("C (0)",0,0);
  pop();
  push();
  translate(b.x*1.2,b.y*1.2,b.z*1.2);
  text("E (1)",0,0);
  pop();
  push();
  translate(c.x*1.2,c.y*1.2,c.z*1.2);
  text("G (2)",0,0);
  pop();
  push();
  translate(d.x*1.2,d.y*1.2,d.z*1.2);
  text("B (3)",0,0);
  pop();
  
  push();
  translate(e.x*1.15,e.y*1.15,e.z*1.15);
  text("D (4)",0,0);
  pop();
  push();
  translate(f.x*1.15,f.y*1.15,f.z*1.15);
  text("D (5)",0,0);
  pop();
  push();
  translate(g.x*1.15,g.y*1.15,g.z*1.15);
  text("D (6)",0,0);
  pop();
  push();
  translate(h.x*1.15,h.y*1.15,h.z*1.15);
  text("D (7)",0,0);
  pop();
  
  push();
  translate(i.x*1.1,i.y*1.1,i.z*1.1);
  text("F# (8)",0,0);
  pop();
  push();
  translate(j.x*1.1,j.y*1.1,j.z*1.1);
  text("F# (9)",0,0);
  pop();
  push();
  translate(k.x*1.1,k.y*1.1,k.z*1.1);
  text("F# (10)",0,0);
  pop();
  push();
  translate(l.x*1.1,l.y*1.1,l.z*1.1);
  text("F# (11)",0,0);
  pop();
  push();
  translate(m.x*1.1,m.y*1.1,m.z*1.1);
  text("F# (12)",0,0);
  pop();
  push();
  translate(n.x*1.1,n.y*1.1,n.z*1.1);
  text("F# (13)",0,0);
  pop();
}

void draw(){
 background(0);
  shape(thd, 0, 0);
  show_points(); 
  
  // The axes
  stroke(200, 0, 0, 100);
  line(0, 0, 0, -4*s, -4*s, -4*s);
  stroke(255, 50, 50, 100);
  line(0, 0, 0,  4*s,  4*s,  4*s);
  stroke(0, 200, 0, 100);
  line(0, 0, 0, -4*s, -4*s,  4*s);
  stroke(50, 255, 50, 100);
  line(0, 0, 0,  4*s,  4*s, -4*s);
  stroke(0, 0, 200, 100);
  line(0, 0, 0, -4*s,  4*s, -4*s);
  stroke(50, 50, 255, 100);
  line(0, 0, 0,  4*s, -4*s,  4*s);
  stroke(255, 100);
  line(0, 0, 0,  4*s, -4*s, -4*s);
  stroke(150, 100);
  line(0, 0, 0, -4*s,  4*s,  4*s);
}
2 Likes

Hello,

One observation…

You are missing F#(14) o.x, o.y, o.z.

I have used modelX(), modelY() and modelZ() in the past.

I tried it on your missing point:

 push();
  translate(o.x*1.1,o.y*1.1,o.z*1.1);
  text("F# (14)",0,0);
  println(modelX(o.x*1.1,o.y*1.1,o.z*1.1));
  println(modelY(o.x*1.1,o.y*1.1,o.z*1.1));
  println(modelZ(o.x*1.1,o.y*1.1,o.z*1.1));
  pop();

1 Like

@dew point
If you use ortho(); in setup() you will see a far more accurate drawing.
The further you go outside, the more distortion you will get. Also now a neighbour won’t fit. They will overlap each other.
I think the use of an PVector array is quite good; however it doesn’t reduce the amount of code. Probably there is a better way.
Maybe you could build a rotating structure like a Boerdijk–Coxeter helix
It is not only used in art, as showed in picture below, but also in the science of nano structures.
See here.
A tetrahedron helix will also fit in a tube. Would be nice to see.

2 Likes

Ah thanks @noel , using ortho is indeed much nicer!
I understand that neighbours will overlap, but I could (for what I’m trying to represent) get away with choosing 1 of the coordinates and having the other neighbour not be a perfect tetrahedron, does that make sense?
The boerdijk-coxeter helix is not per se interesting for me, as I’m only interested in generating tetrahedra that share at least 1 vertex with an existing central tetrahedron.

1 Like