SVG Maker Project - Loop-de-Loop

This project will continually add path commands (lineto) based on a number sequence. An array of numbers is used repeatedly creating new line segments in the order right, up, left, down. It continues for 50 line segments or until it repeats. When it repeats, it colors the pattern red.

The image to the left is the pattern created by the number sequence 6,6,3,9,8,8,1. Starting at 2 from the left and 8 from the top on a 20x20 grid, the path is drawn first 6 to the right, 6 up, 3 left, 9 down, then 8 right, 8 up, 1 left, then repeating the sequence, 6 down, 6 right, 3 up, 9 left, 8 down, etc. The pattern eventually repeats itself.

The code for the SVG image is shown below. The javascript will be added later. The code defines some style attributes which will be used as class attributes on the SVG elements. It also defines a <clipPath> with id="cp1" that is used to clip any path that falls outside the 20x20 grid.

The viewBox of the image defines a width and height of 550 pixels. A grid will be defined that is 500 by 500 pixels, so to center the grid, a <g> element has a translate transformation of (25,25) to allow for a margin around the grid. The element also has a style attribute the sets a clipping path on the group of elements.

To draw the 20x20 grid, the code draws vertical and horizontal lines through the center of the grid. Using a stroke-width value of 500 pixels, the stroke will be 250 pixels on each side of the line. Using a stroke-dasharray of 0.2,24.6,0.2,0 the stroke starts with a dash of 0.2 pixels, then has a 24.6 pixel gap, then a 0.2 pixel dash, followed by a 0 pixel gap. The total length of the line segment is 25 pixels and is repeated for the stroke-width of 500 pixels. This creates the appearance of a line every 25 pixels. 500/25 = 20 lines both horizontally and vertically. Cool trick, nice grid.

The group also contains a <circle> and <path> element. The circle is given an id="start" and will be used to show the starting point of the Loop-de-Loop. It is placed at the center of the grid for now. The path is given the id="loop" and the d attribute is set to the Loop-de-Loop created by the number sequence 6,6,3,9,8,8,1. When script is added, the d attribute will be overwritten, but if script is turned off in a browser, at least an image will show.

Loop-de-Loop SVG image

<svg width="100%" height="100%" viewBox="0 0 550 550"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">

<style type="text/css"><![CDATA[
.grid {stroke-dasharray:0.2,24.6,0.2,0;stroke-width:500;fill:none;stroke:blue;}
.black {stroke:black;fill:none;}
.red {stroke:black;fill:red;fill-opacity:0.7;fill-rule:evenodd;}
.green {stroke;green;}
.magenta {stroke;magenta;}
]]>
</style>

<defs>
<clipPath id="cp1">
<rect x="-1" y="-1" width="502" height="502" style="fill:white" />
</clipPath>
</defs>

<g transform="translate(25,25)" style="clip-path:url(#cp1);">
<!-- Draw the Grid -->
<line x1="0" y1="250" x2="500" y2="250" class="grid" />
<line x1="250" y1="0" x2="250" y2="500" class="grid" />

<circle id="start" cx="250" cy="250" r="2" style="fill:black;" />

<path id="loop" d="M50,200l150,0l0,-150l-75,0l0,225l200,0l0,-200l-25,0l0,150l150,0l0,-75l-225,0l0,200l200,0l0,-25l-150,0l0,150l75,0l0,-225l-200,0l0,200l25,0l0,-150l-150,0l0,75l225,0l0,-200l-200,0l0,25" style="stroke-width:2px;" class="red" />
</g>

</svg>

Entire Loop-de-Loop code

That's it for the SVG image. Below is the same code with the javascript added in. It will create any Loop-de-Loop that fits on a 20 by 20 grid. Copy and paste the code into a text editor, save it, then open in a browser. Change the "seqs" array of arrays to add or remove number sequences. Set the "maxseq" variable to the number of arrays defined in the seqs variable. Also change the "startxs" and "startys" array variables for where the Loop-de-Loop should start for the number sequence.

<svg width="100%" height="100%" viewBox="0 0 550 550"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">

