## Thursday, August 14, 2014

### Drawing a Sine Wave using Java Graphics [Swing]

Drawing a sine wave using Swing: The number of cycles is adjustable. Code Source: Thinking In Java 4th Edition.

Code :

```import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;

class SineDraw extends JPanel {

//Scalefactor manipulates the number of points on the screen
//Joining these points forms our wave. It will represent the clarity
//of the display but low values may distort the wave
private static final int SCALEFACTOR = 200;
//The number of complete waves to display on the screen
private int cycles;
//Total number of points that will be joined to form the wave
private int points;
//The actual sine values calculated. Drawing will be done relative to
//the height (actual vertical coordinates calculated from sine values). These aren't used as some coordinates.
private double[] sines;
//The actual coordinates (vertical) calculated based on the sine values
private int[] pts;

public SineDraw() {
//Constructor Sets the number of cycles to display on the screen
setCycles(1);
}

public void paintComponent(Graphics g) {
super.paintComponent(g);
//The current width of the Panel
int maxWidth = getWidth();
//hstep mean the horizontal distance between two points on the screen
double hstep = (double) maxWidth / (double) points;
//The maximum height
int maxHeight = getHeight();
pts = new int[points];
for (int i = 0; i < points; ++i) {
/*
This is for calculating the vertical coordinates of the
points to be joined to form the wave. Horizontal coordinates are calculated
based on hstep ( horizontal distance between consecutive points )
eg.
if sine[i] is 1, maxHeight = 500
pts[i] = 1 * 250 * 0.95 (which means 95%) + maxHeight / 2
= 237.5 + 250
= 487.5
This is the bottom of the wave [*0.95 sets a 95% margin on display]```
```
Now if sine[i] is -1, maxHeight = 500
pts[i] = -1 * 250 * 0.95 (which means 95%) + maxHeight / 2
= -237.5 + 250
= 12.5
This is the top of the display. Note that +maxHeight/2 is necessary

( * maxHeight / 2 ) calculates vertical coordinate values relative to sine values
as sines are positive, the graph will rise, and if sine is negative, the
graph will fall
*/
pts[i] = (int) (sines[i] * maxHeight / 2 * 0.95 + maxHeight / 2);
}
g.setColor(Color.RED);
for (int i = 1; i < points; ++i) {
/*
Lets say first point gets
x1 = 0 * hstep = 0
x2 = 0 * hstep = 0

as expected.

Second one
x1 = 1 * hstep = Lets say the points have a horizontal distance of 0.1
x2 = 2 * hstep = Add the same horizontal distance to the second point two times
as the values are caculated from the left most edge of the panel

and so on...
*/
int x1 = (int) ((i - 1) * hstep);
int x2 = (int) (i * hstep);

//The vertical values have already been calculated
int y1 = pts[i - 1];
int y2 = pts[i];

//Now draw a line based on these calculated coordinates based on sine values
g.drawLine(x1, y1, x2, y2);
}
}

public void setCycles(int newCycles) {
cycles = newCycles;

/*Total number of points = SCALEFACTOR ( here this is manipulating the sharpness of display
by changing the total number of points ) * cycles ( Lets say 1 cycle ) so that
by multiplying it with SCALEFACTOR is becomes 200 cycles in this case.
We need both negative values and positive values to draw. So the number of
points is multiplied by 2. [One cycle is represented by an upper + lower curve
on the graph]. If you remove the (*2) and set the slider to 1, notice that
the upper curve isn't drawn.

Before the first half that is before the effect of *2, sines have +ve values
After the first half, negative sin values begin.
*/
points = SCALEFACTOR * cycles * 2;
sines = new double[points];
for (int i = 0; i < points; ++i) {
if(i == points/2){
System.out.println("***** Rest half *****");
}
/*
FYI, 1 radian is the angle subtended by an arc of the circle that
has the same length as the radius of the circle. And PI is the angle
subtended by half the circle on the center = 180 Degrees.
Hers's a nice GIF explanation : http://en.wikipedia.org/wiki/Radian

Multiply Math.PI by i (to get reltively increasing radian values
and divide by SCALEFACTOR for getting values relative to the number
of points.

Angle is getting increased by 180deg (Math.PI) every time and then divided by SCALEFACTOR
to get small increasing sine values based on these slowly increasing radian values
*/
double radians = (Math.PI / SCALEFACTOR) * i; //Notice that last iteration (in case scalefactor = 200, cycles = 2, points = 800
//radians = PI * 800 / 200 = PI * 4 = 180 degrees 4 times which gets you 2 complete cycles.

//Now calculate the actual sin values based on radians
System.out.println("Radian = " + radians + " sines = " + sines[i]);
}
repaint();
}
}

public class SineWave extends JFrame {

private SineDraw sines = new SineDraw();
private JSlider adjustCycles = new JSlider(1, 30, 1);

public SineWave() {
public void stateChanged(ChangeEvent e) {
//Slider sets the number of cycles
sines.setCycles(
((JSlider) e.getSource()).getValue());
}
});
setSize(700, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}

public static void main(String[] args) {
//Nimbus look and feel. Looks nice to me.
try {
for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (Exception e) {
// If Nimbus is not available, you can set the GUI to another look and feel.
}
new SineWave();
}
}```