SVG Maker Project - Make Polygons

This project makes an SVG image that will build a polygon using javascript. The number of sides to the polygon is determined by setting a javascript variable. Change the numsides variable in the javascript using a text editor, save the .svg file, open in a browser. Then click on the View Polygon Path link to see the <path> command that created it. Copy and paste the <path> command to another SVG image. Polygon can be easily resized using a scale transformation.

The image to the left is a 6 sided polygon. Also known as a hexagon.

The <path> element below draws the hexagon. It has an id="s1" and the style properties that for that id are defined in the <style> container with the line that starts with #s1, the preceding # sign refers to an element with that id, so the hexagon is filled with the color red.

Additionally, the code has 2 <text> elements for the title and what will be a clickable point to view the path command. The <text> element with id="title" contains a <tspan> element which is a way to change attributes of only a part of the total text contained. In this case, a new id="nsides" is defined. This id will be used in javascript to update the number of sides of the polygon.

The <text> element with id="viewcmd" contains a <style> attribute that sets the cursor property to pointer. This changes the cursor to a finger pointer to indicate a clickable point instead of the default text cursor when hovering over the text. This text will become a clickable point when we add javascript.

Code for Hexagon

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

<style type="text/css"><![CDATA[
.polygon {fill:none;stroke:black;stroke-width:1px;}
#title {font-family:Arial;font-size:10px;fill:navy;stroke:none;font-weight:bold;text-anchor:middle;}
#s1 {fill:red;}
#viewcmd {font-family:Arial;font-size:10px;fill:white;stroke:none;text-anchor:middle;}
]]>
</style>

<g class="polygon" transform="translate(10,17)">
<text id="title" x="100" y="-6">Polygon Maker - <tspan id="nsides1">6</tspan> Sides</text>
<path id="s1" d="M100,0L186.6,50L186.6,150L100,200L13.4,150L13.4,50L100,0" />
<text id="viewcmd" x="100" y="100" style="cursor:pointer;">View Path Command</text>
</g>

</svg>

Adding Javascript

The javascript below will create and draw a polygon with the number of sides determined by the variable, numsides, which defaults to 8.

The javascript is different from other projects in that it contains a wrapper variable, Polygon which is set to a new function. The code for the Polygon function is contained within the {} braces and variables declared within the {} are not accessible outside the wrapper. This is good practice and should be done for all javascipt code. Also, note that the function ending bracket is followed by a function declaration (), i.e. }();.

The first thing the code does is declare a numsides variable and set it to 8, then adds an event listener to call the init() function when the image is loaded. The init() function just calls the Draw() function at this point. The init() function will be added to after understanding this code.

The Draw() function will determine an angle based on the number of sides. The angle is from the first point which is on the y-axis to the end of the line segment for the first side of the polygon. Next, the code will update the title at the top of the image to reflect the number of sides. It gets the element with id="nsides", which is a <tspan> element, then updates the nodeValue for the first child element.

Next, the code creates what will be the "d" attribute for the <path> element that will draw the polygon. It does that by initializing a path variable to moveto (100,0). The polygon will be drawn on a grid 200 pixels square, so (100,0) is at the top center.

A "for loop" will loop based on the number of sides set in variable, numsides. It will add a lineto command to the path variable for the next line segment of the polygon, then loop until all sides have been added.

The code uses the angle to determine the coordinates of the end of the line segment. This is simple trigonometry and uses the javascript Math object's sin and cos functions. Those functions take the angle in radians, so the angle has to be converted from degrees to radians. That is done by mutiplying degrees by radians/degree - which is PI/180. The javascript Math object defines PI so the code uses that as Math.PI.

