SVG and Javascript for interactive web graphics
This is something that I did quite sometime back. I hadn’t written a post about it at that time. So I thought it maybe worth the time to write a little about this.
I was handed the responsibility to come up with a front-end technology for developing a cool online game. Initially we thought of using flash but then I remembered about SVG from my earlier days when we were creating an application to convert CGM graphics to SVG so that the images and image fragments could be indexed and searched. If you are not familiar, SVG is a language for describing 2 dimensional graphics in XML. SVG is really “the” way for interactive web graphics. And after W3C came up with SVG Tiny more and more smart mobile phones have started supporting SVG as a native format for their browsers. For example – the mobile Safari browser in iPhone has native support for SVG.
One of the requirements of the game was to allow the user to draw polygons on a canvas. And then the application had to determine whether a graphic object say, a circle, is inside a polygon that is drawn by the user.
In order to give my team with a headstart I created a little prototype which lets a user click on a canvas to create a polygon. It assumes that all points are on the same polygon until the “Complete Poly” button is clicked. More polygons can then be created by clicking on different points on the canvas and then clicking the “Complete Poly” button. A red circle can be dragged to one of the polygons. And on clicking “Where?” it prints out inside which polygon the red circle is. Since this was just a prototype, I created the Javascript functions inside the SVG document itself.
Interactivity is achieved by manipulating the SVG DOM at runtime by Javascript. It’s really the same as manipulating the HTML DOM with Javascript.
Here is the SVG fragment that I used for my prototype:
<rect width="100%" height="100%" style="fill:blue;stroke:black;stroke-width:5;fill-opacity:0.1;stroke-opacity:0.9" />
<text x="15" y="50" font-family="Verdana" font-size="14" fill="black"> Complete Poly</text>
<text x="155" y="50" font-family="Verdana" font-size="16" fill="black"> Where? </text>
<rect id="button1" x="10" y="30" width="110" height="30" style="fill:green;stroke:black;stroke-width:3;fill-opacity:0.7;stroke-opacity:0.9" onclick="completepoly(evt)" />
<rect id="button2" x="150" y="30" width="70" height="30" style="fill:green;stroke:black;stroke-width:3;fill-opacity:0.7;stroke-opacity:0.9" onclick="in_or_out(evt)" />
<circle id="circ2" cx="700" cy="70" r="20" fill="red" />
Here is my Canvas. As you see it has got three rectangles. The first one is the canvas on which the polygons are drawn. On the other two I place two text nodes – “Complete Poly” and “Where?”. I use these rectangles as buttons by creating handlers for them when they are clicked.
When users click on the canvas, those points serve as the vertices of the polygon they want to draw. When they click the rectangle with id – “button1″, the polygon is complete and the subsequent clicks on the canvas are taken to be the vertices of the next polygon.
Here is how the Javascript function to add the vertices onto the polygon and draw them looks like:
function add(evt){
if (evt.target.getAttribute("id") =="button1" || evt.target.getAttribute("id") =="button2" || isDrawingDone == 1)
return
points = points + evt.clientX + ","+ evt.clientY + " "
indices += 1;
if(indices > 2)
{
var target = evt.target
if ( window.svgDocument == null )
svgDocument = evt.target.ownerDocument;
var shape = svgDocument.createElementNS(xmlns, "polygon");
shape.setAttributeNS(null, "id", "poly"+polyindx);
shape.setAttributeNS(null, "points", points);
shape.setAttributeNS(null, "fill", "none");
shape.setAttributeNS(null, "stroke", "red");
shape.setAttributeNS(null, "stroke-width", "3");
if(polyindx > 0 )
{
for (var indx = 0;polyindx >= indx;indx++)
{
var polyid = "poly"+indx;
var polyg = document.getElementById(polyid);
if(polyg != null)
{
svgDocument.documentElement.removeChild(polyg);
}
}
}
svgDocument.documentElement.appendChild(shape);
polyindx +=1;
}
As you would see from the code that each time the user clicks on the canvas, I’m drawing a polygon with all the available vertices. But I also want to erase off the previous polygons that I had drawn earlier. This is done by following a naming convention, so that the finder looks incrementally through the ids and deletes them. If I didn’t delete those polygons it would have looked like this:

But erasing the polygons poses a new problem. When I’m done with the first polygon, and I start drawing the new polygon, I want the first one to stay. So I cheat here and rename the last polygon I created breaking away from the naming convention I was following so that the finder does not find this one while trying to delete them.
And Finally when the user clicks “Complete Poly”, the following function is executed:
function completepoly(evt)
{
var polyidindx = polyindx-1;
var polyg = document.getElementById("poly"+ polyidindx);
if(polyg != null)
{
currpolyindx = polyidindx*11;
polyg.setAttributeNS(null, "id", "region"+currpolyindx);
}
points = "";
}
And finally, the function – in_or_out determines inside which polygon the circle lies:
function in_or_out(evt)
{
isDrawingDone = 1;
if(document.getElementById("txt1") != null)
{
var txtnode = document.getElementById("txt1");
Root.removeChild(txtnode);
}
var shape = document.getElementById("circ2");
var polygons = document.getElementsByTagNameNS(xmlns, "polygon");
var indx = 0;
var foundflag = 0;
var numpolygons = polygons.length;
var data="Outside";
for(var indx=0; numpolygons>indx;indx++)
{
var points = polygons.item(indx).getAttribute("points");
var parray = points.split(" ");
var numvertices = parray.length;
var vertx = new Array();
var verty = new Array();
var ix = 0;
while(numvertices > ix)
{
vertx.push(parray[ix].split(",")[0]);
verty.push(parray[ix].split(",")[1]);
ix=ix+1;
}
if ( isPointinPoly(numvertices, vertx, verty, shape.getAttribute("cx"), shape.getAttribute("cy")))
{
data = document.createTextNode("Inside Region =>" + indx);
foundflag = 1;
break;
}
else
{
data = document.createTextNode("Outside All Regions");
}
}
var text = document.createElementNS(xmlns, "text");
text.setAttributeNS(null, "font-size", "12pt");
text.setAttributeNS(null, "x", 30);
text.setAttributeNS(null, "y", 500);
text.setAttributeNS(null, "id", "txt1");
text.appendChild(data);
Root.appendChild(text);
return;
}
Pretty simple, isn’t it? This, in short was the base on which the Innovation Game – Prune The Product Tree was built.
