Thursday, 27 April 2017

Pattern making - Spirograph - Javascript - using array of rods

In the previous post we saw how to create two rods and make them rotate relative to each other, with each rod having different speed and different length. In this post we will extend the previous code so as to make more than two rods rotate relative to the previous rod, and we will also trace the trajectory of end point of the last rod as done in previous post.

The following is the code:

HTML Code

<html>
 <title>Rakshit</title>
 <script type="text/javascript" src="a.js"></script>
 <body>
  <canvas id="mycanvas" width="500" height="500"></canvas>
  <canvas id="desingcanvas" width="500" height="500"></canvas>
  <button id="stopButton">Stop</button>
  <button id="resumeButton">Resume</button>
  <button id="restartButton">Restart</button>

 </body>

</html>

Javascript Code
/*
    Taking array of rods.
*/

var width=0;
var height=0;
var interval;

var rods=[];
var drawing=false; // a global variable that keeps track of whether we are drawing or not.

window.onload = function(){
    var cns = document.getElementById("mycanvas");

    width = cns.width;
    height = cns.height;
    var designContext =  document.getElementById("desingcanvas").getContext("2d");
    var context = cns.getContext("2d");
    rod1=Rod.create(250,250,100,0,10.0,"#0000FF");
    rod2=Rod.create(rod1.endx,rod1.endy,70,Math.PI,60.0,"#FF0000");
    rod3=Rod.create(rod2.endx,rod2.endy,50,Math.PI,30.0,"#00FF00");
    rod4=Rod.create(rod3.endx,rod3.endy,20,Math.PI,10.0,"#0F00F0");

    rods.push(rod1);
    rods.push(rod2);
    rods.push(rod3);
    rods.push(rod4);

    interval = setInterval(function(){draw(context,designContext);},1);

    var stopButton = document.getElementById("stopButton");
    stopButton.addEventListener("click",stopButtonClicked,false);
    var resumeButton = document.getElementById("resumeButton");
    resumeButton.addEventListener("click",resumeButtonClicked,false);
    var restartButton = document.getElementById("restartButton");
    restartButton.addEventListener("click",restartButtonClicked,false);

};

function stopButtonClicked()
{
    if(drawing)
    {
        clearInterval(interval);
        drawing=false;
    }
}
function resumeButtonClicked()
{
    if(drawing===false)
    {
        var context = document.getElementById("mycanvas").getContext("2d");
        var designContext =  document.getElementById("desingcanvas").getContext("2d");
        interval = setInterval(function(){draw(context,designContext);},1);
    }
}
function restartButtonClicked()
{
    if(drawing===true)
        clearInterval(interval);
    var canvas1 = document.getElementById("mycanvas");
    var canvas2 = document.getElementById("desingcanvas");
    var context = canvas1.getContext("2d");
    var designContext =  canvas2.getContext("2d");

    context.clearRect(0,0,canvas1.width,canvas1.height);
    designContext.clearRect(0,0,canvas2.width,canvas2.height);

    interval = setInterval(function(){draw(context,designContext);},1);
}

var Rod={
    startx:0,
    starty:0,
    endx:0,
    endy:0,
    length:0,
    angle:0,
    speed:100,
    color:"#000000",
    create:function(startx,starty,length,angle,speed,color)
    {
        var obj=Object.create(this);
        obj.startx=startx;
        obj.starty=starty;
        obj.length=length;
        obj.angle=angle;
        obj.speed=speed/500.0;

        obj.color=color;

        obj.endx=startx+length*Math.cos(angle);
        obj.endy=starty+length*Math.sin(angle);
        return obj;
    },

    update:function(startx,starty)
    {
        this.startx=startx;
        this.starty=starty;
        this.angle+=this.speed;
        if(this.angle>2*Math.PI)
            this.angle=0;
        this.endx=this.startx+this.length*Math.cos(this.angle);
        this.endy=this.starty+this.length*Math.sin(this.angle);
    },

    drawRod:function(context)
    {
        context.strokeStyle = this.color;
        context.lineWidth = 2;
        context.beginPath();
        context.moveTo(this.startx,this.starty);
        context.lineTo(this.endx,this.endy);
        context.stroke();
        context.closePath();
    }

};


function draw(context,designContext)
{
    var n=rods.length; // n is the number of rods.
    drawing=true;
    designContext.strokeStyle="#FF0000";
    designContext.lineWidth=0.25;
    designContext.beginPath();
    designContext.moveTo(rods[n-1].endx,rods[n-1].endy);

    context.clearRect(0,0,width,height);

    for(var i=0;i<n;i++)
    {
        if(i===0)
        {
            rods[i].drawRod(context);
            rods[i].update(rods[i].startx,rods[i].starty);
        }
        else {
            rods[i].drawRod(context);
            rods[i].update(rods[i-1].endx,rods[i-1].endy);
        }
    }

    designContext.lineTo(rods[n-1].endx,rods[n-1].endy);
    designContext.stroke();
    designContext.closePath();
}


The following animation is the illustration of the above mentioned code. You can try changing the length and speed of each rod and generate some really amazing patterns. You can also try adding more rods, or removing some of the rods so as to get something interesting.