The y-axis will be at x=100 pixels in our 200 pixel square grid. The origin will be at (100,100). The polygon starts at point (100,0), so a line to the origin is 100 pixels long. Rotating that line by the angle (360/numsides), will still have the same length, 100. That is the hypotenuse of a triangle from the origin to the point to be calculated, back to the y-axis, then back to the origin. The sin function returns a ratio of the opposite side of the triangle to the hypotenuse when the hypotenuse is 1. Since the hypotenuse is 100, the sin function's return value must be mutiplied by 100. And, since the origin is not at 0, but at 100 on the grid, 100 must be added. That is how the x-coordinate is determined.

Similarly, the y-cordinate is calculated using the cos function. The cos function returns a ratio of the adjacent side of the triangle to the hypotenuse when the hypotenuse is 1. Since the hypotenuse is 100, the cos function's return value must be mutiplied by 100. The adjacent side of the triangle is from the origin along the y-axis. The origin is at (100,100), so the y-coordinate is determined by subtracting the value (100*cos(angle)) from 100. A bit confusing but if it is drawn out on paper, it makes sense.

Once the x and y coordinates are determined, adding that point to the path is simple. Just add the text Lx,y where the values of x and y are calculated. Note that the calculation returns many decimal places. To keep the SVG code clean, the x and y values are rounded using the Math object's round function. The round function rounds to the nearest whole number, so the code multiplies the value by 10 first, then divides by 10 after to get 1 decimal place of resolution.

Before looping for the next line segment, the angle is increased by the original angle (360/numsides) and the calculation and path variables are updated until all sides have been done. The path variable has a lineto command for each line segment of the polygon.

All that is left is to update the SVG <path> element's "d" attribute, which is done in the last 2 lines of code in the Draw() fucnction.

<script type="application/x-javascript"><![CDATA[
Polygon = new function()
{
var numsides = 8;

window.addEventListener("load", init);

function init()
{
Draw();
}

function Draw()
{
var i;
var node;
var angle = 360/numsides;

node = document.getElementById("nsides1");
node.firstChild.nodeValue = numsides;

var path = "M100,0";
for(i=0;i<numsides;i++)
{
var radians = Math.PI/180*angle;

var x = 100+100*Math.sin(radians);
var y = 100-100*Math.cos(radians);

path += "L"+Math.round(x*10)/10+","+Math.round(y*10)/10;

angle += 360/numsides;
}

node = document.getElementById("s1");
node.setAttribute("d",path);
}
}();
]]>
</script>

Adding to the Javascript

Before finishing with the code, a couple of nice features are added. A function to display the path command that was calculated, and the ability to rotate the polygon to a more familiar way of looking at it. The additions to the code are highlighted in yellow.

The image on the left shows 2 octagons, but the top one is not the familiar way of looking at an octagon. The code below allows rotating any of the polygons created by 1/2 of the angle calculated based on the number of sides of the polygon. Setting the variable, rotate, to "true" will add a rotate transformation to the <path> element that draws the polygon. In the Draw() function, an "if(rotate)" statement is added that, when true, gets the element with id="s1" and sets its transform attribute to rotate the element by angle/2 degrees about point (100,100).

Adding the ability to view the path command requires a function to do that and a way to call the function. In the init() function, which is called when the image loads, the code gets the element with id="viewcmd", which is the <text> element that displays the text "View Path Command". An event listener is added to the element to call the ShowPathCommand() function when clicked on.

The ShowPathCommand() function will create the text of the <path> element and store it in variable, cmd. It builds the text of a path element, then adds the "d" and "transform" attributes by getting them from the SVG document. Once the path command is built, it is displayed using the javascript alert() function. The text displayed in the alert box can be copied and pasted into another SVG file, which is how the image to the right was created.

