Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

An XY Plot in SVG

4.33/5 (3 votes)
26 Jun 2010Zlib3 min read 34.6K   554  
This is a demo plot in HTML and SVG where JavaScript controls SVG.

Introduction

The SVG Demo Plot demonstrates how to modify SVG using HTML forms dynamically. This web page modifies the plot using DOM Level 3, which is an XML transformation standard. I plotted some random functions. One is an x=4 function, another a y=5x-2 function, and another a y=-x^2+5x-2 function. This also complies with the SVG standards written in Jonathon Watt's page. He apparently is a Mozilla developer. I received his guidelines when working with SVG professionally.

Background

The SVG Demo Plot does not yet work on Firefox and Chrome. Both apparently have issues with SVG. For example, this SVG example from W3C works in both Chrome and Opera, but not in Firefox: http://www.w3.org/TR/SVG/images/animate/animMotion01.svg. Only the Opera browser supports JavaScript dynamically, adding lots of elements based on HTML forms. (I thought that the reason was that Firefox and Chrome do not support modifying the root element, but only support modifying the children, as suggested in my blog. However, I was wrong.)

Using the code

When you enter the page 'default.html', you get the following page:

PlotDemo_orig.JPG

You could easily edit the parameters like below:

PlotDemo_90x800.JPG

You could also edit it so that it renders no functions.

PlotDemo_noimage.JPG

I made two HTML frames. I used 40% for the left and 60% for the right frame. Then, besides the form that the user sends for the left frame, a little form would trigger the plot to render for the right frame. The left frame calls JavaScript that would submit information to the form on the right frame after validating that numbers were submitted. Then, once the JavaScript for the right frame gets submitted, it triggers JavaScript (and ECMAScript, which is used for SVG and essentially is JavaScript) to render the plot. It is kind of a chain all right.

There is a need for transformation from XY plot space to SVG space. Because, the top of the screen in SVG space is 0 and the left of the screen is 0. Henceforth, I transformed the plot 25 pixels from the top. (This is the maximum y.) And 50 pixels from the left. (The minimum X is located 50 pixels from the left.) All figures, have the fill #FFF because SVG is all lines and shapes. This could be expressed using the code below for the Y-axis, for example:

JavaScript
function yCoordinateToScreen(y, minY, maxY) {
     var ySpace=yMinCoord-yMaxCoord;
     var yCoords=maxY-minY;
     var svgIndex = (y-minY)*ySpace/yCoords;
     return yMinCoord-svgIndex;
}

All of the object rendering is similar to DOM in XML. I get the document node and the group 'plot', which is where I will insert the XY plot, in SVG, as follows:

JavaScript
var target=window.document.getElementById("PlotData");
svgDoc=target.getSVGDocument();
svgRoot=svgDoc.getElementById("plot");

First this renders the ticks by doing the following on the 'startup' function:

JavaScript
tick = svgDoc.createElementNS(svgns, "line");
tick.setAttributeNS(svgns, "id", "yTickRight"+i);
tick.setAttributeNS(svgns, "x1", xMaxCoord+4);
tick.setAttributeNS(svgns, "y1", yPos);
tick.setAttributeNS(svgns, "x2", xMaxCoord);
tick.setAttributeNS(svgns, "y2", yPos);
tick.setAttributeNS(svgns, "stroke-width", 2);
tick.setAttributeNS(svgns, "stroke", "black");
svgRoot.appendChild(tick);

Now a tick along the right side Y-axis is created. The tick will be 4 pixels long and 2 pixels wide.

Then when the form for the plot is submitted on the right frame by the form on the left frame, a special ordering is done for each of the figures. Because this renders a quadratic function, this needs to render the quadratic function before the other functions. Otherwise, the quadratic function will overlap the elements that were formerly added. (Even if you use a 'polyline' element, that will overlap the other elements that were formerly added as well.) The best way to render a quadratic function is to use a path as follows:

JavaScript
var y_val = y2(xMin);
var xScreenCoord= 0;
var yScreenCoord=0;
var data = "";
if (y_val <= yMax && y_val >= yMin) {
    xScreenCoord=xCoordinateToScreen(xMin, xMin, xMax);
    yScreenCoord=yCoordinateToScreen(y_val, yMin, yMax); 
    data= "M"+xScreenCoord + " " + yScreenCoord + " Q";    
}
for (var j=xMin+1; j <= xMax; j++) {
     y_val = y2(j);
     if (y_val <= yMax && y_val >= yMin) {
        xScreenCoord=xCoordinateToScreen(j, xMin, xMax);
        yScreenCoord=yCoordinateToScreen(y_val, yMin, yMax);
        if(data == "") 
          data= "M"+xScreenCoord + " " + yScreenCoord + " Q";
        else
          data += xScreenCoord + " " + yScreenCoord + " ";
     }
}

drawing3.setAttributeNS(svgns, "d", data);
drawing3.setAttributeNS(svgns, "stroke-width", 1);
drawing3.setAttributeNS(svgns, "stroke", "#00F");
drawing3.setAttributeNS(svgns, "fill", "#FFF");
svgRoot.appendChild(drawing3);

Then this renders the lines. Afterwards, this renders the origins and then the gradients, using a similar method as above. The SVG Plot Demo refreshes the page by ridding the page of the formerly rendered elements in the 'plotCalc' function if the page reloads.

Well, the world is not perfect. But one could at least try to pragmatically solve the problems that the world offers.

License

This article, along with any associated source code and files, is licensed under The zlib/libpng License