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; }