import java.util.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class EngineFrame extends JFrame implements ChangeListener, WindowListener {
private JPanel south = null;
private EngineSurface esf = null;
private JSlider crankthrow = null;
private JSlider conrodlength = null;
private JSlider speed = null;
private JSlider seperation = null;
public EngineFrame() {
super("V-Twin");
addWindowListener(this);
getContentPane().setLayout(new BorderLayout());
esf = new EngineSurface();
getContentPane().add(esf, BorderLayout.CENTER);
south = new JPanel(new GridLayout(4,2));
south.add(new JLabel("Crank Throw:"));
crankthrow = new JSlider(50,200,90);
crankthrow.addChangeListener(this);
south.add(crankthrow);
south.add(new JLabel("Connecting Rod Length:"));
conrodlength = new JSlider(100,300,200);
conrodlength.addChangeListener(this);
south.add(conrodlength);
south.add(new JLabel("Speed:"));
speed = new JSlider(1,100,80);
speed.addChangeListener(this);
south.add(speed);
south.add(new JLabel("Seperation:"));
seperation = new JSlider(0,180,90);
seperation.addChangeListener(this);
south.add(seperation);
getContentPane().add(south, BorderLayout.SOUTH);
setSize(600,600);
}
public static void main(String args[]) {
EngineFrame fr = new EngineFrame();
fr.setVisible(true);
}
public void stateChanged(ChangeEvent e) {
esf.radius = crankthrow.getValue();
esf.conrodlength = conrodlength.getValue();
esf.counterInc = (float)(speed.getValue()/1000.0);
esf.seperation = seperation.getValue();
}
//WINDOW LISTENER METHODS
public void windowClosing(WindowEvent w){
System.exit(0);
}
public void windowOpened(WindowEvent w){}
public void windowIconified(WindowEvent w){}
public void windowDeiconified(WindowEvent w){}
public void windowClosed(WindowEvent w){}
public void windowActivated(WindowEvent w){}
public void windowDeactivated(WindowEvent w){}
}
class EngineSurface extends Surface implements Runnable {
public float counterInc = (float)0.08;
public double radius = 90;
public double conrodlength = 200;
public double seperation = 90;
private float counter = 0;
public EngineSurface() {
super();
}
// ...implement this routine for painting...
public void render(int w, int h, Graphics2D g2) {
float size = (w > h) ? h : w;
double journalsep = seperation*Math.PI/90;
Ellipse2D ellipse = new Ellipse2D.Double();
ellipse.setFrame(w/2-radius,2*h/3-radius,2*radius,2*radius);
Line2D crank1 = new Line2D.Double();
crank1.setLine(w/2,2*h/3,w/2+radius*Math.cos(counter),2*h/3+radius*Math.sin(counter));
Line2D axis1 = new Line2D.Double();
axis1.setLine(0,0,w/2,2*h/3);
Line2D conrod1 = new Line2D.Double();
conrod1 = getLinePosSlope(0,0,w/2,2*h/3,w/2+radius*Math.cos(counter),2*h/3+radius*Math.sin(counter),conrodlength,0,w/2);
Line2D crank2 = new Line2D.Double();
crank2.setLine(w/2,2*h/3,w/2+radius*Math.cos(counter+journalsep),2*h/3+radius*Math.sin(counter+journalsep));
Line2D axis2 = new Line2D.Double();
axis2.setLine(w,0,w/2,2*h/3);
Line2D conrod2 = new Line2D.Double();
conrod2 = getLineNegSlope(w,0,w/2,2*h/3,w/2+radius*Math.cos(counter+journalsep),2*h/3+radius*Math.sin(counter+journalsep),conrodlength,w/2,w);
g2.setColor(Color.black);
g2.setStroke(new BasicStroke((float)1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, new float[]{0,6,0,6}, 0));
g2.draw(ellipse);
g2.setStroke(new BasicStroke((float)1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, new float[]{0,6,0,6}, 0));
g2.draw(axis1);
g2.draw(axis2);
g2.setColor(Color.red);
g2.setStroke(new BasicStroke((float)12, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.draw(crank1);
g2.setStroke(new BasicStroke((float)6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.draw(conrod1);
g2.setStroke(new BasicStroke((float)48, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
g2.draw(getParallelRect(conrod1.getX1(), conrod1.getY1(), w/2, 2*h/3, 24));
g2.setColor(Color.blue);
g2.setStroke(new BasicStroke((float)12, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.draw(crank2);
g2.setStroke(new BasicStroke((float)6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.draw(conrod2);
g2.setStroke(new BasicStroke((float)48, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
g2.draw(getParallelRect(conrod2.getX1(), conrod2.getY1(), w/2, 2*h/3, 24));
}
//return a line of distance d, one endpoint (p,q) and one endpoint on the line defined by (x1,y1) and (x2,y2)
//returned x values must be between xmin and xmax
//slope must be positive!!!
Line2D getLinePosSlope(double x1, double y1, double x2, double y2, double p, double q, double d, double xmin, double xmax) {
double xsamp, ysamp, dist;
while (true) {
xsamp = (xmax+xmin)/2;
ysamp = (y2-y1)*(xsamp-x1)/(x2-x1)+y1;
dist = Math.sqrt((xsamp-p)*(xsamp-p)+(ysamp-q)*(ysamp-q));
if (Math.abs(dist-d)<1) break;
else if (dist<d) xmax = xsamp;
else if (dist>d) xmin = xsamp;
}
Line2D tmp = new Line2D.Double();
tmp.setLine(xsamp,ysamp,p,q);
return tmp;
}
//slope must be negative!!!
Line2D getLineNegSlope(double x1, double y1, double x2, double y2, double p, double q, double d, double xmin, double xmax) {
double xsamp, ysamp, dist;
while (true) {
xsamp = (xmax+xmin)/2;
ysamp = (y2-y1)*(xsamp-x1)/(x2-x1)+y1;
dist = Math.sqrt((xsamp-p)*(xsamp-p)+(ysamp-q)*(ysamp-q));
if (Math.abs(dist-d)<1) break;
else if (dist>d) xmax = xsamp;
else if (dist<d) xmin = xsamp;
}
Line2D tmp = new Line2D.Double();
tmp.setLine(xsamp,ysamp,p,q);
return tmp;
}
//get a rectangle located parallel to line defined by (x1,y1) and (x2,y2), located at one end, and side length s.
Line2D getParallelRect(double x1, double y1, double x2, double y2, double s) {
double theta = Math.atan2(y2-y1, x2-x1);
theta += Math.PI/2;
Line2D tmp = new Line2D.Double();
tmp.setLine((int)(x1+s*Math.cos(theta)), (int)(y1+s*Math.sin(theta)), (int)(x1+s*Math.cos(theta+Math.PI)), (int)(y1+s*Math.sin(theta+Math.PI)));
return tmp;
}
//threading....
public void run() {
long lastRepaint = 0;
long sleepTime = 0;
while (true) {
//mark when we started
lastRepaint = new Date().getTime();
//do the work
counter+=counterInc;
repaint();
//calculate time we must sleep
sleepTime = 10 - (new Date().getTime() - lastRepaint);
try {
if (sleepTime > 0) Thread.currentThread().sleep(sleepTime);
else Thread.currentThread().yield();
} catch (java.lang.InterruptedException e) {}
}
}
}