We build up an SVG image which demonstrates the hypocycloid. We roll a circle of radius r=p/q around the interior of a circle of radius one. Then we put a pen at on the boundary of the rolling circle, and look at the curve drawn.
The following draws the hypocycloid when r=1/3, but the code is written in such a way that it will draw any hypocycloid with a small change to the code.
01: <svg version="1.1" baseProfile="full" 02: xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 03: width="600px" height="600px" viewBox="-300 -300 600 600" 04: onload="setup()"> 05: 06: <script> 07: // <![CDATA[ 08: 09: var svgNS = "http://www.w3.org/2000/svg"; 10: 11: var p=1,q=3,r,period; 12: 13: function setup() { 14: r=1.0*p/q; // radius of small disk 15: period=2*Math.PI*p; // period of curve 16: var n=8*q; // number of points to plot 17: var dt=period/n; // distance between points plotted 18: 19: var i=0; // index of current point 20: var t=0; // current point on curve 21: curve="M 300,0 "; // start of curve 22: while (i<n) { 23: curve += "C "; 24: curve += (curveX(t)+derivativeX(t)/3*dt) + "," + (curveY(t)+derivativeY(t)/3*dt) + " "; 25: i=i+1; 26: t=i*period/n; 27: curve += (curveX(t)-derivativeX(t)/3*dt) + "," + (curveY(t)-derivativeY(t)/3*dt) + " "; 28: curve += curveX(t) + "," + curveY(t) + " "; 29: } 30: document.getElementById("curve").setAttributeNS(null,"d",curve); 31: } 32: 33: function curveX(t) { 34: return 300*((1-r)*Math.cos(t)+r*Math.cos((r-1)*t/r)); 35: } 36: 37: function curveY(t) { 38: return 300*((1-r)*Math.sin(t)+r*Math.sin((r-1)*t/r)); 39: } 40: 41: function derivativeX(t) { 42: return 300*(r-1)*(Math.sin(t)+Math.sin((1-r)*t/r)); 43: } 44: 45: function derivativeY(t) { 46: return 300*(1-r)*(Math.cos(t)-Math.cos((1-r)*t/r)); 47: } 48: 49: // ]]> 50: </script> 51: 52: <circle cx="0" cy="0" r="300" fill="yellow" stroke-width="0px"/> 53: <path id="curve" d="M 0,0" stroke="black" fill="none"/> 54: 55: </svg> 56:
This version adds some code to indicate the values of p and q. We also allow the user to increase and decrease these values.
01: <svg version="1.1" baseProfile="full" 02: xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 03: width="600px" height="600px" viewBox="-300 -300 600 600" 04: fill="black" onload="setup()" 05: font-family="Times, serif" font-size="20px"> 06: 07: <script> 08: // <![CDATA[ 09: 10: var svgNS = "http://www.w3.org/2000/svg"; 11: 12: var p=1,q=3,r,period; 13: 14: function setup() { 15: r=1.0*p/q; // radius of small disk 16: period=2*Math.PI*p; // period of curve 17: var n=8*q; // number of points to plot 18: var dt=period/n; // distance between points plotted 19: 20: var i=0; // index of current point 21: var t=0; // current point on curve 22: curve="M 300,0 "; // start of curve 23: while (i<n) { 24: curve += "C "; 25: curve += (curveX(t)+derivativeX(t)/3*dt) + "," + (curveY(t)+derivativeY(t)/3*dt) + " "; 26: i=i+1; 27: t=i*period/n; 28: curve += (curveX(t)-derivativeX(t)/3*dt) + "," + (curveY(t)-derivativeY(t)/3*dt) + " "; 29: curve += curveX(t) + "," + curveY(t) + " "; 30: } 31: document.getElementById("curve").setAttributeNS(null,"d",curve); 32: } 33: 34: function curveX(t) { 35: return 300*((1-r)*Math.cos(t)+r*Math.cos((r-1)*t/r)); 36: } 37: 38: function curveY(t) { 39: return 300*((1-r)*Math.sin(t)+r*Math.sin((r-1)*t/r)); 40: } 41: 42: function derivativeX(t) { 43: return 300*(r-1)*(Math.sin(t)+Math.sin((1-r)*t/r)); 44: } 45: 46: function derivativeY(t) { 47: return 300*(1-r)*(Math.cos(t)-Math.cos((1-r)*t/r)); 48: } 49: 50: function pAdd() { 51: if (p<q-1) { 52: p=p+1; 53: var pText = document.getElementById("p"); 54: pText.firstChild.nodeValue = p; 55: setup(); 56: } 57: } 58: 59: function pSubtract() { 60: if (p>1) { 61: p=p-1; 62: var pText = document.getElementById("p"); 63: pText.firstChild.nodeValue = p; 64: setup(); 65: } 66: } 67: 68: function qAdd() { 69: q=q+1; 70: var qText = document.getElementById("q"); 71: qText.firstChild.nodeValue = q; 72: setup(); 73: } 74: 75: function qSubtract() { 76: if (q>p+1) { 77: q=q-1; 78: var qText = document.getElementById("q"); 79: qText.firstChild.nodeValue = q; 80: setup(); 81: } 82: } 83: 84: // ]]> 85: </script> 86: 87: <circle cx="0" cy="0" r="300" fill="yellow" stroke-width="0px"/> 88: <path id="curve" d="M 0,0" stroke="black" fill="none"/> 89: 90: <text x="-300" y="-285" fill="black">p=<tspan id="p">1</tspan></text> 91: <text x="-300" y="-265" fill="blue" text-decoration="underline" cursor="pointer" onclick="pSubtract()">subtract one</text> 92: <text x="-300" y="-245" fill="blue" text-decoration="underline" cursor="pointer" onclick="pAdd()">add one</text> 93: <text x="300" y="-285" fill="black" text-anchor="end" >q=<tspan id="q">3</tspan></text> 94: <text x="300" y="-265" fill="blue" text-decoration="underline" cursor="pointer" text-anchor="end" onclick="qSubtract()">subtract one</text> 95: <text x="300" y="-245" fill="blue" text-decoration="underline" cursor="pointer" text-anchor="end" onclick="qAdd()">add one</text> 96: </svg> 97:
This version adds animation of the circle rolling around.
001: <svg version="1.1" baseProfile="full" 002: xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 003: width="610px" height="610px" viewBox="-305 -305 610 610" 004: fill="black" onload="setup()" 005: font-family="Times, serif" font-size="20px"> 006: 007: <script> 008: // <![CDATA[ 009: 010: var svgNS = "http://www.w3.org/2000/svg"; 011: 012: var p=1,q=3,r,period; 013: 014: function setup() { 015: r=1.0*p/q; // radius of small disk 016: period=2*Math.PI*p; // period of curve 017: var n=8*q; // number of points to plot 018: var dt=period/n; // distance between points plotted 019: 020: var i=0; // index of current point 021: var t=0; // current point on curve 022: curve="M 300,0 "; // start of curve 023: while (i<n) { 024: curve += "C "; 025: curve += (curveX(t)+derivativeX(t)/3*dt) + "," + (curveY(t)+derivativeY(t)/3*dt) + " "; 026: i=i+1; 027: t=i*period/n; 028: curve += (curveX(t)-derivativeX(t)/3*dt) + "," + (curveY(t)-derivativeY(t)/3*dt) + " "; 029: curve += curveX(t) + "," + curveY(t) + " "; 030: } 031: document.getElementById("curve").setAttributeNS(null,"d",curve); 032: startAnimation(); 033: } 034: 035: function curveX(t) { 036: return 300*((1-r)*Math.cos(t)+r*Math.cos((r-1)*t/r)); 037: } 038: 039: function curveY(t) { 040: return 300*((1-r)*Math.sin(t)+r*Math.sin((r-1)*t/r)); 041: } 042: 043: function derivativeX(t) { 044: return 300*(r-1)*(Math.sin(t)+Math.sin((1-r)*t/r)); 045: } 046: 047: function derivativeY(t) { 048: return 300*(1-r)*(Math.cos(t)-Math.cos((1-r)*t/r)); 049: } 050: 051: function pAdd() { 052: if (p<q-1) { 053: p=p+1; 054: var pText = document.getElementById("p"); 055: pText.firstChild.nodeValue = p; 056: setup(); 057: } 058: } 059: 060: function pSubtract() { 061: if (p>1) { 062: p=p-1; 063: var pText = document.getElementById("p"); 064: pText.firstChild.nodeValue = p; 065: setup(); 066: } 067: } 068: 069: function qAdd() { 070: q=q+1; 071: var qText = document.getElementById("q"); 072: qText.firstChild.nodeValue = q; 073: setup(); 074: } 075: 076: function qSubtract() { 077: if (q>p+1) { 078: q=q-1; 079: var qText = document.getElementById("q"); 080: qText.firstChild.nodeValue = q; 081: setup(); 082: } 083: } 084: 085: // Global animation variables: 086: var positionsPerPeriod, currentPosition, rolling, point, speed=600, timeout=null; 087: 088: function startAnimation() { 089: currentPosition=0; 090: positionsPerPeriod=p*speed; 091: rolling=document.getElementById("rolling"); 092: point=document.getElementById("point"); 093: rolling.setAttributeNS(null,"r",300*r); 094: if (timeout!=null) { 095: clearTimeout(timeout); 096: } 097: timeout=setTimeout(animationStep,20); 098: } 099: 100: function stopAnimation() { 101: if (timeout!=null) { 102: clearTimeout(timeout); 103: } 104: } 105: 106: function animationStep() { 107: var theta=2*Math.PI*currentPosition/speed; 108: rolling.setAttributeNS(null,"cx",300*(1-r)*Math.cos(theta)); 109: rolling.setAttributeNS(null,"cy",300*(1-r)*Math.sin(theta)); 110: point.setAttributeNS(null,"cx",curveX(theta)); 111: point.setAttributeNS(null,"cy",curveY(theta)); 112: currentPosition=currentPosition+1; 113: if (currentPosition==positionsPerPeriod) { 114: currentPosition=0; 115: } 116: timeout=setTimeout(animationStep,20); 117: } 118: // ]]> 119: </script> 120: <g transform="scale(1,-1)"> 121: <circle cx="0" cy="0" r="300" fill="yellow" stroke-width="0px"/> 122: <circle id="rolling" cx="200" cy="0" r="100" stroke-width="0px" fill="red"/> 123: <path id="curve" d="M 0,0" stroke="black" fill="none"/> 124: <circle id="point" cx="300" cy="0" r="3" fill="blue" stroke-width="0.5px"/> 125: </g> 126: <text x="-300" y="-285" fill="black">p=<tspan id="p">1</tspan></text> 127: <text x="-300" y="-265" fill="blue" text-decoration="underline" cursor="pointer" onclick="pSubtract()">subtract one</text> 128: <text x="-300" y="-245" fill="blue" text-decoration="underline" cursor="pointer" onclick="pAdd()">add one</text> 129: <text x="300" y="-285" fill="black" text-anchor="end" >q=<tspan id="q">3</tspan></text> 130: <text x="300" y="-265" fill="blue" text-decoration="underline" cursor="pointer" text-anchor="end" onclick="qSubtract()">subtract one</text> 131: <text x="300" y="-245" fill="blue" text-decoration="underline" cursor="pointer" text-anchor="end" onclick="qAdd()">add one</text> 132: </svg> 133: