Generating Sine-Based Petals

   



Generating a Sine-Based Wire Frame Effect

Consider the image in Figure 14.2.

click to expand
Figure 14.2: A sine-based wire frame effect.

The SVG document sinePetalsWireFrame4.svg in Listing 14.2 demonstrates how to define an ECMAScript function in order to dynamically generate a sine-based wireframe.

Listing 14.2 sinePetalsWireFrame4.svg

start example
<?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 basePointX    = 400.;     var basePointY    = 200.;     var currentX      = 0.;     var currentY      = 0.;     var offsetX       = 0.;     var offsetY       = 0.;     var slantX        = 0.;     var slantY        = 0.;     var slantAngle    = 20.;     var slantLength   = 300.;     var minorAxis     = 80.;     var majorAxis     = 20.;     var radius        = 0.;     var smallRadius   = 2.;     var Constant      = 100.;     var branches      = 3.;     var angle         = 0.;     var maxAngle      = 60.;     var angleDelta    = 1.;     var hGap          = 1.;     var strokeWidth   = 1.;     var fillColor     = "black";     var style         = "";     var circleColors  = ['red','green','blue',                          'yellow','white','magenta'];     var colorCount    = 6.;     var lineNode      = null;     var circleNode    = null;     var svgDocument   = null;     var target        = null;     var gcNode        = null;     function init(event)     {        slantX = slantLength*Math.cos(                             slantAngle*Math.PI/180);        slantY = slantLength*Math.sin(                             slantAngle*Math.PI/180);        target = event.getTarget();        svgDocument = target.getOwnerDocument();        gcNode = svgDocument.getElementById("gc");        drawSpiral();     }     function drawSpiral()     {        for(angle=0; angle<maxAngle; angle+=angleDelta)        {           radius = Constant*Math.sin(                          branches*angle*Math.PI/180);           offsetX = radius*Math.cos(angle*Math.PI/180);           offsetY = radius*Math.sin(angle*Math.PI/180);           currentX = basePointX+offsetX;           currentY = basePointY-offsetY;           addCircle(currentX, currentY);           currentX = basePointX-offsetX;           currentY = basePointY-offsetY;           addCircle(currentX, currentY);           currentX = basePointX+offsetX-slantX;           currentY = basePointY+offsetY-slantY;           addCircle(currentX, currentY);           currentX = basePointX-offsetX-slantX;           currentY = basePointY+offsetY-slantY;           addCircle(currentX, currentY);           if( angle % hGap == 0 )           {              currentX = basePointX+offsetX;              currentY = basePointY-offsetY;              addLine(currentX,                      currentY,                      currentX-slantX,                      currentY-slantY);              currentX = basePointX-offsetX;              currentY = basePointY+offsetY;              addLine(currentX,                      currentY,                      currentX+slantX,                      currentY+slantY);              currentX = basePointX+offsetX;              currentY = basePointY+offsetY;              addLine(currentX,                      currentY,                      currentX+slantX,                      currentY+slantY);              currentX = basePointX-offsetX;              currentY = basePointY-offsetY;              addLine(currentX,                      currentY,                      currentX+slantX,                      currentY+slantY);           }        }     } // drawSpiral     function addCircle(currentX, currentY)     {        circleNode = svgDocument.createElement("circle");        fillColor = circleColors[2*(angle%2)];        style  = "fill:"+fillColor+";stroke:";        style += circleColors[angle%2];        style += ";stroke-width:"+strokeWidth;        circleNode.setAttribute("style",style);        circleNode.setAttribute("cx", currentX);        circleNode.setAttribute("cy", currentY);        circleNode.setAttribute("r",  smallRadius);        gcNode.appendChild(circleNode);     } // addCircle     function addLine(x1, y1, x2, y2)     {        style  = "stroke:"+circleColors[2*(angle%2)];        style += ";stroke-width:"+strokeWidth;        lineNode = svgDocument.createElement("line");        lineNode.setAttribute("style",style);        lineNode.setAttribute("x1", x1);        lineNode.setAttribute("y1", y1);        lineNode.setAttribute("x2", x2);        lineNode.setAttribute("y2", y2);        gcNode.appendChild(lineNode);     } // addLine   ]]> </script>  <!-- ============================ -->  <g  transform="translate(10,10)">    <rect x="0" y="0"          width="800" height="500"          fill="none" stroke="none"/>  </g>  </svg>
end example

Remarks

The SVG code in Listing 14.2 starts with a block of global ECMAScript variables, followed by the ECMAScript function init() that initializes the global variables svgDocument, target, and gcNode. The function init() also initializes the variables slantX and slantY as follows:

slantX = slantLength*Math.cos(                       slantAngle*Math.PI/180); slantY = slantLength*Math.sin(                       slantAngle*Math.PI/180);

The variables slantX and slantY are used for rendering slanted line segments in the function addLines() and they provide a good example of variables that can be initialized once because they have fixed values. In situations where you define ECMAScript functions that dynamically generate hundreds or even thousands of SVG elements, you can improve performance by placing unchanging variables outside of code loops. If you wish, you could also add a new ECMAScript function called something like initializeVariables() that can be invoked from the init() function. Since there is flexibility of coding style in this regard, code your functions in a manner that is comfortable and convenient for you.

