Search code examples
javascriptcanvassaverestore

Javascript drawing fractals on canvas, issue with recursive save and restore


I am trying to draw about 8 transformations on a canvas and I was using the example from "Sierpniski triangle" to draw my own, but I seem to be struggling whenever I increase the step size to be bigger than 1. Here is a fiddle I've got going: Current code

If you uncomment lines 43 to 49 you would see how it gets broken, but if you decrease the step size in line 14 to be: 1. Then you get a perfect visualization on how it should look like. So the problem is that I've tried multiple methods to save the canvas and restore it, but the fact that I am drawing recursively makes things break.

Another thing, if you uncomment lines 43 to 49 along with line 40 and leave the step size: 2. You get almost a perfect visualization of how it should look like, but the fact that there is an additional flipped shape near every transformation makes it not "perfect".

I have tried HTML5 litten forum, W3S schools and searched along here, but could not find anything similar to it.


Solution

  • I have solved this issue with the following code:

    <html>
    <head>
    	<script type="application/javascript">
    	function draw(val=1) {
    		var canvas = document.getElementById('canvas');
    		if (canvas.getContext) {
    			var ctx = canvas.getContext("2d");
    			ctx.clearRect(0, 0, canvas.width, canvas.height);
    			drawT(); //Trait around the canvas (black border)
    			drawF(val); //draw Fractal
    		}
    		function drawF(step,color=null)
    		{
    			var toDraw=false; //Color management variable to separate 4 transformations
    			if (step > 0)
    			{
    				if(color) { toDraw=true; }
    				step = step-1;
    				ctx.save();
            
    				ctx.transform(0.5, 0, 0, 0.5, 250,0);
    				ctx.rotate(90*Math.PI/180);
    				if(toDraw) { ctx.fillStyle=color; }
    				else { ctx.fillStyle="#FF0000"; color="#FF0000"; }
    	
    				drawF(step, color);
    				ctx.restore();
    
    				ctx.save();
    				ctx.transform(0.5, 0, 0, 0.5, 250, 0);
    				ctx.scale(-1, 1);
    				ctx.rotate(90*Math.PI/180);
    				if(toDraw) { ctx.fillStyle=color; }
    				else { ctx.fillStyle="#00FF00"; color="#00FF00"; }
    
    				drawF(step, color);
    				ctx.restore();
            
    				ctx.save();
    				ctx.transform(0.5, 0, 0, 0.5, 250, 250);
    if(toDraw) { ctx.fillStyle=color; }
    else { ctx.fillStyle="#0000FF"; color="#0000FF"; }
    				
    				drawF(step, color);
    				ctx.restore();
    				
    				ctx.save();
    				ctx.transform(0.2, 0, 0, 0.25, 250, 250);
    				ctx.scale(1,1);
    				ctx.rotate(90*Math.PI/180);
            if(toDraw) { ctx.fillStyle=color; }
            else { ctx.fillStyle="#FFFF00"; color="#FFFF00"; }
            
    				drawF(step, color);
    				ctx.restore();
    			} else {
    				drawShape();
    			}
    		}
    		function drawT() {
    			ctx.beginPath();
    			ctx.moveTo(0, 0);
    			ctx.lineTo(500,0);
    			ctx.lineTo(500,500);
    			ctx.lineTo(300,500);
    			ctx.lineTo(300,400);
    			ctx.lineTo(150,400);
    			ctx.lineTo(150,250);
    			ctx.lineTo(0, 250);
    			ctx.closePath();
    			ctx.stroke();
    		}
    
    		function drawShape() {
    			ctx.beginPath();
    			ctx.moveTo(0,0);
    			ctx.lineTo(500,0);
    			ctx.lineTo(500,500);
    			ctx.lineTo(300,500);
    			ctx.lineTo(300,400);
    			ctx.lineTo(150,400);
    			ctx.lineTo(150,250);
    			ctx.lineTo(0, 250);
    			ctx.closePath();
    			ctx.fill();
    		}
    	}
    	function showVal(newVal){
    		draw(newVal);
    	}
    	</script>
    </head>
    	<body onload="draw();">
    	   <canvas id="canvas" width="500" height="500"></canvas>
    	</body>
    	<input type="range" min="1" max="7" step="1" value="1"
    	oninput="showVal(this.value)" onchange="showVal(this.value)">
    </html>

    Use the slider for transformation proccess.