Plotting Engineering and Scientific Data Using Javaby Richard G. BALDWIN
Go to Page: 1 2 Next
Java programing notes # 1468
Preface Preview Discussion and Sample Code Run The Program Summary Commipment Complete Program LISTINGS
Preface
Excellent language for engineering computations Because of its platform independence, Java provides an excellent programming language for engineering and scientific computational experiments, particularly where extreme execution speed is not a requirement. Programs developed for such experiments on one platform can be successfully executed on a variety of platforms without the need to rewrite or recompile. A large Math library Furthermore, because if its inherent simplicity, and the availability of a large Math library, Java provides an excellent programming language for engineers and scientists who want to do their own programming, but who have no desire to become programming experts. The code required to conduct an engineering or scientific computational experiment often consists of little more than the most rudimentary application of arithmetic in loops using data stored in arrays or read from disk files. Now for the bad news However , there is a downside to this happy story .when doing this sort of work, it is often very important to see the results of the experiments in the form of graphs or plots. Unfortunately, the programming required to produce graphical output from simple engineering and scientific computational experiments can not be accomplished using rudimentary programming techniques. Rather, to do this Job Right Requires ConsideRable Expertise In Java Programming.
A generalized plotting program This lesson develops a generalized plotting program, which is easy to connect to other programs, (whether they are simple or complex), in order to display the output from those programs in two-dimensional Cartesian coordinates. The plotting program is specifically designed to be useful to persons having very little knowledge of Java programming. (Actually, the lesson develops two very similar plotting programs each designed to display the data in a different format.)
Viewing tip You may find it useful to open another copy of this lesson in a separate browser window. That will make it easier for you to scroll back and forth among the different listings and figures while you are reading about them. Supplementary material I recommend that You Also Study The Other Lessons in My Extensive Collection Of Online Java Tutorials. You will Find Those Lessons Published at Gamelan.com. However, Asfi THE OF this Writing
t maintain a consolidated index of my Java tutorial lessons, and sometimes they are difficult to locate there. You will find a consolidated index at www.DickBaldwin.com. Preview Figure 1 shows a typical display produced by one of the plotting programs that I will develop in this lesson. (The other program superimposes all of the curves on the same set of axes instead of spacing them vertically as shown in Figure 1.) Figure 1 Sample Display While the plotting program itself is quite complex, the code required to produce the data to be plotted can be very simple. For example, because of the use of the Java Math library, only fourteen lines of simple Java code were required to produce the data plotted in Figure 1. The Graph01Demo program The data displayed in Figure 1 was produced by a Graph01Demo. A listing program named of that program is shown in Listing 37 near the end of the lesson. I will explain that program in detail shortly. A more substantive example in addition, I will provide and discuss another sample program, which produces and plots data having considerably more engineering and scientific significance than the data shown in Figure 1. (This will be a digital signal processing (DSP) example). Even in that case, you will see that the program that produces the data is much less complex than the program used to plot the data. Using the plotting program with your data During the course of this lesson, I will explain everything that you will need to know to cause the output from your own .
The Graph01 program The graphical display of the data shown in Figure 1 was produced by my generalized plotting program named Graph01. As you will see later, this is a long and fairly complex program. A listing of the plotting program is shown in Listing 39 near the end of the lesson. User need not understand the plotting program fortunately, the user of the plotting program does not need to understand anything about the code that makes up the plotting program. All the user needs to understand is the interface to the program, which I will explain later. However, for those of you who may be interested, I will also discuss and explain the plotting program later in this lesson. Plotting format As you can see in Figure 1, the plotting program allows for plotting up to five independent functions stacked vertically, each with the same vertical and horizontal axes. This vertical stacking format makes it easy to compare up to five plots at the same points on the horizontal axes. If you need more than five functions, the number of functions can easily be increased with a few minor changes to the program. (I will also provide, but will not discuss, another version of the program, named Graph02, which superimposes up to five plots on the Same Coordinate System. in Some Cases, That Is A More Useful Form of Display. You Will Find A Complete Listing Of this Program in listing 40 Near the end of the length.)
Plotting parameters As you can also see in Figure 1, a set of text fields and a button on the bottom of the frame make it possible for the user to modify the plotting parameters and to re-plot the same data with an entirely new set of Plotting parameters.
(It is often true that important but subtle pieces of information can only be exposed by viewing the same data with different sets of plotting parameters.) Same data, different parameters Figure 2 shows the same data as in Figure 1, but plotted with a different set of plotting parameters. Figure 2 Sample Display for Same Data with Different Plotting Parameters In the case of Figure 2, the origin was moved to the left, the total expanse of the horizontal axis was increased, and the space between the tic marks on the Vertical Axis Was Increased from 20 Units to 50 Units.
(IT WILL HELP you to see the differences if you will position two browser windows side-by ")
Discussion and Sample Code
Testing the plotting program I am assuming that you have accomplished the minimal steps required to get the Java SDK that is available from Sun up and running To run the plotting program named Graph01 in self-test mode, do the following.:
Copy the code in Listing 39 into a file named Graph01.java. Copy the code in Listing 1 into a file named GraphIntfc.java, and put that file in the same directory as the file named Graph01.java above. Compile the program named Graph01 .java using the Java SDK. (Note, you must be using SDK version 1.4 or later.) At this point, you should be able to execute the program named Graph01 in self-test mode by entering the following command at the command prompt in the same directory where you compiled the program: java Graph01 If everything has been done correctly up to this point, the display shown in Figure 4 should appear on your screen Using the plotting program to use the plotting program with your own data generator program,. Do The Following:
Still working in the same directory, define and compile a data generator class that implements the interface named GraphIntfc01, shown in Listing 1. Start the plotting program named Graph01 running by following the instructions that I will provide below. Plotting your data using Graph01 Assume that your data-generator class is named MyData, and that you have successfully compiled it in the same directory as the compiled version of Graph01. The next step is to enter the following command at the command prompt in the same directory. (Note that this command differs from the command given earlier. This command provides the name of your class as a command-line parameter following the name of the plotting program.) java Graph01 MyData When you do this, the plotting program should start pulling the necessary data from your data -Generator Program and Plotting That Data In The Format Shown in Figure 1. Modifying Plotting Parameters Once All The Curves Have Been Plotted, You Can Change Any Of the plotting parameter values in the text fields at the bottom of the display and press the button labeled Graph. When you press the button, the plotting program will re-plot your data using the new plotting parameters. The plotting parameters Here is the meaning of The Plotting-Parameter Text Fields Shown in Figure 1:
xMin and xMax - The values of the left and right ends of all horizontal axes yMin and yMax -. The values of the bottom and top of the vertical axis in each plotting area (Note that the different plotting areas are identified by alternating white and. gray backgrounds) xTicInt -. The distance between tic marks on the x-axis yTicInt -. The distance between tic marks on the y-axis xCalcInc -. The distance between the points on the x-axis where values for y are computed (. Unless your data-generator program is taking too long to run, you should probably leave this set to 1.0 in order to get the best quality plots.) The labels on the axes Each x-axis has a label at the left end and the right end. Similarly, each y-axis has a label at the bottom and the top. These labels represent the values at the extreme ends of the axes. For example in Figure 2, the label 800 appears at the right end of each x-axis . This is value of the x-axis where the axis intersects the border of the frame. Keep the pixels in mind When adjusting the plotting parameters, keep in mind that the total width of each of the plotting areas is slightly less than 400 pixels. (You can easily increase this to full screen width by changing one value in the Graph01 program and recompiling The program.
While you can theoretically make the horizontal expanse of the x-axes as wide as you wish, because of the pixel limitation, you can not see details that require a resolution of more than 400 points along the x-axis (unless you modify the program as described above) The interface named GraphIntfc01 Regardless of its simplicity or its complexity, there are only two requirements for your data-generator program to operate successfully with the plotting program named Graph01:.. It must implement the interface named GraphIntfc01 It must have a constructor that does not require any parameters (the default constructor will satisfy that requirement if you do not need another constructor). Implementing GraphIntfc01 All that is required to implement the interface is to define a class that provides a concrete definition for the six methods declared IN LISTING 1.
Public interface graphintfc01 {
Public int Getnmbr ();
Public Double F1 (double x);
PUBLIC DOUBLE F2 (Double X);
Public Double F3 (Double X);
Public Double F4 (Double X);
Public Double F5 (Double X);
} // end graphintfc01
Listing 1 The getNmbr method On several occasions, I have stated that the plotting program can plot up to five functions. However, it does not have to plot all five functions. The plotting program can be used to plot any number of functions from one to five. The method named getNmbr must return an integer value between 1 and 5 that specifies the number of functions to be plotted. The plotting program uses that value to divide the total plotting surface into the specified number of plotting areas, and plots each of the functions named f1 through fn in one of those plotting areas. The methods named f1, f2, f3, f4, and f5 As you can see in Listing 1, each of these methods receives a double value as an incoming parameter and returns a double value. in essence, each of these methods receives a value for x and returns the corresponding value for y. One plotting area per method each of these methods provides the data to be plotted in one plotting area. The method named f1 provides the dat a for the top plotting area, the method named f2 provides the data for the first plotting area down from the top, and so forth. (For example, if the getNmbr method returns a value of 4, the method named f5 will never be invoked . IF GetNMbr Returns 5, The Method Named F5 Will Be Invoked To Provide The Data for the Bottom Plotting Area.)
How does it work? Each plotting area contains a horizontal axis. The plotting program moves across the horizontal axis in each plotting area one step at a time (moving in incremental steps equal to the plotting parameter named xCalcInc). At each step along the way , the plotting program invokes the method associated with that plotting area, (f1, f2, etc.), passing the horizontal position as a parameter to the method. The value returned by the method is assumed to be the vertical value associated with that horizontal position, and that is the vertical value that is plotted for that horizontal position. does not know and does not care The plotting program does not know, and does not care how the method decides on the value to return for each value that it receives as an incoming parameter. The plotting program simply invokes the methods to get the data, and then plots the data. computed "on the fly" For example, the returned values could be computed and returned "on the fly,"
as is the case in the example named Graph01Demo, which we will look at shortly. Returned from an array On the other hand, the values could have been computed earlier and saved in an array, as will be the case in the DSP example that we will look at later. from a disk file, a database, the internet, etc. The returned values could be read from a disk file, obtained from a database on another computer, or obtained from any other source such as another computer on the internet . All that matters is that when the plotting program invokes one of the five methods named f1 through f5, passing a double value as a parameter, it expects to receive a double value as a return value, and it will plot the value that it receives . It is up to you It is up to you, the author of the data-generator program, to decide how you will implement the methods named f1 through f5. In some cases, the implementation may be simple. In other cases, the implementation May Be More Complex. The First Case That We Will examine is very simple. A subsequent case involving DSP is not so simple. The class named Graph01Demo Although this is a very simple class definition, I am going to break it up and discuss it in fragments in order to help you focus your attention on the important points. A complete listing of the class definition is shown in Listing 37 near the end of the lesson. Defining data-generator classes This class is used to demonstrate how to write data-generator classes that will operate successfully with the program named Graph01. .........................
Listing 2 DEFINITION, WHICH NAMES THE CLASS
IMPLEMENTS graphintfc01 {
.
Public int GetNMBR () {
Return 5;
} // end genmbr
Listing 3 Recall from above that this method must return an integer value between 1 and 5, which tells the plotting program how many functions to plot. This demonstration plots all five functions, as shown in Figure 1, so this method returns the value 5. The topmost plotting area Listing 4 shows the entire method named f1, whose output is plotted in the topmost plotting area of the display in Figure 1. (This is the area at the top with the white background.)
Public Double F1 (double x) {
RETURN - (x * x) / 200.0;
} // end f1
Listing 4 This method receives an incoming parameter known locally as x. (In all five methods defined in this class, the computations are performed on the fly.) The method computes and returns the negative square of the incoming parameter (divided by 200). This produces the inverted bowl shape at the top of Figure 1. The top-most plotting area with a gray background The curve plotted in the top-most plotting area with the gray background in Figure 1 is produced by the method named f2, shown in Listing 5.
PUBLIC DOUBLE F2 (double x) {
RETURN - (x * x * x) / 200.0;
} // end f2
Listing 5 As before, this function receives an incoming parameter known locally as x. The function computes and returns the negative cube of the incoming parameter (divided by 200). This produces the curve shown in the top-most gray area of Figure 1. The Middle White Plotting Area The Method Named F3, Shown in Listing 6, Products The Curve Shown In The Center Plotting Area with The White Background in Figure 1.public Double F3 (Double X) {
Return 100 * Math.cos (X / 10.0);
} // end f3
Listing 6 This is a simple cosine curve, which is computed on the fly. Each time the method is invoked, the incoming parameter named x, is used to calculate the cosine of an angle in radians given by one-tenth the value of x. The cosine of what angle is multiplied by 100 and returned.
(Note That the cosine of the Angle is complerd)
The Bottom-Most Gray Plotting Area The Curve Shown in The Bottom-Most Gray Plotting Area of Figure 1 Is Producted by The Method Named F4, Shown in Listing 7.
PUBLIC DOUBLE F4 (Double X) {
Return 100 * math.sin (x / 20.0);
} // end f4
Listing 7 The body of f4 is similar to the body of f3, except that f4 computes and returns sine values instead of cosine values. Also, the value of x is used differently so that the period of the curve produced by f4 is twice the period Of the curve produdued by f3. The Bottom White Plotting Area Finally, The Bottom White Plotting Area in Figure 1 Shows The Output Produced by The Method Named F5, Shown In Listing 8.
Public Double F5 (double x) {
Return 100 * (Math.sin (X / 20.0)
* Math.cos (X / 10.0));
} // end f5
} // end sample class graph01demo
Listing 8 This method computes and returns the product of sine and cosine functions identical to those discussed above. The end of the class definition Listing 8 also shows the closing curly brace that signifies the end of the class definition for the class named Graph01Demo. That's all You ued to know what '
s really all that you need to know to be able to make effective use of the generalized plotting program named Graph01. If you can define the methods named f1 through f5, which will return the required values for your computational experiment, then you can make use of this program to plot your data. A more substantive example However, lest you go away believing that this is all too trivial to be interesting, I am going to show you another example that is far from trivial. In the next example, I will demonstrate two of the most important operations in the field commonly referred to as digital signal processing, or DSP for short. Because many of you are unlikely to be familiar with the techniques and terminology involved, the discussion will of necessity be fairly shallow. However, I do Want To Show At Least One Example of How You CAN Perform Substantive Computational Experiments Using this Approach. A DSP Example A DSP Example Showing Convolution Filtering and Spectral Analysis IS SHO wn in Figure 3. Figure 3 A Digital Signal Processing (DSP) Example In the field of DSP, the five individual plots shown in the plotting areas of Figure 3 are commonly referred to as traces. I will use that terminology in this discussion. White random noise The top trace in the area with the white background shows about 256 samples of white random noise. When we get to the code, we will see that this data was created using a Java pseudo-random number generator. A convolution filter The second trace from the top shows a 33-point narrow-band convolution filter, which is simply a chunk taken out of a sinusoid whose frequency is one-fourth the sampling frequency. In other words, the sinusoid is represented by four samples per cycle.
The convolution filter output The middle trace shows the result of applying the narrow-band convolution filter to the white noise. The output from the convolution process was amplified to bring it back into an appropriate amplitude range for visual analysis. If you compare the middle trace with the top trace, you will notice that much of the high-frequency energy and much of the low-frequency energy has been removed. Most of the energy in the middle trace appears to be about the same frequency as the frequency of the convolution filter (which is what we would expect). Time-domain vs. frequency-domain The top three traces represent information in the time domain. The bottom two traces represent information in the frequency domain. (Think of the frequency domain as the information that is Visible ON Many Audio Systems, Consisting Of Parallel Vertical Bars With Lights That Dance Up and Down. There Lights Are Off To As a Frequency Equalizer. WHEN THE music contains a lot of drums, or other sounds at the bass end, the lights at the low (usually left) end of the frequency spectrum are very active. When the music contains a lot of symbols, or sounds at the treble end, the Lights at the high (right) end of the frequency spectrum all us at the frequency. That is a form of real-time spectrum analyysis.
Frequency spectrum analysis The two bottom traces in Figure 3 result from performing frequency spectrum analysis on the top trace and the middle trace respectively. The white noise spectrum The trace in the gray area immediately below the center is an estimate of the spectral distribution of the white noise in the top trace. The spectrum analysis was performed across the frequency range from zero frequency to the sampling frequency. While not perfectly flat, as would be the case for perfectly white noise, you can see that the energy appears to be distributed across that Entire Range. (IF We Wanted to Improve Our Estimate, We Could Capture and Analyze A Much Longer Sample of The White Noise.)
If you examine this trace carefully, you might notice that there is a point of near symmetry in the middle. The values that you see above that point are a mirror-image of the values that you see below that point. (I will have more to say about this later.) The filtered noise spectrum The bottom trace shows an estimate of the spectral distribution of the filtered noise in the center trace. Again, the spectrum analysis was performed across the frequency range from zero frequency to the sampling frequency. Again also, there is a symmetry point in the middle with everything to the right of that point being a mirror image of everything to the left of that point. Two spectral peaks are visible Unlike the spectral analysis of the white noise, this spectral analysis shows two obvious peaks. One peak appears at one-fourth the sampling frequency, and the other peak appears at three-fourths the sampling frequency. In other words, as we concluded from examining the center trace, the filtering process removed much of the energy above and below the design frequency of the convolution filter. (By changing the design frequency of the convolution filter, and repeating the process, we could move this peak up or down along the frequency axis.)
What does the symmetry mean? Without getting into a lot of detail at this point, the point of symmetry that I identified above is known as the Nyquist folding frequency. In order to be able to identify the frequency of a sine wave, you must have at least two samples per cycle of the sine wave. The Nyquist folding frequency is the frequency at which you have exactly two samples per cycle. As the frequency of the sine wave continues to increase beyond that point, without a corresponding change in the sampling frequency , it is impossible to determine from the samples so obtained whether the frequency is increasing or decreasing. An ambiguity in the spectrum analysis As a result, the spectrum analysis process was unable to determine if the peak in the frequency spectrum was below or above the folding Frequency. Thus, The Bottom TRACE IN FIGURE 3 Shows Two Peaks Which Are Mirror Images of One Another with the folding frequency being hAlf Way Between The Two Peaks. (AS A Practical Matter, W hen doing spectrum analysis, there is no point in computing the values above the folding frequency. I did that here just to illustrate that there is a folding frequency, which is equal to one-half the sampling frequency.)
Let '
s see some code The class used to produce the data displayed in Figure 3 is named Dsp002. A complete listing of this class definition is shown in Listing 38 near the end of the lesson. I will break this class up into fragments and briefly discuss it to show how you can define significant classes and easily connect them to the generalized plotting program named Graph01 As before, having compiled the class named Dsp002, you would exercise it by entering the following at a command prompt.:
java Graph01 Dsp002 Different from the previous example class This class differs from the class named Graph01Demo in one very significant way. In that class, all the values returned by the methods named f1 through f5 were computed on the fly as the methods were called. In this new class named Dsp002, all the data is generated and stored in array objects when an object of the class named Dsp002 is instantiated. When the methods named f1 through f5 are invoked later, they simply retrieve the data from the array objects and return that data to the plotting program. Basic operation of the program As mentioned earlier, this program applies a narrow-band convolution filter to white noise, and then computes the amplitude spectrum of the filtered noise using a Discrete Fourier Transform algorithm (DFT). The spectrum Of The White Noise Is Also Computed. All of the processing occurshen an Object of the class is instantiated, and the process results area saved in arrays. The input noise, t he filter, the filtered output, and the two spectra are deposited in five arrays for later retrieval and display. The data in the five arrays are returned by the methods named f1, f2, f3, f4, and f5 respectively. The values that are returned by the methods are scaled for appropriate display in the plotting areas provided by the program named Graph01. Data and filter lengths The code in Listing 9 establishes the data lengths for the white noise, the convolution filter, the filtered output, and the spectrum. Class DSP002 IMPLEMENTS graphintfc01 {
INT operatorlen = 33;
INT DATALEN = 256 Operatorlen;
INT OUTPUTLEN =
Datalen - Operatorlen;
INT spectrumpts = outputlen;
Listing 9
Create Data Arrays The Code in Listing 10 Creates The Array Objects That Will Be Used To Store The Data Until It Is Retrieved by The Methods Named F1 THROUGH F5.DOUBLE [] DATA = New Double [DataLen];
Double [] Operator =
New Double [Operatorlen];
Double [] Output =
New double [outputlen];
Double [] Spectruma =
New double [Spectrumpts];
Double [] spectrumb =
New double [Spectrumpts];
Listing 10 Generate and save the white noise Most of the hard work is done by the constructor or by methods called by the constructor. The code in Listing 11 generates and saves the white noise in the array object named data.
Public dsp002 () {// constructor
Random generator = new random
New date (). gettime ());
For (int CNT = 0; CNT CNT ) { // Get Data, Scale IT, Remove The // DC Offset, And Save IT. Data [CNT] = 100 * generator. Nextdouble () - 50; } // End for loop Listing 11 The random noise generator seed Note that by virtue of the way this white noise is being generated, a different seed is passed to the constructor for the Random class each time an object of the Dsp002 class is instantiated. Thus, each new object presents DiffERENT Random Noise. (In Some Cases) Create The Convolution Operator The Core in Listing 12 Creates The 33-Point Convolution Operator, AS A Segment of a Cosine Wave, And Saves It in The Designated Array. For (int CNT = 0; CNT CNT ) { Operator [cnt] = math.cos CNT * 2 * math.pi / 4); } // End for loop Listing 12 Note that the constant value of 4 in the denominator of the argument to the cos method specifies the frequency of the cosine wave relative to the sampling frequency. (In this case, the frequency of the cosine wave is one-fourth the sampling frequency .) Apply the convolution operator The code in Listing 13 invokes a static method named convolve in a class named Convolve01 to apply the convolution operator to the white noise and save the filtered result in the appropriate array. I will briefly discuss this method later.Convolve01 .convolve (Data, Datalen, Operator, operatorlen, output; Listing 13 Compute the spectrum of the two traces The code in Listing 14 invokes a static method named dft of a class named Dft01 twice in succession to compute the spectra for the white noise and the filtered noise, and to save those spectra in the appropriate arrays . DFT01.DFT (Data, Spectrumpts, Spectruma); DFT01.DFT (Output, Spectrumpts, Spectrumb); } // End Constructor Listing 14 All results have been computed and saved That is the end of the constructor. At this point, all the results have been computed and saved in the appropriate arrays for later retrieval by the methods named f1 through f5. The object is simply setting in memory waiting to have its encapsulated data retrieved and plotted. The getNmbr method The getNmbr method for this class is exactly the same as for the class discussed earlier. As before, it returns the integer value 5, telling the plotting program that there are five plots To be generated. Public int GetNMBR () { Return 5; } // end genmbr Listing 15 The method named f1 The method named f1 is representative of all five of the methods named f1 through f5 in this class. Therefore, I will discuss only the first of the five methods in detail.public double f1 (double x) { INT index = (int) math.round (x); IF (INDEX <0 || Index> Data.Length-1) { Return 0; } else { Return Data [index]; } // END ELSE } // end f1 Listing 16 In all five cases, the purpose of the method is to fetch and return a value from an array, where the incoming parameter will be converted to an array index. Convert incoming parameter to an index The incoming parameter is received as type double. However, an array must be indexed using type int. The first statement in the method invokes the round method of the Math class to convert the double value to the nearest integer. That integer will be used as an array index. Stay within array bounds Following this, the method applies some logic to confirm that the index value is within the bounds of the array. If not, the method returns the value 0. If the index is within the array bounds, the method retrieves and returns the value stored at that index location in the array. And that's all there is to it. Methods f2 through f5 Except for the scale factors applied to the data before returning it, the behavior of the methods named f2 through f5, is essentially the same as the behav ior of the method named f1. In each case, the method retrieves, scales, and returns a value previously stored in an array. Therefore, I will not discuss these other methods. You can view them in Listing 38 near the end of the lesson. And that ends the definition of the class named Dsp002. The class named Convolve01 The entire class named Convolve01 is shown in Listing 17. If you already understand convolution, you will probably find the code in this class straightforward. If not, the code Will Probably Still Be Straightforward, But The Reason for the code may be obscure.class convolve01 { Public Static Void Convolve Double [] DATA, Int Datalen, Double [] Operator, Int Operatorlen, Double [] Output) { // Apply the Operator to the data, // dealing with the index // Reversal Required by // convolution. For (int i = 0; I Output [i] = 0; For (int J = Operatorlen-1; J> = 0; J -) { Output [i] = Data [i j] * Operator [J]; } // end inner loop } // End Outer Loop } // End convolve method } // End class convolve01 Listing 17 Making a long story short To make a long story short, the class named Convolve01 provides a static method named convolve, which applies an incoming convolution operator to an incoming set of data and deposits the filtered data in an output array whose reference is received as an incoming parameter. This class could easily be broken out and put in a library as a stand-alone class, or the convolve method could be added to a class containing a variety of DSP methods. The discrete Fourier transform (DFT) The entire class named Dft01 is shown in Listing 18. As with convolution, if you already understand the discrete Fourier transform, you will probably find the code in this class to be straightforward. If not, the code will probably still be straightforward, but the reasons for . ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, Class DFT01 { Public Static Void DFT Double [] DATA, Int Datalen, Double [] Spectrum) { // set the frequency increment to // the Reciprocal of the data // Length. this is convenience @ @ly, and is not a requestment // of the dft algorithm. Double DELF = 1.0 / datalen; // Outer Loop Iterates on Frequency // VALUES. For (int i = 0; i Double FREQ = I * Delf; double real = 0; Double IMAG = 0; // inner loop itrates on time- // Series Points. For (int J = 0; J Real = Data [J] * Math.cos 2 * math.pi * freq * j); IMAG = DATA [J] * Math.sin 2 * math.pi * freq * j); Spectrum [i] = math.sqrt Real * Real iMag * iMag); } // end inner loop } // End Outer Loop } // end DFT Listing 18 Brief explanation Once again, to make a long story short, this class provides a static method named dft, which computes and returns the amplitude spectrum of an incoming time series. The amplitude spectrum is computed as the square root of the sum of the squares of the real and imaginary parts. A DFT algorithm can compute any number of points in the frequency domain. in this case, the number of points computed in the frequency domain is equal to the number of samples in the incoming time series, which is a fairly common practice. The method deposits the frequency data in an array whose reference is received as an incoming parameter. As with convolution, this class could easily be broken out and put in a library as a stand-alone class, or the dft method could be added to a class containing a variety of DSP methods. The plotting programs Now that you have examined the examples, some of you may be interested in an explanation of the plotting program itself. If you are interest ed only in how to use the plotting programs, and are not interested in the details of the plotting programs, skip ahead to the section entitled Run the Program. If you are interested in learning how the plotting programs do what they do, keep reading. Two plotting programs Two very similar plotting programs are shown in the listings near the end of the lesson. The program named Graph01, shown in Listing 39, can be used to plot as many as five separate functions, each in its own plotting area. Examples . The program named Graph02, shown in Listing 40, can also be used to plot as many as five separate functions. In this case, however, the graphs produced by the functions are superimposed in the same plotting area. This is simply an alternative display format . I will not discuss any of the particulars of this program, but if you understand the program named Graph01, you will have no difficulty understanding this program named Graph02 well. The program named Graph01 This program is designed to access a class file that implements the interface named GraphIntfc01, and to plot up to five functions defined in that class file. The methods in the class corresponding to the functions to be plotted are named f1, f2, f3, f4, and f5. As you learned in the earlier discussion , the class containing the functions must also define a static method named getNmbr. This method takes no parameters and returns the number of functions to be plotted. If this method returns a value greater than 5, a NoSuchMeth odException will be thrown. Separate plotting areas The overall plotting surface is divided into the required number of equally sized plotting areas. One function is plotted on Cartesian coordinates in each plotting area. A noarg constructor is required The constructor for the class that implements GraphIntfc01 must not require any parameters. This is because the newInstance method of the class class is used to instantiate an object, based on a String provided as a command-line parameter. The newInstance method can only create objects using a noarg constructor. Some methods may not be invoked If the getNmbr method returns a value less than 5, then the methods that will not be invoked begin with f5 and work down toward f1. For example, if the value returned by getNmbr is 3, then the program will invoke the methods named f1, f2, and f3. While the methods named f4 and f5 must exist in order to satisfy the interface, they will not be invoked. Therefore, it does not matter what those methods return as long as it is type double. The visual appearance As shown in Figure 1, the plotting areas have alternating white and gray backgrounds to make them easy to separate visually. All curves are plotted in black. A Cartesian coordinate system with axes, tic marks, and labels is drawn in red in each plotting area. The Cartesian coordinate system in each plotting area has the same horizontal and vertical scale, as well as the same tic marks and labels on the axes. The labels displayed on the axes, correspond to the values of the extreme Edges of the Plotting area. A test class The program also compiles a test class named junk, which contains the five required methods plus the method named getNmbr. This makes it easy to compile and test the program in a stand-alone mode. Usage instructions At runtime, the . If the command-line parameter is missing, the program instantiates an object from the internal test class named junk and plots the data provided by that object. Thus, you can test the program by running it with no command-line parameter. This will produce the display shown in Figure 4. Figure 4 Graphic Display for Self-Test class If the command-line parameter is provided, the program instantiates an object of the class whose name matches the parameter, and plots the data provided by that object. Plotting parameters This program provides the following text fields for user input, along with a button labeled Graph This allows the user to adjust the parameters and re-plot the graph as many times as needed with as many different plotting scales as may be needed:. xMin = minimum x-axis value xMax = maximum x-axis value yMin = minimum y-axis value yMax = maximum y-axis value xTicInt = tic interval on x-axis yTicInt = tic interval on y-axis xCalcInc = calculation interval The user can mod ify any of these parameters and then press the Graph button to cause the five functions to be re-plotted according to the new parameters. A new object Whenever the Graph button is pressed, the event handler for that button instantiates a new object of the class that implements the GraphIntfc01 interface. Depending on the nature of that class, this may be redundant in some cases. However, it is useful in those cases where it is necessary to refresh the values of instance variables defined in the class (such as a counter , for example. (I Will show you how to eliminate this feature from the plotting program if you decide That IT IS Unnecessary for your data.) Requires Java SDK 1.4 or later This program uses constants that were first defined in the Color class of v1.4.0. Therefore, the program requires v1.4.0 or later to compile and execute correctly. Will discuss in fragments I will discuss this program in fragments . As mentioned earlier, a complete listing of the program is provided in Listing 39 near the end of the lesson. You should be able to copy and paste that code into your Java source file, and then compile and execute it successfully. The class named Graph01 the entire class, incruding the main method is shown in listing 19.class graph01 { Public static void main String [] ARGS) Throws nosuchmethodexception, ClassNotFoundException, InstantiationException, IllegaCCESSEXCEPTION { IF (args.length == 1) { // Pass Command-Line Parameter New GUI (Args [0]); } else { // no command-line parameter given New Gui (NULL); } // END ELSE } // end main } // End class graph01 definition Listing 19 The primary purpose of main method is to instantiate an object of the class named GUI. In addition, the main method checks to see if the user provided a command-line parameter, and if so, passes it along to the constructor for the GUI Class. The class named gui the beginning of the gui class is shown in listing 20. Class GUI Extends Jframe Implements anctionsListener { // define plotting parameters and // Their default value. Double xmin = -10.0; Double XMAX = 256.0; Double ymin = -100.0; Double YMAX = 100.0; // Tic Mark Intervals Double Xticint = 16.0; Double Yticint = 20.0; // Tic Mark Lengths. If Too Small // on X-Axis, a Default Value IS // useful. Double Xticlen = (YMAX-YMIN) / 50; Double Yticlen = (XMAX-XMIN) / 50; // Calculation Interval Along X-Axisdouble Xcalcinc = 1.0; // Text Fields for PLOTTING Parameters JtextField Xmintxt = New jtextfield (" xmin); JtextField XMaxTxt = New jtextfield (" xmax); Jtextfield Ymintxt = New jtextfield (" ymin); Jtextfield ymaxtxt = New jtextfield (" ymax); JtextField Xticinttxt = New JtextField ("" xticint); JtextField Yticinttxt = New jtextfield (" yticint); Jtextfield Xcalcinctxt = New jtextfield (" xcalcinc); // panels to contain a label and a // Text Field JPanel PAN0 = New jPanel (); JPANel PAN1 = New jPanel (); JPANel PAN2 = new jPanel (); JPanel Pan3 = New jPanel (); JPANel PAN4 = New jPanel (); JPANel PAN5 = New jPanel (); JPanel Pan6 = New jPanel (); // Misc Instance Variables INT frmwidth = 400; INT frmheight = 430; Int width; INT height; Int number; Graphintfc01 data; String args = NULL; // Plots Are Drawn on the canvarated // in this arch. Canvas [] canvas; Listing 20 The code in Listing 20 declares and in some cases initializes several instance variables that are required later to support the plotting process. The comments and the names of the variables generally indicate the purpose of those variables. The constructor for the GUI class The beginning Of the constructor for the gui class is shown in listing 21. Gui (String Args) Throws Nosuchmethodexception, ClassNotFoundException, InstantiationException, IllegaCCESSEXCEPTION { IF (args! = null) { // Save for use later in the // ActionEvent Handler THIS.Args = args; // instantiate an Object of the // Target class using the string // Name of the class. Data = (graphintfc01) Class.Forname (ARGS). NEWINSTANCE (); } else { // instantiate an Object of the // Test class named junk. Data = new junk (); } // END ELSE Listing 21 The main purpose of the code in Listing 21 is to instantiated the object that will provide the data to be plotted. If the user provided the name of a class as a command-line argument, an attempt will be made to create a newInstance Of That Class. (In case you are unfamiliar with this approach, this is one Way to create an Object of a class whose name isotime.) Otherwise, the code in Listing 21 will instantiate an object of the test class named junk (to be discussed later). Array to hold Canvas objects Each of the separate plotting areas in Figure 1 is an object of a class that extends the Canvas class. ..................... .. // Create Array to Hold Correct // Number of Canvas Objects. Canvastes = New canvas [data.getnmbr ()]; // Throw Exception if Number of // Functions is Greater Than 5. Number = DATA.GETNMBR (); IF (Number> 5) { Throw new nosuchmethodexception "Too Many Functions." "Only 5 allowed."); } // end if Listing 22 Although the limit could easily be increased, this program is currently limited to plotting the output from five functions. The code in Listing 22 checks this limit and throws an exception if an attempt is made to plot more than five functions. Routine GUI construction code Although somewhat long and rather tedious, the code in Listing 23 is completely straightforward. This code continues with the construction of the GUI object, creating text fields, a button, etc.//Create the control panel and // give it a border for cosmetics. JPanel CTLPNL = New jPanel (); CTLPNL.SETLAYOUT (//? ROWS X 4 COLS New GridLayout (0, 4)); CTLPNL.SetBorder New etchedborder ()); // Button for replotting the graph JButton graphbtn = New jbutton ("graph"); Graphbtn.addactionListener (this); // Populate Each Panel with a label // and a text field. Will Place // Thase Panels in a Grid on the // Control Panel Later. PAN0.ADD (New Jlabel ("Xmin")); PAN0.Add (Xmintxt); Pan1.add (New Jlabel ("XMAX")); PAN1.Add (XMAXTXT); PAN2.Add (New Jlabel ("Ymin")); PAN2.ADD (YMINTXT); PAN3.Add (New Jlabel ("Ymax")); PAN3.Add (YMAXTXT); PAN4.Add (New Jlabel ("Xticint")); PAN4.ADD (XTICINTTXT); Pan5.Add (New Jlabel ("Yticint")); PAN5.Add (YticinTxt); PAN6.Add (New Jlabel ("Xcalcinc"); Pan6.add (xcalcinctxt); // add the populate panels and the // Button to the Control Panel with // a Grid Layout. CTLPNL.Add (PAN0); CTLPNL.Add (PAN1); CTLPNL.Add (PAN2); CTLPNL.Add (PAN3); CTLPNL.Add (PAN4); CTLPNL.Add (PAN5); CTLPNL.Add (PAN6); CTLPnl.Add (Graphbtn); Listing 23 Because of the routine nature of the code in Listing 23, I will let the comments suffice as an explanation. The Canvas objects If you refer back to Figure 1, you will see that from one to five Canvas objects are stacked vertically in the center of a frame. This is accomplished by placing a JPanel object in the center of the frame, and setting the layout manager on the JPanel to GridLayout. The grid is defined as having one column and an unspecified number of rows. Then one Canvas object is placed in each cell of the grid, beginning at the top and working downward from the top, until the required number of Canvas objects have been placed in the grid. This is accomplished by the code in Listing 24.//Create a panel to Contain the // canvas objects. They Will Be // Displayed in a one-column grid. JPanel canvaspanel = new jpanel (); CanvasPanel.setLayout (//? Rows, 1 col New gridLayout (0, 1)); // Create a Custom Canvas Object for // Each function to be plotted and // add Them to the one-column grid. // Make Background Colors Alternate // Between White and gray. For (int CNT = 0; CNT Switch (cnt) { Case 0: Canvas [CNT] = New Mycanvas (CNT); Canvas [CNT] .SetBackground Color.white); Break; Case 1: Canvas [CNT] = New Mycanvas (CNT); Canvas [CNT] .SetBackground Color.light_gray); Break; Case 2: Canvas [CNT] = New Mycanvas (CNT); Canvas [CNT] .SetBackground Color.white); Break; Case 3: Canvas [CNT] = New Mycanvas (CNT); Canvas [CNT] .SetBackground Color.light_gray); Break; Case 4: Canvas [CNT] = New Mycanvas (CNT); Canvas [CNT]. SetBackground (Color.White); } // End Switch // add the object to the grid. CanvasPanel.add (canvas [cnt]);} // end for loop Listing 24 The Code in Listing 24: Creates the JPanel object, and sets its layout property to GridLayout. Creates the requisite number of objects of the MyCanvas class (which extends Canvas), setting the background colors of the panels alternately to white and gray. Adds the MyCanvas objects to the cells in the grid. (Note that the constructor for each MyCanvas object receives an integer that specifies its position in the stack of MyCanvas objects. We will see how that information is used later.) More routine construction code The code in Listing 25 is simply more routine Code Required TO: . // add the sub-assemblyum to the // frame. set its location, size, // And Title, And make it visible. getContentPane (). Add (CTLPNL, "South"); getContentPane (). Add (canvaspanel, "center"); Setbounds (0, 0, frmwidth, frmheight); Settitle ("graph01," CopyRight 2002, " "Richard G. Baldwin"); SetVisible (TRUE); // set to exit on x-button click SetDefaultCloseOperation EXIT_ON_CLOSE; // Get and save the size of the // Plotting Surface Width = canvases [0] .Getwidth (); Height = canvases [0] .getHeight (); Listing 25 Force a repaint As you will see later, the actual plotting behavior of this program is defined by the code in an overridden version of the paint method in the MyCanvas class. I will discuss that code in some detail later. One way to cause the code in the overridden paint method is to invoke the repaint method on a reference to a MyCanvas object. The code in Listing 26 invokes the repaint method on each MyCanvas object in sequence, to guarantee that they are properly painted when the GUI object first becomes Visible.for (int CNT = 0; CNT Canvas [CNT] .repaint (); } // End for loop } // End Constructor Listing 26 Similar code will be used again later to cause the graphs to be repainted each time the user presses the Graph button in the bottom right corner of Figure 1. End of the constructor The code in Listing 26 also ends the constructor for the GUI object . When the constructor finishes execution, the GUI appears on the screen with all plotting areas properly painted. Re-plotting the data Listing 27 shows the beginning of the event handler that is registered on the button to cause the functions to be re-plotted. Public Void ActionPerformed ( ActionEvent evt) { // re-instantiate the object thing // provides the data Try { IF (args! = null) { DATA = (graphintfc01) Class. Forname (args) .newinstance (); } else { Data = new junk (); } // END ELSE } catch (exception e) { // KNown to Be Safe At this point. // Otherwise Would Have Aborted // Earlier. } // end catch Listing 27 The purpose of the event handler is to cause the functions to be re-plotted after the user changes the plotting parameters. A new object of the target class However, the code in Listing 27 goes beyond that. In particular, the code in Listing 27 creates a new object from which to get the data that is to be plotted. In some cases, this may be required, depending on the nature of the class from which that object is instantiated. In other cases, it may not be necessary , and could slow down the re-plotting process. If your class does not contain counters or other variables that need to be re-initialized whenever you re-plot, you could probably safely remove or disable the code in Listing 27. This will Make The Program Run Faster, Although My May Not Be Able To See The Difference. The Rest of the Event Handler The Remaining Code in The Event Handler Is Shown in Listing 28./Set Plotting Parameters Using // Data from the text fields. Xmin = Double.Parsedouble ( XMintxt.getText ()); XMAX = double.parsedouble ( XMaxtxt.getText ()); Ymin = double.pasedouble ( YMINTXT.GETTEXT ()); YMAX = double.pasedouble ( YMAXTXT.GETTEXT ()); Xticint = double.parsedouble ( Xticinttxt.getText ()); Yticint = double.parsedouble ( Yticinttxt.getText ()); Xcalcinc = double.pasedouble Xcalcinctxt.getText ()); // Calculate New Values for Thae // Length of the tic marks on The THE // axes. if Too Small on X-AXIS, // a Default value is buy. Xticlen = (YMAX-YMIN) / 50; Yticlen = (XMAX-XMIN) / 50; // Repaint the Plotting Areas For (int CNT = 0; CNT Canvas [CNT] .repaint (); } // End for loop } // End ActionPerformed Listing 28 This code is very straightforward It performs the following actions:.. Get new plotting parameters from the text fields Perform some calculations Cause each of the MyCanvas objects to be repainted using the new plotting parameters Again, I will let the comments provide.. any necessary explanations. That brings us to the most interesting part of the program, the extended Canvas class. The class named MyCanvas The class named MyCanvas is an inner class of the GUI class, which is used to override the paint method in each of the Plotting Areas Shown in Figure 1. . The beginning of this class definition is shown in listing 29. Class Mycanvas Extends Canvas { Int cnt; // Object Number // Factors to Convert from Double // VALUES TO INTEGER PIXEL LOCATIONS. Double Xscale; Double Yscale; Mycanvas (int CNT) {// Save Obj Number THIS.CNT = CNT; } // End Constructor Listing 29 Floating data vs. pixels Most of the calculations in this program are performed on data of type double. However, graphics operations are ultimately performed in terms integer numbers of pixels. The code in Listing 29 declares scale factors used later to convert from double values to integer pixel locations. A simple constructor The code in Listing 29 also defines the constructor, whose only purpose is to save an integer identifying the position of this object in the vertical stack of MyCanvas objects. The overridden paint method The beginning of the overridden Paint Method for the Mycanvas Class Is Shown in Listing 30. // Override the Paint Method Public void paint (graphics g) { // Calculate the scale factorsxscale = width / (xmax-xmin); Yscale = height / (ymax-ymin); // set the Origin Based on the // minimum value in x and y G.Translate ((int) ((0-xmin) * xscale), (int) ((0-ymin) * yscale); Drawaxes (g); // Draw the axes g.setcolor (color.ble); Listing 30 The Code in Listing 30: Calculates and saves the scale factors for converting from double coordinate values to integer values in pixels. Moves the plotting origin to the correct location. Invokes a method to draw the axes (in red) on the MyCanvas object. Sets the color to black for the remainder of the plotting activity on the object. Get old values The plotting process consists of drawing a straight line segment between two points. One of the points is defined by a pair of old coordinate values. The other point is defined by a pair of new Coordinate values. The code in listing............... // Get Initial Data Values Double XVAL = Xmin; INT OLDX = GetThex (XVAL); INT OLDY = 0; // use the canvas obj number to // determine Which Method to // Invoke to get the value for y. Switch (cnt) { Case 0: Oldy = getthey (data.f1 (xval)); Break; Case 1: Oldy = GetThey (data.f2 (xval)); Break; Case 2: Oldy = GetThey (data.f3 (xval)); Break; Case 3: Oldy = GetThey (Data.f4 (xval)); Break; Case 4: Oldy = GetThey (Data.f5 (xval)); } // End Switch Listing 31 The initial y-coordinate value The initial value for the y-coordinate depends on which function is being plotted on the MyCanvas object. Recall that each MyCanvas object contains an instance variable that identifies its position in the vertical stack of MyCanvas objects. The switch statement in Listing 31 uses that information to invoke one of the five methods named f1 through f5. This gets the correct value for the y-coordinate based on the value of the x-coordinate. The methods named getTheX and getTheY called by the code in Listing 31 convert the coordinate values from type double to integer values in pixels. The method named getTheY also changes the sign on the data so that positive y-values go up the screen rather than down the screen. (By default, positive vertical coordinate Values Go Down The Screen From Top To Bottom in Java.) Plot The Points The Remainder of The Overridden Paint Method Is Shown in Listing 32. // Now loop and plot the point While (XVAL INT YVAL = 0; // Get next data value. Uses Use T // canvas Obj Number TO // determine Which Method to // Invoke to get the value for y. Switch (cnt) { Case 0: Yval = GetThey (data.f1 (xval)); Break; Case 1: Yval = GetThey (data.f2 (xval)); Break; Case 2: Yval = GetThey (data.f3 (xval)); Break; Case 3: Yval = GetThey (data.f4 (xval)); Break; Case 4: Yval = GetThey (data.f5 (xval)); } // end switch1 // convert the x-value to an int // and draw the next line segment INT x = GetThex (XVAL); g.drawline (Oldx, Oldy, X, YVAL); // increment along the x-axis XVAL = Xcalcinc; // save end point to use as start // Point for Next Line segment. Oldx = x; Oldy = yval; } // End while loop } // end overridden paint methodListing 32 The code in Listing 32 is relatively straightforward. This code simply iterates from the minimum x-value to the maximum x-value, invoking the appropriate method (from f1 through f5) to get the new y values . In the process, it invokes the drawLine method of the Graphics class to connect the points. The drawAxes method As it turns out, it is more difficult to draw and label the axes with tic marks than it is to plot the actual data. The Code to Accomplish this is shown in listing 33. Void Drawaxes (Graphics G) { g.setColor (Color.Red); // Label Left X-Axis and Bottom // Y-axis. Thase Are the Easy // ones. Separate the labels from // the ends of the tic marks by // TWO Pixels. g.drawstring (" (int) xmin, GetThex (Xmin), Getthey (XTIicleN / 2) -2); g.drawstring ("" (int) ymin, GetThex (YTIicleN / 2) 2, GetThey (Ymin)); // label the right x-axis and the // Top Y-Axis. Thase Are The Hard // Ones Because The position MUST // be adjusted by the font size and // the number of character. // Get the width of the string for // Right End of X-Axis and The //HeiHT of the string for top of // Y-Axis // Create a string this is an // integer representation of the // label for the right end of the // x-axis. Then Get a character // array what represents the // String. INT XMAXINT = (int) xmax; String xmaxstr = "" xmaxint; CHAR [] array = xmaxstr. Tochararray (); // Get A FontMetrics Object That Can // be used to get the size of the // String in Pixels. FontMetrics Fontmetrics = g.getfontmetrics (); // Get a bounding reccTangle for the // String Rectangle2D R2D = FontMetrics.getstringbounds Array, 0, Array.length, G); // Get the width and the height of // the bunking reccTangle. The Bounding Rectangle. // width is the width of the label // at the right end of the // x-axis. The Height Applies To // all the labels, but is needed // Specify for the label at /// The top end of the y-axis. INT labwidth = (int) (R2D.Getwidth ()); INT Labheight = (int) (R2D.getHeight ()); // Label the Positive X-Axis and the // Positive Y-Axis Using The Width // and height from above to // Position The Labels. There // Labels Apply to the Very Ends of // the axes at the Edge of Thae // Plotting Surface. g.drawstring ("" (int) xmax, GetThex (Xmax) -Labwidth, Getthey (XTIicleN / 2) -2); g.drawstring ("" (int) YMAX, GetThex (YTIicleN / 2) 2, GetThey (Ymax) Labheight); // Draw the axes g.drawline (GetThex (Xmin), Getthey (0.0), GetThex (XMAX), GetThey (0.0)); g.drawline (GetThex (0.0), Getthey (Ymin), GetThex (0.0), GetThey (YMAX)); // Draw the Tic Marks ON AxES XTICS (g); YTICS (g); } // end drawaxes Listing 33 The code in Listing 33 is fairly complex, particularly with respect to putting the labels on the ends of the axes. However, I doubt that many of you are interested in the details, so I will let the comments suffice to explain the code TiC Marks invoked from the code in listing 33 to aptually draw the tic marks on the ax. // Method to Draw Tic Marks on X-Axis Void XTICs (Graphics G) { Double xdoub = 0; INT x = 0; // Get the ends of the tic mark. INT TOPEND = GetThey (Xticlen / 2); INT bottomend = Getthey (-Xticlen / 2); // if the vertical size of the // Plotting Area IS Small, The // Calculated Tic Size May Be Too // Small. In That Case, set it to // 10 Pixels. IF (TopEnd <5) { TOPEND = 5; Bottomend = -5; } // end if // loop and draw a series of short // Lines to serve as tic marks. // begin with the Positive X-AXIS //Moving to the right from zero. While (xdoub x = getthex (xdoub); g.drawline (x, topnd, x, bottomend); XDOUB = XTICINT; } // End while // Now do the negative x-axis moving // to the left from Zero XDOUB = 0; While (xdoub> xmin) { x = getthex (xdoub); g.drawline (x, topnd, x, bottomend); XDOUB - = xTicint; } // End while } // End XTICS // --------------------------------- // // Method to Draw Tic Marks on y-axis Void YTICS (Graphics G) { Double Ydoub = 0; INT Y = 0; INT RIGHTEND = GetThex (YTIicleN / 2); INT leftend = getthex (-yticlen / 2); // loop and draw a series of short // Lines to serve as tic marks. // begin with the Positive Y-AXIS // Moving Up from Zero. While (YDOUB Y = getthey (ydoub); g.drawline (Rightend, Y, Leftend, Y); YDOUB = YTICINT; } // End while // Now do the negative y-axis moving // Down from Zero. YDOUB = 0; While (YDOUB> YMIN) { Y = getthey (ydoub); g.drawline (Rightend, Y, Leftend, Y); YDOUB - = Yticint; } // End while } // end ytics Listing 34 Again, I am going to let the comments suffice to explain this code. The getTheX and getTheY methods As mentioned earlier, methods named getTheX and getTheY are used to convert coordinate values from type double to integer values in pixels. Those two methods are Shown in listing 35. // this method translates and scales // a Double Y Value to Plot Properly // in The Integer Coordinate System. // in addition to schelation, IT Causes // The Positive Direction of the // Y-Axis to Be from bottom to top. INT getthey { Double Ydoub = (YMAX Ymin) -y; INT YINT = (int); YDOUB * YSCALE); Return Yint; } // End GetThey // --------------------------------- // // this Method Scales a Double X Value // TO PLOT PROPERLY in The Integer // Coordinate System. Int getthex (double x) { Return (int) (x * xscale); } // end getthex // --------------------------------- // } // end inner class mycanvas / / =================================== // } // End Class GUI Listing 35 Listing 35 also marks the end of the inner class named MyCanvas and the end of the class named GUI. The test class named junk Listing 36 defines a test class named junk that implements the interface named GraphIntfc01. Class Junk Implements Graphintfc01 { Public int GetNMBR () { // Return Number of Functions to // process. Must Not Exceed 5. Return 4; } // end genmbr Public Double F1 (double x) { Return (x * x * x) / 200.0; } // end f1 PUBLIC DOUBLE F2 (double x) { RETURN - (x * x * x) / 200.0; } // end f2 PUBLIC DOUBLE F3 (double x) { Return (x * x) / 200.0; } // end f3 PUBLIC DOUBLE F4 (Double X) { Return 50 * Math.cos (X / 10.0); } // end f4 Public Double F5 (double x) { Return 100 * math.sin (x / 20.0); } // end f5 } // End Sample Class Junk Listing 36 This class defines the methods declared in the interface, and makes it possible to test the plotting program in a stand-alone mode without having access to another class that implements the interface. Since I discussed the implementation of this interface in some detail earlier in the lesson, there should be no need for me to provide further discussion of the code in Listing 36. You might note, however, that since the method named getNmbr returns the value 4, the method named f5 will not be invoked by the plotting Plotting Engineering and Scientific Data Using Javaby Richard G. Baldwin Go To Page: Prev 1 2run The Program Copy the code for the plotting program from Listing 39 into a Java source file named Graph01.java. Copy the code for the interface from Listing 1 into a Java source file named GraphIntfc01.java. Compile and run the program named Graph01 with no command- line parameters. This should use the internal test class named junk discussed earlier to produce the display shown in Figure 4. Once you have the display on your screen, make changes to the plotting parameters in the text fields at the bottom and press the button labeled Graph. When you do, you should see the same functions being re-plotted with different plotting parameters. Once that is successful, copy the code in Listing 37 into a file named Graph01Demo.java. Copy the code in Listing 38 into a file named Dsp002.java. Compile these two files. Rerun the plotting program named Graph01 providing Graph01Demo as a command-line parameter. Also rerun the plotting program providing Dsp002 as a command-line parameter. This should produc e displays similar to Figures 1 and 3. Remember, however, that you must be running Java version 1.4 or later to successfully compile and execute this program. Summary I provided two generalized plotting programs in this lesson. One of the programs plots up to five functions in a vertical stack. The other program superimposes the plots for up to five functions on the same Cartesian coordinate system. Each of these programs is capable of plotting the data produced by any object that implements a simple interface named GraphIntfc01. I explained the interface . I Also Provided Two Different Sample Classes That Implement The Interface for You To Use As Models As You Come Up To Speed In Defining your owned Classes.comPlete Program Listings COMPLETE LISTINGS OF THE Programs Discussed in this Lesson Are Shown Below. / * File graph01demo.java Copyright 2002, R.g.baldwin This class is buy to demonstrate how To Write Data-Generator Classes That Will Operate SuccessFully with the Program named graph01. TESTED Using JDK 1.4.0 Under Win 2000. *********************************************** / Class graph01demo IMPLEMENTS graphintfc01 { Public int GetNMBR () { // Return Number of Functions to // process. Must Not Exceed 5. Return 5; } // end genmbr Public Double F1 (double x) { // this is a simple x-square // Function with a negative // sigh. RETURN - (x * x) / 200.0; } // end f1 PUBLIC DOUBLE F2 (double x) { // this is a simple x-cubed // function RETURN - (x * x * x) / 200.0; } // end f2 PUBLIC DOUBLE F3 (double x) { // this is a simple cosine // function Return 100 * Math.cos (X / 10.0); } // end f3 PUBLIC DOUBLE F4 (Double X) { // this is a simple sine // function Return 100 * math.sin (x / 20.0); } // end f4 Public Double F5 (double x) { // this is Function Which // Returns the Product of // The Above sine and cosines. Return 100 * (Math.sin (X / 20.0) * Math.cos (X / 10.0)); } // end f5 } // end sample class graph01demo Listing 37 / * File dsp002.java Copyright 2002, R.g.baldwin Note: this Program Requires Access To The interface named graphintfc01. This is a sample dsp Program whose Output is designed to be plotted By the programs named graph01 and Graph02. This Requires That the Class Implement GraphintFc01. IT Also Requires a Noarg Constructor. This Program Applies a Narrow-Band Convolution Filter to White Noise, Andthen Computes The Amplitude Spectrum of The Filtered Result Using A Simple Discrete Fourier Transform (DFT) Algorithm. The Spectrum of The White Noise is also computed. The Program CONVOLVES A 33-POINT SINUSOIDAL Convolution Filter with Wide-band noise, and then computes the Amplitude Spectrum of the Raw Data and The Filtered Result. The processing Occurs when an Object of the Class IS Instantiated. The Input Noise, The Filter, The Filter, THE Filtered Output, And The Two SpecTra Are Depositphotos ARRAYS for Later Retrieval and Display. The Input Noise, The Filter, The Filter, THE Filtered Output, The Spectrum of The Noise, and the spectrum of the filter Result area Returned by the methods Named F1, F2, F3, F4, And F5 Respectively. The Output Values That Are Returned Are Scaled for ApproPriate Display in The Plotting Areas Provided by the Plotting Areas Provided by THE Program named graph01. TESTED Using JDK 1.4.0 Under Win 2000. *********************************************** / Import java.util. *; Class DSP002 IMPLEMENTS graphintfc01 { // Establish Data and Spectrum // Lengths. INT operatorlen = 33; INT DATALEN = 256 Operatorlen; INT OUTPUTLEN = Datalen - Operatorlen; INT spectrumpts = outputlen; // Create Arrays for the data and // The resultS. Double [] data = new double [datalent]; Double [] Operator = New Double [Operatorlen]; Double [] Output = New double [outputlen]; Double [] Spectruma = New double [Spectrumpts]; Double [] spectrumb = New double [Spectrumpts]; Public dsp002 () {// constructor // generate and save some wide-band // random noise. SEED with a // DiffERENT VALUE EACH TIME TIME TIME // Object is constructed. Random generator = new random New Date (). getTime ()); for (int CNT = 0; CNT CNT ) { // Get Data, Scale IT, Remove The // DC Offset, And Save IT. Data [CNT] = 100 * generator. Nextdouble () - 50; } // End for loop // Create a convolution operator and // Save it in the array. For (int CNT = 0; CNT CNT ) { // Note, The Value of the // Denominator in the argument // To the cos method specifies // the frequency relative to the // sampling frequency. Operator [cnt] = math.cos CNT * 2 * math.pi / 4); } // End for loop // Apply the Operator to the data Convolve01.convolve (Data, Datalen, Operator, operatorlen, output; // compute dft of the raw data and // Save it in spectruma array. DFT01.DFT (Data, Spectrumpts, Spectruma); // compute dft of the filtered data // and Save it in spectrumb array. DFT01.DFT (Output, Spectrumpts, Spectrumb); // all of the data haas now been // Product and Saved. It may be // Retrieved by Invoking the // Following Methods Named F1 // THROUGH F5. } // End Constructor // --------------------------------- // // The Following Six Methods Are // Required by the interface name // graphintfc01. Public int GetNMBR () { // Return Number of Functions to // process. Must Not Exceed 5. Return 5; } // end genmbr // --------------------------------- // Public Double F1 (double x) { INT index = (int) math.round (x); // this Version of this Method // Returns the Random Noise Data. // be Careful to stay within the // array bounds. IF (INDEX <0 || Index> Data.Length-1) { Return 0; } else { Return Data [index]; } // END ELSE } // end f1 // --------------------------------- // PUBLIC DOUBLE F2 (double x) { // Return The Convolution Operator INDEX = (int) math.Round (x); if (Index <0 || INDEX> Operator.length-1) { Return 0; } else { // scale for good visibility in // the Plot Return Operator [INDEX] * 50; } // END ELSE } // end f2 // --------------------------------- // PUBLIC DOUBLE F3 (double x) { // Return Filtered Output INT index = (int) math.round (x); IF (INDEX <0 || INDEX> OUTPUT.LENGTH-1) { Return 0; } else { // Scale to Approx Same P-P AS // Input data Return Output [INDEX] / 6; } // END ELSE } // end f3 // --------------------------------- // PUBLIC DOUBLE F4 (Double X) { // Return Spectrum of Raw Data INT index = (int) math.round (x); IF (INDEX <0 || Index> Spectruma.length-1) { Return 0; } else { // scale for good visibility in // The Plot. Return Spectruma [Index] / 10; } // END ELSE } // end f4 // --------------------------------- // Public Double F5 (double x) { // Return The Spectrum of Thae // filtered data. INT index = (int) math.round (x); IF (INDEX <0 || INDEX> Spectrumb.length-1) { Return 0; } else { // scale for good visibility in // The Plot. Return Spectrumb [INDEX] / 100; } // END ELSE } // end f5 } // End Sample Class DSP002 / / =================================== // // this Class Provides a Static Method // Named Convolve, Which Applies An // incoming convolution operator to // an incoming set of data and deposits // the filtered data in an output // array whose reason is received // as an incoming parameter. // this class could Easily Be Broken Out // and put in a library as a stand- // Alone Class, or the convolve method // could be added to a class containing // a variety of dsp methods. Class convolve01 { Public Static Void Convolve Double [] DATA, Int Datalen, Double [] Operator, Int Operatorlen, Double [] Output) { // Apply the Operator to the data, // DEALING WITH INDEX // Reversal Required by // convolution. For (int i = 0; I Output [i] = 0; For (int J = Operatorlen-1; J> = 0; J -) { Output [i] = Data [i j] * Operator [J]; } // end inner loop } // End Outer Loop } // End convolve method } // End class convolve01 / / =================================== // // this Class Provides a Static Method // Named DFT, Which computes and // Returns the amplitude spectrum of // an incoming time series. The // Amplitude Spectrum is Computed AS // the Square root of the sum of the // Squares of the real and imaginary // Parts. // Returns a Number of Points in the // Frequency Domain Equal to the number // samples in the incoming time // series. Deposits the frequency // Data in an Array Whose Reference IS // received as an incoming parameter. // this class could Easily Be Broken Out // and put in a library as a stand- // Alone Class, or the dft method // could be added to a class containing // a Variety of DSP Methods. Class DFT01 { Public Static Void DFT Double [] DATA, Int Datalen, Double [] Spectrum) { // set the frequency increment to // the Reciprocal of the data // Length. this is convenience @ @ly, and is not a requestment // of the dft algorithm. Double DELF = 1.0 / datalen; // Outer loop itrates onfrequency // value. For (int i = 0; i Double FREQ = i * DELF; Double REAL = 0; Double IMAG = 0; // inner loop itrates on time- // Series Points. For (int J = 0; J Real = Data [J] * Math.cos 2 * math.pi * freq * j); IMAG = DATA [J] * Math.sin 2 * math.pi * freq * j); Spectrum [i] = math.sqrt Real * Real iMag * iMag); } // end inner loop } // End Outer Loop } // end DFT } // end DFT01 Listing 38 / * File graph01.java Copyright 2002, R.g.baldwin Note: this Program Requires Access To The interface named graphintfc01. This is a plotting program. It is IS Designed to Access A Class File, Which Implements graphintfc01, and to plot up To FIVE FUNCTIONS Defined in That Class File. The Plotting Surface Is Divided INTO The Required Number of Equally Sized Plotting Areas, And One Function Is Plotted on Cartesian Coordinates in Each area. The methods corresponding to the Functions Are Named F1, F2, F3, F4, And f5. THE CLASS Containing The Functions Must Also Define a static method named Getnmbr (), Which Takes no parameters And returns the number of functions to Be plotted. if this method returns a Value Greater Than 5, A Nosuchmethodexception Will Be Thrown. Note That the constructor for the class That Implements Graphintfc01 Must Not Require Any Parameters Due To The Use of the newinstance method of the newinstance method Class Class to Instantiate An Object Of That Class. IF the number of functions is less Than 5, Then the Absent Method Names Must Begin With F5 and Work Down Toward F1. for esample, if the number of Functions IS 3, THEN THE Program Will Expect to Call Methods Named F1, F2, And f3. It is ok for the absentmethods to be defined in the class. The Simply Won't be invoked. The Plotting Areas Have Alternating White and gray backgrounds to make them Easy to Separate Visually. All Curves Are Plotted in Black. A Cartesian Coordinate System with Axes, Tic Marks, And Labels Is Drawn in Red IN Each Plotting Area. THE CARTESIAN COORDINATE System in Each Plotting Area Has The Same Horizontal And Vertical Scale, AS Well as the Same Tic Marks and Labels on the axes. The labels Displayed on The Axes, Correspond to the value of the extreme EDGES OF The Plotting Area. The Program Also Compiles A Sample Class Named Junk, Which Contains Five Methods and the method named getnmbr. This Makes It Easy To Compile and Test This Program in a stand-alone mode. At Runtime, The Name of the Class That Implements the graphintfc01 interface Must Be supplied as a commnd-line Parameter. if this parameter is Missing, The Program Instantiates An Object from the intence Class Named Junk and plots the data provided by That Class. Thus, you can test the Program by Running It with no Command-line parameter. This Program Provides the Following Text Fields for User Input, Along with a button labeled graph. this allows The user to adjut the parameters and Replot The Graph As Many Times with as Many Plotting Scales As Needed: Xmin = minimum x-axis value XMAX = Maximum X-Axis Value Ymin = minimum y-axis value YMAX = Maximum Y-Axis Value Xticint = Tic Interval On X-Axis Yticint = Tic Interval On Y-AXIS Xcalcinc = Calculation Interval The User CAN Modify Any of There Parameters and the Click The Graph Button to Cause the Five Functions To be re-plotted accounting to.. WHENEVER THE Graph Button Is Clicke, The Event Handler Instantiates a New Object of the class thing imports The graphintfc01 interface. Depending On the Nature of That Class, this May Be Redundant in Some Cases. However, IT IS USEful in Those Cases Where IT Is Necessary To Refresh The Values of Instance variables defined in the Class (SUCH AS A Counter, for Example). TESTED Using JDK 1.4.0 Under Win 2000. This Program Uses Constants That Were First Defined in the Color Class of v1.4.0. Therefore, the program Requires v1.4.0 or later to compile and Run Correctly. *********************************************** / Import java.awt. *; Import java.awt.event. *; Import java.awt.geom. *; Import javax.swing. *; Import javax.swing.border. *; Class graph01 { Public static void main String [] ARGS) Throws nosuchmethodexception, ClassNotFoundException, InstantiationException, IllegaCCESSEXCEPTION { IF (args.length == 1) { // Pass Command-Line Parameter New GUI (Args [0]); } else { // no command-line parameter given New Gui (NULL); } // END ELSE } // end main } // End class graph01 definition / / =================================== // Class GUI Extends Jframe Implements anctionsListener { // define plotting parameters and // Their default value. Double xmin = -10.0; Double XMAX = 256.0; Double ymin = -100.0; Double YMAX = 100.0; // Tic Mark Intervals Double Xticint = 16.0; Double Yticint = 20.0; // Tic Mark Lengths. If Too Small // on X-Axis, a Default Value IS // useful. Double Xticlen = (YMAX-YMIN) / 50; Double Yticlen = (XMAX-XMIN) / 50; // Calculation Interval Along X-AXIS Double Xcalcinc = 1.0; // Text Fields for PLOTTING Parameters JtextField Xmintxt = New jtextfield (" xmin); JtextField XMaxTxt = New jtextfield (" xmax); Jtextfield Ymintxt = New jtextfield (" ymin); Jtextfield ymaxtxt = New jtextfield (" ymax); JtextField Xticinttxt = New JtextField ("" xticint); JtextField Yticinttxt = New jtextfield (" yticint); Jtextfield Xcalcinctxt = New jtextfield (" xcalcinc); // panels to contain a label and a // Text Field JPanel PAN0 = New jPanel (); JPANel PAN1 = New jPanel (); JPANel PAN2 = new jPanel (); JPanel Pan3 = New jPanel (); JPANel PAN4 = New jPanel (); JPANel PAN5 = New jPanel (); JPanel Pan6 = New jPanel (); // Misc Instance Variables INT frmwidth = 400; INT frmheight = 430; Int width; INT height; Int number; Graphintfc01 data; String args = NULL; // Plots Are Drawn on the canvarated // in this arch. Canvas [] canvas; // conncture Gui (String Args) Throws Nosuchmethodexception, ClassNotFoundException, InstantiationException, IllegaCCESSEXCEPTION { IF (args! = null) { // Save for use later in the // ActionEvent Handler THIS.Args = args; // instantiate an Object of the // Target class using the string // Name of the class. Data = (graphintfc01) Class.Forname (ARGS). NEWINSTANCE (); } else { // instantiate an Object of the // Test class named junk. Data = new junk (); } // END ELSE // Create Array to Hold Correct // Number of Canvas Objects. Canvastes = New canvas [data.getnmbr ()]; // throw Exception if Number of // Functions is Greater Than 5. Number = DATA.GETNMBR (); IF (Number> 5) { Throw new nosuchmethodexception "Too Many Functions." "Only 5 allowed."); } // end if // Create The Control Panel and // give it a border for cosmetics. JPanel CTLPNL = New jPanel (); CTLPNL.SETLAYOUT (//? ROWS X 4 COLS New GridLayout (0, 4)); CTLPNL.SetBorder New etchedborder ()); // Button for replotting the graph JButton graphbtn = New jbutton ("graph"); Graphbtn.addactionListener (this); // Populate Each Panel with a label // and a text field. Will Place // Thase Panels in a Grid on the // Control Panel Later. PAN0.ADD (New Jlabel ("Xmin")); PAN0.Add (Xmintxt); Pan1.add (New Jlabel ("XMAX")); PAN1.Add (XMAXTXT); PAN2.Add (New Jlabel ("Ymin")); PAN2.ADD (YMINTXT); PAN3.Add (New Jlabel ("Ymax")); PAN3.Add (YMAXTXT); PAN4.Add (New Jlabel ("Xticint")); PAN4.ADD (XTICINTTXT); Pan5.Add (New Jlabel ("Yticint")); PAN5.Add (YticinTxt); PAN6.Add (New Jlabel ("Xcalcinc"); Pan6.add (xcalcinctxt); // add the populate panels and the // Button to the Control Panel with // a Grid Layout. CTLPNL.Add (PAN0); CTLPNL.Add (PAN1); CTLPNL.Add (PAN2); CTLPNL.Add (PAN3); CTLPNL.Add (PAN4); CTLPNL.Add (PAN5); CTLPNL.Add (PAN6); CTLPnl.Add (Graphbtn); // Create a panel to contact the // canvas objects. They Will Be // Displayed in a one-column grid. JPanel canvaspanel = new jpanel (); CanvasPanel.setLayout (//? Rows, 1 col New gridLayout (0, 1)); // Create a Custom Canvas Object for // Each function to be plotted and // add Them to the one-column grid. // Make Background Colors Alternate // Between white and gray.FOR (int CNT = 0; CNT Switch (cnt) { Case 0: Canvas [CNT] = New Mycanvas (CNT); Canvas [CNT] .SetBackground Color.white); Break; Case 1: Canvas [CNT] = New Mycanvas (CNT); Canvas [CNT] .SetBackground Color.light_gray); Break; Case 2: Canvas [CNT] = New Mycanvas (CNT); Canvas [CNT] .SetBackground Color.white); Break; Case 3: Canvas [CNT] = New Mycanvas (CNT); Canvas [CNT] .SetBackground Color.light_gray); Break; Case 4: Canvas [CNT] = New Mycanvas (CNT); Canvas [CNT]. SetBackground (Color.White); } // End Switch // add the object to the grid. Canvaspanel.Add (canvas "; } // End for loop // add the sub-assemblyum to the // frame. set its location, size, // And Title, And make it visible. getContentPane (). Add (CTLPNL, "South"); getContentPane (). Add (canvaspanel, "center"); Setbounds (0, 0, frmwidth, frmheight); Settitle ("graph01," CopyRight 2002, " "Richard G. Baldwin"); SetVisible (TRUE); // set to exit on x-button click SetDefaultCloseOperation EXIT_ON_CLOSE; // Get and save the size of the // Plotting Surface Width = canvases [0] .Getwidth (); Height = canvases [0] .getHeight (); // Guarantee a repaint on startup. For (int CNT = 0; CNT Canvas [CNT] .repaint (); } // End for loop } // End Constructor // --------------------------------- // // this Event Handler is Registered // on the jbutton to cause the // functions to be replotted. Public Void ActionPerformed ( ActionEvent evt) { // re-instantiate the object thing // provides the data Try { IF (args! = null) { DATA = (graphintfc01) Class. Forname (args) .newinstance ();} else { Data = new junk (); } // END ELSE } catch (exception e) { // KNown to Be Safe At this point. // Otherwise Would Have Aborted // Earlier. } // end catch // set Plotting Parameters Using // Data from the text fields. Xmin = Double.Parsedouble ( XMintxt.getText ()); XMAX = double.parsedouble ( XMaxtxt.getText ()); Ymin = double.pasedouble ( YMINTXT.GETTEXT ()); YMAX = double.pasedouble ( YMAXTXT.GETTEXT ()); Xticint = double.parsedouble ( Xticinttxt.getText ()); Yticint = double.parsedouble ( Yticinttxt.getText ()); Xcalcinc = double.pasedouble Xcalcinctxt.getText ()); // Calculate New Values for Thae // Length of the tic marks on The THE // axes. if Too Small on X-AXIS, // a Default value is buy. Xticlen = (YMAX-YMIN) / 50; Yticlen = (XMAX-XMIN) / 50; // Repaint the Plotting Areas For (int CNT = 0; CNT Canvas [CNT] .repaint (); } // End for loop } // End ActionPerformed // --------------------------------- // // this is an inner class, Which is buy // to override the Paint Method on The // Plotting Surface. Class Mycanvas Extends Canvas { Int cnt; // Object Number // Factors to Convert from Double // VALUES TO INTEGER PIXEL LOCATIONS. Double Xscale; Double Yscale; Mycanvas (int CNT) {// Save Obj Number THIS.CNT = CNT; } // End Constructor // Override the Paint Method Public void paint (graphics g) { // Calculate the scale factors Xscale = width / (xmax-xmin); Yscale = height / (ymax-ymin); // set the Origin Based on the // minimum value in x and y G.Translate ((int) ((0-xmin) * xscale), (int) ((0-ymin) * yscale); Drawaxes (g); // Draw the axes g.setcolor (color.ble); // Get Initial Data Values Double XVAL = Xmin; int = getThex (xval); INT OLDY = 0; // use the canvas obj number to // determine Which Method to // Invoke to get the value for y. Switch (cnt) { Case 0: Oldy = getthey (data.f1 (xval)); Break; Case 1: Oldy = GetThey (data.f2 (xval)); Break; Case 2: Oldy = GetThey (data.f3 (xval)); Break; Case 3: Oldy = GetThey (Data.f4 (xval)); Break; Case 4: Oldy = GetThey (Data.f5 (xval)); } // End Switch // Now loop and plot the point While (XVAL INT YVAL = 0; // Get next data value. Uses Use T // canvas Obj Number TO // determine Which Method to // Invoke to get the value for y. Switch (cnt) { Case 0: Yval = GetThey (data.f1 (xval)); Break; Case 1: Yval = GetThey (data.f2 (xval)); Break; Case 2: Yval = GetThey (data.f3 (xval)); Break; Case 3: Yval = GetThey (data.f4 (xval)); Break; Case 4: Yval = GetThey (data.f5 (xval)); } // end switch1 // convert the x-value to an int // and draw the next line segment INT x = GetThex (XVAL); g.drawline (Oldx, Oldy, X, YVAL); // increment along the x-axis XVAL = Xcalcinc; // save end point to use as start // Point for Next Line segment. Oldx = x; Oldy = yval; } // End while loop } // end overridden Paint Method // --------------------------------- // // Method to Draw Axes with tic marks // and labels in the color red Void Drawaxes (Graphics G) { g.setColor (Color.Red); // Label Left X-Axis and Bottom // Y-axis. Thase Are the Easy // ones. Separate the labels from // the ends of the tic marks by // TWO Pixels. g.drawstring (" (int) xmin, GetThex (Xmin), Getthey (XTIicleN / 2) -2); g.drawstring ("" (int) ymin, GetThex (YTIicleN / 2) 2, GetThey (Ymin)); // label the right x-axis and the // Top Y-Axis. Thase Are The Hard // Ones Because The position MUST // be adjusted by the font size and // the number of character. // Get the width of the string for // Right End of X-Axis and The //HeiHT of the string for top of // Y-Axis // Create a string this is an // integer representation of the // label for the right end of the // x-axis. Then Get a character // array what represents the // String. INT XMAXINT = (int) xmax; String xmaxstr = "" xmaxint; CHAR [] array = xmaxstr. Tochararray (); // Get A FontMetrics Object That Can // be used to get the size of the // String in Pixels. FontMetrics Fontmetrics = g.getfontmetrics (); // Get a bounding reccTangle for the // String Rectangle2D R2D = FontMetrics.getstringbounds Array, 0, array.length, g); // Get the width and the height of // the bunking reccTangle. The Bounding Rectangle. // width is the width of the label // at the right end of the // x-axis. The Height Applies To // all the labels, but is needed // Specify for the label at /// The top end of the y-axis. INT labwidth = (int) (R2D.Getwidth ()); INT Labheight = (int) (R2D.getHeight ()); // Label the Positive X-Axis and the // Positive Y-Axis Using The Width // and height from above to // Position The Labels. There // Labels Apply to the Very Ends of // the axes at the Edge of Thae // Plotting Surface. g.drawstring ("" (int) xmax, GetThex (Xmax) -Labwidth, Getthey (XTIicleN / 2) -2); g.drawstring ("" (int) YMAX, GetThex (YTIicleN / 2) 2, GetThey (Ymax) Labheight); // Draw the axes g.drawline (GetThex (Xmin), Getthey (0.0), GetThex (XMAX), GetThey (0.0)); g.drawline (GetThex (0.0), Getthey (Ymin), GetThex (0.0), GetThey (YMAX)); // Draw the Tic Marks ON AxES XTICS (g); YTICS (g); } // end drawaxes // --------------------------------- // // Method to Draw Tic Marks on X-Axis Void XTICs (Graphics G) { Double xdoub = 0; INT x = 0; // Get the ends of the tic mark. INT TOPEND = GetThey (Xticlen / 2); INT bottomend = Getthey (-Xticlen / 2); // if the vertical size of the // Plotting Area IS Small, The // Calculated Tic Size May Be Too // Small. In That Case, Set It To // 10 Pixels. IF (TopEnd <5) { TOPEND = 5; Bottomend = -5; } // end if // loop and draw a series of short // Lines to serve as tic marks. // begin with the Positive X-AXIS //Moving to the right from zero. While (xdoub x = getthex (xdoub); g.drawline (x, topnd, x, bottomend); XDOUB = XTICINT; } // End while // Now do the negative x-axis moving // to the left from Zero XDOUB = 0; While (xdoub> xmin) { x = getthex (xdoub); g.drawline (x, topnd, x, bottomend); XDOUB - = xTicint; } // End while } // End XTICS // --------------------------------- // // Method to Draw Tic Marks on y-axis Void YTICS (Graphics G) { Double Ydoub = 0; INT Y = 0; INT RIGHTEND = GetThex (YTIicleN / 2); INT leftend = getthex (-yticlen / 2); // loop and draw a series of short // Lines to serve as tic marks. // begin with the Positive Y-AXIS // Moving Up from Zero. While (YDOUB Y = getthey (ydoub); g.drawline (Rightend, Y, Leftend, Y); YDOUB = YTICINT; } // End while // Now do the negative y-axis moving // Down from Zero. YDOUB = 0; While (YDOUB> YMIN) { Y = getthey (ydoub); g.drawline (Rightend, Y, Leftend, Y); YDOUB - = Yticint; } // End while } // end ytics // --------------------------------- // // this method translates and scales // a Double Y Value to Plot Properly // in The Integer Coordinate System. // in addition to schelation, IT Causes // The Positive Direction of The // Y-axis to be from bottom to top. INT getthey { Double Ydoub = (YMAX Ymin) -y; INT YINT = (int); YDOUB * YSCALE); Return Yint; } // End GetThey // --------------------------------- // // this Method Scales a Double X Value // TO PLOT PROPERLY in The Integer // Coordinate System. Int getthex (double x) { Return (int) (x * xscale); } // end getthex // --------------------------------- // } // end inner class mycanvas / / =================================== // } // End Class GUI / / =================================== // // Sample Test Class. Required for // compilation and stand-alone // Testing. Class Junk Implements Graphintfc01 { Public int GetNMBR () { // Return Number of Functions to // process. Must Not Exceed 5. Return 4; } // end genmbr Public Double F1 (double x) { Return (x * x * x) / 200.0; } // end f1 PUBLIC DOUBLE F2 (double x) { RETURN - (x * x * x) / 200.0; } // end f2 PUBLIC DOUBLE F3 (double x) { Return (x * x) / 200.0; } // end f3 PUBLIC DOUBLE F4 (Double X) { Return 50 * Math.cos (X / 10.0); } // end f4 Public Double F5 (double x) { Return 100 * math.sin (x / 20.0); } // end f5 } // End Sample Class Junk Listing 39 / * File graph02.java Copyright 2002, R.g.baldwin Note: This Program Requires Access Tothe Interface named graphintfc01. This is a modified version of the Program named graph01. That Program Plots up to FIVE SEPARATE CURVES IN Separate Plotting Areas. This Program Superimposes up to FIVE SEPARATE CURVES In Different Colors in The Same Plotting area. This is a plotting program. It is IS Designed to Access A Class File, Which Implements graphintfc01, and to plot up To FIVE FUNCTIONS Defined in That Class File. The methods corresponding to the Functions Are Named F1, F2, F3, F4, And f5. THE CLASS Containing The Functions Must Also Define a static method named Getnmbr (), Which Takes no parameters And returns the number of functions to Be plotted. if this method returns a Value Greater Than 5, A Nosuchmethodexception Will Be Thrown. Note That the constructor for the class That Implements Graphintfc01 Must Not Require Any Parameters Due To The Use of the newinstance method of the newinstance method Class Class to Instantiate An Object Of That Class. IF the number of functions is less Than 5, Then the Absent Method Names Must Begin With F5 and Work Down Toward F1. for esample, if the number of Functions IS 3, THEN THE Program Will Expect to Call Methods Named F1, F2, And f3. it is ok for the absent Methods to be defined in the class. The Simply Won't be invoked. Each Curve Is Plotted in A Different Color. The Correspondence Between Colors and Function Calls IS AS Follows: F1: Black F2: Blue F3: Red F4: MAGENTA F5: CYAN A Cartesian Coordinate System with SYSTEM Axes, Tic Marks, And Labels Is Drawn in Green. The labels Displayed on The Axes, Correspond to the value of the extreme EDGES OF The Plotting Area. The Program Also Compiles A Sample Class Named Junk, Which Contains Five Methods and the method named getnmbr. This Makes It Easy To Compile and Test This Program in a stand-alone mode. At Runtime, The Name of the Class That Implements the graphintfc01 interface Must Be supplied as a commnd-line Parameter. if this parameter is Missing, The Program Instantiates An Object from the intence Class Named Junk and plots the data provided by That Class. Thus, you can test the Program by Running It with no Command-line parameter. This Program Provides the Following Text Fields for User Input, Along with a button labeled graph. this allows The user to adjut the parameters and Replot The Graph As Many Times with as Many Plotting Scales As Needed: Xmin = minimum x-axis value XMAX = Maximum X-Axis Value Ymin = minimum y-axis value YMAX = Maximum Y-Axis Value Xticint = Tic Interval On X-Axis Yticint = Tic Interval On Y-AXIS Xcalcinc = Calculation Interval The User CAN Modify Any of There Parameters and the Click The Graph Button to Cause the Five Functions To be re-plotted accounting to the NEW parameters. WHENEVER THE Graph Button Is Clicke, The Event Handler Instantiates a New Object of the class thing imports The graphintfc01 interface. Depending On the Nature of That Class, this May Be Redundant in Some Cases. However, IT IS USEful in Those Cases Where IT Is Necessary To Refresh The Values of Instance variables defined in the Class (SUCH AS A Counter, for Example). TESTED Using JDK 1.4.0 Under Win 2000. This Program Uses Constants That Were First Defined in the Color Class of v1.4.0. Therefore, the program Requires v1.4.0 or later to compile and Run Correctly. ******************************************************** / Import java.Awt. *; Import java.awt.event. *; Import java.awt.geom. *; Import javax.swing. *; Import javax.swing.border. *; Class graph02 { Public static void main String [] ARGS) Throws nosuchmethodexception, ClassNotFoundException, InstantiationException, IllegaCCESSEXCEPTION { IF (args.length == 1) { // Pass Command-Line Parameter New GUI (Args [0]); } else { // no command-line parameter given New Gui (NULL); } // END ELSE } // end main } // End class graph02 definition / / =================================== // Class GUI Extends Jframe Implements anctionsListener { // define plotting parameters and // Their default value. Double xmin = -10.0; Double XMAX = 325.0; Double ymin = -100.0; Double YMAX = 100.0; // Tic Mark Intervals Double Xticint = 20.0; Double Yticint = 20.0; // Tic Mark Lengths. If Too Small // on X-Axis, a Default Value IS // useful. Double Xticlen = (YMAX-YMIN) / 50; Double Yticlen = (XMAX-XMIN) / 50; // Calculation Interval Along X-AXIS Double Xcalcinc = 1.0; // Text Fields for PLOTTING Parameters JtextField Xmintxt = New jtextfield (" xmin); JtextField XMaxTxt = New jtextfield (" xmax); Jtextfield Ymintxt = New jtextfield (" ymin); Jtextfield ymaxtxt = New jtextfield (" ymax); JtextField Xticinttxt = New JtextField ("" xticint); JtextField Yticinttxt = New jtextfield (" yticint); Jtextfield Xcalcinctxt = New jtextfield (" xcalcinc); // panels to contain a label and a // Text Field JPanel PAN0 = New jPanel (); JPANel PAN1 = New jPanel (); JPANel PAN2 = new jPanel (); JPanel Pan3 = New jPanel (); JPANel PAN4 = New jPanel (); JPANel PAN5 = New jPanel (); JPanel Pan6 = New jPanel (); // Misc Instance Variables INT frmwidth = 400; INT frmheight = 430; Int width; INT height; Int number; Graphintfc01 data; String args = NULL; // Plots Are Drawn on Thecanvas Canvas thermanvas; // conncture Gui (String Args) Throws Nosuchmethodexception, ClassNotFoundException, InstantiationException, IllegaCCESSEXCEPTION { IF (args! = null) { // Save for use later in the // ActionEvent Handler THIS.Args = args; // instantiate an Object of the // Target class using the string // Name of the class. Data = (graphintfc01) Class.Forname (ARGS). NEWINSTANCE (); } else { // instantiate an Object of the // Test class named junk. Data = new junk (); } // END ELSE // Throw Exception if Number of // Functions is Greater Than 5. Number = DATA.GETNMBR (); IF (Number> 5) { Throw new nosuchmethodexception "Too Many Functions." "Only 5 allowed."); } // end if // Create The Control Panel and // give it a border for cosmetics. JPanel CTLPNL = New jPanel (); CTLPNL.SETLAYOUT (//? ROWS X 4 COLS New GridLayout (0, 4)); CTLPNL.SetBorder New etchedborder ()); // Button for replotting the graph JButton graphbtn = New jbutton ("graph"); Graphbtn.addactionListener (this); // Populate Each Panel with a label // and a text field. Will Place // Thase Panels in a Grid on the // Control Panel Later. PAN0.ADD (New Jlabel ("Xmin")); PAN0.Add (Xmintxt); PAN1.Add (New Jlabel ("Xmax")); PAN1.Add (XMAXTXT); PAN2.Add (New Jlabel ("Ymin")); PAN2.ADD (YMINTXT); PAN3.Add (New Jlabel ("Ymax")); PAN3.Add (YMAXTXT); PAN4.Add (New Jlabel ("Xticint")); PAN4.ADD (XTICINTTXT); Pan5.Add (New Jlabel ("Yticint")); PAN5.Add (YticinTxt); PAN6.Add (New Jlabel ("Xcalcinc"); Pan6.add (xcalcinctxt); // add the populate panels and the // Button to the Control Panel with // a Grid Layout. CTLPNL.Add (PAN0); CTLPNL.Add (PAN1); CTLPNL.Add (PAN2); CTLPNL.Add (PAN3); CTLPNL.Add (PAN4); CTLPNL.Add (PAN5); CTLPNL.Add (PAN6); CTLPnl.Add (Graphbtn); // Create a Custom Canvas Object for // all functions to be plotted. Thecanvas = new mycanvas (); Thecanvas.SetBackground Color.white); // add the sub-assemblyum to the // frame. set its location, size, // And Title, And make it visible. GetContentPane (). add ( CTLPNL, "South"); GetContentPane (). add ( Thecanvas, "Center"); Setbounds (0, 0, frmwidth, frmheight); Settitle ("graph02," CopyRight 2002, " "Richard G. Baldwin"); SetVisible (TRUE); // set to exit on x-button click SetDefaultCloseOperation EXIT_ON_CLOSE; // Get and save the size of the // Plotting Surface Width = Thecanvas.GetWidth (); Height = Thecanvas.getHEight (); // Guarantee a repaint on startup. Thecanvas.repaint (); } // End Constructor // --------------------------------- // // this Event Handler is Registered // on the jbutton to cause the // functions to be replotted. Public Void ActionPerformed ( ActionEvent evt) { // re-instantiate the object thing // provides the data Try { IF (args! = null) { DATA = (graphintfc01) Class. Forname (args) .newinstance (); } else {data = new JUNK (); } // END ELSE } catch (exception e) { // KNown to Be Safe At this point. // Otherwise Would Have Aborted // Earlier. } // end catch // set Plotting Parameters Using // Data from the text fields. Xmin = Double.Parsedouble ( XMintxt.getText ()); XMAX = double.parsedouble ( XMaxtxt.getText ()); Ymin = double.pasedouble ( YMINTXT.GETTEXT ()); YMAX = double.pasedouble ( YMAXTXT.GETTEXT ()); Xticint = double.parsedouble ( Xticinttxt.getText ()); Yticint = double.parsedouble ( Yticinttxt.getText ()); Xcalcinc = double.pasedouble Xcalcinctxt.getText ()); // Calculate New Values for Thae // Length of the tic marks on The THE // axes. if Too Small on X-AXIS, // a Default value is buy. Xticlen = (YMAX-YMIN) / 50; Yticlen = (XMAX-XMIN) / 50; // Repaint the Plotting Area Thecanvas.repaint (); } // End ActionPerformed // --------------------------------- // // this is an inner class, Which is buy // to override the Paint Method on The // Plotting Surface. Class Mycanvas Extends Canvas { // Factors to Convert from Double // VALUES TO INTEGER PIXEL LOCATIONS. Double Xscale; Double Yscale; // Override the Paint Method Public void paint (graphics g) { // Calculate the scale factors Xscale = width / (xmax-xmin); Yscale = height / (ymax-ymin); // set the Origin Based on the // minimum value in x and y G.Translate ((int) ((0-xmin) * xscale), (int) ((0-ymin) * yscale); Drawaxes (g); // Draw the axes // Draw Each Curve in a Different // color. For (int CNT = 0; CNT CNT ) { // Get Initial Data Values Double XVAL = Xmin; INT OLDX = GetThex (XVAL); INT OLDY = 0; // use the curve number to // determine Which Method to // invoke to get the value for y.switch (cnt) { Case 0: Oldy = getthey (data.f1 (xval)); g.setcolor (color.ble); Break; Case 1: Oldy = GetThey (data.f2 (xval)); G.SetColor (color.blue); Break; Case 2: Oldy = GetThey (data.f3 (xval)); g.setColor (Color.Red); Break; Case 3: Oldy = GetThey (Data.f4 (xval)); g.setcolor (color.magenta); Break; Case 4: Oldy = getthey (data.f5 (xval)); g.setcolor (color.cyan); } // End Switch // Now loop and plot the point While (XVAL INT YVAL = 0; // Get next data value. Uses Use T // Curve Number to Determine // Which Method to Invoke To To // Get the value for y. Switch (cnt) { Case 0: Yval = getthey DATA.F1 (XVAL); Break; Case 1: Yval = getthey DATA.F2 (XVAL)); Break; Case 2: Yval = getthey DATA.F3 (XVAL); Break; Case 3: Yval = getthey DATA.F4 (XVAL); Break; Case 4: Yval = getthey DATA.F5 (XVAL)); } // end switch1 // convert the x-value to an int // and draw the next line // segment INT x = GetThex (XVAL); g.drawline (Oldx, Oldy, X, YVAL); // increment along the x-axis XVAL = Xcalcinc; // Save end point to use as // Start Point for Next Line // segment. Oldx = x; Oldy = yval; } // End while loop } // End for loop } // end overridden Paint Method // --------------------------------- // // Method to Draw Axes with tic marks // and labels in The Color Green Void Drawaxes (Graphics G) { g.setcolor (color.green); // Label Left X-Axis and Bottom // Y-axis. Thase Are the Easy // ones. Separate the labels from // the ends of the tic marks by // TWO Pixels. g.drawstring (" (int) xmin, GetThex (Xmin), Getthey (XTIicleN / 2) -2); g.drawstring ("" (int) ymin, GetThex (Yticlen / 2) 2, Getthey (Ymin)); // label the right x-axis and the // Top Y-Axis. Thase Are The Hard // Ones Because The position MUST // be adjusted by the font size and // the number of character. // Get the width of the string for // Right End of X-Axis and The //HeiHT of the string for top of // Y-Axis // Create a string this is an // integer representation of the // label for the right end of the // x-axis. Then Get a character // array what represents the // String. INT XMAXINT = (int) xmax; String xmaxstr = "" xmaxint; CHAR [] array = xmaxstr. Tochararray (); // Get A FontMetrics Object That Can // be used to get the size of the // String in Pixels. FontMetrics Fontmetrics = g.getfontmetrics (); // Get a bounding reccTangle for the // String Rectangle2D R2D = FontMetrics.getstringbounds Array, 0, array.length, g); // Get the width and the height of // the bunking reccTangle. The Bounding Rectangle. // width is the width of the label // at the right end of the // x-axis. The Height Applies To // all the labels, but is needed // Specify for the label at /// The top end of the y-axis. INT labwidth = (int) (R2D.Getwidth ()); INT Labheight = (int) (R2D.getHeight ()); // Label the Positive X-Axis and the // Positive Y-Axis Using The Width // and height from above to // Position The Labels. There // Labels Apply to the Very Ends of // the axes at the Edge of Thae // Plotting Surface. g.drawstring ("" (int) xmax, GetThex (Xmax) -Labwidth, Getthey (XTIicleN / 2) -2); g.drawstring ("" (int) YMAX, GetThex (YTIicleN / 2) 2, GetThey (Ymax) Labheight); // Draw the axes g.drawline (GetThex (Xmin), Getthey (0.0), GetThex (XMAX), GetThey (0.0)); g.drawline (getThex (0.0), Getthey (Ymin), GetThex (0.0), GetThey (YMAX)); // Draw the Tic Marks ON AxES XTICS (g); YTICS (g); } // end drawaxes // --------------------------------- // // Method to Draw Tic Marks on X-Axis Void XTICs (Graphics G) { Double xdoub = 0; INT x = 0; // Get the ends of the tic mark. INT TOPEND = GetThey (Xticlen / 2); INT bottomend = Getthey (-Xticlen / 2); // if the vertical size of the // Plotting Area IS Small, The // Calculated Tic Size May Be Too // Small. In That Case, Set It To // 10 Pixels. IF (TopEnd <5) { TOPEND = 5; Bottomend = -5; } // end if // loop and draw a series of short // Lines to serve as tic marks. // begin with the Positive X-AXIS //Moving to the right from zero. While (xdoub x = getthex (xdoub); g.drawline (x, topnd, x, bottomend); XDOUB = XTICINT; } // End while // Now do the negative x-axis moving // to the left from Zero XDOUB = 0; While (xdoub> xmin) { x = getthex (xdoub); g.drawline (x, topnd, x, bottomend); XDOUB - = xTicint; } // End while } // End XTICS // --------------------------------- // // Method to Draw Tic Marks on y-axis Void YTICS (Graphics G) { Double Ydoub = 0; INT Y = 0; INT RIGHTEND = GetThex (YTIicleN / 2); INT leftend = getthex (-yticlen / 2); // loop and draw a series of short // Lines to serve as tic marks. // begin with the Positive Y-AXIS // Moving Up from Zero. While (YDOUB Y = getthey (ydoub); g.drawline (Rightend, Y, Leftend, Y); YDOUB = YTICINT; } // End while // Now do the negative y-axis moving // Down from Zero. YDOUB = 0; While (YDOUB> YMIN) { Y = getthey (ydoub); g.drawline (Rightend, Y, Leftend, Y); YDOUB - = Yticint;} // end while } // end ytics // --------------------------------- // // this method translates and scales // a Double Y Value to Plot Properly // in The Integer Coordinate System. // in addition to schelation, IT Causes // The Positive Direction of The // Y-axis to be from bottom to top. INT getthey { Double Ydoub = (YMAX Ymin) -y; INT YINT = (int); YDOUB * YSCALE); Return Yint; } // End GetThey // --------------------------------- // // this Method Scales a Double X Value // TO PLOT PROPERLY in The Integer // Coordinate System. Int getthex (double x) { Return (int) (x * xscale); } // end getthex // --------------------------------- // } // end inner class mycanvas / / =================================== // } // End Class GUI / / =================================== // // Sample Test Class. Required for // compilation and stand-alone // Testing. Class Junk Implements Graphintfc01 { Public int GetNMBR () { // Return Number of Functions to // process. Must Not Exceed 5. Return 5; } // end genmbr Public Double F1 (double x) { Return (x * x * x) / 200.0; } // end f1 PUBLIC DOUBLE F2 (double x) { RETURN - (x * x * x) / 200.0; } // end f2 PUBLIC DOUBLE F3 (double x) { Return (x * x) / 200.0; } // end f3 PUBLIC DOUBLE F4 (Double X) { Return 50 * Math.cos (X / 10.0); } // end f4 Public Double F5 (double x) { Return 100 * math.sin (x / 20.0); } // end f5 } // End Sample Class Junk Listing 40