October 25, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Drawing grids, Bezier Curves and Elliptical Arcs Using Java and SVG

  • July 17, 2007
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming Notes # 2220


Preface

General

Although Bézier is the proper spelling of Pierre Bézier's name, the use of the special character as the second character in the name makes it very difficult to compose text using a standard computer keyboard.  The name will appear dozens of times in this lesson.  Therefore, to make it easier to compose the remainder of this tutorial, I will take the liberty of spelling it Bezier.

Part of a series

This lesson is part of a series (see Resources) with the following major objectives:

  • To teach you how to write Java code that will produce SVG/XML output, which can be rendered in graphic form by an SVG graphics engine such as Firefox 1.5.
  • To teach you how to write servlets that will produce SVG/XML output, which can be rendered in graphic form by an SVG-capable browser such as Firefox 1.5.

What you have learned

In previous lessons, you have learned:

  • How to write Java code that will deposit SVG/XML code into an output file that I refer to as an SVG file.
  • How to write Java code that will deposit in-line SVG code into an XHTML file.
  • How to write Java servlet code that will deposit in-line SVG code into XHTML code in the servlet's output data stream.
  • How to write Java code that will create an output XHTML file that references an external SVG file.
  • How to write Java servlet code that will create an output XHTML data stream that references an external SVG file.
  • How to write a Java/SVG graphics library that removes much of the pain from writing Java code to produce SVG/XML output.
  • How to program and use many of the features of SVG to produce rich graphics in an SVG-capable browser window.

The servlet objective has been satisfied

At this point, I believe that I have satisfied the second objective listed above involving servlets.  However, even though I have taught you a lot about the many features of SVG, there are other important features that I have not yet covered.  Therefore, the first objective listed above has not yet been satisfied.

Will assume that you understand SVG/servlet code in general

Beginning with this lesson, most of the remaining lessons in this series will assume that once you understand how to write Java code to implement a particular SVG feature, you will already understand how to incorporate that Java code into a servlet such that the output data stream from the servlet will deposit that SVG code into an XHTML data output stream.  Consequently, I will have little to say about servlets in the remaining lessons in the series, and will concentrate on helping you to understand how to write the Java code necessary to take advantage of various SVG features.  Typically, the sample programs will produce output SVG files that can be rendered in graphic form by an SVG graphics engine such as Firefox 1.5.

What you will learn in this lesson

In this lesson you will learn how to write Java code that uses an SVG graphics library and the SVG path element to efficiently draw grid lines, geometric shapes, cubic Bezier curves, quadratic Bezier curves, and elliptical arcs.

An SVG graphics library

In earlier lessons, I taught you how write your own Java SVG graphics library to eliminate, or at least alleviate the requirement to write raw XML code or raw JAXP DOM code.  The use of the SVG graphics library makes it possible for you to produce SVG output simply by making typical Java method calls.  I updated my version of the Java SVG graphics library to contain several new methods for use in this lesson.

Viewing tip

I recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures and listings while you are reading about them.

Figures

  • Figure 1. The graphic output from the program.
  • Figure 2. Sample cubic Bezier curves.
  • Figure 3. Sample d attribute value for a cubic Bezier curve.
  • Figure 4. Combinations of large-arc-flag and sweep-flag.
  • Figure 5. A portion of the SVG/XML code for the grid.
  • Figure 6. A text element.
  • Figure 7. XML code for the red cubic Bezier curve.
  • Figure 8. XML code for the green cubic Bezier curve.
  • Figure 9. XML code for the blue cubic Bezier curve.

Listings

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials.  You will find a consolidated index at www.DickBaldwin.com.

General background information

What is a path element?

Paraphrasing the information in the SVG documentation, an SVG path represents the outline of a shape.  The shape can be filled, stroked, used as a clipping path, or any combination of the three.  It can also be used to cause text to be positioned on curving lines, which will be the topic for a future lesson.

A path is described using the concept of a current point.  In an analogy with drawing on paper, the current point can be thought of as the current location of the pen. The position of the pen can be changed, and the outline of a shape can be traced by dragging the pen in either straight lines or curves.  The shape can be either open or closed.

Paths represent the geometry of the outline of a shape, defined in terms of the following commands:

  • moveto (set a new current point)
  • lineto (draw a straight line)
  • curve (draw a curve using a cubic or quadratic Bezier curve or an elliptical arc)
  • closepath (close the current shape by drawing a straight line to the last moveto point)

A path is defined in SVG using the path element.

The required d attribute

A path element can have a large number of optional attributes.  It appears from the documentation that all but two are common attributes that can be applied to many different elements such as the attributes named transform, style, etc.

It also appears from the documentation that there are at least two attributes that are specific to a path element:

  • d="path data"
  • pathLength = "number"

A required data set

The d attribute is required while the pathLength attribute is optional.  The value for the d attribute must be a data set that describes the path.  In other words, the data in the data set is the definition of the outline of the shape.

In summary, a path is defined by defining a path element, which contains a d attribute, where the d-attribute value contains the moveto, lineto, curve, and closepath commands and the associated coordinate values necessary to define the outline of the shape.

Syntax of the path data set

The syntax of the path data set is designed to minimize download time and associated bandwidth requirements for downloading and drawing the path.  The following rules generally describe the syntax of the value of the d attribute:

  • For download efficiency, all instructions (commands) are expressed using a single character.  For example, a moveto command is expressed as either M or m.
  • Superfluous white space and separators such as commas can be eliminated.  For example, "M 100 100 L 200 200" contains unnecessary spaces and could be expressed as "M100 100L200 200".
  • The command letter can be eliminated on subsequent commands if the same command is used multiple times in a row.  For example, you can drop the second "L" in "M 100 200 L 200 100 L -100 -200" and use "M 100 200 L 200 100 -100 -200" instead.
  • Relative versions of all commands are available (uppercase means absolute coordinates, lowercase means relative coordinates).
  • For the relative versions of the commands, all coordinate values are relative to the current point at the start of the command.
  • Alternate forms of the lineto command are available to optimize the special cases of horizontal and vertical lines.
  • Alternate forms of the curve command are available to optimize the special cases where the first control point on the current segment can be determined automatically from the last control point on the previous segment.
  • The path data syntax is a prefix notation (commands are followed by parameters).
  • The only allowable non-numeric characters for numeric parameter values are the minus ("-") and period (".") characters.  No other delimiter characters are allowed within the parameter value. (For example, the following is an invalid numeric value in path data: "13,000.56".)

The commands

If I counted correctly, there are twenty single-character commands that fall in four general categories.  The four categories and their single character commands are given below:

  • moveto:  M or m
  • closepath:  Z or z
  • lineto
    • L or l (general lineto, [note the lowercase L])
    • H or h (horizontal lineto)
    • V or v (vertical lineto)
  • curve:
    • Cubic Bezier: C, c, S, and s (will explain in detail later)
    • Quadratic Bezier: Q, q, T, and t (will explain in detail later)
    • Elliptical arc: A and a (will explain in detail later)

Recall that the uppercase version of each command indicates that the coordinate values following the command are to be interpreted as absolute values and the lowercase version of the command indicates that the coordinate values following the command are to be interpreted as being relative to "the current point at the start of the command."

The information in the above list is provided mainly to give you an overview.  I will discuss the different categories and their commands in much more detail later in this lesson.

The graphic output from the program

Figure 1 shows the graphic output that is produced by the program that I will present and explain in this lesson.

Figure 1. The graphic output from the program.

Figure 1 illustrates a diagonal line, grid lines that give the appearance of graph paper, a simple triangle composed of straight lines, three different cubic Bezier curves, one quadratic Bezier curve, and six elliptical arcs.  Each of the shapes in Figure 1 was designed to illustrate one or more features of the SVG path element.  I will refer back to Figure 1 frequently throughout the remainder of this lesson.

The SVG graphics library

Throughout most of the earlier lessons in this series, I have been using a Java/SVG graphics library of my own design to make it easier to generate the detailed SVG/XML code required to render SVG graphics in an SVG-capable graphics engine.  In several previous lessons, I have updated the library to add new functionality, and this lesson is no exception.

I added the following new methods to my Java SVG graphics library for this lesson.

  • makePath
  • makeGridString
  • makeText

I will present and explain the code for these methods later.  At this point, however, we are still viewing the graphics process from a somewhat higher level and we don't need to know the details of the code.

The moveto command

