class GlassNode
{
int num;
float diam;
float nx, ny, nz;
float angle = 0;
float angle1 = 0;
PVector[] nodes;
// number of nodes, diameter, and which floor it is
GlassNode (int number, float diameter, float f, float ratio, boolean isTruss)
{
num = number;
diam = diameter;
ny = - f * floorHeight * ratio ;
angle = TWO_PI / num;
if (isTruss) {
if (f % 2 == 0) angle1 = -rotAngle; // for the truss structure
}
else {
if (f % 2 != 0) angle1 = rotAngle * ratio; //for the glaze fabrication
}
nodes = new PVector[num+2];
for (int i=0; i<nodes.length; i++) {
nx = diam * cos(angle * i + angle1 ) /2;
nz = diam * sin(angle * i + angle1) /2;
nodes[i] = new PVector(nx, ny, nz);
}
}
void display()
{
stroke(255, 255, 0, 200);
strokeWeight(1.5);
noFill();
for (int i=0; i<nodes.length; i++) {
pushMatrix();
translate(nodes[i].x, nodes[i].y, nodes[i].z);
box(5, 5, 5);
popMatrix();
}
}
}
// the function to calculate the diameter of each floor
void floor_diameter()
{
arcD = floorHeight * relation;
floorDiam = new float[23];
for (int i=0; i<9; i++) {
floorDiam[8-i] = (sqrt(sq(arcD/2.0) - sq(i*floorHeight)) - arcD/2.1) * 2;
floorDiam[8+i] = floorDiam[8-i];
}
for (int i=13; i<floorDiam.length; i++) {
floorDiam[i] = roof_diameter(arcD, floorDiam[12], floorHeight*4, floorHeight*10.5, floorHeight*(i-12));
}
for (int i=1; i<floorDiam.length; i=i+2) {
floorDiam[i] = secondary_diameter(floorDiam[i-1], floorDiam[i+1], rotAngle);
}
}
// the funciont to calculate the new set of diameters on the roof part
// d1=arcD, d2=D16, h1=floorHeight*8, h2=floorHeight*6, h3=floorHeight*(i+1)
float roof_diameter(float d1, float d2, float h1, float h2, float h3)
{
float A = asin(h1/(d1/2));
float B = atan(h2/(d2/2));
float p = sqrt(sq(h2) + sq(d2/2)); // xuan chang
float r3 = (p/2) / sin(PI/2 - A - B);
float X = asin(sin(A) + h3/r3);
float Q = (A + X) / 2;
float diff = h3 * tan(Q);
float d3 = d2 - diff*2;
return d3;
}
// the function to calculate the secondary diameter between two main diameters
// for fabricating of glaze in practice
float secondary_diameter(float da, float dc, float rot)
{
float db = (da + dc) / 2;
db = db / cos(rot);
return db;
}
color col_glass = color(0, 120, 240, 210);
color col_truss = color(220, 20, 180, 200);
float stroke_truss = 1.5;
// the function is to draw the cladding of the glass surface
void glass_Cladding()
{
float [] glassDiam = new float[storey+1];
float glassHeight = floorHeight * 22.0 / storey;
int f = 8*storey/22;
for (int i=0; i<f+1; i++) {
glassDiam[f-i] = (sqrt(sq(arcD/2.0) - sq(i*glassHeight)) - arcD/2.1) * 2;
glassDiam[f+i] = glassDiam[f-i];
}
int t = 12*storey/22;
for (int i=t+1; i<glassDiam.length; i++) {
glassDiam[i] = roof_diameter(arcD, glassDiam[t], glassHeight*4*storey/22, glassHeight*10.5*storey/22, glassHeight*(i-t));
}
for (int i=1; i<glassDiam.length; i=i+2) {
glassDiam[i] = secondary_diameter(glassDiam[i-1], glassDiam[i+1], rotAngle*22.0/storey);
}
strokeWeight(0.8);
stroke(90, 20, 90, 200);
noFill();
fill(col_glass);
glasses = new GlassNode[3];
int nod = nodeNum*storey/22;
for (int i=0; i<storey-2; i=i+2) {
glasses[0] = new GlassNode(nod, glassDiam[i], i, 22.0/storey, false);
glasses[1] = new GlassNode(nod, glassDiam[i+1], i+1, 22.0/storey, false);
glasses[2] = new GlassNode(nod, glassDiam[i+2], i+2, 22.0/storey, false);
for (int j=1; j<=nod; j++) {
beginShape(QUADS);
vertex(glasses[0].nodes[j].x, glasses[0].nodes[j].y, glasses[0].nodes[j].z);
vertex(glasses[1].nodes[j].x, glasses[1].nodes[j].y, glasses[1].nodes[j].z);
vertex(glasses[2].nodes[j].x, glasses[2].nodes[j].y, glasses[2].nodes[j].z);
vertex(glasses[1].nodes[j+1].x, glasses[1].nodes[j+1].y, glasses[1].nodes[j+1].z);
endShape();
beginShape(TRIANGLES);
vertex(glasses[0].nodes[j].x, glasses[0].nodes[j].y, glasses[0].nodes[j].z);
vertex(glasses[0].nodes[j+1].x, glasses[0].nodes[j+1].y, glasses[0].nodes[j+1].z);
vertex(glasses[1].nodes[j+1].x, glasses[1].nodes[j+1].y, glasses[1].nodes[j+1].z);
vertex(glasses[2].nodes[j].x, glasses[2].nodes[j].y, glasses[2].nodes[j].z);
vertex(glasses[2].nodes[j+1].x, glasses[2].nodes[j+1].y, glasses[2].nodes[j+1].z);
vertex(glasses[1].nodes[j+1].x, glasses[1].nodes[j+1].y, glasses[1].nodes[j+1].z);
endShape();
}
}
}
// the function is to draw the truss structure of the building
void truss_Structure()
{
trusses = new GlassNode[3];
for (int i=0; i<20; i=i+2) {
trusses[0] = new GlassNode(nodeNum, floorDiam[i], i, 1.0, true);
trusses[1] = new GlassNode(nodeNum, floorDiam[i+1], i+1, 1.0, true);
trusses[2] = new GlassNode(nodeNum, floorDiam[i+2], i+2, 1.0, true);
if (showNodes) {
trusses[0].display();
trusses[1].display();
trusses[2].display();
}
strokeWeight(stroke_truss);
stroke(col_truss);
noFill();
for (int j=1; j<=nodeNum; j++) {
beginShape(QUADS);
vertex(trusses[0].nodes[j].x, trusses[0].nodes[j].y, trusses[0].nodes[j].z);
vertex(trusses[1].nodes[j].x, trusses[1].nodes[j].y, trusses[1].nodes[j].z);
vertex(trusses[2].nodes[j].x, trusses[2].nodes[j].y, trusses[2].nodes[j].z);
vertex(trusses[1].nodes[j+1].x, trusses[1].nodes[j+1].y, trusses[1].nodes[j+1].z);
endShape();
beginShape(TRIANGLES);
vertex(trusses[0].nodes[j].x, trusses[0].nodes[j].y, trusses[0].nodes[j].z);
vertex(trusses[0].nodes[j+1].x, trusses[0].nodes[j+1].y, trusses[0].nodes[j+1].z);
vertex(trusses[1].nodes[j+1].x, trusses[1].nodes[j+1].y, trusses[1].nodes[j+1].z);
vertex(trusses[2].nodes[j].x, trusses[2].nodes[j].y, trusses[2].nodes[j].z);
vertex(trusses[2].nodes[j+1].x, trusses[2].nodes[j+1].y, trusses[2].nodes[j+1].z);
vertex(trusses[1].nodes[j+1].x, trusses[1].nodes[j+1].y, trusses[1].nodes[j+1].z);
endShape();
}
}
}
// the function is to draw top of the truss
void truss_Top()
{
GlassNode [] top = new GlassNode[3];
for (int i=0; i<3; i++) {
top[i] = new GlassNode(nodeNum, floorDiam[i+20], i+20, 1.0, true);
if (showNodes) top[i].display();
}
strokeWeight(stroke_truss);
stroke(col_truss);
noFill();
for (int i=1; i<top.length; i++) {
if (i % 2 != 0) {
for( int j=0; j<nodeNum; j++) {
line(top[i].nodes[j].x, top[i].nodes[j].y, top[i].nodes[j].z, top[i-1].nodes[j].x, top[i-1].nodes[j].y, top[i-1].nodes[j].z);
if (j > 0) line(top[i].nodes[j].x, top[i].nodes[j].y, top[i].nodes[j].z, top[i-1].nodes[j-1].x, top[i-1].nodes[j-1].y, top[i-1].nodes[j-1].z);
if (j == 0) line(top[i].nodes[j].x, top[i].nodes[j].y, top[i].nodes[j].z, top[i-1].nodes[nodeNum-1].x, top[i-1].nodes[nodeNum-1].y, top[i-1].nodes[nodeNum-1].z);
}
}
else {
for( int j=0; j<nodeNum; j++) {
line(top[i].nodes[j].x, top[i].nodes[j].y, top[i].nodes[j].z, top[i-1].nodes[j].x, top[i-1].nodes[j].y, top[i-1].nodes[j].z);
if (j < nodeNum) line(top[i].nodes[j].x, top[i].nodes[j].y, top[i].nodes[j].z, top[i-1].nodes[j+1].x, top[i-1].nodes[j+1].y, top[i-1].nodes[j+1].z);
if (j == nodeNum-1) line(top[i].nodes[j].x, top[i].nodes[j].y, top[i].nodes[j].z, top[i-1].nodes[0].x, top[i-1].nodes[0].y, top[i-1].nodes[0].z);
}
}
}
}
float k = 4.0; // scale key
float floorHeight = 8.2 * k; // the floor height is 8.2m for actual two floors
float relation = 145.0; // the relation could be justed in term of real relation
float arcD;
int storey = 22; // 22 as default, controlled by UP and DOWN KEYS
int nodeNum = 18; // 18 as default, controlled by LEFT and RIGHT KEYS
float rotAngle = -PI/nodeNum; // rotate or not rotate, controlled by SHIFT KEY
float [] floorDiam;
GlassNode [] trusses;
GlassNode [] glasses;
boolean showTruss = true;
boolean showGlaze = true;
boolean showNodes = true;
boolean mouseControl = false;
////////////////////////////////////////////////////////////////////////////
void setup()
{
size(600, 800, P3D);
lights();
// calculate the diameter of each floor
floor_diameter();
for (int i=0; i<floorDiam.length; i++) {
println(i*2 + " F, diameter: " + floorDiam[i]/k);
}
println("total height: " + floorHeight*22.0/k);
}
//////////////////////////////////////////////////////////////////////////////////////
void draw()
{
background(30);
smooth();
stroke(200, 50);
noFill();
if (mouseControl) {
noSmooth();
rotateX(PI * (height/2-mouseY)/height);
rotateY(-PI * (width/2-mouseX)/width);
}
rotateX(-PI/9);
translate(width/2, height/2+100, 0);
if (mouseControl) rotateY(frameCount*0.01);
else rotateY(frameCount*0.05);
translate(0, 8*floorHeight, 0); // basement coordinate
box(300, 20, 300);
floor_diameter();
// draw the glaze
if (showGlaze) {
glass_Cladding();
}
// draw the truss and nodes of the truss
if (showTruss) {
// draw the top
pushMatrix();
translate(0, -22*floorHeight*0.99, 0);
fill(col_truss);
noStroke();
sphere(15); //floorDiam[22]/2 * 1.5
popMatrix();
truss_Top();
truss_Structure();
}
}
//////////////////////////////////////////////////////////////
void mousePressed()
{
mouseControl = !mouseControl;
}
void keyPressed()
{
if (key == CODED) {
if (keyCode == UP) {
if (storey < 88) storey = storey * 2;
}
else if (keyCode == DOWN) {
if (storey > 22) storey = storey / 2;
}
if (keyCode == LEFT) {
if (nodeNum > 3) nodeNum--;
}
else if (keyCode == RIGHT) {
if (nodeNum < 36) nodeNum++;
}
if (keyCode == CONTROL) {
relation = 145.0;
storey = 88;
nodeNum = 18;
}
rotAngle = -PI/nodeNum;
if (keyCode == SHIFT) {
rotAngle = 0;
}
}
if (key == 't'|| key == 'T') {
showTruss = !showTruss;
}
if (key == 'g'|| key == 'G') {
showGlaze = !showGlaze;
}
if (key == 'n'|| key == 'N') {
showNodes = !showNodes;
}
if (key == 'z'|| key == 'Z') {
relation -= 14.5;
}
else if (key == 'x'|| key == 'X') {
relation += 14.5;
}
println("rotate: "+ degrees(rotAngle) + ", node number: " + nodeNum + ", glaze detail: " + storey);
// println(floors[5].nodes[3]);
// println(floors[6].nodes[3]);
}
This is an example of Parametric Modeling. Use key boards to change the parameters.