<script type="application/x-javascript"><![CDATA[
var timeout=250;

var maxseq=4;
var seqs = Array(Array(6,6,18),Array(3,1,6,5,4,2,7,6,4),Array(6,6,3,9,8,8,1),Array(1,1,2,2,6,8,4));
var startxs = Array(13,7,2,6);
var startys = Array(19,5,8,13);

var seq;
var startx;
var starty;

var node;
var path;
var xpos;
var ypos;
var index;
var mult = new Array(1,-1,-1,1);
var cnt;
var count=0;

window.addEventListener("load",function(){init(count);});

function init(idx)
{
seq = seqs[idx];
startx = startxs[idx];
starty = startys[idx];

index = 0;
cnt = 0;

xpos = startx;
ypos = starty;

node = document.getElementById("start");
node.setAttribute("cx",startx*25);
node.setAttribute("cy",starty*25);

node = document.getElementById("loop");
node.setAttribute("class","black");
path = "M"+25*xpos+","+25*ypos;
node.setAttribute("d",path);

setTimeout(Draw,100);
}

function Draw()
{
var x=0;
var y=0;

if(cnt%2) {y = seq[index] * mult[cnt%4];}
else {x = seq[index] * mult[cnt%4];}

xpos += x;
ypos += y;

path += "l"+x*25+","+y*25;
node.setAttribute("d",path);

index++;
if(index>=seq.length) index = 0;

if(xpos==startx && ypos==starty && index==0)
{
node.setAttribute("class","red");
count++;
if(count>=maxseq) count=0;
setTimeout(function(){init(count);},3000);
return;
}

cnt++;
if(cnt<50) setTimeout(Draw,timeout);
}
]]>
</script>

<style type="text/css"><![CDATA[
.grid {stroke-dasharray:0.2,24.6,0.2,0;stroke-width:500;fill:none;stroke:blue;}
.black {stroke:black;fill:none;}
.red {stroke:black;fill:red;fill-opacity:0.7;fill-rule:evenodd;}
.green {stroke;green;}
.magenta {stroke;magenta;}
]]>
</style>

<defs>
<clipPath id="cp1">
<rect x="-1" y="-1" width="502" height="502" style="fill:white" />
</clipPath>
</defs>

<g transform="translate(25,25)" style="clip-path:url(#cp1);">
<!-- Draw the Grid -->
<line x1="0" y1="250" x2="500" y2="250" class="grid" />
<line x1="250" y1="0" x2="250" y2="500" class="grid" />

<circle id="start" cx="250" cy="250" r="2" style="fill:black;" />

<path id="loop" d="M50,200l150,0l0,-150l-75,0l0,225l200,0l0,-200l-25,0l0,150l150,0l0,-75l-225,0l0,200l200,0l0,-25l-150,0l0,150l75,0l0,-225l-200,0l0,200l25,0l0,-150l-150,0l0,75l225,0l0,-200l-200,0l0,25" style="stroke-width:2px;" class="red" />
</g>

</svg>

When the image loads, the init() function is called, passing the count variable. The init function expects one variable and will call it "idx" in the function. It initializes variables seq, startx, and starty to the array indexed by the variable "idx", which it was passed when called. It positions the circle with id="start" by setting the "cx" and "cy" attributes to the startx and starty variables, but must multiply those values by 25 since our grid lines are 25 pixels apart. The function also initializes the "path" variable with a moveto command to position the start at the startx and starty coordinates, multiplied by 25 pixels, then sets "d" attribute of the <path> element with id="loop". Lastly, it sets a timer to call the draw() function after 100 milliseconds.

The "cnt" variable is incremented everytime a line segment is added to the path. After 50, it quits drawing line segments if it hasn't repeated yet. The modulus operation (%) is a remaindering function and is used in the code to provide a 0 & 1 value (cnt%2), which is true if 1 and false if 0. It will determine if the path should be drawn in the x-direction (0) or y-direction (1). The modulus operation is also used to provide a 0, 1, 2, or 3 index (cnt%4) into the "mult" array variable. The array contains 1, -1, -1, 1, which provides the right, up, left, down movement. Remember that negative values for y go up and positive values of y go down.

The x or y position is calculated from the number sequence array item indexed by the variable "index" and mutiplied by 25 pixels then multiplied by 1 or -1 depending on which movement is required next. The absolute coordinates of the end of the line segment is determined by adding x to xpos and y to ypos variables, since x and y are relative values from the end of the last drawn line segment. The "path" variable has a relative "lineto" (lowercase l) command appended to it using the x and y variables and multiplying them by 25 pixels.

After updating the path which draws the latest line segment, it increments the "index" variable to reference the next number in the sequence, starting over at 0 if past the end of the array. Then checks to see if the pattern is at the repeating point (xpos,ypos) = (startx,starty) and index=0, which indicates first number in the number sequence array. If so, the pattern will repeat, so the code indicates a repeating pattern by setting the <path> element's "class" attribute to "red" which fills the pattern red using the evenodd rule (see the W3C specification for a defintion).

The "count" variable is used to count the number sequences drawn and advances the code to use the next array of number sequences. The code sets a timer to call the init() function after 3000 milliseconds (3 seconds) passing the init() function the next index (count variable) to draw.

Should a non-repeating number sequence be drawn, the code stops drawing after 50 line segments have been added to the path command. If the pattern has not repeated yet and the number of line segments is less than 50, the next line segment is added by setting a timer to call the draw function after 250 milliseconds as set in the "timeout" variable.

SVG Maker Projects - coding SVG images - at STEAMcoded.org

STEAMcoded.org STEAMcoded.org