Survey
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
Dr. Manuel Carcenac - European University of Lefke
Advanced topics
in animation
- presented with JOGL 2
kinematic animation: time interpolation of degrees of freedom
dynamic animation: time integration of degrees of freedom
dynamic animation: all-pairs n-body problem and gravitation
References:
http://jogamp.org/ (for JOGL 2.0)
http://download.java.net/media/jogl/jogl-2.x-docs/
http://www.glprogramming.com/red/
http://eng.eul.edu.tr/manuel/Course_on_Graphics_in_Java/Course_on_Graphics_in_Java.htm
(for practical introduction to JOGL 2.0)
Advanced Animation and Rendering Techniques – Theory and Practice
Alan Watt, Mark Watt; Addison-Wesley, ACM press
Michael Thomas Flanagan's Java Scientific and Numerical Library
http://www.ee.ucl.ac.uk/~mflanaga/java/index.html
http://www.ee.ucl.ac.uk/~mflanaga/java/CubicSpline.html (for cubic spline interpolation)
http://farside.ph.utexas.edu/teaching/329/lectures/node35.html
for Runge-Kutta method
http://www.ast.cam.ac.uk/~sverre/web/pages/nbody.htm
for n-body problem
http://www.scholarpedia.org/article/N-body_simulations_(gravitational)
1
Dr. Manuel Carcenac - European University of Lefke
kinematic animation: time interpolation of degrees of freedom
cubic spline interpolation:
install Flanagan's scientific library:
put flanagan.jar into folder C:\Program Files\Java\jdk1.6.0_23\jre\lib\ext
add to system variable CLASSPATH (already exists, was created for JOGL)
Computer Properties Advanced system settings Environment Variables
edit system variable CLASSPATH (already exists, was created for JOGL)
add at the end of it:
;C:\Program Files\Java\jdk1.6.0_23\jre\lib\ext\flanagan.jar
import statement:
import flanagan.interpolation.*;
cubic spline with Nk control points ( tk , xk ) :
double[] xk = new double[Nk];
double[] tk = new double[Nk];
CubicSpline csx = new CubicSpline(tk , xk);
interpolation of function x(t) :
double x = csx.interpolate(t);
for every control point k , csx.interpolate(tk[k]) yields value xk[k]
2
Dr. Manuel Carcenac - European University of Lefke
example: interpolated curve with interactive selection and modification of control points
..........
import flanagan.interpolation.*;
public class P
{ public static void main(String[] arg)
{ Gui gui = new Gui(); } }
class Gui
{
JFrame f; DrawingPanel p;
JPanel ps; JSlider skx , sky , skz , skt; JLabel lkx , lky , lkz , lkt;
int Nk = 8; int ks = 0; // ks = k of selected key
double[] xk = new double[Nk] , yk = new double[Nk] , zk = new double[Nk] , tk = new double[Nk];
int N = 60;
double L = 100.0 , T = 100.0;
{ for (int k = 0 ; k < Nk ; k++)
{ xk[k] = 0.0; yk[k] = - L / 2 + L * k / (Nk - 1); zk[k] = 0.0;
tk[k] = T * k / (Nk - 1); } }
class DrawingPanel extends GLJPanel
{
GLU glu; GLUquadric quad;
float[] RED = new float[] { 1.0f , 0.0f , 0.0f , 1.0f } , GREEN = new float[] { 0.0f , 1.0f , 0.0f , 1.0f }
, WHITE = new float[] { 1.0f , 1.0f , 1.0f , 1.0f };
DrawingPanel()
{ super(new GLCapabilities(GLProfile.getDefault()));
this.addGLEventListener(new GLEventListener()
{
..........
public void display(GLAutoDrawable drawable) //****
{
..........
DISPLAY
////------- KEY POINTS
for (int k = 0 ; k < Nk ; k++)
{
gl.glMaterialfv(GL.GL_FRONT_AND_BACK
, GLLightingFunc.GL_AMBIENT_AND_DIFFUSE , (k == ks ? RED : GREEN) , 0);
gl.glPushMatrix(); gl.glTranslatef((float)xk[k] , (float)yk[k] , (float)zk[k]);
glu.gluSphere(quad , 2.0f , 10 , 10);
gl.glPopMatrix();
}
3
Dr. Manuel Carcenac - European University of Lefke
////------- INTERPOLATED POINTS
gl.glMaterialfv(GL.GL_FRONT_AND_BACK
, GLLightingFunc.GL_AMBIENT_AND_DIFFUSE , WHITE , 0);
CubicSpline csx = new CubicSpline(tk , xk)
, csy = new CubicSpline(tk , yk)
, csz = new CubicSpline(tk , zk);
for (int k = 0 ; k < N ; k++)
{
double t = T * k / (N - 1);
double x = csx.interpolate(t)
, y = csy.interpolate(t)
, z = csz.interpolate(t);
gl.glPushMatrix(); gl.glTranslatef((float)x , (float)y , (float)z);
glu.gluSphere(quad , 1.0f , 10 , 10);
gl.glPopMatrix();
}
gl.glFlush();
}
..........
} );
}
}
4
Dr. Manuel Carcenac - European University of Lefke
Gui()
{
f = new JFrame(); f.setFocusable(true); f.setVisible(true);
p = new DrawingPanel(); f.getContentPane().add(p , BorderLayout.CENTER);
f.addMouseListener(new MouseAdapter()
{ public void mouseClicked(MouseEvent e)
{ int bt = e.getButton();
switch (bt)
{ case MouseEvent.BUTTON1: ks--;
if (ks == -1) ks = Nk - 1;
case MouseEvent.BUTTON3: ks++; if (ks == Nk) ks = 0;
break;
break; }
skx.setValue((int)(1000 * (0.5 + xk[ks] / L)));
sky.setValue((int)(1000 * (0.5 + yk[ks] / L)));
skz.setValue((int)(1000 * (0.5 + zk[ks] / L)));
skt.setValue((int)(1000 * tk[ks] / T));
f.repaint(); } } );
ps = new JPanel(); ps.setLayout(new GridLayout(0 , 2));
f.getContentPane().add(ps , BorderLayout.EAST);
skx = new JSlider(JSlider.HORIZONTAL , 0 , 1000 , 0);
lkx = new JLabel(" x of selected key");
sky = new JSlider(JSlider.HORIZONTAL , 0 , 1000 , 0);
lky = new JLabel(" y of selected key");
skz = new JSlider(JSlider.HORIZONTAL , 0 , 1000 , 0);
lkz = new JLabel(" z of selected key");
skt = new JSlider(JSlider.HORIZONTAL , 0 , 1000 , 0);
lkt = new JLabel(" t of selected key");
ps.add(skx);
ps.add(lkx);
ps.add(sky);
ps.add(lky);
ps.add(skz);
ps.add(lkz);
ps.add(skt);
ps.add(lkt);
skx.addChangeListener( . . . . . . . . . . { xk[ks] = -L/2+L*skx.getValue()/1000; f.repaint(); } } );
sky.addChangeListener( . . . . . . . . . . { yk[ks] = -L/2+L*sky.getValue()/1000; f.repaint(); } } );
skz.addChangeListener( . . . . . . . . . . { zk[ks] = -L/2+L*skz.getValue()/1000; f.repaint(); } } );
skt.addChangeListener( . . . . . . . . . .
{ if (ks != 0 && ks != Nk - 1) tk[ks] = T*skt.getValue()/1000;
if (ks > 0
&& tk[ks - 1] + T / 100 > tk[ks]) { tk[ks] = tk[ks - 1] + T / 100; }
if (ks < Nk - 1 && tk[ks] > tk[ks + 1] - T / 100) { tk[ks] = tk[ks + 1] - T / 100; }
f.repaint(); } } );
f.setSize(new Dimension(900 + 16 , 600 + 38));
}
}
5
Dr. Manuel Carcenac - European University of Lefke
6
Dr. Manuel Carcenac - European University of Lefke
example: interpolated trajectory of a ball:
interactive creation of key frames over time
followed by spline driven animation
..........
import flanagan.interpolation.*;
public class P
{ public static void main(String[] arg)
{ Gui gui = new Gui();
while (true)
{
if (gui.read_perform_anim())
{ gui.animation();
gui.write_perform_anim(false); }
Thread.yield();
}
}
}
class Gui
{
JFrame f; DrawingPanel p;
JPanel ps; JSlider skx , sky , skz;
JLabel lkx , lky , lkz;
JButton b_new_key , b_perform_anim;
int Nk , nk;
double[] xk , yk , zk , tk;
double L = 100.0 , T = 20.0;
double time;
boolean perform_anim = false;
synchronized void write_perform_anim(boolean value) { perform_anim = value; }
synchronized boolean read_perform_anim() { return perform_anim; }
void animation()
{
time = 0; long dt = 50; // 0.05s
while (time + dt * 0.001 < T)
{ long t_start = System.currentTimeMillis();
f.repaint();
time += dt * 0.001;
long dt_real = System.currentTimeMillis() - t_start;
if (dt_real < dt) try {Thread.sleep(dt - dt_real);} catch(InterruptedException ee){}
else System.out.println("PC too slow; please increase dt"); }
}
7
Dr. Manuel Carcenac - European University of Lefke
class DrawingPanel extends GLJPanel
{
GLU glu; GLUquadric quad;
float[] RED = new float[] { 1.0f , 0.0f , 0.0f , 1.0f } , GREEN = new float[] { 0.0f , 1.0f , 0.0f , 1.0f }
, WHITE = new float[] { 1.0f , 1.0f , 1.0f , 1.0f };
DrawingPanel()
{ super(new GLCapabilities(GLProfile.getDefault()));
this.addGLEventListener(new GLEventListener()
{
..........
public void display(GLAutoDrawable drawable) //****
{
..........
DISPLAY
////------- KEY POINTS
for (int k = 0 ; k < nk ; k++)
{
gl.glMaterialfv(GL.GL_FRONT_AND_BACK
, GLLightingFunc.GL_AMBIENT_AND_DIFFUSE , (k == nk - 1 ? RED : GREEN) , 0);
gl.glPushMatrix(); gl.glTranslatef((float)xk[k] , (float)yk[k] , (float)zk[k]);
glu.gluSphere(quad , 4.0f , 10 , 10);
gl.glPopMatrix();
}
////------- ANIMATED POINT
if (read_perform_anim())
{
gl.glMaterialfv(GL.GL_FRONT_AND_BACK
, GLLightingFunc.GL_AMBIENT_AND_DIFFUSE , WHITE , 0);
CubicSpline csx = new CubicSpline(tk , xk)
, csy = new CubicSpline(tk , yk)
, csz = new CubicSpline(tk , zk);
double x = csx.interpolate(time)
, y = csy.interpolate(time)
, z = csz.interpolate(time);
gl.glPushMatrix(); gl.glTranslatef((float)x , (float)y , (float)z);
glu.gluSphere(quad , 4.0f , 10 , 10);
gl.glPopMatrix();
}
gl.glFlush();
}
. . . . . . . . . . } );
} }
8
Dr. Manuel Carcenac - European University of Lefke
Gui()
{
f = new JFrame(); f.setFocusable(true); f.setVisible(true);
p = new DrawingPanel(); f.getContentPane().add(p , BorderLayout.CENTER);
String s = JOptionPane.showInputDialog(f , "number of ctrl pts over " + T + "s ; at least 3");
Nk = Integer.valueOf(s).intValue(); if (Nk<3) { System.out.println("Nk < 3"); System.exit(0); }
nk = 0;
xk = new double[Nk]; yk = new double[Nk]; zk = new double[Nk]; tk = new double[Nk];
ps = new JPanel(); ps.setLayout(new GridLayout(0 , 2));
f.getContentPane().add(ps , BorderLayout.EAST);
skx = new JSlider(JSlider.HORIZONTAL , 0 , 1000 , 0); ps.add(skx);
lkx = new JLabel(" x of new key");
ps.add(lkx);
sky = new JSlider(JSlider.HORIZONTAL , 0 , 1000 , 0); ps.add(sky);
lky = new JLabel(" y of new key");
ps.add(lky);
skz = new JSlider(JSlider.HORIZONTAL , 0 , 1000 , 0); ps.add(skz);
lkz = new JLabel(" z of new key");
ps.add(lkz);
skx.addChangeListener(new ChangeListener()
{ public void stateChanged(ChangeEvent e)
{ if (nk > 0) { xk[nk - 1] = L * skx.getValue() / 1000; f.repaint(); } } } );
sky.addChangeListener(new ChangeListener()
{ public void stateChanged(ChangeEvent e)
{ if (nk > 0) { yk[nk - 1] = L * sky.getValue() / 1000; f.repaint(); } } } );
skz.addChangeListener(new ChangeListener()
{ public void stateChanged(ChangeEvent e)
{ if (nk > 0) { zk[nk - 1] = L * skz.getValue() / 1000; f.repaint(); } } } );
b_new_key = new JButton(); b_new_key.setText("record new key");
b_new_key.addActionListener(new ActionListener()
{ public void actionPerformed(ActionEvent e)
{ if (nk + 1 <= Nk) nk++;
xk[nk - 1] = L * skx.getValue() / 1000;
yk[nk - 1] = L * sky.getValue() / 1000;
zk[nk - 1] = L * skz.getValue() / 1000;
tk[nk - 1] = T * (nk - 1) / (Nk - 1);
f.repaint(); } } );
ps.add(b_new_key);
b_perform_anim = new JButton(); b_perform_anim.setText("anim"); ps.add(b_perform_anim);
b_perform_anim.addActionListener(new ActionListener()
{ public void actionPerformed(ActionEvent e)
{ if (nk < Nk) JOptionPane.showMessageDialog(f , "you have not yet recorded all the keys!");
else write_perform_anim(true); } } );
f.setSize(new Dimension(1000 + 16 , 600 + 38));
}
}
9
Dr. Manuel Carcenac - European University of Lefke
10
Dr. Manuel Carcenac - European University of Lefke
example : angles of an articulated structure:
interactive creation of key frames over time
followed by spline driven animation
..........
import flanagan.interpolation.*;
public class P
{ public static void main(String[] arg)
{ Gui gui = new Gui();
while (true)
{
if (gui.read_perform_anim())
{ gui.animation();
gui.write_perform_anim(false); }
Thread.yield();
}
}
}
class Gui
{
int Na = 7;
JFrame f; DrawingPanel p; JPanel ps;
JSlider[] ska = new JSlider[Na]; JButton b_new_key , b_perform_anim;
float b0 = 110 , b1 = 90 , b2 = 30 , b3 = 20 , b4 = 30 , b5 = 20;
float r0 = 16 , r1 = 10 , r2 = 6 , r3 = 4 , r4 = 6 , r5 = 4;
int Nk , nk;
double[][] ak; double[] tk;
double T = 20.0 , time;
boolean perform_anim = false;
synchronized void write_perform_anim(boolean value) { perform_anim = value; }
synchronized boolean read_perform_anim() { return perform_anim; }
void animation()
{
time = 0; long dt = 50; // 0.05s
while (time + dt * 0.001 < T)
{ long t_start = System.currentTimeMillis();
f.repaint();
time += dt * 0.001;
long dt_real = System.currentTimeMillis() - t_start;
if (dt_real < dt) try {Thread.sleep(dt - dt_real);} catch(InterruptedException ee){}
else System.out.println("PC too slow; please increase dt"); }
}
11
Dr. Manuel Carcenac - European University of Lefke
class DrawingPanel extends GLJPanel
{
GLU glu; GLUquadric quad;
DrawingPanel()
{ super(new GLCapabilities(GLProfile.getDefault()));
this.addGLEventListener(new GLEventListener()
{
..........
public void display(GLAutoDrawable drawable) //**********
{
..........
DISPLAY
////------- LATEST KEY FRAME
if (nk > 0 && ! read_perform_anim())
{
float[] ai = new float[Na];
for (int a = 0 ; a < Na ; a++) ai[a] = (float)(ak[a][nk - 1]);
articulated_arm(gl , glu , quad , ai);
}
////------- ANIMATED ARM
if (read_perform_anim())
{
CubicSpline[] csa = new CubicSpline[Na];
for (int a = 0 ; a < Na ; a++) csa[a] = new CubicSpline(tk , ak[a]);
float[] ai = new float[Na];
for (int a = 0 ; a < Na ; a++) ai[a] = (float)(csa[a].interpolate(time));
articulated_arm(gl , glu , quad , ai);
}
gl.glFlush();
}
. . . . . . . . . . } );
} }
12
Dr. Manuel Carcenac - European University of Lefke
Gui()
{
f = new JFrame(); f.setFocusable(true); f.setVisible(true);
p = new DrawingPanel(); f.getContentPane().add(p , BorderLayout.CENTER);
String s = JOptionPane.showInputDialog(f , "n. of ctrl pts over " + T + "s ; at least 3");
Nk = Integer.valueOf(s).intValue(); if (Nk<3) { System.out.println("Nk<3"); System.exit(0); }
nk = 0;
ak = new double[Na][Nk]; tk = new double[Nk];
ps = new JPanel(); ps.setLayout(new GridLayout(0 , 1));
f.getContentPane().add(ps , BorderLayout.EAST);
for (int a = 0 ; a < Na ; a++)
{ ska[a] = new JSlider(JSlider.HORIZONTAL , 0 , 90 , 0); ps.add(ska[a]);
final int af = a;
ska[a].addChangeListener(new ChangeListener()
{ public void stateChanged(ChangeEvent e) { if (nk > 0) { ak[af][nk - 1] = ska[af].getValue();
f.repaint(); } } } ); }
b_new_key = new JButton(); b_new_key.setText("record new key");
b_new_key.addActionListener(new ActionListener()
{ public void actionPerformed(ActionEvent e)
{ if (nk + 1 <= Nk) nk++;
for (int a = 0 ; a < Na ; a++) ak[a][nk - 1] = ska[a].getValue();
tk[nk - 1] = T * (nk - 1) / (Nk - 1);
f.repaint(); } } );
ps.add(b_new_key);
b_perform_anim = new JButton(); b_perform_anim.setText("anim"); ps.add(b_perform_anim);
b_perform_anim.addActionListener(new ActionListener()
{ public void actionPerformed(ActionEvent e)
{ if (nk < Nk) JOptionPane.showMessageDialog(f , "you have not yet recorded all the keys!");
else write_perform_anim(true); } } );
f.setSize(new Dimension(800 + 16 , 600 + 38));
}
13
Dr. Manuel Carcenac - European University of Lefke
void articulated_arm(GL2 gl , GLU glu , GLUquadric quad , float[] ai)
{
gl.glMaterialfv(GL.GL_FRONT_AND_BACK , GLLightingFunc.GL_AMBIENT_AND_DIFFUSE
, new float[] { 1.0f , 1.0f , 0.0f , 1.0f } , 0);
gl.glRotatef(ai[0] , 0.0f , 0.0f , 1.0f); gl.glRotatef( 90 - ai[1] , 1.0f , 0.0f , 0.0f);
glu.gluSphere(quad , r0 , 10 , 10);
glu.gluCylinder(quad , r0 , r1 , b0 , 10 , 10);
gl.glTranslatef(0f , 0f , b0); gl.glRotatef(ai[2] , 1.0f , 0.0f , 0.0f);
glu.gluSphere(quad , r1 , 10 , 10);
glu.gluCylinder(quad , r1 , r2 , b1 , 10 , 10);
gl.glTranslatef(0f , 0f , b1);
gl.glPushMatrix();
gl.glRotatef( - ai[3] , 1.0f , 0.0f , 0.0f);
glu.gluSphere(quad , r2 , 10 , 10);
glu.gluCylinder(quad , r2 , r3 , b2 , 10 , 10);
gl.glTranslatef(0f , 0f , b2); gl.glRotatef(ai[4] , 1.0f , 0.0f , 0.0f);
glu.gluSphere(quad , r3 , 10 , 10);
glu.gluCylinder(quad , r3 , 0.0f , b3 , 10 , 10);
gl.glPopMatrix();
gl.glRotatef(ai[5] , 1.0f , 0.0f , 0.0f);
glu.gluSphere(quad , r4 , 10 , 10);
glu.gluCylinder(quad , r4 , r5 , b4 , 10 , 10);
gl.glTranslatef(0f , 0f , b4); gl.glRotatef( - ai[6] , 1.0f , 0.0f , 0.0f);
glu.gluSphere(quad , r5 , 10 , 10);
glu.gluCylinder(quad , r5 , 0.0f , b5 , 10 , 10);
}
}
14
Dr. Manuel Carcenac - European University of Lefke
15
Dr. Manuel Carcenac - European University of Lefke
dynamic animation: time integration of degrees of freedom
example: balls under the influence of gravity and bouncing off the floor
..........
public class P
{ public static void main(String[] arg)
{ Gui gui = new Gui();
gui.animation(); } }
class Ball
{
float[] color;
double x , y , z;
double vx , vy , vz;
Ball(float[] color , double x , double y ,double z , double vx , double vy , double vz)
{ this.color = color;
this.x = x; this.y = y;
this.z = z;
this.vx = vx; this.vy = vy; this.vz = vz; }
}
class Gui
{
JFrame f; DrawingPanel p;
int N; Ball[] ball;
double V = 30.0 , DELTA = 10.0 , ALPHA = 0.9;
//// V in m/s; DELTA in degrees; ALPHA <= 1.0
void animation()
{
double delta_t = 0.05; long dt = (long)(delta_t * 1000);
while (true)
{ long t_start = System.currentTimeMillis();
for (int i = 0 ; i < N ; i++)
{
ball[i].vz += -9.81 * delta_t;
ball[i].x += ball[i].vx * delta_t;
ball[i].y += ball[i].vy * delta_t;
if (ball[i].z <= 1.0)
{ ball[i].z = 1.0; ball[i].vx *= ALPHA;
ball[i].vy *= ALPHA;
ball[i].z += ball[i].vz * delta_t;
ball[i].vz *= - ALPHA; }
}
f.repaint();
long dt_real = System.currentTimeMillis() - t_start;
if (dt_real < dt) try {Thread.sleep(dt - dt_real);} catch(InterruptedException ee){}
else System.out.println("PC too slow; please increase dt"); }
}
16
Dr. Manuel Carcenac - European University of Lefke
float[] RED = new float[] { 1.0f , 0.0f , 0.0f , 1.0f } , GREEN = new float[] { 0.0f , 1.0f , 0.0f , 1.0f }
, BLUE = new float[] { 0.0f , 0.0f , 1.0f , 1.0f }, WHITE = new float[] { 1.0f , 1.0f , 1.0f , 1.0f };
class DrawingPanel extends GLJPanel
{
GLU glu; GLUquadric quad;
DrawingPanel()
{ super(new GLCapabilities(GLProfile.getDefault()));
this.addGLEventListener(new GLEventListener()
{
..........
public void display(GLAutoDrawable drawable) //**********
{
..........
DISPLAY
////------- GROUND
gl.glMaterialfv( GL.GL_FRONT_AND_BACK
, GLLightingFunc.GL_AMBIENT_AND_DIFFUSE , WHITE , 0);
glu.gluDisk(quad , 0.0f , 100.0f , 150 , 150);
////------- ANIMATED BALLS
for (int i = 0 ; i < N ; i++)
{
gl.glMaterialfv( GL.GL_FRONT_AND_BACK
, GLLightingFunc.GL_AMBIENT_AND_DIFFUSE , ball[i].color , 0);
gl.glPushMatrix(); gl.glTranslatef((float)(ball[i].x) , (float)(ball[i].y), (float)(ball[i].z));
glu.gluSphere(quad , 1.0f , 10 , 10);
gl.glPopMatrix();
}
gl.glFlush();
}
. . . . . . . . . . } );
} }
17
Dr. Manuel Carcenac - European University of Lefke
Gui()
{
f = new JFrame(); f.setFocusable(true); f.setVisible(true);
p = new DrawingPanel(); f.getContentPane().add(p , BorderLayout.CENTER);
String s = JOptionPane.showInputDialog(f , "number of balls; at least 1");
N = Integer.valueOf(s).intValue(); if (N < 1) { System.out.println("N < 1"); System.exit(0); }
ball = new Ball[N];
for (int i = 0 ; i < N ; i++)
{ double v = V * (0.2 * Math.random() + 0.8);
double a = 2 * Math.PI * Math.random();
double b = DELTA * (Math.PI / 180) * Math.random();
ball[i] = new Ball( (i%3==0 ? RED : i%3==1 ? GREEN : BLUE)
, 0.0 , 0.0 , 0.0
, v * Math.sin(b) * Math.cos(a)
, v * Math.sin(b) * Math.sin(a)
, v * Math.cos(b)); }
f.setSize(new Dimension(600 + 16 , 600 + 38));
}
}
18
Dr. Manuel Carcenac - European University of Lefke
19
Dr. Manuel Carcenac - European University of Lefke
dynamic animation: all-pairs n-body problem and gravitation
all-pairs n-body problem:
n bodies fully interacting with each other:
action of all bodies j
over body i
body i
i
j
position pi and velocity vi of body i :
xi
y
i
p z
pv i i i
v i vx i
vy i
vz i
time derivative of the system's global state:
pv 0
pv 0
.
.
d
. D . , t
dt
.
.
pv n 1
pv n 1
20
Dr. Manuel Carcenac - European University of Lefke
time integration:
global state s(t) is known at time t
d
s t D s t , t
dt
obtain global state s(t + t)
at time t + t
Euler method:
s t Δt s t D s t , t Δt
inaccurate + numerical instability: errors pile up over time
second-order Runge-Kutta method:
D1 D s t , t
first estimate of D (at t )
D 2 D s t D1 Δt / 2 , t Δt / 2
second estimate of D (at t + t / 2 )
s t Δt s t D 2 Δt
accurate + numerically stable = excellent
fourth-order Runge-Kutta method is often preferred...
21
Dr. Manuel Carcenac - European University of Lefke
gravitational interactions:
body i of mass mi
gravitational constant G = 6.67384e-11
softening length ε
Newton's law of gravity:
D s t , t
.
.
p i
d
dt
v i
G
.
.
.
.
vi
j i
m j p j pi
p p
j
.
.
2
i
2
3
2
22
Dr. Manuel Carcenac - European University of Lefke
case study: collision of two galaxies - simulation parameters and initial state:
total mass of the two galaxies = 4×1042 kg
initial diameter of each galaxy H = 1021 m
softening length ε = H / 100
initial velocity between the two galaxies V = 350×103 m/s
initial state:
random distribution
of half the bodies
V/2
V/2
random distribution
of half the bodies
time step DT = 105×31536000 s (100,000 years)
23
Dr. Manuel Carcenac - European University of Lefke
class encapsulating the simulation parameters:
class Para
{
static double G = 6.67384e-11;
static double M = 4.0e42; // total mass of the two galaxies
static double H = 1.0e21; // diameter of each galaxy
static double EPS = H / 100;
static double DX = H; // distance between the two galaxies over X axis
static double DY = H; // distance between the two galaxies over Y axis
static double V = 350.0 * 1.e3; // velocity between the two galaxies along X axis
static double DT = 1.0e5 * 31536000; // 100,000 years
}
24
Dr. Manuel Carcenac - European University of Lefke
classes describing the state of the n-body system:
class PositionVelocity
{
double x , y , z;
double vx , vy , vz;
PositionVelocity() {}
PositionVelocity(double x , double y ,double z , double vx , double vy , double vz)
{ this.x = x;
this.y = y;
this.z = z;
this.vx = vx; this.vy = vy; this.vz = vz; }
}
class State
{
int n;
double t;
double[] m = new double[n];
PositionVelocity[] pv;
PositionVelocity[] dpv , pv_; // for Runge-Kutta
State(int n)
{ ...... }
void evolve_over_time_step(double dt)
{ ...... }
void increment_positions_velocities( PositionVelocity[] pv1 , PositionVelocity[] pv0
, PositionVelocity[] dpv , double k)
{ ...... }
void derivatives_positions_velocities(PositionVelocity[] pv)
{ ...... }
void display(GL2 gl , GLU glu , GLUquadric quad)
{ ...... }
}
25
Dr. Manuel Carcenac - European University of Lefke
initialization of the state:
State(int n)
{
double h , gal_x , gal_y , gal_z , gal_vx , gal_vy , gal_vz;
this.n = n;
t = 0.0;
m = new double[n];
pv = new PositionVelocity[n];
dpv = new PositionVelocity[n];
pv_ = new PositionVelocity[n];
for (int i = 0 ; i < n ; i++)
{
if (i < n / 2) { gal_x = - Para.DX / 2;
gal_vx = Para.V / 2;
else { gal_x = Para.DX / 2;
gal_vx = - Para.V / 2;
gal_y = - Para.DY / 2; gal_z = 0.0;
gal_vy = 0.0; gal_vz = 0.0; }
gal_y = Para.DY / 2; gal_z = 0.0;
gal_vy = 0.0; gal_vz = 0.0; }
m[i] = Para.M / n;
do
{
pv[i] = new PositionVelocity( gal_x + Para.H * ( -0.5 + Math.random())
, gal_y + Para.H * ( -0.5 + Math.random())
, gal_z + Para.H * ( -0.5 + Math.random())
, gal_vx , gal_vy , gal_vz);
h = Math.sqrt( (pv[i].x - gal_x) * (pv[i].x - gal_x)
+ (pv[i].y - gal_y) * (pv[i].y - gal_y)
+ (pv[i].z - gal_z) * (pv[i].z - gal_z));
}
while (h > Para.H / 2);
dpv[i] = new PositionVelocity();
pv_[i] = new PositionVelocity();
}
}
26
Dr. Manuel Carcenac - European University of Lefke
time integration with Runge-Kutta:
void evolve_over_time_step(double dt)
{
derivatives_positions_velocities(pv);
increment_positions_velocities(pv_ , pv , dpv , dt / 2);
D1
s(t) + D1 × t / 2
derivatives_positions_velocities(pv_);
D2
increment_positions_velocities(pv , pv , dpv , dt);
s(t) + D2 × t
t += dt;
}
void increment_positions_velocities( PositionVelocity[] pv1
, PositionVelocity[] pv0
, PositionVelocity[] dpv
, double k)
{
for (int i = 0 ; i < n ; i++)
{
pv1[i].x = pv0[i].x + dpv[i].x * k;
pv1[i].y = pv0[i].y + dpv[i].y * k;
pv1[i].z = pv0[i].z + dpv[i].z * k;
pv1[i].vx = pv0[i].vx + dpv[i].vx * k;
pv1[i].vy = pv0[i].vy + dpv[i].vy * k;
pv1[i].vz = pv0[i].vz + dpv[i].vz * k;
}
}
27
Dr. Manuel Carcenac - European University of Lefke
time derivatives of positions and velocities:
//// calculate dpv for pv = state.pv or state.pv_
void derivatives_positions_velocities(PositionVelocity[] pv)
{
double pipjx , pipjy , pipjz , d2 , q;
for (int i = 0 ; i < n ; i++)
{
dpv[i].x = pv[i].vx;
dpv[i].y = pv[i].vy;
dpv[i].z = pv[i].vz;
dpv[i].vx = 0.0;
dpv[i].vy = 0.0;
dpv[i].vz = 0.0;
for (int j = 0 ; j < n ; j++)
if (j != i)
{
pipjx = pv[j].x - pv[i].x;
pipjy = pv[j].y - pv[i].y;
pipjz = pv[j].z - pv[i].z;
d2 = pipjx * pipjx + pipjy * pipjy + pipjz * pipjz + Para.EPS * Para.EPS;
q = m[j] / d2 / Math.sqrt(d2);
dpv[i].vx += q * pipjx;
dpv[i].vy += q * pipjy;
dpv[i].vz += q * pipjz;
}
dpv[i].vx *= Para.G;
dpv[i].vy *= Para.G;
dpv[i].vz *= Para.G;
}
}
28
Dr. Manuel Carcenac - European University of Lefke
displaying the state:
void display(GL2 gl , GLU glu , GLUquadric quad)
{
for (int i = 0 ; i < n ; i++)
{
if (i < n / 2)
gl.glMaterialfv( GL.GL_FRONT_AND_BACK
, GLLightingFunc.GL_AMBIENT_AND_DIFFUSE , Gui.COLOR1 , 0);
else
gl.glMaterialfv( GL.GL_FRONT_AND_BACK
, GLLightingFunc.GL_AMBIENT_AND_DIFFUSE , Gui.COLOR2 , 0);
gl.glPushMatrix();
gl.glTranslatef( (float)(pv[i].x / Para.H)
, (float)(pv[i].y / Para.H)
, (float)(pv[i].z / Para.H));
glu.gluSphere(quad , 0.01f , 10 , 10);
gl.glPopMatrix();
}
}
29
Dr. Manuel Carcenac - European University of Lefke
main class and Gui class:
public class P
{ public static void main(String[] arg)
{ Gui gui = new Gui();
gui.animation(); } }
class Gui
{
JFrame f;
DrawingPanel p;
State state;
void animation()
{
while (true)
{
state.evolve_over_time_step(Para.DT);
f.repaint();
}
}
class DrawingPanel extends GLJPanel
{
..........
public void display(GLAutoDrawable drawable)
{
..........
state.display(gl , glu , quad);
..........
}
..........
}
Gui()
{
..........
state = new State(n);
..........
}
}
30
Dr. Manuel Carcenac - European University of Lefke
example: simulation with n = 4000
31
Dr. Manuel Carcenac - European University of Lefke
32