Please feel free to comment if have any problem, or if you find any other thing that is interesting in this context. Also feel free to point any mistakes in the code.

Pattern Making - Spirograph - JavaScript

This post contains an illustration of making a spirograph using HTML5 and Javascript. Note that down there, we have three small buttons for you to play with the animation. The concept used here is that I have two rods/arms attached back to back and I'm plotting the trajectory of end-point of the last rod. See the below animation to get a clear idea.



Note that the code mentioned above is not easily extendable but the coming posts in this series will show how you can create an array of rods, and loop over all the rods to track the path traced by the end point of the last rod.
You can always right click and go to view page source to view the code behind construction of the above spirograph, but for the sake of simplicity and cutting off all the dirty code, here is the clean code used to make the above spirograph.
HTML Code
<html>
<title>Rakshit</title>
<script type="text/javascript" src="a.js"></script>
<body>
    <canvas id="mycanvas" width="500" height="500"></canvas>
    <canvas id="desingcanvas" width="500" height="500"></canvas>
    <button id="stopButton">Stop</button>
    <button id="resumeButton">Resume</button>
    <button id="restartButton">Restart</button>

</body>

</html>


Javascript Code
/*
    This program adds two rotating rods, representing robotic arm

    This is a simple program that just adds 2 rods
    and gives the trajectory of the end point of the second rod
*/
var width=0;
var height=0;
var interval;
var rod1,rod2;
var drawing=false; // a global variable that keeps track of whether we are drawing or not.
window.onload = function(){
    var cns = document.getElementById("mycanvas");

    width = cns.width;
    height = cns.height;
    var context = cns.getContext("2d");
    var designContext =  document.getElementById("desingcanvas").getContext("2d");
    rod1=Rod.create(250,250,80,0,10.0,"#0000FF");
    rod2=Rod.create(rod1.endx,rod1.endy,130,Math.PI,80.0,"#FF0000");

    interval = setInterval(function(){draw(context,designContext);},1);

    var stopButton = document.getElementById("stopButton");
    stopButton.addEventListener("click",stopButtonClicked,false);
    var resumeButton = document.getElementById("resumeButton");
    resumeButton.addEventListener("click",resumeButtonClicked,false);
    var restartButton = document.getElementById("restartButton");
    restartButton.addEventListener("click",restartButtonClicked,false);

};

function stopButtonClicked()
{
    if(drawing)
    {
        clearInterval(interval);
        drawing=false;
    }
}
function resumeButtonClicked()
{
    if(drawing===false)
    {
        var context = document.getElementById("mycanvas").getContext("2d");
        var designContext =  document.getElementById("desingcanvas").getContext("2d");
        interval = setInterval(function(){draw(context,designContext,rod1,rod2);},1);
    }
}
function restartButtonClicked()
{
    if(drawing===true)
        clearInterval(interval);
    var canvas1 = document.getElementById("mycanvas");
    var canvas2 = document.getElementById("desingcanvas");
    var context = canvas1.getContext("2d");
    var designContext =  canvas2.getContext("2d");

    context.clearRect(0,0,canvas1.width,canvas1.height);
    designContext.clearRect(0,0,canvas2.width,canvas2.height);

    interval = setInterval(function(){draw(context,designContext,rod1,rod2);},1);
}

var Rod={
    startx:0,
    starty:0,
    endx:0,
    endy:0,
    length:0,
    angle:0,
    speed:100,
    color:"#000000",
    create:function(startx,starty,length,angle,speed,color)
    {
        var obj=Object.create(this);
        obj.startx=startx;
        obj.starty=starty;
        obj.length=length;
        obj.angle=angle;
        obj.speed=speed/500.0;

        obj.color=color;

        obj.endx=startx+length*Math.cos(angle);
        obj.endy=starty+length*Math.sin(angle);
        return obj;
    },

    update:function(startx,starty)
    {
        this.startx=startx;
        this.starty=starty;
        this.angle+=this.speed;
        if(this.angle>2*Math.PI)
            this.angle=0;
        this.endx=this.startx+this.length*Math.cos(this.angle);
        this.endy=this.starty+this.length*Math.sin(this.angle);
    },

    drawRod:function(context)
    {
        context.strokeStyle = this.color;
        context.lineWidth = 2;
        context.beginPath();
        context.moveTo(this.startx,this.starty);
        context.lineTo(this.endx,this.endy);
        context.stroke();
        context.closePath();
    }

};


function draw(context,designContext)
{
    drawing=true;
    designContext.strokeStyle="#FF0000";
    designContext.lineWidth=0.25;
    designContext.beginPath();
    designContext.moveTo(rod2.endx,rod2.endy);

    context.clearRect(0,0,width,height);

    rod1.drawRod(context);
    rod1.update(rod1.startx,rod1.starty);
    rod2.drawRod(context);
    rod2.update(rod1.endx,rod1.endy);

    designContext.lineTo(rod2.endx,rod2.endy);
    designContext.stroke();
    designContext.closePath();
}

Please bear with me as I am new to javascript and comment if you have any new idea or find anything wrong in this post.