The ECMAScript function drawSpiral() contains a loop that first calculates the values of radius, offsetX, and offsetY that are used for determining the x-coordinate and the y-coordinate of the center of each new circle. These variables are calculated in a manner that is similar to the code in Listing 14.1, as shown below:

radius = Constant*Math.sin(                    branches*angle*Math.PI/180); offsetX = radius*Math.cos(angle*Math.PI/180); offsetY = radius*Math.sin(angle*Math.PI/180);

Next, the ECMAScript function drawSpiral() calculates the x-coordinate and the y-coordinate of the center of four new circles and then invokes the ECMAScript function addCircle() four times as shown below:

currentX = basePointX+offsetX; currentY = basePointY-offsetY; addCircle(currentX, currentY); currentX = basePointX-offsetX; currentY = basePointY-offsetY; addCircle(currentX, currentY); currentX = basePointX+offsetX-slantX; currentY = basePointY+offsetY-slantY; addCircle(currentX, currentY); currentX = basePointX-offsetX-slantX; currentY = basePointY+offsetY-slantY; addCircle(currentX, currentY);

The preceding code fragment is grouped into four sub-blocks of code, each of which assigns values to the variables currentX and currentY and then invokes the addCircle() function. This code style is advantageous because it makes it easy to modify existing sub-blocks of code and to add new sub-blocks. Moreover, the name of the ECMAScript function makes its purpose obvious; while this detail might seem minor, it becomes increasingly important when you have an SVG document consisting of thousands of lines of code in dozens of functions.

The second part of the loop contains a conditional block of code for adding four line segments to the DOM. The calculation of the variables currentX and currentY is similar to the first part of this loop, as listed below:

if( angle % hGap == 0 ) {   currentX = basePointX+offsetX;   currentY = basePointY-offsetY;   addLine(currentX,           currentY,           currentX-slantX,           currentY-slantY);   currentX = basePointX-offsetX;   currentY = basePointY+offsetY;   addLine(currentX,           currentY,           currentX+slantX,           currentY+slantY);   currentX = basePointX+offsetX;   currentY = basePointY+offsetY;   addLine(currentX,           currentY,           currentX+slantX,           currentY+slantY);   currentX = basePointX-offsetX;   currentY = basePointY-offsetY;   addLine(currentX,           currentY,           currentX+slantX,           currentY+slantY); }

Once again, this code style simplifies the process of understanding the ECMASCript function because its purpose is readily discernible by looking at the layout of the code.

The ECMAScript function addCircle() contains the code for creating a new SVG circle element and assigning values to the attributes style, cx, cy, and r:

function addCircle(currentX, currentY) {    circleNode = svgDocument.createElement("circle");    fillColor = circleColors[2*(angle%2)];    style  = "fill:"+fillColor+";stroke:";    style += circleColors[angle%2];    style += ";stroke-width:"+strokeWidth;    circleNode.setAttribute("style",style);    circleNode.setAttribute("cx", currentX);    circleNode.setAttribute("cy", currentY);    circleNode.setAttribute("r",  smallRadius);    gcNode.appendChild(circleNode); } // addCircle

Notice the technique that is used for assigning a value to the style attribute:

fillColor = circleColors[2*(angle%2)]; style  = "fill:"+fillColor+";stroke:"; style += circleColors[angle%2]; style += ";stroke-width:"+strokeWidth;

Since the global style variable is a string variable, you can concatenate its constituent parts in any order; just make sure that you include a semi-colon (';') in all the required locations! Remember the earlier example involving an ECMAScript alert function that displayed attribute values? The style variable is an ideal candidate for display when you get errors in this part of the code. (As you become increasingly proficient in writing SVG code, you'll use this technique less frequently.)

The ECMAScript function addLine() contains the code for creating a new SVG line element and assigning values to the attributes style, x1, y1, x2, and y2:

function addLine(x1, y1, x2, y2) {    style  = "stroke:"+circleColors[2*(angle%2)];    style += ";stroke-width:"+strokeWidth;    lineNode = document.createElement("line");    lineNode.setAttribute("style",style);    lineNode.setAttribute("x1", x1);    lineNode.setAttribute("y1", y1);    lineNode.setAttribute("x2", x2);    lineNode.setAttribute("y2", y2);    gcNode.appendChild(lineNode); } // addLine

The last portion of the SVG document renders a rectangle whose id attribute has the value gc, which is used for adding the sine-based petals:

<g  transform="translate(10,10)">   <rect x="0" y="0"         width="800" height="500"         fill="none" stroke="none"/> </g>



   



Fundamentals of SVG Programming. Concepts to Source Code
Fundamentals of SVG Programming: Concepts to Source Code (Graphics Series)
ISBN: 1584502983
EAN: 2147483647
Year: 2003
Pages: 362

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net