|
|
Consider the image displayed in Figure 16.1, which is a recursion-based example that implements the code template in Listing 16.1.
Figure 16.1: A set of nested triangles.
The SVG document in Listing 16.2 demonstrates how to define an SVG document that generates a set of nested triangles.
Listing 16.2 nestedTriangles1.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 = 0.; var currentX = 0.; var currentY = 0.; var offsetX = 0.0; var offsetY = 0.0; var level = 5.; var triangleWidth = 800.; var triangleHeight = 300.; var vertexCount = 3.; var xPts = Array(vertexCount); var yPts = Array(vertexCount); var newXPts = Array(vertexCount); var newYPts = Array(vertexCount); var pointPath = ""; var ellipseColors = ['red','green', 'blue','yellow']; var colorCount = 4.; var triangleNode = null; var svgDocument = null; var target = null; var gcNode = null; function init(event) { target = event.getTarget(); svgDocument = target.getOwnerDocument(); gcNode = svgDocument.getElementById("gc"); initializeTriangle(); drawPattern(level, xPts, yPts); } function initializeTriangle() { // clockwise from top vertex... xPts[0] = basePointX; yPts[0] = basePointY; xPts[1] = basePointX+triangleWidth/2; yPts[1] = basePointY+triangleHeight; xPts[2] = basePointX-triangleWidth/2; yPts[2] = basePointY+triangleHeight; } // initializeTriangle function drawPattern(level, oldXPts, oldYPts) { if( level >= 0 ) { // arrays for inner triangle... newXPts = Array(vertexCount); newYPts = Array(vertexCount); for(v=0; v<vertexCount; v++) { // clockwise from upper-left vertex... newXPts[v] = (oldXPts[v]+ oldXPts[(v+1)%vertexCount])/2; newYPts[v] = (oldYPts[v]+ oldYPts[(v+1)%vertexCount])/2; } pointPath = ""; for(var v=0; v<vertexCount; v++) { pointPath += newXPts[v]+","+newYPts[v]+" "; } fillColor = "fill:"; fillColor += ellipseColors[level%colorCount]; triangleNode = svgDocument.createElement( "polygon"); triangleNode.setAttribute("points",pointPath); triangleNode.setAttribute("style", fillColor); gcNode.appendChild(triangleNode); drawPattern(level-1, newXPts, newYPts); } } // drawPattern ]]> </script> <!-- ============================ --> <g transform="translate(10,10)"> <rect x="0" y="0" width="800" height="500" fill="none" stroke="none"/> </g> </svg>
The key idea in Listing 16.2 involves creating a new triangle whose vertices are the midpoints of the sides of the current triangle, as illustrated in the following code fragment:
// arrays for inner triangle... newXPts = Array(vertexCount); newYPts = Array(vertexCount); for(var v=0; v<vertexCount; v++) { // clockwise from upper-left vertex... newXPts[v] = (oldXPts[v]+ oldXPts[(v+1)%vertexCount])/2; newYPts[v] = (oldYPts[v]+ oldYPts[(v+1)%vertexCount])/2; }
If you use the same arrays for the current triangle and the nested triangle instead of creating new arrays for the vertices of the nested triangle, you will clobber the values of the vertices of the current triangle and the result will not be what you expected. If you are curious, though, try replacing the existing loop with the code fragment given below:
for(var v=0; v<vertexCount; v++) { // clockwise from upper-left vertex... oldXPts[v] = (oldXPts[v]+ oldXPts[(v+1)%vertexCount])/2; oldYPts[v] = (oldYPts[v]+ oldYPts[(v+1)%vertexCount])/2; }
Don't forget to replace the other occurrences of newXPts and newYPts with oldXPts and oldYPts, respectively.
|
|