<script type="application/x-javascript"><![CDATA[
Polygon = new function()
{
var numsides = 8;
var rotate = true;

window.addEventListener("load", init);

function init()
{
var node = document.getElementById("viewcmd");
node.addEventListener("click",ShowPathCommand);

Draw();
}

function Draw()
{
var i;
var node;
var angle = 360/numsides;

node = document.getElementById("nsides1");
node.firstChild.nodeValue = numsides;

if(rotate)
{
node = document.getElementById("s1");
node.setAttribute("transform","rotate("+angle/2+",100,100)");
}

var path = "M100,0";
for(i=0;i<numsides;i++)
{
var radians = Math.PI/180*angle;

var x = 100+100*Math.sin(radians);
var y = 100-100*Math.cos(radians);

path += "L"+Math.round(x*10)/10+","+Math.round(y*10)/10;

angle += 360/numsides;
}

node = document.getElementById("s1");
node.setAttribute("d",path);
}

function ShowPathCommand()
{
var cmd="<path d=\"";
var node = document.getElementById("s1");
cmd += node.getAttribute("d")+"\"";
if(node.hasAttribute("transform"))
cmd += " transform=\""+node.getAttribute("transform")+"\"";

cmd += " />";

alert(cmd);
}
}();
]]>
</script>

Entire code for the Polygon Maker

The code below is the entire code for the Polygon Maker with all the features described above. Copy and paste the code into your text editor, save the file with a .svg extension, and open it in a browser. Changing the "numsides" and "rotate" variables to make different polygons. The rotate variable should be "true" or "false", but can also be the numbers 0 or 1, where 0 is false and 1 is true. Copy and paste the path command to another SVG image to see just the polygon. Of course, there is no checking in the code, so number of sides should not be less than 3 and anything greater than 50 or so will just look like a circle. Add those checks to the Draw() function and limit the number of sides allowed.

Each polygon is placed on a 200 pixels square grid, so use a viewBox of at least "0 0 200 200". If you want to display 2 of them side by side, increase the viewBox attribute width by 200 and use a transform="translate(200,0) on the second one. Note that transformations occur right to left as listed in the transform attribute, so if the path command has a transform attribute that rotates the polygon, place the translate transformation to the left of the rotate transform with a blank space between the to transformations.

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

<script type="application/x-javascript"><![CDATA[
Polygon = new function()
{
var numsides = 8;
var rotate = true;

window.addEventListener("load", init);

function init()
{
var node = document.getElementById("viewcmd");
node.addEventListener("click",ShowPathCommand);

Draw();
}

function Draw()
{
var i;
var node;
var angle = 360/numsides;

node = document.getElementById("nsides1");
node.firstChild.nodeValue = numsides;

if(rotate)
{
node = document.getElementById("s1");
node.setAttribute("transform","rotate("+angle/2+",100,100)");
}

var path = "M100,0";
for(i=0;i<numsides;i++)
{
var radians = Math.PI/180*angle;

var x = 100+100*Math.sin(radians);
var y = 100-100*Math.cos(radians);

path += "L"+Math.round(x*10)/10+","+Math.round(y*10)/10;

angle += 360/numsides;
}

node = document.getElementById("s1");
node.setAttribute("d",path);
}

function ShowPathCommand()
{
var cmd="<path d=\"";
var node = document.getElementById("s1");
cmd += node.getAttribute("d")+"\"";
if(node.hasAttribute("transform"))
cmd += " transform=\""+node.getAttribute("transform")+"\"";

cmd += " />";

alert(cmd);
}
}();
]]>
</script>

<style type="text/css"><![CDATA[
.polygon {fill:none;stroke:black;stroke-width:1px;}
#title {font-family:Arial;font-size:10px;fill:navy;stroke:none;font-weight:bold;text-anchor:middle;}
#s1 {fill:red;}
#viewcmd {font-family:Arial;font-size:10px;fill:white;stroke:none;text-anchor:middle;}
]]>
</style>

<g class="polygon" transform="translate(10,17)">
<text id="title" x="100" y="-6">Polygon Maker - <tspan id="nsides1">6</tspan> Sides</text>
<path id="s1" d="M100,0L186.6,50L186.6,150L100,200L13.4,150L13.4,50L100,0" />
<text id="viewcmd" x="100" y="100" style="cursor:pointer;">View Path Command</text>
</g>

</svg>

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

STEAMcoded.org STEAMcoded.org