Sphere built from triangles: en.wikipedia.org/wiki/geodesic_polyhedron
Log in to post a comment.
const baseShape = 0; // min=0 max=3 step=1 (Icosahedral,Octahedral,Hexahedral,Tetrahedral)//
const frequency = 1; // min=1 max=6 step=1
const splitMethod = 0; // min=0 max=1 step=1 (Edge,Triangle median)//
const vertexDots = 1; // min=0 max=1 step=1 (No,Yes)
const edgeLines = 1; // min=0 max=1 step=1 (No,Yes)
const xAxisRotation = 0; //min=0 max=360 step=1
const yAxisRotation = 0; //min=0 max=360 step=1
const zAxisRotation = 0; //min=0 max=360 step=1
// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(.7);
// Global code will be evaluated once.
const turtle = new Turtle();
//turtle.jump(0,-80);
//turtle.circle(80);
const r = 80;
let ts = new TriSphere(r);
for(let i = 1; i < frequency; i++) {
ts.generateNext();
}
function walk() {
for(let i = 0; i < ts.points.length; i++) {
dot3(ts.points[i][0] * r, ts.points[i][1] * r, ts.points[i][2]);
}
if(edgeLines == 0) return false;
let segPointer = ts.segments.length - 1;
for(let i = 0; i < ts.segments[segPointer].length; i++) {
turtle.jump(projectPoint3(
ts.points[ts.segments[segPointer][i][0]][0] * r,
ts.points[ts.segments[segPointer][i][0]][1] * r,
ts.points[ts.segments[segPointer][i][0]][2]
));
turtle.goto(projectPoint3(
ts.points[ts.segments[segPointer][i][1]][0] * r,
ts.points[ts.segments[segPointer][i][1]][1] * r,
ts.points[ts.segments[segPointer][i][1]][2]
));
}
return false;
}
function dot3(x, y, z, r = 1) {
let pr = projectPoint3(x, y, z);
dot(pr[0], pr[1], 1.5 + z);
}
function projectPoint3(x, y, z) {
return [x * (1 + (z * .15)), y * (1 + (z * .15))];
}
function dot(x, y, r = 1) {
if(vertexDots == 0) return;
let h = turtle.h();
turtle.seth(0);
for(let rr = 0; rr < r; rr+=.1) {
turtle.jump(x, y-(r/2));
turtle.circle(rr);
}
turtle.jump(x,y);
turtle.seth(h);
}
function TriSphere() {
class TriSphere {
constructor() {
this.generate();
}
generate() {
switch(baseShape) {
case 0:
this.generateIcosahedral();
break;
case 1:
this.generateOctahedral();
break;
case 2:
this.generateHexahedral();
break;
case 3:
this.generateTetrahedral();
break;
}
function rot3(ax, ay, az) { return [Math.cos(ax)*Math.cos(ay), Math.cos(ax)*Math.sin(ay)*Math.sin(az)-Math.sin(ax)*Math.cos(az), Math.cos(ax)*Math.sin(ay)*Math.cos(az)+Math.sin(ax)*Math.sin(az),Math.sin(ax)*Math.cos(ay), Math.sin(ax)*Math.sin(ay)*Math.sin(az)+Math.cos(ax)*Math.cos(az), Math.sin(ax)*Math.sin(ay)*Math.cos(az)-Math.cos(ax)*Math.sin(az),-Math.sin(ay), Math.cos(ay)*Math.sin(az), Math.cos(ay)*Math.cos(az)]; }
function multiply3(a3x3, a) { return [(a[0]*a3x3[0])+(a[1]*a3x3[1])+(a[2]*a3x3[2]), (a[0]*a3x3[3])+(a[1]*a3x3[4])+(a[2]*a3x3[5]), (a[0]*a3x3[6])+(a[1]*a3x3[7])+(a[2]*a3x3[8])]; }
let rot = rot3(Math.PI * (zAxisRotation / 180), Math.PI * (yAxisRotation / 180), Math.PI * (xAxisRotation / 180));
for(let i = 0; i < this.points.length; i++) {
this.points[i] = multiply3(rot, this.points[i]);
}
}
generateTetrahedral() {
//top
this.points = [
[0,-1,0] //0
];
//middle
this.points.push([0 , Math.cos(Math.PI / 3), -Math.sin(Math.PI / 3)]); //1
this.points.push([-Math.sin(Math.PI / 3) * Math.sin(Math.PI / 3), Math.cos(Math.PI / 3), Math.cos(Math.PI / 3) * Math.sin(Math.PI / 3)]); //1
this.points.push([Math.sin(Math.PI / 3) * Math.sin(Math.PI / 3) , Math.cos(Math.PI / 3), Math.cos(Math.PI / 3) * Math.sin(Math.PI / 3)]); //1
this.segments = [[
[0, 1], [0, 2], [0, 3], //0 to 2
[1, 2], [2, 3], [3, 1] //3 to 5
]];
this.triangles = [[
[0, 1, 3], [1, 2, 4], [2, 0, 5],
[3, 4, 5]
]];
}
generateHexahedral() {
//top
this.points = [
[0,-1,0] //0
];
//middle
this.points.push([0, 0, -1]); //1
this.points.push([Math.sin(Math.PI / 3), 0, Math.cos(Math.PI / 3)]); //2
this.points.push([-Math.sin(Math.PI / 3), 0, Math.cos(Math.PI / 3)]); //3
//bottom
this.points.push(
[0,1,0] //4
);
this.segments = [[
[0, 1], [0, 2], [0, 3], //0 to 2
[1, 2], [2, 3], [3, 1], //3 to 5
[1, 4], [2, 4], [3, 4] // 6 to 8
]];
this.triangles = [[
[0, 1, 3], [1, 2, 4], [2, 0, 5],
[6, 7, 3], [7, 8, 4], [8, 6, 5]
]];
}
generateOctahedral() {
//top
this.points = [
[0,-1,0] //0
];
//middle
this.points.push([0, 0, 1]); //1
this.points.push([1, 0, 0]); //2
this.points.push([0, 0, -1]); //3
this.points.push([-1, 0, 0]); //4
//bottom
this.points.push(
[0,1,0] //5
);
this.segments = [[
[0, 1], [0, 2], [0, 3], [0, 4], //0 to 3
[1, 2], [2, 3], [3, 4], [4, 1], //4 to 7
[1, 5], [2, 5], [3, 5], [4, 5] // 8 to 11
]];
this.triangles = [[
[0, 1, 4], [1, 2, 5], [2, 3, 6], [3, 0, 7],
[4, 8, 9], [5, 9, 10], [6, 10, 11], [7, 11, 8]
]];
}
generateIcosahedral() {
let yTropics = Math.cos(Math.PI / 3);
let tropicsScale = Math.sin(Math.PI / 3);
//top
this.points = [
[0,-1,0] //0
];
let startRotation = Math.PI / 4;
let shift = 0;
//top tropic 1, 2, 3, 4, 5
for(let i = 0; i < 5; i++) {
this.points.push([
Math.cos(startRotation + shift + (((2 * Math.PI) / 5) * i)) * tropicsScale,
-yTropics,
Math.sin(startRotation + shift + (((2 * Math.PI) / 5) * i)) * tropicsScale
]);
}
shift = Math.PI / 5;
//bottom tropic 6, 7, 8, 9, 10
for(let i = 0; i < 5; i++) {
this.points.push([
Math.cos(startRotation + shift + (((2 * Math.PI) / 5) * i)) * tropicsScale,
yTropics,
Math.sin(startRotation + shift + (((2 * Math.PI) / 5) * i)) * tropicsScale
]);
}
//bottom
this.points.push(
[0,1,0] //11
);
this.segments = [[
[0, 1], [0, 2], [0, 3], [0, 4], [0, 5], //0 to 4
[1, 2], [2, 3], [3, 4], [4, 5], [5, 1], //5 to 9
[1, 6], [6, 2], [2, 7], [7, 3], [3, 8], //10 to 14
[8, 4], [4, 9], [9, 5], [5, 10], [10, 1], // 15 to 19
[6, 7], [7, 8], [8, 9], [9, 10], [10, 6], // 20 to 24
[6, 11], [7, 11], [8, 11], [9, 11], [10, 11] // 25 to 29
]];
this.triangles = [[
[0, 1, 5], [1, 2, 6], [2, 3, 7], [3, 4, 8], [4, 0, 9],
[10, 11, 5], [11, 12, 20], [12, 13, 6], [13, 14, 21], [14, 15, 7],
[15, 16, 22], [16, 17, 8], [17, 18, 23], [18, 19, 9], [19, 10, 24],
[25, 26, 20], [26, 27, 21], [27, 28, 22], [28, 29, 23], [29, 25, 24]
]];
}
generateNext() {
switch(splitMethod) {
case 0:
return this.generateNextByEdge();
break;
case 1:
return this.generateNextByMedian();
break;
}
throw new Error('Undefined splitMethod');
}
generateNextByMedian() {
let currentGen = this.segments.length - 1;
let newSegments = JSON.parse(JSON.stringify(this.segments[currentGen]));
let newTriangles = [];
for(let i = 0; i < this.triangles[currentGen].length; i++) {
let dSegments = this.triangles[currentGen][i];
let oldPoints = getUniquePointsFromTriangle(dSegments, this.segments[currentGen])
let pointInBetween = [
(this.points[oldPoints[0]][0] + this.points[oldPoints[1]][0] + this.points[oldPoints[2]][0]) / 3,
(this.points[oldPoints[0]][1] + this.points[oldPoints[1]][1] + this.points[oldPoints[2]][1]) / 3,
(this.points[oldPoints[0]][2] + this.points[oldPoints[1]][2] + this.points[oldPoints[2]][2]) / 3
];
//pointInBetweenLength for normalization
let pibLength = Math.sqrt( Math.pow(pointInBetween[0], 2) + Math.pow(pointInBetween[1], 2) + Math.pow(pointInBetween[2], 2) )
let pointSequence = this.points.length;
this.points.push([pointInBetween[0] / pibLength, pointInBetween[1] / pibLength, pointInBetween[2] / pibLength]);
let newSegmentIndicesByOldPoint = [];
for(let j = 0; j < oldPoints.length; j++) {
newSegmentIndicesByOldPoint[oldPoints[j]] = newSegments.push([oldPoints[j], pointSequence]) - 1;
}
for(let j = 0; j < dSegments.length; j++) {
newTriangles.push([
dSegments[j],
newSegmentIndicesByOldPoint[newSegments[dSegments[j]][0]],
newSegmentIndicesByOldPoint[newSegments[dSegments[j]][1]]
]);
}
}
this.segments.push(newSegments);
this.triangles.push(newTriangles);
}
generateNextByEdge() {
let currentGen = this.segments.length - 1;
let newSegments = [];
let segmentTranslations = [];
for(let i = 0; i < this.segments[currentGen].length; i++) {
let startPoint = this.segments[currentGen][i][0];
let endPoint = this.segments[currentGen][i][1];
let pointInBetween = [
(this.points[startPoint][0] + this.points[endPoint][0]) / 2,
(this.points[startPoint][1] + this.points[endPoint][1]) / 2,
(this.points[startPoint][2] + this.points[endPoint][2]) / 2
];
//pointInBetweenLength for normalization
let pibLength = Math.sqrt( Math.pow(pointInBetween[0], 2) + Math.pow(pointInBetween[1], 2) + Math.pow(pointInBetween[2], 2) )
let pointSequence = this.points.length;
this.points.push([pointInBetween[0] / pibLength, pointInBetween[1] / pibLength, pointInBetween[2] / pibLength]);
let segmentSequence = newSegments.length;
newSegments.push([startPoint, pointSequence]);
newSegments.push([pointSequence, endPoint]);
segmentTranslations[i] = [segmentSequence, segmentSequence + 1];
}
let newTriangles = [];
for(let i = 0; i < this.triangles[currentGen].length; i++) {
let dSegments = this.triangles[currentGen][i];
let dPoints = [
newSegments[segmentTranslations[dSegments[0]][0]][1],
newSegments[segmentTranslations[dSegments[1]][0]][1],
newSegments[segmentTranslations[dSegments[2]][0]][1]
]
let dNewSegmentIndex = newSegments.length;
newSegments.push([dPoints[0], dPoints[1]]);
newSegments.push([dPoints[1], dPoints[2]]);
newSegments.push([dPoints[2], dPoints[0]]);
let newTrianglesIndex = newTriangles.length;
newTriangles.push([dNewSegmentIndex]);
newTriangles.push([dNewSegmentIndex + 1]);
newTriangles.push([dNewSegmentIndex + 2]);
newTriangles.push([dNewSegmentIndex, dNewSegmentIndex + 1, dNewSegmentIndex + 2]);
let oldPoints = getUniquePointsFromTriangle(dSegments, this.segments[currentGen])
for(let j = 0; j < 3; j++) {
let jsegs = findJoiningSegments(newSegments[dNewSegmentIndex + j], newSegments, oldPoints);
newTriangles[newTrianglesIndex + j].push(jsegs[0]);
newTriangles[newTrianglesIndex + j].push(jsegs[1]);
}
}
this.segments.push(newSegments);
this.triangles.push(newTriangles);
}
}
return new TriSphere();
}
function getUniquePointsFromTriangle(triangle, segments) {
let points = [];
for(let i = 0; i < triangle.length; i++) {
for(let j = 0; j < segments[triangle[i]].length; j++) {
if(!points.includes(segments[triangle[i]][j])) {
points.push(segments[triangle[i]][j]);
}
}
}
return points;
}
function findJoiningSegments(segment, allSegments, toOneOfPoints) {
for(let i = 0; i < toOneOfPoints.length; i++) {
for(let j = 0; j < allSegments.length; j++) {
if(allSegments[j].includes(segment[0]) && allSegments[j].includes(toOneOfPoints[i])) {
for(let k = 0; k < allSegments.length; k++) {
if(allSegments[k].includes(segment[1]) && allSegments[k].includes(toOneOfPoints[i])) {
return [j, k];
}
}
}
}
}
throw new Error('not found');
}
function findSegmentByPoints(points, allSegments) {
for(let i = 0; i < allSegments.length; i++) {
if(allSegments[i].includes(points[0]) && allSegments[i].includes(points[1])) {
return i;
}
}
return false;
}