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

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

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.

[email protected]

Latest Posts

Related Stories