|
|
Consider the circular pie chart rendered in Figure 15.1.
Figure 15.1: A circular pie chart.
The SVG document in Listing 15.1 demonstrates how to draw a circular pie chart.
Listing 15.1 circularPieChart1.svg
<?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg width="100%" height="100%" onload="init(evt)" xmlns="http://www.w3.org/2000/svg"> <script type="text/ecmascript"> <![CDATA[ var width = 800.; var height = 500.; var basePointX = 300.; var basePointY = 150.; var currentX = 0.0; var currentY = 0.0; var offsetX1 = 0.0; var offsetY1 = 0.0; var offsetX2 = 0.0; var offsetY2 = 0.0; var radius = 100.; var angleSum1 = 0.; var angleSum2 = 0.; var vertexCount = 8.; var xPts = Array(vertexCount); var yPts = Array(vertexCount); var angles = Array(vertexCount); var pointPath = ""; var circleColors = ['red','green', 'blue','yellow']; var colorCount = 4.; var pieNode = null; var svgDocument = null; var target = null; var gcNode = null; function init(event) { target = event.getTarget(); svgDocument = target.getOwnerDocument(); gcNode = svgDocument.getElementById("gc"); initializeChart(); drawChart(); } function initializeChart() { angles[0] = 30.; angles[1] = 60.; angles[2] = 45.; angles[3] = 45.; angles[4] = 20.; angles[5] = 30.; angles[6] = 50.; angles[7] = 80.; } // initializeChart function drawChart() { for(var v=0; v<vertexCount; v++) { angleSum2 = angleSum1 + angles[v]; offsetX1 = radius*Math.cos( angleSum1*Math.PI/180); offsetY1 = radius*Math.sin( angleSum1*Math.PI/180); offsetX2 = radius*Math.cos( angleSum2*Math.PI/180); offsetY2 = radius*Math.sin( angleSum2*Math.PI/180); currentX = basePointX+offsetX2; currentY = basePointY-offsetY2; // the vertical offset must be subtracted, // so we need to "flip" the sign of offsetY1 offsetY1 *= -1; pointPath = "M"+basePointX+","+basePointY; pointPath += " l"+offsetX1+","+offsetY1; pointPath += " A"+radius+","+radius+" 0 0 0 "; pointPath += currentX+","+currentY; pointPath += " L"+basePointX+","+ basePointY+"z"; fillColor = "fill:"+ circleColors[v%colorCount]; pieNode = svgDocument.createElement("path"); pieNode.setAttribute("d", pointPath); pieNode.setAttribute("style",fillColor); gcNode.appendChild(pieNode); angleSum1 += angles[v]; } } // drawPattern ]]> </script> <!-- ============================ --> <g transform="translate(10,10)"> <rect x="0" y="0" width="800" height="500" fill="none" stroke="none"/> </g> </svg>
The SVG code in Listing 15.1 starts with a block of global ECMAScript variables, followed by the ECMAScript function init() that initializes the variables svgDocument, target, and gcNode. The init() function invokes the ECMAScript function initializeChart(), which assigns a value (measured in degrees) to each sector of the pie chart as listed below:
function initializeChart() { angles[0] = 30.; angles[1] = 60.; angles[2] = 45.; angles[3] = 45.; angles[4] = 20.; angles[5] = 30.; angles[6] = 50.; angles[7] = 80.; } // initializeChart
The last line of the init() function invokes drawChart(), which performs all the necessary calculations for determining the coordinates of the circular pie sectors.
The first portion of drawChart() updates the current cumulative angle and then computes the x-coordinates and the y-coordinates of the two 'end points' (of the current circular arc) that lie on the circumference of the outer circle:
angleSum2 = angleSum1 + angles[v]; offsetX1 = radius*Math.cos(angleSum1*Math.PI/180); offsetY1 = radius*Math.sin(angleSum1*Math.PI/180); offsetX2 = radius*Math.cos(angleSum2*Math.PI/180); offsetY2 = radius*Math.sin(angleSum2*Math.PI/180);
The argument to the functions Math.cos() and Math.sin() is interpreted as a radian value; therefore, you must multiply the variables angleSum1 and angleSum2 (which represent degree values) by the constant Math.PI/180.
Next, drawChart() constructs the string pointPath that will be used in an SVG path element:
currentX = basePointX+offsetX2; currentY = basePointY-offsetY2; // the vertical offset must be subtracted, // so we need to "flip" the sign of offsetY1
offsetY1 *= -1;
pointPath = "M"+basePointX+","+basePointY; pointPath += " l"+offsetX1+","+offsetY1; pointPath += " A"+radius+","+radius+" 0 0 0 "; pointPath += currentX+","+currentY; pointPath += " L"+basePointX+","+ basePointY+"z";
The following line of code emphasizes the fact that the vertical offset must be subtracted or its sign must be inverted:
offsetY1 *= -1;
An alternative would be to embed the negative sign as follows:
pointPath += " l"+offsetX1+","+(-offsetY1);
Both techniques have advantage and disadvantages; select one that suits your needs and then use it consistently in your code.
The last portion of drawChart() dynamically creates an SVG path element and assigns values to the attributes path and style:
fillColor = "fill:"+circleColors[v%colorCount]; pieNode = svgDocument.createElement("path"); pieNode.setAttribute("d", pointPath); pieNode.setAttribute("style",fillColor); gcNode.appendChild(pieNode); angleSum1 += angles[v];
Notice how the current 'color index' is determined in the following line of code:
fillColor = "fill:"+circleColors[v%colorCount];
The term v%colorCount ensures that the index into the color array circleColors will not exceed the number of elements in the array. You will see this technique (as well as some other variants) used in many examples in this book.
SVG makes it very easy to create a 'separation' effect between adjacent pie slices; simply add the following code to drawChart():
var style = ""; var strokeWidth = 4.; var strokeColor = 'white'; stroke = "stroke:"+strokeColor+";"; stroke += "stroke-width:"+strokeWidth+";"; fillColor = "fill:"+circleColors[v%colorCount]; style = stroke+fillColor; pieNode.setAttribute("style",style);
If you want to create a 'dotted' effect, you can add this line of code:
style += "stroke-dasharray:2 2 2 2";
Instead of hard-coding the value of the attribute stroke-dasharray, you can use a more flexible approach (which is used extensively in this book):
var dashStyle = "2 2 2 2"; .... style += "stroke-dasharray:"+dashStyle;
If you use the preceding technique, make sure that you include semi-colons (';') in the proper locations, or you will see the following type of message in the status bar of your browser:
bad CSS property or descriptor declaration
If you are dynamically concatenating multiple strings together in order to define the value of the style attribute, you can display the value of this attribute in an ECMAScript alert() function as follows:
if( count == 0 ) { alert("style "+style); }
The if statement is useful when you need to display the value of the style attribute inside a for loop that has a large number of iterations.
Another variation of the code involves creating a shadow-like three-dimensional effect; this change requires adding the following code to the function drawChart():
function drawChart() { circleNode = svgDocument.createElement("circle"); circleNode.setAttribute("cx", basePointX+10); circleNode.setAttribute("cy", basePointY+10); circleNode.setAttribute("r", radius); circleNode.setAttribute("style","fill:black;"); gcNode.appendChild(circleNode); // the remaining code is the same.... }
The complete code for the preceding modifications is available on the CD-ROM in the SVG files and .
|
|