The moveto commands (M or m) establish a new current point. The effect is as if the "pen" were lifted from the paper and moved to a new location where it is then dropped back onto the paper.  A path data segment must begin with a moveto command. Subsequent moveto commands represent the start of a new sub-path.  (Note, however, that I don't deal with multiple sub-paths in this lesson.)

Some special cases.
If a relative moveto (m) appears as the first element of the path, it is treated as a pair of absolute coordinates. If a moveto is followed by multiple pairs of coordinates, the subsequent pairs are treated as implicit lineto commands.
The moveto command must be followed by a pair of coordinate values that specify the new current point.  Depending on whether the command is an uppercase M or a lowercase m, the coordinate values may be either absolute or relative.  The moveto command starts a new sub-path at the location specified by the coordinate values.

Working with straight lines

The lineto command

The various lineto commands draw straight lines from the current point to a new point that is specified by a pair of coordinate values following the command.

The L or l lineto command

This command draws a line from the current point to the specified coordinate which becomes the new current point.  As usual, uppercase L indicates that absolute coordinates will follow.  Lowercase l indicates that relative coordinates will follow.

A number of coordinate pairs may be specified to draw a polyline (not to be confused with a polyline element). At the end of the command, the new current point is set to the final set of coordinates provided.

The H or h lineto command

This is a special case of the lineto command that requires only a single coordinate value following the command.  The single coordinate value is a new horizontal coordinate.  This command draws a horizontal line from the current point to the new horizontal coordinate at the same vertical coordinate.  (This command was used to draw the horizontal lines in the blue background grid in Figure 1.)

The V or v lineto command

This is another special case of the lineto command that requires only a single coordinate value following the command.  The single coordinate value is a new vertical coordinate.  This command draws a vertical line from the current point to the new vertical coordinate at the same horizontal coordinate.  (This command was used to draw the vertical lines in the blue background grid in Figure 1.)

The closepath command

The closepath command (Z or z) ends the current sub-path and causes an automatic straight line to be drawn from the current point to the initial point of the current sub-path.

If a closepath is followed immediately by a moveto, then the moveto identifies the start point of the next sub-path. If a closepath is followed immediately by any other command, then the next sub-path starts at the same initial point as the current sub-path.

The light blue grid pattern

The first thing that I will call your attention to in Figure 1 is the blue grid of straight horizontal and vertical lines that causes the image to resemble a sheet of graph paper.

Why use the path element to draw straight lines?

You learned about the line and polyline elements in earlier lessons in this series (see Resources).  The reality is that the use of the path element to draw straight lines is conceptually more difficult than the use of the line element to draw straight lines.  This may cause you to wonder why you would ever use the path element to draw straight lines.  The reason is that in some cases, (such as the drawing of the background grid in Figure 1), the use of the path element can significantly reduce the size of the XML data download and the corresponding bandwidth requirements.

XML data required for a line element

Note the light blue diagonal line in Figure 1.  This line was drawn using a line element, and its only purpose for being in Figure 1 is to illustrate the improved download efficiency of the path element relative to the line element in certain cases.

The XML code required to draw this line and to set its color to light blue consisted of a total of 74 characters.  This total would not change significantly with changes in the length or the orientation of the line.

Downloading 74 characters to draw a line is not a problem if you are drawing only a few lines.  However, the light blue background grid in Figure 1 contains 45 horizontal lines and 45 vertical lines for a total of 90 lines.  If each of those 90 lines had been drawn and colored using an individual line element, approximately 6660 XML characters would have been required to draw all 90 lines.

Using a path element instead

As you will see later, I developed a method named makeGridString that uses special horizontal and vertical lineto commands of the path element to reduce the download size and attendant bandwidth requirements for drawings that contain a large number of horizontal and vertical lines in a uniform grid pattern.  This method returns a string, which is used as the value for the attribute named d for a path element that draws the lines described by the contents of the string.  Although the code to accomplish this is somewhat more complicated than would be the case for drawing the grid using simple line elements, the download size and bandwidth requirements are significantly reduced with the use of the path element.

A significant reduction in XML data volume

polyline is more efficient than line.
I also wrote a test program and determined that the amount of XML code required to draw the light blue grid using a polyline element was about 1325 characters.   Thus, the path element is also more efficient than the polyline element, but not by nearly as large a factor as when compared to the repeated use of the line element to draw the grid.
If I counted correctly, the XML code required to draw and color all 90 of the light blue horizontal and vertical grid lines in Figure 1 using a path element consists of about 1150 characters.  Thus, the use of the path element to draw the light blue background grid reduced the amount of XML data by almost a factor of 6 relative to the amount required to draw and color the grid using individual line elements.

The red and blue triangle

Next, I want to call your attention to the red triangle with the blue border in the upper left corner of Figure 1.  I used the moveto, lineto, and closepath commands to draw this triangle.  In fact, this program uses one absolute moveto command, one absolute lineto command, and one relative lineto command plus the closepath command to draw the triangle.  Once the triangle had been drawn, separate calls to the setAttribute method were made to fill the triangle with red and to give it a blue border.

The triangle was drawn solely to illustrate the use of the path element and the commands listed above.  In reality, it would probably have been easier to use simple line elements to draw this triangle than to draw it using the path element.

Working with text

The use of text in SVG is an extensive topic, one facet of which is to create a curved path and then to render the text along that path instead of simply rendering it along a straight line.  Text will be the primary topic of a future lesson.

However, I needed to put numeric labels on the graph shown in Figure 1 to make it easier to read coordinate values off the graph.  As you will see later, I added a makeText method to my SVG graphics library to make this relatively easy to accomplish.

Working with Bezier curves

When you need to draw smooth curves to connect points, the path element has a lot more to offer than just a reduction in the XML data volume, although it has that to offer also.  For this situation, the use of the path element also makes it possible to do some things relatively easily that could be very difficult to do otherwise.

Experimenting with Bezier curves.
If you would like to learn a little more about cubic Bezier curves and have some fun in the process, take a look at the interactive Bezier Curve Demo in Resources(Also see Figure 2.)
Cubic Bezier Curves

A Bezier curve is a smooth curve that is used to connect two points.  SVG supports both quadratic and cubic Bezier curves.  Initially, this discussion will be centered on cubic Bezier curves.  Then it will move to quadratic Bezier curves.

Bezier curves according to Darrel Plant

While describing cubic Bezier curves, Darrel Plant tells us, "Almost any type of curve that contains one or two changes in direction can be described by a Bézier equation."  (See "What's a Bézier Curve?" in Resources)  He goes on to describe how cubic Bezier curves can also be used to describe loops and other complex shapes.

Sample cubic Bezier curves

Figure 2 shows four different cubic Bezier curves that I created interactively using the Bezier Curve Demo in Resources.

Figure 2. Sample cubic Bezier curves.

In each of the individual images in Figure 2, the curved line is the actual Bezier curve (or Bezier segment).  The straight lines connect the start point, two control points, and the end point that I will explain in the next section.

A Bezier segment.
The SVG documentation refers to the four points and the curve that they produce as a Bezier segment.  Thus, a Bezier curve may be made up of one or more Bezier segments.
Drawing a Bezier segment

The locations of four points in coordinate space must be specified to draw a cubic Bezier segment.  There is a start point, two control points, and an end point.  The segment is drawn from the start point to the end point.  The locations of the two control points determine the shape of the curve as it winds its way from the start point to the end point.  Figure 2 shows some good examples of how the locations of the two control points, relative to the locations of the start point and the end point impact the shape of the actual Bezier curve or segment.

Special forms of the path element in SVG

With SVG, cubic Bezier segments are drawn using any one of several special forms of the path element.  There are four single-character commands that can be used in the specification of the locations of the points:  C, c, S, and s.

For example, Figure 3 shows the beginning of the XML path element that produced the red Bezier curve shown immediately down and to the right of the red and blue triangle in Figure 1.

Figure 3. Sample d attribute value for a cubic Bezier curve.

<path
 d="M100,100 C100,50 175,50 175,100 S250,150 250,100"

The C-command and the S-command are highlighted in boldface red in Figure 3 to make them easy to for you to spot.  (They would not be red in the actual XML code.)

Either absolute or relative coordinates

Keeping the text uncluttered.
To keep the text from becoming too cluttered, the remainder of this discussion on Cubic Bezier curves will use the uppercase version of the commands, although in many cases the lowercase version could also be used.
Once the start point has been established, the coordinates of the other three points can be specified either in absolute coordinates or in coordinate values that are relative to the location of the start point.  If the uppercase command is used, the coordinate values are interpreted to be absolute.  If the lowercase command is used, the coordinate values are interpreted to be relative. 

Poly-Bezier curves

The specifications for multiple Bezier segments can be strung together following a Bezier command to create a poly-Bezier curve.  For example, each of the images shown in Figure 2 is a single Bezier segment.  On the other hand, the red Bezier curve shown near the upper left in Figure 1 consists of two Bezier segments concatenated end to end and is therefore a poly-Bezier curve.

For the case of poly-Bezier curves, each group of three coordinate values following the original Bezier segment is interpreted to represent the specification of a new Bezier segment.  The start point for the new segment is assumed to be the end point from the previous segment, and hence serves as the first of the four points that are required to specify a cubic Bezier segment.

Difference between C and S Bezier commands

Of the two types of Bezier commands, (C and S), C is the more general of the two.  The C command can be used for a single Bezier segment or for the construction of a poly-Bezier curve.  However, the S command applies only to the construction of a poly-Bezier curve.

A reflected point.
In case you aren't certain what I mean when I speak of the reflection of a point, see the links to the reflection articles in Resources.
When the S command is used to specify the four required points for the next segment, only the second control point and the end point are actually specified.  As is always the case for a poly-Bezier curve, the start point for the next segment is assumed to be the end point from the previous segment.  However, when an S command is used, the first control point for the new segment is assumed to be the reflection of the second control point from the previous segment, leaving only two points that must actually be specified for the new segment.

Quadratic Bezier curves

A quadratic Bezier segment is defined by a start point, an end point, and only one control point.  As with the cubic Bezier segment, there are four commands that are used to specify the locations of the three required points:  Q, q, T and t.  The uppercase commands imply that the coordinate values are absolute while the lowercase commands imply that the coordinate values are relative to the location of the start point.

The Q and q commands for the quadratic segment have essentially the same meaning as the C and c commands for the cubic segment.  The T and t commands for a quadratic segment have essentially the same meaning as the S and s commands for the cubic segment.

The Bezier curves in Figure 1

Figure 1 contains three cubic Bezier curves colored red, green, and blue, and one quadratic Bezier curve colored red.  I will have more to say about each of these curves in conjunction with the explanation of the Java code that produced them.

Working with elliptical arcs

Seven parameters are required

An elliptical arc command is perhaps the most complex of all of the curve commands.  Each elliptical arc command requires seven parameters in the following order:

  1. rx specifies the horizontal radius of the ellipse
  2. ry specifies the vertical radius of the ellipse
  3. x-axis-rotation specifies the rotation of the ellipse relative to the current coordinate system
  4. large-arc-flag (will discuss in detail later)
  5. sweep-flag (will discuss in detail later)
  6. x specifies the horizontal location of the end point of the arc
  7. y specifies the vertical location of the end point of the arc

Behavior of the elliptical arc command

The command draws an elliptical arc beginning at the current point and ending at the location specified by x and y.

The coordinates of the center of the ellipse are calculated automatically to satisfy the constraints imposed by the other parameters.  The large-arc-flag and sweep-flag parameters contribute to the automatic calculations and help determine how the arc is drawn.

The large-arc-flag and sweep-flag parameters

Each of the parameters named large-arc-flag and sweep-flag can have a value of 0 or 1.  This results in four possible combinations of these two parameters.  Thus, there are actually four different arcs (two different ellipses, each with two different arc sweeps), only one of which can satisfy the constraints imposed by a specific combination of these two parameters. 

The rules for drawing elliptical arcs

The combination of large-arc-flag and sweep-flag indicates which one of the four arcs will be drawn according to the following rules:

  • Of the four candidate arcs, two represent an arc sweep of greater than or equal to 180 degrees.  This is a large arc.  The other two candidates represent an arc sweep of less than or equal to 180 degrees.  This is a small arc.

    If the value of large-arc-flag is 1, one of the two larger arc sweeps will be chosen.

    If the value of large-arc-flag is 0, one of the smaller arc sweeps will be chosen,
     
  • The arc is drawn by evaluating the following equations where cx and cy are the coordinates of the center of the ellipse:

    x = cx + rx*cos(theta)
    y =cy + ry*sin(theta)

    If the value of sweep-flag is 1, then the equations are evaluated such that theta starts at an angle corresponding to the current point and increases positively until the arc reaches the end point.

    If the value of sweep-flag is 0, the equations are evaluated such that theta starts at an angle value corresponding to the current point and decreases until the arc reaches the end point.

Elliptical arcs in the graphical output

Six elliptical arcs are shown in Figure 1.  The yellow arc with the red border and the red arc with the yellow border illustrate the effect of the x-axis-rotation parameter on the appearance of the arc.  The yellow arc with the red border was drawn with an x-axis-rotation parameter value of 0.  The red arc with the yellow border was drawn with an x-axis-rotation parameter value of 45 degrees.

In both cases, the start point was at the bottom of the straight-line segment on the left and the end point was at the top of the straight-line segment.

Also in both cases, the value of large-arc-flag was 1 and the value of sweep-flag was 0.  Thus, as explained above, one of the two larger arc sweeps was chosen in each case.  Also as explained above, the equations were evaluated such that theta started at an angle value corresponding to the current point and decreased until the arc reached the end point.

 Combinations of large-arc-flag and sweep-flag

Each of the other four elliptical arcs at the bottom of Figure 1 represents one of the four possible combinations of large-arc-flag and sweep-flag with an x-axis-rotation parameter value of 0.  Note that in each case, the horizontal and vertical radii were the same, causing the elliptical arc to actually be a circular arc.  Also note that in each case, the start point of the elliptical arc was at the bottom of the straight-line segment.

Figure 4 shows the different combinations of these two parameters for each elliptical arc going from left to right across Figure 1.  Hopefully a comparison of these parameter values with the images in Figure 1 will help you to understand the impact of each of the two parameters on the appearance of the elliptical arc.

Figure 4. Combinations of large-arc-flag and sweep-flag.

Case 1: Fill color = red, border = blue
 large-arc-flag = 1
 sweep-flag     = 0
    
Case 2: Fill color = green, border = red
 large-arc-flag = 1
 sweep-flag     = 1
    
Case 3: Fill color = blue, border = red
 large-arc-flag = 0
 sweep-flag     = 0
    
Case 4: Fill color = yellow, border = blue
 large-arc-flag = 0
 sweep-flag     = 1

The large-arc flag

The behavior of the large-arc-flag is pretty straightforward and easy to understand.  Basically the value of the large-arc-flag determines whether the large portion or the small portion of the ellipse will be displayed.  If the large-arc-flag has a value of 1, the large portion of the ellipse will be displayed.  If the large-arc-flag has a value of 0, the small portion will be displayed.

The sweep-flag

The behavior of the sweep-flag is not so straightforward, at least not for me anyway.  The value of the sweep-flag determines which side of the straight-line segment the portion selected by the large-arc-flag will be displayed on.  However, the difference between right and left in this case depends on the locations of the start point and the end point.  Although I haven't worked out the math, I feel reasonably confident in saying that if you stand at the start point and look toward the end point, the arc will be displayed on your right if the sweep-flag has a value of 0, and will be displayed on your left if the sweep-flag has a value of 1.

Preview

I will present and explain a program named Svg12 in this lesson.  The primary purpose of this program is to teach you how to use an SVG path element to draw straight lines, Bezier curves, and elliptical arcs.  The program produces a single SVG file as its output.  The graphic output shown in Figure 1 was produced by rendering the SVG output file using Firefox 1.5.

As is my custom, I will present and explain the program in fragments.  You can view a listing of the entire program in Listing 17 near the end of the lesson.

Discussion and sample code

Description of the program

Overall, the program creates a DOM tree that describes the graphic image shown in Figure 1 in SVG format and writes it out into an SVG file named Svg12.svg.  The output file produced by this program can be rendered by loading it into Firefox 1.5.

The path element

The main purpose of this program is to demonstrate the use of a Java method of my own design named makePath to create the SVG/XML output code necessary to produce the graphic elements shown in Figure 1.  A secondary purpose is to demonstrate the use of a second Java method of my own design named makeGridString.

I updated my SVG graphics library during the writing of this program to add several new methods, including makePath and makeGridString, and to update some of the default values for existing methods as well.

A diagonal blue line

The program uses a line element to draw a diagonal line from the top left corner of Figure 1 to the lower right corner.  The purpose of this line was to provide information about the line element that could be used to calculate the improvement in download efficiency provided by the path element relative to the line element for certain cases involving straight lines.

A background grid

What color is graph paper?
In retrospect, thinking back to the days when I actually used graph paper, I probably should have made the background grid green.  Then it would look even more like the graph paper that I remember from those days.
After taking care of all the preliminary stuff that I have explained in earlier lessons, the program uses the new method named makeGridString in conjunction with the new method named makePath to draw a light blue background grid that resembles graph paper.  The makeGridString method uses the moveto command and special versions of the horizontal and vertical lineto command to reduce the amount of SVG/XML data needed to draw the grid.

As you can see in Figure 1, there are light blue horizontal and vertical lines every 10th pixel.  There are slightly darker blue lines every 50th pixel.  There are even darker blue lines every 100th pixel.

The text labels

The program uses a new Java method of my own design named makeText to place black text labels on the blue lines that mark every 50th pixel.

A red triangle with a blue border

Then the program uses the moveto, lineto, and closepath commands to draw a triangle.  The setAttribute method is used to fill the triangle with solid red color and to give it a blue border.

Three cubic Bezier curves

The program draws three cubic Bezier curves, one red, one green, and one blue, illustrating the effect that the locations of the control points have on the shape of the curve.

One quadratic Bezier curve

Then the program overlays a red quadratic Bezier curve on the blue cubic Bezier curve, illustrating the difference in the degree to which the cubic and quadratic Bezier curves fit the same envelope, where the envelope is defined by the start point, the control point(s), and the end point.

Elliptical arc rotation

The program draws two elliptical arc curves to illustrate the effect of the x-axis-rotation parameter on the appearance of the elliptical arcs.  The yellow arc with the red border has a rotation value of zero, while the red arc with the yellow border has a rotation value of 45 degrees.  Otherwise, the two elliptical arcs were drawn with the same parameter values.

Combinations of large-arc-flag and sweep-flag parameters

Finally, the program draws four more elliptical arc curves, illustrating the difference in shape that results from all four combinations of the large-arc-flag and sweep-flag parameters.

Program testing

The program was tested using J2SE 5.0, Firefox v1.5.0.8, and WinXP.

The beginning of the class named Svg12

The class named Svg12 begins in Listing 1.

Listing 1. The beginning of the class named Svg12.

public class Svg12{

  public static void main(String[] args){
    int width = 450;
    int height = 450;

    //Begin by creating a DOM tree that represents the XML
    // code that will render to produce the image of
    // interest.

    //Get the Document object.
    Document document = SvgGraphics.getDocument();

    //Create the root node named svg and append it to the
    // document.  Specify the parent as null for the
    // special case where the parent is the document.
    Element svg = SvgGraphics.makeNode(
        document,
        null,//parent
        "svg",//node type
        new String[]{"xmlns","http://www.w3.org/2000/svg",
                     "version","1.1",
                     "width",""+ (width + 2),
                     "height","" + (height + 2),
                     "position","absolute",
                     "top","0",
                     "left","0",
                    });//end makeNode method

    //Create a node named g, which will be the parent for
    // several graphic elements. Pass null for the
    // reference to the array object for the special case
    // where the node has no attributes.
    Element g = SvgGraphics.makeNode(document,
                                     svg,//parent
                                     "g",
                                     null);

I have explained the material in Listing 1 in previous lessons, so I won't repeat that explanation here.

Draw a diagonal line

Listing 2 calls the makeLine method to draw a diagonal line from the upper left corner to the bottom right corner in Figure 1, and then calls the setAttribute method to color it blue.

Listing 2. Draw a diagonal line.

    Element diagLine = SvgGraphics.makeLine(
                                  document,g,0,0,450,450);
    diagLine.setAttribute("stroke","#8888ff");

I explained both of these methods in earlier lessons, and won't repeat that explanation here.

Draw the light blue grid lines

Listing 3 calls the makeGridString method followed by the makePath method and the setAttribute method to draw the light blue grid lines on a ten-pixel spacing in Figure 1.  This is the first of three sets of grid lines, followed by a set on 50-pixel spacing and a set on 100-pixel spacing.

Listing 3. Draw the light blue grid lines.

    //First draw light blue grid lines every 10 pixels.
    String gridData = SvgGraphics.makeGridString(
                                         width,height,10);
    //Create the path element.
    Element temp;
    temp = SvgGraphics.makePath(document,g,gridData);
    //Set the color to light blue.
    temp.setAttribute("stroke","#ccccff");

Both the makeGridString method and the makePath method are new to this lesson, so I will put the discussion of the main method on hold while I explain these two methods.  I will explain them in reverse order because, in effect, the makeGridString method is a helper method for the makePath method.  Therefore, I will explain the makePath method first.

The makePath method

The makePath method is shown in its entirety in Listing 4.

Listing 4. The makePath method.

  static Element makePath(Document document,
                          Element parent,
                          String d){
    Element path  = 
                  (Element)document.createElement("path");
    parent.appendChild(path);
    path.setAttribute("d",d);
    path.setAttribute("stroke","black");
    path.setAttribute("stroke-width","1");
    path.setAttribute("fill","none");
    return path;
  }//end makePath

Let me begin by reviewing what is going on here.  At this point in the program, I am constructing a DOM tree that represents the graphic image shown in Figure 1.  At this point, the objective is to create nodes in the DOM tree that will later be transformed into elements in the XML output code produced by the program.

With the exception of the diagonal line and some polylines, all of the shapes in Figure 1 will be created using an SVG/XML path element.  This includes the background grid lines.  Therefore, at this point, I need to create a node of type path and append it to the Dom tree.

Old stuff

There is nothing new about the first two parameters to the makePath method:   document and parent.  Almost all of the methods in my SvgGraphics library require these parameters and I have explained their use in earlier lessons.  There is also nothing new about the first statement or the last four statements in the method.  I have also explained these statements in earlier lessons.  That leaves only one interesting statement, and it is highlighted in boldface in Listing 4.

New stuff

In the earlier section titled The required d attribute, I explained that an SVG path element must have an attribute named d.  I also explained that the value of the d attribute must be a data set that describes the path.  In other words, the data in the data set is the definition of the outline of the shape represented by the path.

In the section titled Syntax of the path data set I explained the rules for constructing the string attribute value for the d attribute.  Briefly, that string must consist of uppercase and lowercase alphabetic commands and numeric coordinate values.

Establish the value of the attribute named d

After constructing the path node and appending it to the DOM tree, the makePath method in Listing 4 calls the setAttribute method on the path node, passing the third parameter to the makePath method as the only parameter to the setAttribute method.  Therefore, the string contents of the parameter named d will become the attribute value for the path element when the DOM tree is transformed to XML.  The makePath method will be called extensively in this program to create all of the different graphic elements shown in Figure 1.  The type of graphic element that is created depends solely on the string contents of the parameter named d.

Set default attributes and return the path

Finally, the method named makePath returns a reference to the new path node that was created.

It is also worth noting that by default, the path will be stroked as a black line (which may be a curve), one pixel in width.  If the path happens to close, it won't be filled with anything.  You can later set new values for the stroke, stroke-width, and fill attributes if you aren't satisfied with the default values.

Set new attribute values

If you refer back to Listing 3, you will see that the returned reference to the path node is saved in a local variable named temp.  Then the setAttribute method is called on the reference to the object to reset the stroke to a new color, which is the lightest shade of blue shown in Figure 1.

So where are we?

We are in the process of constructing a path node that will eventually be transformed into SVG/XML code that will cause the SVG rendering engine to draw the light blue grid shown in Figure 1.  We know that a call to the makePath method will return a reference to a path node, for which the definition of the path is determined by the contents of the String parameter that is passed as the third parameter to the makePath method.

Referring once again to Listing 3, we see that a reference to a String object that is stored in a local variable named gridData is passed as the third parameter to the makePath method.  Therefore, we can expect that the contents of that String object will describe the grid with a ten-pixel spacing shown in Figure 1.  A reference to that String object is returned from the earlier call to the makeGridString method.  Therefore, it is time for us to take a look at the method named makeGridString.

The makeGridString method

This is a highly specialized method that is designed to do one thing and one thing only.  However, that one thing is fairly common, so I decided that it would be worth encapsulating the code into a method and putting the method into my SVG graphics library.

The makeGridString method is a utility method that is used to construct a string that describes a grid pattern consisting of horizontal and vertical lines on a specified pixel spacing for a rectangular area of a specified width and a specified height in pixels.

The string that is returned is intended solely to be used as the data string for a call to the method named makePath.

The makeGridString method can be viewed in its entirety in Listing 5.

Listing 5. The makeGridString method.

  static String makeGridString(
                        int width,int height,int spacing){
    //Construct the data string for the vertical lines.
    String data = "M0,0 ";
    for(int cnt = 0;cnt < width;cnt += spacing){
      data += "v" + height + " M" + cnt + ",0 ";
    }//end for loop
    //Add the final vertical line.
    data += "v" + height + " n";
    
    //Now add the horizontal lines to the data string.
    data += "M0,0 ";
    for(int cnt = 0;cnt < height;cnt += spacing){
      data += "h" + width + " M0," + cnt + " ";
    }//end for loop
    //Add the final horizontal line.
    data += "h" + width + "n ";
    
    return data;

  }//end makeGridString

I'm not going to attempt to explain this code to you in detail.  Rather, I will simply refer you back to the material in the sections titled The moveto command and Working with straight lines and let you work through the details on your own.  I will point out, however that this method makes use of the special horizontal and vertical lineto commands (h and v) in an attempt to reduce the download size and the bandwidth requirements for a drawing containing a large number of horizontal and vertical lines in the grid pattern.

A portion of the XML code for the small grid

Figure 5 shows a portion of the SVG/XML code that was generated by this program.

Figure 5. A portion of the SVG/XML code for the grid.

<path 
d="M0,0 v450 
M0,0 v450 
M10,0 v450 
M20,0 v450 
M30,0 v450 
... 
M430,0 v450 
M440,0 v450&#10;
M0,0 h450 
M0,0 h450 
M0,10 h450 
M0,20 h450 
M0,30 h450 
...
M0,430 h450 
M0,440 h450&#10; " 
fill="none" 
stroke="#ccccff" 
stroke-width="1"/>

The steps that produced the code

Note that the code in Figure 5 was the result of performing the following steps:

  1. Generating the path data string by calling the makeGridString method shown in Listing 5, passing 450 for the width and 450 for the height and passing 10 for the spacing.
  2. Creating the path node by calling the makePath method shown in Listing 4, passing the data string from step 1 above as the third parameter to the makePath method.  The path node was appended to the DOM tree.
  3. Calling the setAttribute method on the path node from step 2 above in Listing 3 to change the color from the default black to the light blue defined by the hexadecimal value #ccccff.
  4. Transforming the DOM tree to XML code and storing it in an output file.  The SVG/XML code shown in Figure 5 was taken from that file.

Drawing the darker grids

If you take a look at the complete program in Listing 17, you will see that essentially the same thing was done two more times.  First, the process was repeated for a spacing of 50 pixels.  This new grid was assigned a stroke color attribute value of "#aaaaff", which was slightly darker than the first grid described above.  The lines in this new grid were drawn on top of the original grid, causing the grid lines on a spacing of 50 pixels to be a slightly darker color of blue than the original grid, as shown in Figure 1.  It is important that these two grids be drawn in this order with the darker one on top.  If the darker grid had been drawn first, it would have been covered by the lighter grid lines and the darker lines would not have been visible.

Finally, the process was repeated one more time for a spacing of 100 pixels and a stroke color attribute value of "#8888ff", which is an even darker shade of blue.  This resulted in the background grid in Figure 1 consisting of very light blue grid lines on 10-pixel spacing, slightly darker grid lines on a 50-pixel spacing, and even darker grid lines on a 100-pixel spacing.

Label the grid

As I mentioned earlier, although the topic of drawing text using SVG is an extensive topic that I plan to cover in a subsequent lesson, I wanted to be able to label the grid in this program, so I wrote a new method for my SVG graphics library named makeText.

Listing 6 makes two calls to the makeText method during each iteration of a for loop to place a text label on the horizontal lines on the 50-pixel spacing and to place a text label next to the vertical lines on the 50-pixel spacing.

Listing 6. Label the grid.

    for(int cnt = 0;cnt < width;cnt += 50){
      SvgGraphics.makeText(document,g,0,cnt,""+cnt);
      SvgGraphics.makeText(document,g,cnt,height,""+cnt);
    }//end for loop

The makeText method

The makeText method is shown in its entirety in Listing 7.

Listing 7. The makeText method.

  static Element makeText(Document document,
                          Element parent,
                          int x,
                          int y,
                          String text){
    Element textNode  = 
                  (Element)document.createElement("text");
    parent.appendChild(textNode);
    
    textNode.setAttribute("x",""+x);
    textNode.setAttribute("y",""+y);
    
    textNode.appendChild(document.createTextNode(text));

    return textNode;
  }//end makePath

This method begins like most of the others in my SVG graphics library by creating a new text node of type Element and appending it to the DOM tree.

Then it sets the values for the attributes named x and y, which specify the location of the text.

Create and append the child node containing the actual text content

Finally, the method calls the createTextNode method to create a new Text object that encapsulates the actual text.  Whereas the location of the text is specified by the values of two attributes of an element named text, the text itself is the content of that element.  This means that it must appear in the DOM tree as a node in its own right and not simply as the value of an attribute.

The makeText method appends the new Text object to the Element node named text.  This may seem a little strange, but this is the JAXP mechanism for creating a DOM tree that contains nodes with text content.  In other words, in this case, the actual text is encapsulated in the node of type Text, the node of type Text is a child of the Element node named text, and the Element node named text is a child of the node named g.

The corresponding XML code

When the DOM tree is later transformed to XML code, the actual text will appear as the content of an element named text as shown in Figure 6.

Figure 6. A text element.

<text x="200" y="450">200</text>

The XML code produced by the program contains one element like that shown in Figure 6 for each of the text labels shown in Figure 1.  The element shown in Figure 6 represents the text label with the value of 200 about half way across the bottom of Figure 1.

The location of the text

Note that by default, the text characters are drawn above and to the right of the specified location.  Also note that the text in Figure 1 is drawn in the default font and size.  I will have more to say about this in a future lesson that discusses SVG text in detail.

Draw the red and blue triangle

Listing 8 makes another call to the makePath method to draw the red triangle with the blue border shown in the upper left of Figure 1.  Recall that the makePath method can be called to draw many different kinds of paths with the actual path specification being contained in a String object that is passed as the third parameter to the method.

The specification for the path

If you examine the contents of that String object carefully, you will see that Listing 8 uses the moveto (M), lineto (L and l), and closepath (z) commands to cause the makePath method to draw a triangle.  (The commands are highlighted in red boldface in Listing 8 to make them easier for you to identify.  However, they have no color in the actual program code.)

Listing 8. Draw the red and blue triangle.

    Element pathA = SvgGraphics.makePath(
                           document,
                           g,//owner
                           "M 50 10 L 100 10 l -25 40 z");

    pathA.setAttribute("stroke","blue");
    pathA.setAttribute("stroke-width","3");
    pathA.setAttribute("fill","red");

Then Listing 8 calls the setAttribute method three times in succession to color the triangle red, and to give it a blue border that is three pixels wide.

The actual path commands

Don't confuse the lowercase L.
Don't confuse the lower-case L (l) between the numeric values of 10 and negative 25 in Listing 8 with the numeric character for one (1).
For illustration purposes, this example uses one absolute moveto command (M) to establish the starting point for the path, one absolute lineto command (L) to draw the line across the top of the triangle, and one relative lineto command (l) to draw the line on the lower right side of the triangle.  Then it uses one closepath command (z) to draw the line on the lower left side of the triangle and to close the triangle.

Draw three cubic Bezier curves

The purpose of the polyline.
It is very important to note that the purpose of the polyline is solely to show the locations of the start points, the control points, and the end points that will be used to draw the Bezier curve.  The polyline has no functional purpose in the actual drawing of the Bezier curve.
Draw the light red polyline

The next task of the program is to draw the red cubic Bezier curve shown immediately to the right and down from the red triangle in Figure 1.  First, however, the program draws a light red polyline to show the start points, the control points, and the end points that will be used to draw the Bezier curve.  I like to think of the polyline as forming an envelope whose shape will determine the shape of the Bezier curve.

Listing 9. Draw the red polyline.

    Element polylineB = SvgGraphics.makePolyline(
                 document,
                 g,
                 new int[] {100,100,100,50,175,50,175,100,
                                175,150,250,150,250,100});
    polylineB.setAttribute("stroke","#FF8888");
    polylineB.setAttribute("stroke-width","2");

How the polyline describes the start, control, and end points

The Bezier curve will consist of two Bezier segments.  The start point for the first segment is at the beginning of the polyline on the left.  The end point for the first segment is the location where the polyline crosses the blue grid line with a y-value of 100.  The two control points for the first segment are at the two corners of the polyline on the blue grid line with a y-value of 50.

The start point for the second Bezier segment coincides with the end point from the first segment as described above.  The two control points for the second Bezier segment are at the two lower corners of the polyline on the right.  The end point for the second Bezier segment is at the right end of the polyline.

Listing 9 calls the makePolyline method to draw the polyline.  I explained this method in an earlier lesson and should not need to repeat that explanation here.

Draw the red cubic Bezier curve

Listing 10 makes another call to the makePath method to draw the red Bezier curve.  Once again, the contents of the String object passed as the third parameter to the makePath method specify the actual path.  Also, once again the path commands are highlighted in red boldface in Listing 10 to make them easy for you to identify.

Listing 10. Draw the red cubic Bezier curve.

    Element pathB = SvgGraphics.makePath(document,g,
      "M100,100 C100,50 175,50 175,100 S250,150 250,100");
    pathB.setAttribute("stroke","red");
    pathB.setAttribute("stroke-width","2");

Uses absolute coordinate values and the S command

Note that the path specification in Listing 10 uses absolute (uppercase C and S) coordinate values only and also uses the S command (see the earlier section titled Difference between C and S Bezier commands).  Consequently, only two pairs of coordinate values are provided following the S command.

An explanation of the various coordinate values

As mentioned earlier, the red Bezier curve in Figure 1 consists of two Bezier segments.  The start point for the first segment is established by the moveto command (M).  This is followed by a curve command (C) followed by three pairs of coordinate values.  The first two pairs of coordinate values specify the control points for the first segment.  The third pair specifies the end point for the first segment.

This is followed by the special curve command (S).  Recall that the start point for the second and subsequent Bezier segments is always assumed to be the end point for the previous segment.  This eliminates the need for one pair of coordinate values following the S command.

Also recall that this special curve command (S) assumes that the first control point is the reflection of the second control point from the previous segment.  This eliminates the need for another pair of coordinate values following the S.  Therefore, the S command in Listing 10 is followed by only two pairs of coordinate values.  The first pair specifies the second control point for the second Bezier segment.  The second pair specifies the end point for the second Bezier segment.

XML output for the red cubic Bezier curve

Figure 7 shows the actual XML output produced by the program for drawing the red Bezier curve.  This will be useful for comparison with other XML elements later.

Figure 7. XML code for the red cubic Bezier curve.

<path d=
"M100,100 C100,50 175,50 175,100 S250,150 250,100" 
fill="none" stroke="red" stroke-width="2"/>

Draw the green cubic Bezier curve

The code in Listing 11 draws the light green polyline and the dark green cubic Bezier curve shown in Figure 1.  This Bezier curve also consists of two Bezier segments.

Listing 11. Draw the green cubic Bezier curve.

    Element polylineC = SvgGraphics.makePolyline(
                 document,
                 g,
                 new int[] {300,100,320,10,415,50,375,100,
                                335,150,430,190,450,100});
    polylineC.setAttribute("stroke","#88ff88");
    polylineC.setAttribute("stroke-width","2");

    //Now draw the two Bezier segments that constitute the
    // Bezier curve.
    Element pathC = SvgGraphics.makePath(document,g,
                        "M300,100 C320,10 415,50 375,100 "
                             + "335,150 430,190 450,100");
    pathC.setAttribute("stroke","green");
    pathC.setAttribute("stroke-width","2");

Note that this version of the path specification uses absolute coordinates only but does not use the S command.  Therefore, there are six pairs of coordinate values following the C command.  They are, in order:

  1. First control point for the first segment
  2. Second control point for the first segment
  3. End point for first the segment (also start point for the second segment)
  4. First control point for the second segment
  5. Second control point for the second segment
  6. End point for the second segment

XML output for the green cubic Bezier curve

Figure 8 shows the actual XML output produced by the program for drawing the green Bezier curve.

Figure 8. XML code for the green cubic Bezier curve.

<path d=
"M300,100 C320,10 415,50 375,100 335,150 430,190 450,100" 
fill="none" stroke="green" stroke-width="2"/>

If you compare the number of characters in the XML code in Figure 8 with the number of characters in Figure 7, you will see that this approach, (which doesn't take advantage of the S command), is a little less efficient from a download and bandwidth viewpoint than the approach shown in Figure 7.  The relative importance of this difference in download efficiency will, of course, depend on the number of Bezier curves that need to be drawn for a particular graphic.

Draw the blue cubic Bezier curve

The code in Listing 12 draws the light blue triangular-wave polyline that begins at an x-value of 50 and a y-value of 200 in Figure 1.  Then it draws the blue cubic Bezier curve shown inside that triangular envelope.

Listing 12. Draw the blue cubic Bezier curve.

    Element polylineD = SvgGraphics.makePolyline(
                document,
                g,
                new int[] {50,200,100,150,100,150,150,200,
                                200,250,200,250,250,200});
    polylineD.setAttribute("stroke","#8888FF");
    polylineD.setAttribute("stroke-width","2");

    Element pathD = SvgGraphics.makePath(document,g,
        "M50,200 c50,-50 50,-50 100,0 50,50 50,50 100,0");
    pathD.setAttribute("stroke","blue");
    pathD.setAttribute("stroke-width","2");

As before, the blue Bezier curve consists of two Bezier segments.  However, there are at least two things about the Bezier curve in Listing 12 that distinguish it from the previous two Bezier curves shown in Figure 1.

  1. The two control points for each segment have the same coordinate values.  In other words, they coincide or overlay one another.
  2. After the start point has been established using an absolute moveto command (M), all subsequent coordinate values are specified in relative terms using a relative curve command (c)(As a reminder, for a Bezier segment, relative coordinate values are always specified relative to the location of the start point for that segment and not relative to the start point for the Bezier curve as a whole.)

XML code for the blue cubic Bezier curve

The final XML code used to draw the blue cubic Bezier curve is shown in Figure 9.  Note that this example did not take advantage of the relative s command.

Figure 9. XML code for the blue cubic Bezier curve.

<path d=
"M50,200 c50,-50 50,-50 100,0 50,50 50,50 100,0" 
fill="none" stroke="blue" stroke-width="2"/>

Even more efficient from a download viewpoint

If you compare Figure 9 with Figure 8, you will see that the use of relative coordinate values can also improve download efficiency.  This is because the absolute coordinate values are often large values, each one requiring several characters to represent.  On the other hand, relative values often represent changes in absolute coordinate values, and the changes are often small values requiring fewer characters to represent.

Could be made even more efficient

This approach could have been made even more efficient from a download viewpoint by taking advantage of the relative curve command (s) similar to the approach illustrated by Figure 7, but using relative coordinate values instead of absolute coordinate values.

If I did the calculations correctly, the use of the s command would have eliminated five characters from the specification of the second Bezier segment in Figure 9, causing it to contain six or seven fewer characters than Figure 7. 

The benefits of using the s command would continue to mount as the overall Bezier curve becomes longer with additional Bezier segments strung onto the end of the first segment.  An example might be drawing a large contour map based on a large number of elevation samples.

It seems to me, therefore, that if you need to draw long Bezier curves, the best approach is probably to use the relative s command for the second and all subsequent segments if possible.

Draw a quadratic Bezier curve

An exercise for the reader.
Each quadratic Bezier segment defined using the Q or q command has only one control point.  What would you expect to be the outcome if you strung together a large number of segments using the special curve commands T or t in place of Q or q?   As an exercise of your understanding of this material, predict the outcome and then write a short program to test the accuracy of your prediction.

One of the reasons that I drew the blue cubic Bezier curve in Figure 1 is to make it possible to compare the curve-fitting capabilities of a cubic Bezier curve with the curve-fitting capabilities of a quadratic Bezier curve.

Superimpose the quadratic curve on the cubic curve

Figure 1 shows a red quadratic Bezier curve overlaid on top of the blue cubic Bezier curve discussed above.  Recall, however, that a quadratic Bezier segment has only one control point, in addition to the start point and the end point.  This quadratic Bezier curve consists of two Bezier segments.  The locations of the two control points in Figure 1 are at (100,150) and (200,250).  These are the same locations where the each pair of control points for the blue cubic Bezier curve were located on top of one another.  Therefore, if I were to draw a polyline showing the start points, the control points, and the end points for the quadratic Bezier curve, it would exactly overlay the light blue polyline that was drawn for the blue cubic Bezier curve.

How do the curve fits compare?

The objective here is to be able to compare the curve fitting capabilities of the cubic and quadratic Bezier curves.  As you can see in Figure 1, the blue cubic Bezier curve does a much better job of representing, but also smoothing the triangular polyline than the quadratic Bezier curve.  On the other hand, the cubic curve also requires more download data due to the extra control point in each segment, and probably requires more computational resource as well.

The quadratic Bezier curve

Listing 13 shows the call to the makePath method and the two calls to the setAttribute method that draw the red quadratic Bezier curve in Figure 1.  Note that an absolute T command was used.  As a result, there is only one pair of coordinate values following the T command.

Listing 13. The quadratic Bezier curve.

    Element pathE = SvgGraphics.makePath(document,g,
                     "M50,200 Q100,150 150,200 T250,200");
    pathE.setAttribute("stroke","RED");
    pathE.setAttribute("stroke-width","2");

By now, you should be able to understand the code in Listing 13 with no further explanation.

Draw six elliptical arcs

An elliptical arc with no rotation

The code in Listing 14 draws the yellow elliptical arc with a red border shown in Figure 1 to illustrate the appearance of such an arc with no rotation.

Listing 14. An elliptical arc with no rotation.

    Element pathF = SvgGraphics.makePath(document,g,
                        "M230,250 a70,30 0 1,0 50,-50 z");
    pathF.setAttribute("stroke","red");
    pathF.setAttribute("stroke-width","2");
    pathF.setAttribute("fill","yellow");

Recall that I told you earlier that an elliptical arc command is perhaps the most complex of all of the curve commands.  Each elliptical arc command requires seven parameters.  Dissecting the path command in Listing 14 gives the following:

  • An absolute moveto command (M) followed by a pair of absolute coordinate values (230,250) that specify establish the start point of the elliptical arc.
  • A relative curve command (a).
  • Two numeric values (70,30) that specify the horizontal and vertical radii of the ellipse.
  • An x-axis-rotation value of 0.
  • A large-arc-flag value of 1.
  • A sweep-flag value of 0.
  • A pair of relative coordinate values (50,-50) that specify the end point of the elliptical arc.
  • A closepath command (z) that draws the straight-line segment of the yellow elliptical arc shape shown in Figure 1.

An elliptical arc with a 45-degree rotation

The code in Listing 15 draws the red elliptical arc with a yellow border shown in Figure 1 to illustrate the appearance of such an arc with a rotation of 45 degrees.

Listing 15. An elliptical arc with a 45-degree rotation.

    Element pathG = SvgGraphics.makePath(document,g,
                       "M350,250 a70,30 45 1,0 50,-50 z");
    pathG.setAttribute("stroke","yellow");
    pathG.setAttribute("stroke-width","2");
    pathG.setAttribute("fill","red");

The specification of the x-axis-rotation value of 45-degrees is highlighted in red boldface in Listing 15 to make it easy for you to identify.  Other than the rotation, all of the parameter that control the shape of the elliptical arc in Listing 15 are the same as the elliptical arc in Listing 14.

Four combinations of large-arc-flag and sweep-flag

The two parameters large-arc-flag and sweep-flag can each take on either of two values, 0 or 1.  Therefore, there are four possible combinations of these two parameters.

The code in Listing 16 calls the makePath method four times in succession to draw the four elliptical arcs shown across the bottom of Figure 1.

Listing 16. Four combinations of large-arc-flag and sweep-flag.

    //Case 1: Fill color = red, border = blue
    // large-arc-flag = 1
    // sweep-flag     = 0
    Element pathH = SvgGraphics.makePath(document,g,
                         "M50,370 a50,50 0 1,0 50,-50 z");
    pathH.setAttribute("stroke","blue");
    pathH.setAttribute("stroke-width","2");
    pathH.setAttribute("fill","red");

    //Case 2: Fill color = green, border = red
    // large-arc-flag = 1
    // sweep-flag     = 1
    Element pathI = SvgGraphics.makePath(document,g,
                        "M225,400 a50,50 0 1,1 50,-50 z");
    pathI.setAttribute("stroke","red");
    pathI.setAttribute("stroke-width","2");
    pathI.setAttribute("fill","green");
    
    //Case 3: Fill color = blue, border = red
    // large-arc-flag = 0
    // sweep-flag     = 0
    Element pathJ = SvgGraphics.makePath(document,g,
                        "M300,370 a50,50 0 0,0 50,-50 z");
    pathJ.setAttribute("stroke","red");
    pathJ.setAttribute("stroke-width","2");
    pathJ.setAttribute("fill","blue");

    //Case 4: Fill color = yellow, border = blue
    // large-arc-flag = 0
    // sweep-flag     = 1
    Element pathK = SvgGraphics.makePath(document,g,
                        "M375,370 a50,50 0 0,1 50,-50 z");
    pathK.setAttribute("stroke","blue");
    pathK.setAttribute("stroke-width","2");
    pathK.setAttribute("fill","yellow");

Other than the values of large-arc-flag and sweep-flag, all of the parameter that control the shape of these four elliptical arcs are the same.  Therefore, by comparing the code in Listing 16 with the images of the four elliptical arc shapes in Figure 1, you should be able to gain an understanding of how these two parameters impact the appearance of an elliptical arc.

All remaining code is "old stuff"

All of the remaining code in this program is code that I have explained one or more times in earlier lessons (see Resources).  Therefore, I won't repeat those explanations in this lesson.

That concludes my explanation of the path element and the program named Svg12.

Run the program

I encourage you to copy the code from Listing 17 into your text editor, compile it, and execute it.  Load the SVG output file into an SVG-capable graphics engine such as Firefox 1.5.  Experiment with the code, making changes, and observing the results of your changes.

Above all, enjoy the process. Programming, particularly graphics programming, can be fun.

Summary

In this lesson, I taught you how to write Java code that uses an SVG graphics library of my own design and the SVG/XML path element to efficiently draw grid lines, simple geometric shapes composed of straight lines, cubic Bezier curves, quadratic Bezier curves, and elliptical arcs.

What's next?

Future lessons in this series will teach you how to write servlets that:

  • Deal with the following graphics elements:
    • text
    • image (Deal with bit-mapped images in SVG.)
    • use (Create and re-use graphics elements.)
  • Use SVG symbols.
  • Deal with stroke caps in SVG in comparison with similar caps in Java 2D.
  • Use the switch element in SVG.
  • Deal with other features of SVG, such as animation.

Resources

Java 2D Graphics
300 Java 2D Graphics, Nested Top-Level Classes and Interfaces 
302 Java 2D Graphics, The Point2D Class 
304 Java 2D Graphics, The Graphics2D Class 
306 Java 2D Graphics, Simple Affine Transforms 
308 Java 2D Graphics, The Shape Interface, Part 1 
310 Java 2D Graphics, The Shape Interface, Part 2 
312 Java 2D Graphics, Solid Color Fill 
314 Java 2D Graphics, Gradient Color Fill 
316 Java 2D Graphics, Texture Fill 
318 Java 2D Graphics, The Stroke Interface 
320 Java 2D Graphics, The Composite Interface and Transparency 
322 Java 2D Graphics, The Composite Interface, GradientPaint, and Transparency 
324 Java 2D Graphics, The Color Constructors and Transparency
Java 2D API Specification
Java 2D API

Java API for XML Processing (JAXP)
2200 Java API for XML Processing (JAXP), Getting Started
2202 Getting Started with Java JAXP and XSL Transformations (XSLT)
2204 Java JAXP, Exposing a DOM Tree
2206 Java JAXP, Implementing Default XSLT Behavior in Java
2208 Java JAXP, Writing Java Code to Emulate an XSLT Transformation
2210 Java JAXP, Transforming XML to XHTML
Links to numerous XML tutorials by Richard G. Baldwin

Scalable Vector Graphics (SVG)
2212 Java JAXP, Creating graphics using Java and SVG
2214 An improved approach for creating SVG/XML code and SVG/XML DOM nodes using Java
2216 Using Java to produce SVG code in XHTML data
2218 Writing Java servlets to produce XHTML code that references external SVG files
Scalable Vector Graphics (SVG) 1.1 Specification
Adobe SVG Viewer plug-in
Create vector graphics in the browser with SVG by Uche Ogbuji
SVG Tutorial
SVG Basics
SVG in HTML pages

Bézier Curves
What's a Bézier Curve?
Wikipedia, Bézier curve
Bézier Curve Demo

Miscellaneous
W3C Markup Validation Service
XMLvalidation.com
Reflection in a Line
An Intuitive Notion of Line Reflections

Complete program listing

A complete listing of the program that I presented and explained in this lesson is shown in Listing 17 below.

Listing 17. Program code for Svg012

/*File Svg12.java
Copyright 2007 R.G.Baldwin

THE path ELEMENT
The purpose of this program is to demonstrate the use of 
an SVG graphics method named makePath. The SVG graphics
library in the class named SvgClass was updated during the
writing of this program to add several new methods and to
update some of the default values for existing methods.

A BACKGROUND GRID
The program begins by using a new method named 
makeGridString in conjunction with a new method named
makePath to draw a background grid that resembles graph
paper. The makeGridString method uses the moveto command 
and special versions of the horizontal and vertical lineto
command to reduce the amount of XML SVG data needed to
draw the grid. There are light blue horizontal and 
vertical lines every 10th pixel. There are slightly darker
blue lines every 50th pixel. There are even darker blue 
lines every 100th pixel

A RED AND BLUE TRIANGLE
Then the program uses the moveto, lineto, and closepath
commands to draw a triangle.

THREE CUBIC BEZIER CURVES
Then the program draws three cubic Bezier curves,
illustrating the effect of the locations of the control
points on the shape of the curve.

ONE QUADRATIC BEZIER CURVE
Then the program overlays a quadratic Bezier curve on the
third cubic Bezier curve, illustrating the difference in
the degree to which the cubic and quadratic Bezier curves
fit the same envelope.

ELLIPTICAL ARC ROTATION
Then the program draws two elliptical arc curves 
illustrating the effect of the x-axis-rotation parameter
on the appearance of the elliptical arcs.

COMBINATIONS OF large-arc-flag and sweep-flag PARAMETERS
Finally, the program draws four more elliptical arc 
curves, illustrating the difference in shape that results 
from all four combinations of the large-arc-flag and 
sweep-flag parameters.

The program creates a DOM tree describing a specific 
graphic image in SVG format and writes it out into an XML 
file named Svg12.svg.

The output file produced by this program can be rendered 
by loading it into Firefox 1.5.

Tested using J2SE 5.0, Firefox v1.5.0.8, and WinXP.
*********************************************************/

import javax.xml.parsers.*;
import org.w3c.dom.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;

public class Svg12 {

  public static void main(String[] args){
    int width = 450;
    int height = 450;

    //Begin by creating a DOM tree that represents the XML
    // code that will render to produce the image of
    // interest.

    //Get the Document object.
    Document document = SvgGraphics.getDocument();

    //Create the root node named svg and append it to the
    // document.  Specify the parent as null for the
    // special case where the parent is the document.
    Element svg = SvgGraphics.makeNode(
        document,
        null,//parent
        "svg",//node type
        new String[]{"xmlns","http://www.w3.org/2000/svg",
                     "version","1.1",
                     "width",""+ (width + 2),
                     "height","" + (height + 2),
                     "position","absolute",
                     "top","0",
                     "left","0",
                    });//end makeNode method

    //Create a node named g, which will be the parent for
    // several graphic elements. Pass null for the
    // reference to the array object for the special case
    // where the node has no attributes.
    Element g = SvgGraphics.makeNode(document,
                                     svg,//parent
                                     "g",
                                     null);


    //**DIAGONAL LINE**
    //Draw a blue diagonal line that will be used to
    // illustrate the effect of the path element in
    // reducing the size of the download and the attendant
    // bandwidth requirements.
    Element diagLine = SvgGraphics.makeLine(
                                  document,g,0,0,450,450);
    diagLine.setAttribute("stroke","#8888ff");


    //** GRID LINES **
    //Use the moveto command and the special horizontal
    // and vertical lineto commands to draw three sets of
    // grid lines that cause the output image to resemble
    // a sheet of graph paper.
    
    //First draw light blue grid lines every 10 pixels.
    String gridData = SvgGraphics.makeGridString(
                                         width,height,10);
    //Create the path element.
    Element temp;
    temp = SvgGraphics.makePath(document,g,gridData);
    //Set the color to light blue.
    temp.setAttribute("stroke","#ccccff");
    
    //Now draw darker grid lines every 50 pixels 
    gridData = SvgGraphics.makeGridString(
                                         width,height,50);
    temp = SvgGraphics.makePath(document,g,gridData);
    //Set the color to a slightly darker blue.
    temp.setAttribute("stroke","#aaaaff");
    
    //Now draw even darker grid lines every 100 pixels.
    gridData = SvgGraphics.makeGridString(
                                        width,height,100);
    temp = SvgGraphics.makePath(document,g,gridData);
    //Set the color to an even darker blue.
    temp.setAttribute("stroke","#8888ff");
    
    
    //** LABEL THE GRID **
    for(int cnt = 0;cnt < width;cnt += 50){
      SvgGraphics.makeText(document,g,0,cnt,""+cnt);
      SvgGraphics.makeText(document,g,cnt,height,""+cnt);
    }//end for loop
    

    //** RED AND BLUE TRIANGLE **
    //Use the moveto, lineto, and closepath commands to
    // draw a red triangle with a blue border.  This 
    // example uses one absolute moveto command, one 
    // absolute lineto command, and one relative lineto
    // command.  (Don't confuse the lower-case L (1) 
    // between the numeric values of 10 and -25 with the
    // numeric character for one (1).)
    Element pathA = SvgGraphics.makePath(
                           document,
                           g,//owner
                           "M 50 10 L 100 10 l -25 40 z");
    pathA.setAttribute("stroke","blue");
    pathA.setAttribute("stroke-width","3");
    pathA.setAttribute("fill","red");


    //** THREE CUBIC BEZIER CURVES **
    //Draw a red cubic Bézier curve. This curve consists
    // of two Bezier segments. First draw a light red
    // polyline whose nodes coincide with the start
    // points, end points, and control points for the two
    // segments. The purpose of the polyline is solely to
    // show the locations of those points.  In has no
    // functional purpose in the drawing of the curve.
    // In this case, the three start and end points fall
    // on a horizontal line with a y-coordinate value
    // of 100.
    Element polylineB = SvgGraphics.makePolyline(
                 document,
                 g,
                 new int[] {100,100,100,50,175,50,175,100,
                                175,150,250,150,250,100});
    polylineB.setAttribute("stroke","#FF8888");
    polylineB.setAttribute("stroke-width","2");
    
    //Draw the cubic curve. Note the use of absolute
    // coordinate values only and also the use of the S
    // command.
    Element pathB = SvgGraphics.makePath(document,g,
      "M100,100 C100,50 175,50 175,100 S250,150 250,100");
    pathB.setAttribute("stroke","red");
    pathB.setAttribute("stroke-width","2");
    

    //Draw a green cubic Bézier curve. This curve also
    // consists of two Bezier segments. First draw a light
    // green polyline whose nodes coincide with the start
    // points, end points, and control points for the two
    // segments.  
    Element polylineC = SvgGraphics.makePolyline(
                 document,
                 g,
                 new int[] {300,100,320,10,415,50,375,100,
                                335,150,430,190,450,100});
    polylineC.setAttribute("stroke","#88ff88");
    polylineC.setAttribute("stroke-width","2");

    //Now draw the two Bezier segments that constitute the
    // Bezier curve. Note that this version uses
    // absolute coordinates but doesn't use the S command.
    Element pathC = SvgGraphics.makePath(document,g,
                        "M300,100 C320,10 415,50 375,100 "
                             + "335,150 430,190 450,100");
    pathC.setAttribute("stroke","green");
    pathC.setAttribute("stroke-width","2");
    

    //Fit a pair of cubic Bezier segments into a set of
    // start points, end points, and control points where
    // the two control points on each side of the
    // horizontal axis coincide with one another so as to
    // form a triangular envelope. Note that this curve is
    // defined using relative coordinate values and does
    // not use the S command. It is colored blue. Also
    // note that a red quadratic Bezier curve will be
    // drawn later so as to coincide with the start
    // points, end points, and control points of this
    // curve.
    //As a reminder, for a Bezier segment, relative
    // coordinate values are relative to the location of
    // the start point for each segment.
    Element polylineD = SvgGraphics.makePolyline(
                document,
                g,
                new int[] {50,200,100,150,100,150,150,200,
                                200,250,200,250,250,200});
    polylineD.setAttribute("stroke","#8888FF");
    polylineD.setAttribute("stroke-width","2");

    Element pathD = SvgGraphics.makePath(document,g,
        "M50,200 c50,-50 50,-50 100,0 50,50 50,50 100,0");
    pathD.setAttribute("stroke","blue");
    pathD.setAttribute("stroke-width","2");


    //** ONE QUADRATIC BEZIER CURVE **
    //Now fit a pair of quadratic Bezier segments into
    // the same triangular envelope as the above cubic
    // Bezier segments.  Recall that for a quadratic
    // segment, there is a start point, an end point, 
    // and only one control point. In this case, the
    // control points for the two quadratic Bezier
    // segments coincide with the overlaid control points
    // for the cubic Bezier segments discussed above.
    //This quadratic Bezier curve is colored red. This
    // curve uses absolute coordinate values and also
    // uses the T command to join the second segment to 
    // the first segment.  Note that no polyline was
    // created for this example because the same polyline
    // that was used for the cubic Bezier curve discussed
    // above also shows the locations of the start points,
    // end points, and control points for this quadratic
    // curve.
    Element pathE = SvgGraphics.makePath(document,g,
                     "M50,200 Q100,150 150,200 T250,200");
    pathE.setAttribute("stroke","RED");
    pathE.setAttribute("stroke-width","2");


    //** ROTATION OF AN ELLIPTICAL ARC **
    //Draw a yellow elliptical arc with a red border to
    // illustrate the appearance of such an arc with no
    // rotation.
    Element pathF = SvgGraphics.makePath(document,g,
                        "M230,250 a70,30 0 1,0 50,-50 z");
    pathF.setAttribute("stroke","red");
    pathF.setAttribute("stroke-width","2");
    pathF.setAttribute("fill","yellow");
    
    
    //Draw a red elliptical arc with a yellow border to
    // illustrate the appearance of such an arc with a
    // rotation of 45 degrees.
    Element pathG = SvgGraphics.makePath(document,g,
                       "M350,250 a70,30 45 1,0 50,-50 z");
    pathG.setAttribute("stroke","yellow");
    pathG.setAttribute("stroke-width","2");
    pathG.setAttribute("fill","red");

    
    //** PARAMETERS large-arc-flag AND sweep-flag **
    //Demonstrate four combinations of large-arc-flag and
    // sweep-flag
    
    //Case 1: Fill color = red, border = blue
    // large-arc-flag = 1
    // sweep-flag     = 0
    Element pathH = SvgGraphics.makePath(document,g,
                         "M50,370 a50,50 0 1,0 50,-50 z");
    pathH.setAttribute("stroke","blue");
    pathH.setAttribute("stroke-width","2");
    pathH.setAttribute("fill","red");

    //Case 2: Fill color = green, border = red
    // large-arc-flag = 1
    // sweep-flag     = 1
    Element pathI = SvgGraphics.makePath(document,g,
                        "M225,400 a50,50 0 1,1 50,-50 z");
    pathI.setAttribute("stroke","red");
    pathI.setAttribute("stroke-width","2");
    pathI.setAttribute("fill","green");
    
    //Case 3: Fill color = blue, border = red
    // large-arc-flag = 0
    // sweep-flag     = 0
    Element pathJ = SvgGraphics.makePath(document,g,
                        "M300,370 a50,50 0 0,0 50,-50 z");
    pathJ.setAttribute("stroke","red");
    pathJ.setAttribute("stroke-width","2");
    pathJ.setAttribute("fill","blue");

    //Case 4: Fill color = yellow, border = blue
    // large-arc-flag = 0
    // sweep-flag     = 1
    Element pathK = SvgGraphics.makePath(document,g,
                        "M375,370 a50,50 0 0,1 50,-50 z");
    pathK.setAttribute("stroke","blue");
    pathK.setAttribute("stroke-width","2");
    pathK.setAttribute("fill","yellow");
    

    //** OUTLINE ON GRAPHIC DISPLAY **
    Element outline = SvgGraphics.makeNode(
                      document,
                      g,//parent
                      "rect",//type
                      new String[]{"x","1",
                                   "y","1",
                                   "width",""+ width,
                                   "height",""+ height,
                                   "fill","none",
                                   "stroke","black",
                                   "stroke-width","1",
                                  });//end makeNode method
    //** END OF GRAPHICS **
    
    //Transform the DOM and write the output file.
    SvgGraphics.transformTheDom(document,"Svg12.svg");

  }// end main()
  //----------------------------------------------------//
  
}// class Svg12
//======================================================//

//This class was updated on 01/06/07 to add several new
// methods.

//This is a proof-of-concept graphics class that
// provides method calls for the creation of several
// different DOM tree nodes.

//Each method receives a reference to the overall document
// along with a reference to the parent for the new node.
//When the method returns, the new node has been appended
// to the parent node.

//The class also contains some utility methods that are
// useful for the creation of graphic output using SVG.
class SvgGraphics{
  //----------------------------------------------------//

  //This method creates a linear gradient node to which
  // stop elements must be appended.
  static Element makeLinearGradient(Document document,
                                    Element parent,
                                    String id){
    Element gradient = 
        (Element)document.createElement("linearGradient");
    parent.appendChild(gradient);
    gradient.setAttribute("id",id);
    return gradient;
  }//End makeLinearGradient
  //----------------------------------------------------//
  
  //This method creates a radial gradient node to which
  // stop elements must be appended. Note that numeric
  // attributes are set as type String.
  static Element makeRadialGradient(Document document,
                                    Element parent,
                                    String id,
                                    String gradientUnits,
                                    int cx,
                                    int cy,
                                    int r){
    Element gradient = 
        (Element)document.createElement("radialGradient");
    parent.appendChild(gradient);
    gradient.setAttribute("id",id);
    gradient.setAttribute("gradientUnits",gradientUnits);
    gradient.setAttribute("cx",""+cx);
    gradient.setAttribute("cy",""+cy);
    gradient.setAttribute("r",""+r);
    return gradient;
  }//End makeRadialGradient
  //----------------------------------------------------//

  //This method creates a gradient stop node to be
  // appended to a linear gradient node or a radial
  // gradient node.                                      
  static Element makeGradientStop(Document document,
                                  Element parent,
                                  String location,
                                  String color){
    Element stopElement = 
                  (Element)document.createElement("stop");
    parent.appendChild(stopElement);
    stopElement.setAttribute("offset",location);
    stopElement.setAttribute("stop-color",color);
    return stopElement;
  }//End makeGradientStop
  //----------------------------------------------------//
  
  //This method returns a reference to an ellipse. The
  // xCoor and yCoor parameters specify the center of the
  // ellipse.  The xRadius and yRadius parameters specify
  // the width and height of the  ellipse respectively
  // while it is in the horizontal plane before being
  // rotated.  Numeric attributes are set at type String.
  // By default, the ellipse is drawn with a black stroke
  // one pixel wide and a fill of none.
  static Element makeEllipse(Document document,
                             Element parent,
                             int xCoor,
                             int yCoor,
                             int xRadius,
                             int yRadius){
    Element ellipse  = 
               (Element)document.createElement("ellipse");
    parent.appendChild(ellipse);
    ellipse.setAttribute("cx",""+xCoor);
    ellipse.setAttribute("cy",""+yCoor);
    ellipse.setAttribute("rx",""+xRadius);
    ellipse.setAttribute("ry",""+yRadius);
    ellipse.setAttribute("stroke","black");
    ellipse.setAttribute("stroke-width","1");
    ellipse.setAttribute("fill","none");
    return ellipse;
  }//end makeEllipse
  //----------------------------------------------------//

  //This method returns a reference to a circle. The
  // xCoor and yCoor parameters specify the center of the
  // circle.  The radius parameter specifies the radus of
  // the circle.  Numeric attributes are set as type
  // String.  By default, the stroke is black one pixel
  // wide and the fill is none.
  static Element makeCircle(Document document,
                            Element parent,
                            int xCoor,
                            int yCoor,
                            int radius){
    Element circle  = 
                (Element)document.createElement("circle");
    parent.appendChild(circle);
    circle.setAttribute("cx",""+xCoor);
    circle.setAttribute("cy",""+yCoor);
    circle.setAttribute("r",""+radius);
    circle.setAttribute("stroke","black");
    circle.setAttribute("stroke-width","1");
    circle.setAttribute("fill","none");
    return circle;
  }//end makeCircle
  //----------------------------------------------------//
  
  //This method returns a reference to a rectangle. The
  // xCoor and yCoor parameters specify the location of
  // the upper left corner.  The width and height
  // parameters specify the width and the height while
  // the rectangle is in the horizontal plane before
  // being rotated.  Numeric attributes are set as type
  // String.  By default, the stroke is set to black, one
  // pixel wide and the fill is set to none.
  static Element makeRect(Document document,
                          Element parent,
                          int xCoor,
                          int yCoor,
                          int width,
                          int height){
    Element rect  = 
                  (Element)document.createElement("rect");
    parent.appendChild(rect);
    rect.setAttribute("x",""+xCoor);
    rect.setAttribute("y",""+yCoor);
    rect.setAttribute("width",""+width);
    rect.setAttribute("height",""+height);
    rect.setAttribute("stroke","black");
    rect.setAttribute("stroke-width","1");
    rect.setAttribute("fill","none");
    return rect;
  }//end makeRect
  
  //----------------------------------------------------//
  
  //This method returns a reference to a line. x1 and y1
  // specify the starting point of the line before it is
  // rotated. x2 and y2 specify the end point.  By
  // default, the stroke is set to black one pixel wide.
  // This can be overridden to speciy other colors and
  // other widths if you need to do so.
  static Element makeLine(Document document,
                          Element parent,
                          int x1,
                          int y1,
                          int x2,
                          int y2){
    Element line  = 
                  (Element)document.createElement("line");
    parent.appendChild(line);
    line.setAttribute("x1",""+x1);
    line.setAttribute("y1",""+y1);
    line.setAttribute("x2",""+x2);
    line.setAttribute("y2",""+y2);
    line.setAttribute("stroke","black");
    line.setAttribute("stroke-width","1");
    return line;
  }//end makeLine
  //----------------------------------------------------//

  //This method returns a reference to a polyline. The
  // array of type int[] must contain an even number of
  // values for things to work correctly.
  //The values are extracted from the array and treated
  // as coordinate values x1,y1, x2,y2, x3,y3 ... etc.
  // By default, the stroke is set to black one pixel
  // wide with no fill.  This can be overridden to other
  // colors and other widths if you need to do so.
  static Element makePolyline(Document document,
                              Element parent,
                              int[] points){
    Element polyline  = 
              (Element)document.createElement("polyline");
    parent.appendChild(polyline);

    String dataPoints = "";
    for(int cnt=0;cnt<points.length;cnt++){
      dataPoints += "" + points[cnt] + ",";
    }//end for loop
    
    polyline.setAttribute("points",dataPoints);
    polyline.setAttribute("stroke","black");
    polyline.setAttribute("stroke-width","1");
    polyline.setAttribute("fill","none");
    return polyline;
  }//end makePolyline
  //----------------------------------------------------//
  
  //This method returns a reference to a polygon. The
  // array of type int[] must contain an even number of
  // values for things to work correctly.
  //The values are extracted from the array and treated
  // as coordinate values x1,y1, x2,y2, x3,y3 ... etc.
  // By default, the stroke is set to black, one pixel
  // wide with no fill.  This can be overridden to other
  // colors and other widths if you need to do so.
  //The major difference between a polygon and a polyline
  // is that a polyline leaves the last point dangling.
  // However, a polygon automatically draws a line from
  // the last point back to the first point to close
  // the polygon.
  static Element makePolygon(Document document,
                           Element parent,
                           int[] points){
    Element polygon  = 
               (Element)document.createElement("polygon");
    parent.appendChild(polygon);

    String dataPoints = "";
    for(int cnt=0;cnt<points.length;cnt++){
      dataPoints += "" + points[cnt] + ",";
    }//end for loop
    
    polygon.setAttribute("points",dataPoints);
    polygon.setAttribute("stroke","black");
    polygon.setAttribute("stroke-width","1");
    polygon.setAttribute("fill","none");
    return polygon;
  }//end makePolygon
  
  //----------------------------------------------------//
  
  /*
  One of the most frustrating things about using Java
   to create elements in XML, XHTML, or HTML is having
   to deal with the escape characters for the many
   required quotation marks. This method constructs an
   element, which may or may not have attributes. Also,
   the element may or may not be empty.
  The user of this method does not have to deal with the
   required quotation marks surrounding attribute values
   and the corresponding escape characters     
  The first incoming parameter must be true if the
   element is empty and false if the element is not
   empty.
  If the first parameter is true, the element is sealed
   off in the required manner for an empty element. If
   the first parameter is false, the method returns the
   complete start tag for the element but does not
   return a complete element. It is the responsibility
   of the calling method to provide the content and the
   end tag for the element.
  The second parameter to the method must be a String
   that specifies the name of the element.
  The third parameter to the method must be a reference
   to an array object of type String.  This array must 
   contain an even number of elements.  Each pair of 
   elements constitutes the name and the value of an 
   attribute, in the order name, value, name, value, etc.

  If the reference to the array object is null and the
   first parameter is false, the method returns the start
   tag for an element that has no attributes and is not 
   empty.
  If the reference is null and the first parameter is
   true, the method returns a complete empty element with 
   no attributes (which probably doesn't make any sense).
   
  An example of the recommended usage of the method
   follows:
   
  String newElement = SvgGraphics.makeElement(
                  true/false,
                  name,
                  new String[]{"name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value"
                              });//end call to makeElement
   
  */
  
  static String makeElement(
          boolean empty,String elementName,String[] data){

    //Begin constructing the start tag.
    String element = "<" + elementName + " ";
    
    //Deal with elements that have no attributes.
    if((empty==false) && (data == null)){
      //Return a complete start tag.
      return element + ">";
    }else if((empty==true) && (data == null)){
      //Return a complete empty element.
      return element + "/>";
    }//end if

    for(int cnt=0;cnt<data.length;cnt+=2){

      String name = data[cnt];
      String value = data[cnt+1];
      element += name + "=" + """ + value + "" ";
    }//end for loop
    
    if(empty){
      //Terminate the element appropriately for an
      // empty element. A complete empty element will
      // be returned.
      element += "/>";
    }else{
      //End the start tag for an element that is not
      // empty. In this case, only the start tag will
      // be returned.  The calling program must provide
      // the content for the element as well as the end
      // tag for the element.
      element += ">";
    }//end else
      
  return element;
  }//end makeElement
  //----------------------------------------------------//
  
  /*
  The purpose of this method is to create a general node
   having any name, and any number of attributes with any 
   attribute names and any String values for the 
   attributes, or no attributes at all.
   
  The first parameter is a reference to the document to
   which the new node belongs.
  
  The second parameter is a reference to the parent node
   to which this node is to be appended so as to become a
   child of that node. If this parameter is null, the new
   node is appended to the document.  Otherwise, it is
   appended to the specified parent node.
   
  The third parameter is a String that specifies the type
   of node.
  
  The fourth parameter to the method must be a reference
   to an array object of type String.  This array must 
   contain an even number of elements.  Each pair of 
   elements constitutes the name and the value of an 
   attribute, in the order name, value, name, value, etc.
  
  An example of the recommended usage of the method
   follows:
  Element abc = SvgGraphics.makeNode(
                     document,
                     def,//parent could be null
                     "ghi",//node type
                     new String[]{"name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value"
                                 });//end call to makeNode
  */
  static Element makeNode(Document document,
                          Element parent,
                          String nodeType,
                          String[] data){
  
    Element element = 
                (Element)document.createElement(nodeType);
    
    if(parent == null){
      //For the special case of parent equal to null,
      // append the new node to the document.
      document.appendChild(element);
    }else{
      //Otherwise, append the new node to the specified
      // parent.
      parent.appendChild(element);
    }//end else
  
    //Deal with elements that have no attributes.
    if(data == null){
      return element;
    }//end if
    
    for(int cnt=0;cnt<data.length;cnt+=2){
      String name = data[cnt];
      String value = data[cnt+1];
      element.setAttribute(name,value);
    }//end for loop
    
    return element;
  }//end makeNode
  //----------------------------------------------------//
  
  //This method returns a reference to a path. By
  // default, the stroke is set to black one pixel wide,
  // and the fill is set to none.
  //See the method named makeGridString for a utility
  // method designed to create data strings for this
  // method for the special case of drawing grids that
  // resemble graph paper.
  static Element makePath(Document document,
                          Element parent,
                          String d){
    Element path  = 
                  (Element)document.createElement("path");
    parent.appendChild(path);
    path.setAttribute("d",d);
    path.setAttribute("stroke","black");
    path.setAttribute("stroke-width","1");
    path.setAttribute("fill","none");
    return path;
  }//end makePath
  //----------------------------------------------------//

  //This is a utility method that is used to construct a
  // string that describes a grid pattern consisting of 
  // horizontal and vertical lines at a specified pixel
  // spacing for a rectangular area of a specified width
  // and height in pixels. The string is intended to be
  // used as the data string for a call to the method
  // named makePath.
  //Note that this method makes use of the special
  // horizontal and vertical lineto commands in an attempt
  // to reduce the download size and the bandwidth
  // requirements for a drawing containing a large number
  // of horizontal and vertical lines in the grid pattern.
  static String makeGridString(
                        int width,int height,int spacing){
    //Construct the data string for the vertical lines.
    String data = "M0,0 ";
    for(int cnt = 0;cnt < width;cnt += spacing){
      data += "v" + height + " M" + cnt + ",0 ";
    }//end for loop
    //Add the final vertical line.
    data += "v" + height + " n";
    
    //Now add the horizontal lines to the data string.
    data += "M0,0 ";
    for(int cnt = 0;cnt < height;cnt += spacing){
      data += "h" + width + " M0," + cnt + " ";
    }//end for loop
    //Add the final horizontal line.
    data += "h" + width + "n ";
    
    return data;

  }//end makeGridString
  //----------------------------------------------------//

  //This method returns a reference to a text element
  // node.
  static Element makeText(Document document,
                          Element parent,
                          int x,
                          int y,
                          String text){
    Element textNode  = 
                  (Element)document.createElement("text");
    parent.appendChild(textNode);
    
    textNode.setAttribute("x",""+x);
    textNode.setAttribute("y",""+y);
    
    textNode.appendChild(document.createTextNode(text));

    return textNode;
  }//end makePath
  //----------------------------------------------------//
  
  //This is a utility method that is used to execute code
  // that is the same regardless of the graphic image
  // being produced.
  static Document getDocument(){
    Document document = null;
    try{
      DocumentBuilderFactory factory = 
                     DocumentBuilderFactory.newInstance();

      DocumentBuilder builder = 
                             factory.newDocumentBuilder();
      document = builder.newDocument();
    }catch(Exception e){
      e.printStackTrace(System.err);
      System.exit(0);
    }//end catch
    return document;
  }//end getDocument
  //----------------------------------------------------//
  
  //This is a utility method that is used to execute code
  // that is the same regardless of the graphic image
  // being produced.  This method transforms the DOM into
  // raw XML code and writes that code into the output.
  static void transformTheDom(Document document,
                              String filename){
    try{
      //Get a TransformerFactory object.
      TransformerFactory xformFactory =
                         TransformerFactory.newInstance();
           
      //Get an XSL Transformer object.
      Transformer transformer = 
                            xformFactory.newTransformer();
    
      //Get a DOMSource object that represents the
      // Document object
      DOMSource source = new DOMSource(document);

      //Get an output stream for the output file.
      PrintWriter outStream = new PrintWriter(filename);

      //Get a StreamResult object that points to the
      // output file.  Then transform the DOM sending XML
      // code to the file
      StreamResult fileResult = 
                              new StreamResult(outStream);
      transformer.transform(source,fileResult);
    }//end try block

    catch(Exception e){
      e.printStackTrace(System.err);
    }//end catch
  }//end transformTheDom
  //----------------------------------------------------//

}//end class SvgGraphics

 


Copyright

Copyright 2007, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP).  His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments.  (TI is still a world leader in DSP.)  In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel