Initial revision
authorteodor <teodor>
Fri, 21 Jan 2005 16:46:54 +0000 (16:46 +0000)
committerteodor <teodor>
Fri, 21 Jan 2005 16:46:54 +0000 (16:46 +0000)
23 files changed:
COPYRIGHT [new file with mode: 0644]
Makefile [new file with mode: 0644]
antialias_draw.c [new file with mode: 0644]
entry.c [new file with mode: 0644]
examples/doublemoon [new file with mode: 0644]
examples/doublemoon_doublesputnik [new file with mode: 0644]
examples/doublemoon_doublesputnik1 [new file with mode: 0644]
examples/doublemoon_ellipse [new file with mode: 0644]
examples/doublemoon_sputnik [new file with mode: 0644]
examples/quad_moon [new file with mode: 0644]
examples/three_generator.pl [new file with mode: 0644]
examples/threemoon [new file with mode: 0644]
galaxy.c [new file with mode: 0644]
galaxy.h [new file with mode: 0644]
graphics.c [new file with mode: 0644]
gtkcellrendererbutton.c [new file with mode: 0644]
gtkcellrendererbutton.h [new file with mode: 0644]
menuaction.c [new file with mode: 0644]
quaternions.c [new file with mode: 0644]
trackball.c [new file with mode: 0644]
trackball.h [new file with mode: 0644]
xgalaxy.c [new file with mode: 0644]
xgalaxy.h [new file with mode: 0644]

diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644 (file)
index 0000000..7290c45
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2005 Teodor Sigaev <teodor@sigaev.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ *        may be used to endorse or promote products derived from this software
+ *        without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..2f6293a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,28 @@
+CC=gcc
+#pkg-config gtk+-2.0
+INCLUDE=-I. -I../tedtools `pkg-config gtk+-2.0 --cflags` 
+CFLAGS=-Wall -g -O2
+DEFINES=
+#LIB=-lm -L../tedtools -ltedtools `pkg-config gtk+-2.0 --libs` -lgthread-2.0
+LIB=-lm -L../tedtools -ltedtools `pkg-config gtk+-2.0 --libs` -lpthread
+AR=ar rcv
+RANLIB=ranlib
+LD=ld -x -shared
+
+OBJ=xgalaxy.o galaxy.o menuaction.o entry.o graphics.o \
+       antialias_draw.o gtkcellrendererbutton.o trackball.o \
+       quaternions.o
+
+.SUFFIXES: .o.c
+
+.c.o:
+       $(CC) $(CFLAGS) $(DEFINES) $(INCLUDE) -c $<
+
+all: xgalaxy
+
+xgalaxy: $(OBJ)
+       $(CC) -o $@  $(OBJ) $(LIB) 
+
+clean:
+       rm -rf *core *.o xgalaxy
+
diff --git a/antialias_draw.c b/antialias_draw.c
new file mode 100644 (file)
index 0000000..cc3b8b8
--- /dev/null
@@ -0,0 +1,184 @@
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <math.h>
+#include <pthread.h>
+
+#include "tmalloc.h"
+#include "xgalaxy.h"
+
+
+       
+void
+setBrightPoint(GdkGC *gc, int x, int y, int c) {
+       GdkColor color;
+
+       color.red=color.green=color.blue=(255-c)*256;
+       gdk_gc_set_rgb_fg_color(gc, &color);
+
+       gdk_draw_point( XGalaxy.pixmap, gc, x, y );
+}
+
+void
+setBrightPoints(GdkGC *gc, GdkPoint *points, int n,  int c) {
+       GdkColor color;
+
+       color.red=color.green=color.blue=(255-c)*256;
+       gdk_gc_set_rgb_fg_color(gc, &color);
+
+       gdk_draw_points( XGalaxy.pixmap, gc, points, n );
+}
+
+#define sqr(x)  ((x)*(x))
+
+void
+DrawDisk(GdkGC *gc, double CenterX, double CenterY, double Radius, double Feather, double brightness) {
+// (c) http://www.simdesign.nl/tips/tip002.html
+// Draw a disk on Bitmap. Bitmap must be a 256 color (pf8bit)
+// palette bitmap, and parts outside the disk will get palette
+// index 0, parts inside will get palette index 255, and in the
+// antialiased area (feather), the pixels will get values
+// inbetween.
+// ***Parameters***
+// Bitmap:
+//   The bitmap to draw on
+// CenterX, CenterY:
+//   The center of the disk (float precision). Note that [0, 0]
+//   would be the center of the first pixel. To draw in the
+//   exact middle of a 100x100 bitmap, use CenterX = 49.5 and
+//   CenterY = 49.5
+// Radius:
+//   The radius of the drawn disk in pixels (float precision)
+// Feather:
+//   The feather area. Use 1 pixel for a 1-pixel antialiased
+//   area. Pixel centers outside 'Radius + Feather / 2' become
+//   0, pixel centers inside 'Radius - Feather/2' become 255.
+//   Using a value of 0 will yield a bilevel image.
+// Copyright (c) 2003 Nils Haeck M.Sc. www.simdesign.nl
+       
+       int  x, y;
+       int  Fact;
+       double  RPF2 = sqr(Radius + Feather/2.0);
+       double  RMF2 = sqr(Radius - Feather/2.0);
+       double   SqY, SqDist;
+       // Determine bounds:
+       int LX = floor(CenterX - RPF2);
+       int RX = ceil (CenterX + RPF2);
+       int LY = floor(CenterY - RPF2);
+       int RY = ceil (CenterY + RPF2);
+       double SqX[RX - LX + 1];
+       GdkPoint darkPoint[ (RY-LY)*(RX-LX) ];
+       int nDarkDoint=0;
+
+
+       // Optimization run: find squares of X first
+       for(x=LX; x<=RX; x++)
+               SqX[x - LX] = sqr(x - CenterX);
+
+       for(y=LY; y<=RY; y++) {
+               SqY = sqr(y - CenterY);
+               // Loop through X values
+               for(x=LX; x<=RX; x++) {
+
+                       // determine squared distance from center for this pixel
+                       SqDist = SqY + SqX[x - LX];
+
+                       if (SqDist < RMF2) { // inside inner circle? Most often..
+                               // inside the inner circle.. just give the scanline the
+                               // new color
+                               darkPoint[ nDarkDoint ].x = x;
+                               darkPoint[ nDarkDoint ].y = y;
+                               nDarkDoint++;
+                       } else  if (SqDist < RPF2) { // inside outer circle? 
+                               // We are inbetween the inner and outer bound, now
+                               // mix the color
+                               Fact = round(((Radius - sqrt(SqDist)) * 2.0 / Feather) * 127.5 + 127.5);
+                               // just in case limit to [0, 255]
+                               setBrightPoint(gc, x,y,brightness*fmax(0, fmin(Fact, 255)));
+                       }
+               }
+       }
+       if (nDarkDoint>0)
+               setBrightPoints( gc, darkPoint, nDarkDoint, 255*brightness ); 
+}
+
+// Draw a line with simple anti-aliasing, suitable for draggable envelope displays.
+// By Paul Kellett (@mda-vst.com) October 2002.
+//
+// The anti-aliasing works by oversampling the drawing position by 256 then using
+// the fractional part to fade in the pixel on one side of the line while fading
+// out the pixel on the other side. The ends of the line aren't perfect, but these
+// will probably be hidden by drag handles.
+//
+// Uses fictional function: drawpixel(int x, int y, int brightness) where brightness
+// is a value from 0 to 255, so it's easiest to draw a red, green or blue line on a
+// black (or near-black) background.
+
+
+void
+drawPixel(GdkGC *gc, GdkColor *color, int x, int y, int c) {
+       GdkColor clr;
+       double ratio = ((double)c)/255.0;
+               
+       clr.red   = color->red   + (65535-color->red)   * ratio;
+       clr.green = color->green + (65535-color->green) * ratio;
+       clr.blue  = color->blue  + (65535-color->blue)  * ratio;
+       gdk_gc_set_rgb_fg_color(gc, &clr);
+
+       gdk_draw_point( XGalaxy.pixmap, gc, x, y );
+}
+
+void 
+antialiasedLine(GdkGC *gc, int x0, int y0, int x1, int y1, GdkColor *color) {
+  int x, y, xx, yy, v;
+  int dx = x1 - x0;
+  int dy = y1 - y0;
+  
+  if (dx==0 && dy==0) { 
+       drawPixel(gc, color, x1, y1, 0);
+       return;
+  }
+  if(abs(dx) > abs(dy)) //for each x pixel
+  {
+    if(x0 > x1) //for increasing x
+    {
+      x = x0;  x0 = x1;  x1 = x;
+      y = y0;  y0 = y1;  y1 = y;
+    }
+    dy = (dy << 8) / dx; //oversample y by 256
+    yy = y0 << 8;
+    
+    for(x=x0; x<x1; x++) //draw line
+    {
+      v = yy & 0xFF;
+      int y = yy >> 8;
+      yy = yy + dy;
+      drawPixel(gc, color,x, y, 0);
+      drawPixel(gc, color,x, y + 1, 255-v);
+      drawPixel(gc, color,x, y - 1, v);
+    }
+  }
+  else //for each y pixel
+  {
+    if(y0 > y1) //for increasing y
+    {
+      y = y0;  y0 = y1;  y1 = y;
+      x = x0;  x0 = x1;  x1 = x;
+    }
+    dx = (dx << 8) / dy;  //oversample x by 256
+    xx = x0 << 8;
+
+    for(y=y0; y<y1; y++)
+    {
+      v = xx & 0xFF;
+      x = xx >> 8;
+      xx = xx + dx;
+      drawPixel(gc, color,x, y, 0);
+      drawPixel(gc, color,x + 1, y, 255-v);
+      drawPixel(gc, color,x - 1, y, v);
+    }
+  }
+}
diff --git a/entry.c b/entry.c
new file mode 100644 (file)
index 0000000..1a0dc1d
--- /dev/null
+++ b/entry.c
@@ -0,0 +1,89 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmalloc.h"
+#include "galaxy.h"
+#include "xgalaxy.h"
+
+
+void
+freeStarEntry() {
+       if ( XGalaxy.entry ) 
+               tfree(XGalaxy.entry);
+       XGalaxy.entry=NULL;
+       if ( XGalaxy.tmpentry ) 
+               tfree(XGalaxy.tmpentry);
+       XGalaxy.tmpentry=NULL;
+       XGalaxy.nentry = XGalaxy.lenentry = 0; 
+}
+
+void
+addEntry(Star *star) {
+       if ( XGalaxy.nentry >= XGalaxy.lenentry ) {
+               XGalaxy.lenentry = (XGalaxy.lenentry) ? XGalaxy.lenentry*2 : 16;
+               XGalaxy.entry = (Star*) ((XGalaxy.entry) ? 
+                       trealloc(XGalaxy.entry, sizeof(Star)*XGalaxy.lenentry)
+                       : 
+                       tmalloc(sizeof(Star)*XGalaxy.lenentry));
+               XGalaxy.tmpentry = (Star*) ((XGalaxy.entry) ? 
+                       trealloc(XGalaxy.tmpentry, sizeof(Star)*XGalaxy.lenentry)
+                       : 
+                       tmalloc(sizeof(Star)*XGalaxy.lenentry));
+       }
+
+       if ( star ) 
+               memcpy(XGalaxy.entry + XGalaxy.nentry, star, sizeof(Star));
+       else 
+               memset(XGalaxy.entry + XGalaxy.nentry, 0, sizeof(Star));
+       
+       XGalaxy.nentry++;
+}
+
+void
+deleteEntry(u_int32_t i) {
+       if ( i>=XGalaxy.nentry || XGalaxy.nentry==0)
+               return;
+
+       if ( i!=XGalaxy.nentry-1)
+               memmove( XGalaxy.entry + i, XGalaxy.entry + i + 1, sizeof(Star) * ( XGalaxy.nentry-i-1) );
+       XGalaxy.nentry--;
+}
+
+void
+editEntry(u_int32_t i, int col, double val) {
+       if ( i>=XGalaxy.nentry )
+               return;
+       switch(col) {
+               case 1:  XGalaxy.entry[i].mass = val; break;
+               case 2:  XGalaxy.entry[i].c.x = val; break;
+               case 3:  XGalaxy.entry[i].c.y = val; break;
+               case 4:  XGalaxy.entry[i].c.z = val; break;
+               case 5:  XGalaxy.entry[i].v.x = val; break;
+               case 6:  XGalaxy.entry[i].v.y = val; break;
+               case 7:  XGalaxy.entry[i].v.z = val; break;
+               default: break;
+       }
+}
+
+void
+cntEntry() {
+       Galaxy galaxy;
+       char buf[128];
+
+       memset(&galaxy,0,sizeof(galaxy));
+
+       galaxy.stars = XGalaxy.entry;
+       galaxy.nstars = XGalaxy.nentry;
+
+       EnergyImpulseGalaxy(&galaxy);
+
+       sprintf(buf,"%G",galaxy.kineticEnergy + galaxy.potentialEnergy);
+       gtk_label_set_text( GTK_LABEL(XGalaxy.dataEnergyField), buf);
+
+       sprintf(buf,"%G",galaxy.Impulse);
+       gtk_label_set_text( GTK_LABEL(XGalaxy.dataImpulseField), buf);
+
+       sprintf(buf,"%G",galaxy.Moment);
+       gtk_label_set_text( GTK_LABEL(XGalaxy.dataMomentField), buf);
+}
diff --git a/examples/doublemoon b/examples/doublemoon
new file mode 100644 (file)
index 0000000..9be1d20
--- /dev/null
@@ -0,0 +1,5 @@
+delta=3600
+error=1E-10
+
+7.4E+22        1E+07   0       0       0       350     0
+7.4E+22        -1E+07  0       0       0       -350    0
diff --git a/examples/doublemoon_doublesputnik b/examples/doublemoon_doublesputnik
new file mode 100644 (file)
index 0000000..09d8f25
--- /dev/null
@@ -0,0 +1,7 @@
+delta=3600
+error=1E-11
+
+7.4E+22        1E+07   0       0       0       350     0
+7.4E+22        -1E+07  0       0       0       -350    0
+1      2E+07   0       0       0       1120    0
+1      -2E+07  0       0       0       -1120   0
diff --git a/examples/doublemoon_doublesputnik1 b/examples/doublemoon_doublesputnik1
new file mode 100644 (file)
index 0000000..22479c9
--- /dev/null
@@ -0,0 +1,7 @@
+delta=3600
+error=1E-11
+
+7.4E+22        1E+07   0       0       0       350     0
+7.4E+22        -1E+07  0       0       0       -350    0
+1      1E+06   0       0       0       -1100   0
+1      -1E+06  0       0       0       1100    0
diff --git a/examples/doublemoon_ellipse b/examples/doublemoon_ellipse
new file mode 100644 (file)
index 0000000..929771e
--- /dev/null
@@ -0,0 +1,5 @@
+delta=3600
+error=1E-11
+
+7.4E+22        7.7E+06 0       0       0       254     0
+7.4E+22        -7.7E+06        0       0       0       -254    0
diff --git a/examples/doublemoon_sputnik b/examples/doublemoon_sputnik
new file mode 100644 (file)
index 0000000..9b6abbc
--- /dev/null
@@ -0,0 +1,6 @@
+delta=3600
+error=1E-11
+
+7.4E+22        1E+07   0       0       0       350     0
+7.4E+22        -1E+07  0       0       0       -350    0
+1      1E+06   0       0       0       -1100   0
diff --git a/examples/quad_moon b/examples/quad_moon
new file mode 100644 (file)
index 0000000..fcadbaa
--- /dev/null
@@ -0,0 +1,7 @@
+delta=3600
+error=1E-10
+
+7.4E+22        7.7E+06 0       0       0       600     0
+7.4E+22        -7.7E+06        0       0       0       -600    0
+7.4E+22        0       7.7E+06 0       -600    0       0
+7.4E+22        0       -7.7E+06        0       600     0       0
diff --git a/examples/three_generator.pl b/examples/three_generator.pl
new file mode 100644 (file)
index 0000000..b1407eb
--- /dev/null
@@ -0,0 +1,22 @@
+use strict;
+my $x = 0.97000436e0;
+my $y = 0.24308753e0;
+my $vx =0.93240737e0;
+my $vy =0.86473146e0;
+
+my $R=4.e7;
+my $V=176.5e0;
+my $M=7.4e22;
+
+print<<EOT;
+delta=3600
+error=1E-10
+
+EOT
+
+print $M,"\t",sprintf("%.13E",-$x*$R),"\t",sprintf("%.13E", $y*$R),"\t0\t",sprintf("%.13E", $vx*$V),"\t",sprintf("%.13E", $vy*$V), "\t0\n";
+print $M,"\t",     0,"\t",     0,"\t0\t",sprintf("%.13E", -2*$vx*$V),"\t",sprintf("%.13E", -2*$vy*$V), "\t0\n";
+print $M,"\t",sprintf("%.13E", $x*$R),"\t",sprintf("%.13E", -$y*$R),"\t0\t",sprintf("%.13E", $vx*$V),"\t",sprintf("%.13E", $vy*$V), "\t0\n";
+
+
diff --git a/examples/threemoon b/examples/threemoon
new file mode 100644 (file)
index 0000000..5ccd44e
--- /dev/null
@@ -0,0 +1,6 @@
+delta=3600
+error=1E-10
+
+7.4e+22        -3.8800174400000E+07    9.7235012000000E+06     0       1.6456990080500E+02     1.5262510269000E+02     0
+7.4e+22        0       0       0       -3.2913980161000E+02    -3.0525020538000E+02    0
+7.4e+22        3.8800174400000E+07     -9.7235012000000E+06    0       1.6456990080500E+02     1.5262510269000E+02     0
diff --git a/galaxy.c b/galaxy.c
new file mode 100644 (file)
index 0000000..1db3522
--- /dev/null
+++ b/galaxy.c
@@ -0,0 +1,434 @@
+#include <math.h>
+#include <string.h>
+
+
+#include "tmalloc.h"
+#include "tlog.h"
+
+#include "galaxy.h"
+
+#if defined(SORT_STAR) || defined (SORT_ENERGYSTAR)
+static int
+maxN2(int n) {
+       int i=0;
+
+       while(n) {
+               n >>= 1;
+               i++;
+       }
+
+       return 1<<i;
+}
+
+static double
+safeSumSortedArray(double *arr, int N) {
+       int i;
+
+       while(N>1) {
+               for(i=0;i<N;i+=2)
+                       arr[i>>1] = arr[i]+arr[i+1];
+               N>>=1;
+       }
+
+       return arr[0];
+}
+
+#endif
+
+void
+initGalaxy(Galaxy *galaxy, u_int32_t nstars) {
+       memset(galaxy,0,sizeof(Galaxy));
+
+       if ( nstars < 2 ) 
+               nstars=2;
+       galaxy->nstars = nstars;
+
+       galaxy->stars=(Star*)t0malloc( nstars*sizeof(Star) );
+       galaxy->forces=(Vector*)t0malloc( CNT_FARRAY(nstars)*sizeof(Vector) );
+}
+
+void 
+freeGalaxy(Galaxy *galaxy) {
+       if ( galaxy->stars ) 
+               tfree(galaxy->stars);
+       if ( galaxy->forces ) 
+               tfree(galaxy->forces);
+       memset(galaxy,0,sizeof(Galaxy));
+}
+
+static void
+cntForce(Vector *force, Star *istar, Star *jstar) {
+       double          R2,F;
+       R2 = (jstar->c.x - istar->c.x)*(jstar->c.x - istar->c.x) +
+            (jstar->c.y - istar->c.y)*(jstar->c.y - istar->c.y) +
+            (jstar->c.z - istar->c.z)*(jstar->c.z - istar->c.z);
+       
+       F = G*istar->mass*jstar->mass / (R2*sqrt(R2));
+
+       force->x = F * fabs(jstar->c.x - istar->c.x);
+       force->y = F * fabs(jstar->c.y - istar->c.y);
+       force->z = F * fabs(jstar->c.z - istar->c.z);
+} 
+
+void 
+forceGalaxy(Galaxy *galaxy) {
+       Vector          *force = galaxy->forces;
+       Star            *jstar, *istar = galaxy->stars;
+       
+       while( istar < galaxy->stars + galaxy->nstars - 1 ) {   
+               jstar = istar+1;
+               while( jstar < galaxy->stars + galaxy->nstars ) {
+                       cntForce(force, istar, jstar);
+                       force++; jstar++;
+               }
+               istar++;
+       }
+}
+
+#ifdef SORT_STAR
+
+#define SUMFORCES(AXIS)                                                                        \
+                                                                                       \
+static int                                                                             \
+cmpAcc_##AXIS(const void *a, const void *b) {                                          \
+       return ( fabs((*(Vector**)a)->AXIS) > fabs((*(Vector**)b)->AXIS) ) ? 1 : -1;    \
+}                                                                                      \
+                                                                                       \
+static double                                                                          \
+sumAxis_##AXIS( Vector **a, int n ) {                                                  \
+       int N = maxN2(n), i;                                                            \
+       double  arr[N];                                                                 \
+                                                                                       \
+       qsort(a, n, sizeof(Vector*), cmpAcc_##AXIS);                                    \
+                                                                                       \
+       memset(arr, 0, sizeof(double)*(N-n));                                           \
+       for(i=0;i<n;i++)                                                                \
+               arr[i+N-n] = a[i]->AXIS;                                                \
+                                                                                       \
+       return safeSumSortedArray(arr, N);                                              \
+}
+
+SUMFORCES(x)
+SUMFORCES(y)
+SUMFORCES(z)
+
+#endif
+
+
+void
+accelerationGalaxy(Galaxy *galaxy) {
+       u_int32_t       i,j;
+       Star            *istar = galaxy->stars,*jstar;
+#ifdef SORT_STAR
+       Vector*         ptrforces[galaxy->nstars];
+#else
+       Vector          *force;
+#endif
+
+       for(i=0; i<galaxy->nstars; i++ ) {
+               istar = galaxy->stars+i;
+               jstar = galaxy->stars;
+
+               memcpy( &(istar->pa), &(istar->a), sizeof(Vector) );
+
+#ifdef SORT_STAR
+               for(j=0; j<galaxy->nstars; j++ ) { 
+                       if ( j<i ) { 
+                               ptrforces[j] = GET_FORCE(galaxy, j, i);
+                               ptrforces[j]->x = ( jstar->c.x>istar->c.x ) ? fabs(ptrforces[j]->x) : -fabs(ptrforces[j]->x);
+                               ptrforces[j]->y = ( jstar->c.y>istar->c.y ) ? fabs(ptrforces[j]->y) : -fabs(ptrforces[j]->y);
+                               ptrforces[j]->z = ( jstar->c.z>istar->c.z ) ? fabs(ptrforces[j]->z) : -fabs(ptrforces[j]->z);
+                       } else if ( j>i ) {
+                               ptrforces[j-1] = GET_FORCE(galaxy, i, j);
+                               cntForce( ptrforces[j-1],  istar, jstar );
+                               ptrforces[j-1]->x = ( jstar->c.x>istar->c.x ) ? ptrforces[j-1]->x : -ptrforces[j-1]->x;
+                               ptrforces[j-1]->y = ( jstar->c.y>istar->c.y ) ? ptrforces[j-1]->y : -ptrforces[j-1]->y;
+                               ptrforces[j-1]->z = ( jstar->c.z>istar->c.z ) ? ptrforces[j-1]->z : -ptrforces[j-1]->z;
+                       }
+                       jstar++;
+               }
+
+               istar->a.x = sumAxis_x(ptrforces, galaxy->nstars-1);
+               istar->a.y = sumAxis_y(ptrforces, galaxy->nstars-1);
+               istar->a.z = sumAxis_z(ptrforces, galaxy->nstars-1);
+               
+#else
+               memset(&(istar->a), 0, sizeof(Vector));
+
+               force=GET_FORCE(galaxy, 0, i);  
+               for(j=0;j<i;j++) {
+                       istar->a.x += ( jstar->c.x>istar->c.x ) ? force->x : -force->x;
+                       istar->a.y += ( jstar->c.y>istar->c.y ) ? force->y : -force->y;
+                       istar->a.z += ( jstar->c.z>istar->c.z ) ? force->z : -force->z;
+                       force++; jstar++;
+               }
+               jstar++;
+               for(j=i+1;j<galaxy->nstars;j++) {
+                       force = GET_FORCE(galaxy, i, j);
+                       cntForce(force, istar, jstar);
+                       
+                       istar->a.x += ( jstar->c.x>istar->c.x ) ? force->x : -force->x;
+                       istar->a.y += ( jstar->c.y>istar->c.y ) ? force->y : -force->y;
+                       istar->a.z += ( jstar->c.z>istar->c.z ) ? force->z : -force->z;
+                       jstar++;
+               }
+#endif
+
+               istar->a.x /= istar->mass; 
+               istar->a.y /= istar->mass; 
+               istar->a.z /= istar->mass;
+
+#ifdef TAYLOR3
+               if ( galaxy->firsttime )
+                       memcpy( &(istar->pa), &(istar->a), sizeof(Vector) );
+#endif
+       } 
+#ifdef TAYLOR3
+       galaxy->firsttime = 0;
+#endif
+}
+
+void
+stepGalaxy(Galaxy *galaxy, double delta) {
+       Star            *star = galaxy->stars;
+
+       while( star - galaxy->stars < galaxy->nstars ) {
+#ifdef TAYLOR3
+               star->c.x += delta*( delta*( (star->a.x - star->pa.x)/3.0 + star->a.x )/2.0 + star->v.x );
+               star->v.x += delta*( star->a.x + (star->a.x - star->pa.x)/2.0 ); 
+               star->c.y += delta*( delta*( (star->a.y - star->pa.y)/3.0 + star->a.y )/2.0 + star->v.y );
+               star->v.y += delta*( star->a.y + (star->a.y - star->pa.y)/2.0 ); 
+               star->c.z += delta*( delta*( (star->a.z - star->pa.z)/3.0 + star->a.z )/2.0 + star->v.z );
+               star->v.z += delta*( star->a.z + (star->a.z - star->pa.z)/2.0 ); 
+#else
+               star->c.x += delta*(star->v.x + star->a.x*delta/2.0); 
+               star->v.x += star->a.x * delta; 
+               star->c.y += delta*(star->v.y + star->a.y*delta/2.0); 
+               star->v.y += star->a.y * delta; 
+               star->c.z += delta*(star->v.z + star->a.z*delta/2.0); 
+               star->v.z += star->a.z * delta;
+#endif 
+
+               star++;
+       }
+}
+
+void
+unstepGalaxy(Galaxy *galaxy, double delta) {
+       Star            *star = galaxy->stars;
+
+       while( star - galaxy->stars < galaxy->nstars ) {
+#ifdef TAYLOR3
+               star->v.x -= delta*( star->a.x + (star->a.x - star->pa.x)/2.0 ); 
+               star->c.x -= delta*( delta*( (star->a.x - star->pa.x)/3.0 + star->a.x )/2.0 + star->v.x );
+               star->v.y -= delta*( star->a.y + (star->a.y - star->pa.y)/2.0 ); 
+               star->c.y -= delta*( delta*( (star->a.y - star->pa.y)/3.0 + star->a.y )/2.0 + star->v.y );
+               star->v.z -= delta*( star->a.z + (star->a.z - star->pa.z)/2.0 ); 
+               star->c.z -= delta*( delta*( (star->a.z - star->pa.z)/3.0 + star->a.z )/2.0 + star->v.z );
+#else
+               star->v.x -= star->a.x * delta; 
+               star->c.x -= delta*(star->v.x + star->a.x*delta/2.0); 
+               star->v.y -= star->a.y * delta; 
+               star->c.y -= delta*(star->v.y + star->a.y*delta/2.0); 
+               star->v.z -= star->a.z * delta; 
+               star->c.z -= delta*(star->v.z + star->a.z*delta/2.0);
+#endif 
+
+               star++;
+       }
+}
+
+#ifdef SORT_ENERGYSTAR
+static double
+getImpulseX(Star *star) {
+       return star->mass * star->v.x; 
+}
+
+static double
+getImpulseY(Star *star) {
+       return star->mass * star->v.y; 
+}
+
+static double
+getImpulseZ(Star *star) {
+       return star->mass * star->v.z; 
+}
+
+static double
+getMomentX(Star *star) {
+       return star->mass * ( star->v.y * star->c.z - star->c.y * star->v.z );
+}      
+
+static double
+getMomentY(Star *star) {
+       return star->mass * ( star->v.z * star->c.x - star->c.z * star->v.x );
+}      
+
+static double
+getMomentZ(Star *star) {
+       return star->mass * ( star->v.x * star->c.y - star->c.x * star->v.y );
+}      
+
+static double
+getKinetic(Star *star) {
+       return star->mass * (star->v.x * star->v.x + star->v.y * star->v.y + star->v.z * star->v.z) / 2.0;
+}
+
+static int
+cmpDouble(const void *a, const void *b) {
+       return ( fabs(*(double*)a) > fabs(*(double*)b) ) ? 1 : -1;      
+}
+
+static double
+safeSumArray(double *arr, int N) {
+       qsort(arr, N, sizeof(double), cmpDouble);
+       return safeSumSortedArray(arr,N);
+}
+
+static double
+safeSumStar(Star *stars, int n, double (*extractor)(Star*)) {
+       int N=maxN2(n), i;
+       double arr[N];
+       
+       for(i=0;i<n;i++) 
+               arr[i] = (*extractor)(stars+i);
+       memset( arr+n, 0, sizeof(double)*(N-n) );
+
+       return safeSumArray(arr, N);
+}
+
+#endif
+
+void
+EnergyImpulseGalaxy(Galaxy *galaxy) {
+       Star            *istar=galaxy->stars, *jstar;
+       double potential=0.0;
+       double kinetic=0.0;
+       double impulseX=0.0, impulseY=0.0, impulseZ=0.0, momentX=0.0, momentY=0.0, momentZ=0.0;
+#ifdef SORT_ENERGYSTAR
+       double          pe[ maxN2(CNT_FARRAY(galaxy->nstars)) ], *ptrp;
+
+       ptrp=pe;
+       memset(pe, 0, sizeof(double)*maxN2(CNT_FARRAY(galaxy->nstars)));
+
+#endif
+       while( istar - galaxy->stars < galaxy->nstars ) {
+
+#ifndef SORT_ENERGYSTAR
+               impulseX = impulseX + istar->mass * istar->v.x;
+               impulseY = impulseY + istar->mass * istar->v.y;
+               impulseZ = impulseZ + istar->mass * istar->v.z;
+               
+               momentX +=  istar->mass * ( istar->v.y * istar->c.z - istar->c.y * istar->v.z );
+               momentY +=  istar->mass * ( istar->v.z * istar->c.x - istar->c.z * istar->v.x );
+               momentZ +=  istar->mass * ( istar->v.x * istar->c.y - istar->c.x * istar->v.y );
+
+               kinetic += istar->mass * (istar->v.x * istar->v.x + istar->v.y * istar->v.y + istar->v.z * istar->v.z) / 2.0;
+#endif
+               
+               jstar=istar+1;
+               while( jstar - galaxy->stars < galaxy->nstars ) {
+#ifdef SORT_ENERGYSTAR
+                       *ptrp++ = 
+#else
+                       potential +=
+#endif 
+                                       G * istar->mass * jstar->mass / sqrt(
+                               (jstar->c.x - istar->c.x)*(jstar->c.x - istar->c.x) +
+                               (jstar->c.y - istar->c.y)*(jstar->c.y - istar->c.y) +
+                               (jstar->c.z - istar->c.z)*(jstar->c.z - istar->c.z)
+                       );
+                       jstar++;
+               }
+               istar++;
+       }
+
+#ifdef SORT_ENERGYSTAR
+       potential = safeSumArray(pe, maxN2(ptrp-pe));
+       impulseX = safeSumStar(galaxy->stars, galaxy->nstars, getImpulseX);
+       impulseY = safeSumStar(galaxy->stars, galaxy->nstars, getImpulseY);
+       impulseZ = safeSumStar(galaxy->stars, galaxy->nstars, getImpulseZ);
+       momentX = safeSumStar(galaxy->stars, galaxy->nstars, getMomentX);
+       momentY = safeSumStar(galaxy->stars, galaxy->nstars, getMomentY);
+       momentZ = safeSumStar(galaxy->stars, galaxy->nstars, getMomentZ);
+       kinetic = safeSumStar(galaxy->stars, galaxy->nstars, getKinetic); 
+#endif
+       galaxy->Impulse = sqrt(impulseX*impulseX + impulseY*impulseY + impulseZ*impulseZ);
+       galaxy->Moment  = sqrt(momentX *momentX  + momentY *momentY  + momentZ *momentZ );
+       galaxy->kineticEnergy = kinetic;
+       if ( !isfinite(potential) ) potential=0; 
+       galaxy->potentialEnergy = -potential;
+}
+
+void 
+initLiveGalaxy(Galaxy *galaxy) {
+       if ( galaxy->errorLimit<=0) galaxy->errorLimit = 1e-8;
+       if ( galaxy->desiredDelta<=0 ) galaxy->desiredDelta=3600;
+
+       galaxy->error = galaxy->errorLimit;
+       galaxy->delta=galaxy->desiredDelta;
+       galaxy->elapsedTime=0.0;
+
+#ifdef TAYLOR3
+       galaxy->firsttime=1;
+#endif
+       EnergyImpulseGalaxy(galaxy);
+}
+
+
+#define iszero(x)      ( fpclassify(x) & FP_ZERO )
+#define  CHK_ERRORVAL( what, err, val, prevval )                                               \
+if  ( isfinite(val) && isfinite(prevval) ) {                                                   \
+       err = fabs(  (val)/(prevval) - 1.0 );                                                   \
+       if ( !isfinite(err) )                                                                   \
+               err =  galaxy->error;                                                           \
+} else {                                                                                       \
+       fprintf(stderr, "%s is not finite: val:%G prevval:%G\n", what, (val), (prevval));       \
+       err = galaxy->error;                                                                    \
+}
+
+void
+liveGalaxy(Galaxy *galaxy, pthread_mutex_t *mutex) {
+       double prevEnergy = galaxy->kineticEnergy + galaxy->potentialEnergy;
+       double prevImpulse = galaxy->Impulse;
+       double prevMoment = galaxy->Moment;
+       double errorI, errorE, errorM, error, prevError;
+
+       accelerationGalaxy(galaxy);
+       prevError = galaxy->error;
+
+       if ( mutex ) pthread_mutex_lock(mutex);
+       while(1) {
+               stepGalaxy(galaxy, galaxy->delta);
+               EnergyImpulseGalaxy(galaxy);
+
+               CHK_ERRORVAL("Energy", errorE, galaxy->kineticEnergy + galaxy->potentialEnergy, prevEnergy);
+               CHK_ERRORVAL("Impulse",errorI, galaxy->Impulse, prevImpulse);
+               CHK_ERRORVAL("Moment", errorM, galaxy->Moment, prevMoment);
+
+               /*error=fmax(errorE, fmax(errorI, errorM));*/
+               error = errorE; 
+       
+               if ( error > galaxy->errorLimit ) {
+                       unstepGalaxy(galaxy, galaxy->delta);
+                       galaxy->delta /= 2.0;
+               } else if ( error*1e2 < galaxy->errorLimit && prevError < galaxy->errorLimit ) {
+                       unstepGalaxy(galaxy, galaxy->delta);
+                       galaxy->delta *= 2.0;
+               } else {
+                       galaxy->error = error;
+                       break;
+               }
+               prevError=error;
+       }
+       if ( mutex ) pthread_mutex_unlock(mutex);
+
+       galaxy->elapsedTime += galaxy->delta;
+}
+
+void 
+printStar(Star *star) {
+       printf("\tMass: %e\n", star->mass);
+       printf("\tPosit: x:%e y:%e z:%e\n", star->c.x, star->c.y, star->c.z);
+       printf("\tSpeed: x:%e y:%e z:%e\n", star->v.x, star->v.y, star->v.z);
+}
diff --git a/galaxy.h b/galaxy.h
new file mode 100644 (file)
index 0000000..a43950e
--- /dev/null
+++ b/galaxy.h
@@ -0,0 +1,77 @@
+#ifndef __GALAXY_H__
+#define __GALAXY_H__
+
+#include <sys/types.h>
+#include <pthread.h>
+
+/* sort forces by fabs before additinal. Additional by pair */ 
+#define SORT_STAR
+
+/* add third part of taylor series */ 
+#define TAYLOR3
+
+/* sort energy. impulse and moment by fabs before additinal. Additional by pair. Very slow */ 
+/* #define SORT_ENERGYSTAR */
+
+typedef struct {
+       double  x;
+       double  y;
+       double  z;
+} Vector;
+
+typedef struct {
+       double  mass;
+       Vector  c; /* coordinate */
+       Vector  v; /* velocity */
+       Vector  a; /* acceleration */
+#ifdef TAYLOR3
+       Vector  pa;
+#endif
+} Star;
+
+typedef struct {
+       u_int32_t       nstars;
+       Star    *stars;
+       Vector  *forces; /* forces between i-th and j-th stars */
+
+       /* stat data */
+       double Impulse;
+       double Moment;
+       double potentialEnergy;
+       double kineticEnergy;
+       double elapsedTime;
+
+       /* cfg options */
+       double delta;
+       double desiredDelta;
+       
+       double errorLimit;
+       double error;
+
+#ifdef TAYLOR3
+       int     firsttime;
+#endif
+} Galaxy;
+
+
+#define CNT_FARRAY(n)  ( (((n)+1)*((n)+2)) >> 1 )
+/* 
+  XXX 0<=i<j<nstars 
+  #define GET_FORCE(g, i, j)  ( (g)->forces + CNT_FARRAY(n) - CNT_FARRAY(n-i) + (j) - (i) - 1)
+*/ 
+#define GET_FORCE(g, i, j)  ( (g)->forces + (((j)*((j)+1))>>1) + i )
+
+
+void initGalaxy(Galaxy *galaxy, u_int32_t nstars);
+void forceGalaxy(Galaxy *galaxy);
+void accelerationGalaxy(Galaxy *galaxy);
+void stepGalaxy(Galaxy *galaxy, double delta);
+void unstepGalaxy(Galaxy *galaxy, double delta);
+void freeGalaxy(Galaxy *galaxy);
+void EnergyImpulseGalaxy(Galaxy *galaxy); 
+void initLiveGalaxy(Galaxy *galaxy);
+void liveGalaxy(Galaxy *galaxy, pthread_mutex_t *mutex);
+void printStar(Star *star);
+
+#define G      (6.742e-11)
+#endif
diff --git a/graphics.c b/graphics.c
new file mode 100644 (file)
index 0000000..1daa205
--- /dev/null
@@ -0,0 +1,316 @@
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <math.h>
+#include <pthread.h>
+
+#include "tmalloc.h"
+#include "xgalaxy.h"
+#include "trackball.h"
+
+typedef struct {
+       Vector  v;
+       Vector  V;
+       GdkColor color;
+       char    name[2];
+} Axis3D;
+
+static Axis3D axises[]={
+       {
+               { 1.0, 0.0, 0.0 },
+               { 0.0, 0.0, 0.0 },
+               { 0,  8480, 57570, 22360 },
+               "X"
+       },
+       {
+               { 0.0, 1.0, 0.0 },
+               { 0.0, 0.0, 0.0 },
+               { 0,  8480, 47300, 57570 },
+               "Y"
+       },
+       {
+               { 0.0, 0.0, 1.0 },
+               { 0.0, 0.0, 0.0 },
+               { 0,  57570, 8480, 8480 },
+               "Z"
+       }
+};
+
+static int
+cmpAxis3D(const void *a, const void *b) {
+       return ( ((Axis3D*)a)->V.z > ((Axis3D*)b)->V.z ) ? 1 : -1;
+}
+
+void
+drawAxis() {
+       double          minsize = fmin( XGalaxy.drawing_area->allocation.width, XGalaxy.drawing_area->allocation.height );
+       double Length = 0.4*minsize;
+       GdkGC   *gc = gdk_gc_new(XGalaxy.drawing_area->window);
+       PangoLayout *pl = gtk_widget_create_pango_layout(XGalaxy.drawing_area, NULL);
+       int i;
+
+       for(i=0;i<3;i++) {
+               rotate( &(axises[i].V), &(axises[i].v), &(XGalaxy.angle) );
+               vector_mul( &(axises[i].V),  &(axises[i].V), Length );
+       }
+
+       qsort(axises, 3, sizeof(Axis3D), cmpAxis3D); 
+               
+
+       for(i=0;i<3;i++) {
+               antialiasedLine(
+                       gc, 
+                       XGalaxy.drawing_area->allocation.width/2,
+                       XGalaxy.drawing_area->allocation.height/2,
+                       XGalaxy.drawing_area->allocation.width/2  + axises[i].V.x,
+                       XGalaxy.drawing_area->allocation.height/2 - axises[i].V.y,
+                       &(axises[i].color)
+               );
+               gdk_gc_set_rgb_fg_color(gc, &(axises[i].color));
+               pango_layout_set_text(pl, axises[i].name, -1);
+               gdk_draw_layout(
+                       XGalaxy.pixmap,
+                       gc,
+                       XGalaxy.drawing_area->allocation.width/2 + axises[i].V.x,
+                       XGalaxy.drawing_area->allocation.height/2 - axises[i].V.y,
+                       pl
+               );
+       }
+
+       g_object_unref(G_OBJECT(pl));
+       g_object_unref(G_OBJECT(gc));
+}
+
+
+void
+fitGalaxy() {
+       double max=-1.0;
+       int i;
+
+       for(i=0;i<XGalaxy.nentry;i++) {
+               rotate( &(XGalaxy.entry[i].a), &(XGalaxy.entry[i].c), &(XGalaxy.angle) );
+               if ( fabs(XGalaxy.entry[i].a.x) > max )
+                       max = fabs(XGalaxy.entry[i].a.x); 
+               if ( fabs(XGalaxy.entry[i].a.y) > max )
+                       max = fabs(XGalaxy.entry[i].a.y); 
+               if ( fabs(XGalaxy.entry[i].a.z) > max )
+                       max = fabs(XGalaxy.entry[i].a.z);
+       }
+
+       XGalaxy.Scale = 1.0/(3.0*max); 
+}
+
+static int
+cmpStar(const void *a, const void *b) {
+       return ( ((Star*)a)->a.z > ((Star*)b)->a.z ) ? 1 : -1;
+}
+
+#define MIN_BRIGHT (0.4)
+
+
+
+void
+drawStars() {
+       int i;
+       double          minsize = fmin( XGalaxy.drawing_area->allocation.width, XGalaxy.drawing_area->allocation.height );
+       double  r, brightness;
+       GdkGC   *gc = gdk_gc_new(XGalaxy.drawing_area->window);
+
+       if ( !XGalaxy.trace ) {
+               gdk_draw_rectangle (XGalaxy.pixmap,
+                      XGalaxy.drawing_area->style->white_gc,
+                      TRUE,
+                      0, 0,
+                      XGalaxy.drawing_area->allocation.width,
+                      XGalaxy.drawing_area->allocation.height
+               );
+               if ( XGalaxy.drawaxis) drawAxis();
+       }
+
+       if ( XGalaxy.runing ) {
+               pthread_mutex_lock(&(XGalaxy.mutex));
+               memcpy( XGalaxy.tmpentry, XGalaxy.galaxy.stars, sizeof(Star)*XGalaxy.galaxy.nstars );
+               pthread_mutex_unlock(&(XGalaxy.mutex));
+
+               for(i=0;i<XGalaxy.nentry;i++)
+                       rotate( &(XGalaxy.tmpentry[i].a), &(XGalaxy.tmpentry[i].c), &(XGalaxy.angle) );
+       
+               qsort(XGalaxy.tmpentry, XGalaxy.nentry, sizeof(Star), cmpStar); 
+
+               for(i=0;i<XGalaxy.nentry;i++) {
+                       r = (10 + (10*XGalaxy.Scale*XGalaxy.tmpentry[i].a.z))/2.0;
+                       if (r<1.5) r = 1.5;
+                       brightness = MIN_BRIGHT + (1.0-MIN_BRIGHT)*(1.0 + XGalaxy.Scale*XGalaxy.tmpentry[i].a.z) / 2.0;
+                       if ( brightness < MIN_BRIGHT ) brightness = MIN_BRIGHT;
+                       if ( brightness > 1.0 )     brightness = 1.0;
+                       DrawDisk(
+                               gc, 
+                               ((double)XGalaxy.drawing_area->allocation.width)/2.0 + (minsize*XGalaxy.Scale*XGalaxy.tmpentry[i].a.x),
+                               ((double)XGalaxy.drawing_area->allocation.height)/2.0 - (minsize*XGalaxy.Scale*XGalaxy.tmpentry[i].a.y),
+                               r, 
+                               1.0,
+                               brightness
+                       );
+               }
+       }
+       g_object_unref(G_OBJECT(gc));
+}
+
+void
+drawGalaxy() {
+       GdkRectangle update_rect;
+
+        if (!XGalaxy.pixmap)
+               return;
+       update_rect.x=0;        
+       update_rect.y=0;
+       update_rect.width = XGalaxy.drawing_area->allocation.width;     
+       update_rect.height = XGalaxy.drawing_area->allocation.height;
+
+       gdk_window_invalidate_rect (XGalaxy.drawing_area->window, &update_rect, FALSE);
+}
+
+void
+angle_changed(GtkRange *range, gpointer user_data) {
+       double angle = gtk_range_get_value(range)*M_PI/180.0;
+       Quaternion      old, newquaternion;
+
+       if (XGalaxy.locksignal)
+               return;
+
+       if (XGalaxy.trace) {
+               XGalaxy.locksignal=1;
+               switch((int)user_data) {
+                       case 'X': gtk_range_set_value(range, XGalaxy.Xangle*180.0/M_PI); break;
+                       case 'Y': gtk_range_set_value(range, XGalaxy.Yangle*180.0/M_PI); break;
+                       case 'Z': gtk_range_set_value(range, XGalaxy.Zangle*180.0/M_PI); break;
+                       default: break;
+               }
+               XGalaxy.locksignal=0;
+               return;
+       }
+
+       memcpy(&old, &(XGalaxy.angle), sizeof(old));
+       memset(&newquaternion, 0 , sizeof(newquaternion));
+       switch( (int)user_data ) {
+               case 'X': 
+                       newquaternion.d.x = sin( (angle-XGalaxy.Xangle)/2.0 ); 
+                       newquaternion.w = cos( (angle-XGalaxy.Xangle)/2.0 );
+                       XGalaxy.Xangle=angle; 
+                       break; 
+               case 'Y': 
+                       newquaternion.d.y = sin( (angle-XGalaxy.Yangle)/2.0 ); 
+                       newquaternion.w = cos( (angle-XGalaxy.Yangle)/2.0 );
+                       XGalaxy.Yangle=angle; 
+                       break; 
+               case 'Z': 
+                       newquaternion.d.z = sin( (angle-XGalaxy.Zangle)/2.0 ); 
+                       newquaternion.w = cos( (angle-XGalaxy.Zangle)/2.0 );
+                       XGalaxy.Zangle=angle; 
+                       break;
+               default: g_print("Unknown axis: %c\n", (char)(int)user_data);
+       }
+
+       //quater_vmul( &(XGalaxy.angle), &old, &newquaternion );
+       quater_vmul( &(XGalaxy.angle), &newquaternion, &old );
+
+        drawGalaxy();
+}
+
+void
+clearDraw() {
+       if ( !XGalaxy.pixmap )
+               return;
+               gdk_draw_rectangle (XGalaxy.pixmap,
+                XGalaxy.drawing_area->style->white_gc,
+                TRUE,
+                0, 0,
+                XGalaxy.drawing_area->allocation.width,
+                XGalaxy.drawing_area->allocation.height);
+       if ( XGalaxy.drawaxis ) drawAxis();
+}
+
+gint
+expose_event( GtkWidget      *widget, GdkEventExpose *event ) {
+       /* draw only last expose */
+       if ( event->count > 0) 
+               return TRUE;
+
+       drawStars();
+        gdk_draw_pixmap(widget->window,
+                  widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+                  XGalaxy.pixmap,
+                  event->area.x, event->area.y,
+                  event->area.x, event->area.y,
+                  event->area.width, event->area.height);
+
+        return FALSE;
+}
+
+gint
+configure_event( GtkWidget         *widget, GdkEventConfigure *event ) {
+        if (XGalaxy.pixmap)
+                g_object_unref(XGalaxy.pixmap);
+        
+        XGalaxy.pixmap = gdk_pixmap_new(widget->window,
+                          widget->allocation.width,
+                          widget->allocation.height,  
+                          -1);
+
+       clearDraw();
+        return TRUE;
+}
+
+gboolean 
+mouse_motion_notify(GtkWidget      *widget, GdkEventMotion *event) {
+       int x = 0;
+       int y = 0;
+       GdkModifierType state = 0;
+       
+       if (XGalaxy.locksignal || XGalaxy.trace )
+               return TRUE;
+
+       if (event->is_hint) 
+               gdk_window_get_pointer(event->window, &x, &y, &state);
+       else {
+               x = event->x;
+               y = event->y;
+               state = event->state;
+       }
+       if (state & GDK_BUTTON1_MASK) {
+               double width, height;
+               Quaternion old;
+               Vector angles;
+
+               width = widget->allocation.width;
+               height = widget->allocation.height;
+               trackball( &(XGalaxy.motion),
+                       (2.0*XGalaxy.beginx -            width) / width,
+                       (          height - 2.0*XGalaxy.beginy) / height,
+                       (           2.0*x -            width) / width,
+                       (          height -            2.0*y) / height );
+
+               memcpy(&old, &(XGalaxy.angle), sizeof(old));
+               //quater_vmul( &(XGalaxy.angle), &old, &(XGalaxy.motion) );
+               quater_vmul( &(XGalaxy.angle), &(XGalaxy.motion), &old );
+
+               XGalaxy.locksignal=1;
+               quater_to_angles( &(XGalaxy.angle), &angles);
+               XGalaxy.Xangle = angles.x;
+               gtk_range_set_value(GTK_RANGE(XGalaxy.XSlider), angles.x*180.0/M_PI);
+               XGalaxy.Yangle = angles.y;
+               gtk_range_set_value(GTK_RANGE(XGalaxy.YSlider), angles.y*180.0/M_PI);
+               XGalaxy.Zangle = angles.z;
+               gtk_range_set_value(GTK_RANGE(XGalaxy.ZSlider), angles.z*180.0/M_PI);
+               XGalaxy.locksignal=0;
+
+               XGalaxy.beginx = x;
+               XGalaxy.beginy = y;
+
+               drawGalaxy();
+       }
+       return TRUE;
+}
diff --git a/gtkcellrendererbutton.c b/gtkcellrendererbutton.c
new file mode 100644 (file)
index 0000000..403f562
--- /dev/null
@@ -0,0 +1,412 @@
+/* gtkcellrendererbutton.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+//#include <config.h>
+#include <stdlib.h>
+#include "gtkcellrendererbutton.h"
+//#include "gtkintl.h"
+//#include "gtkmarshalers.h"
+//#include "gtktreeprivate.h"
+
+static void gtk_cell_renderer_button_get_property  (GObject                    *object,
+                                                   guint                       param_id,
+                                                   GValue                     *value,
+                                                   GParamSpec                 *pspec);
+static void gtk_cell_renderer_button_set_property  (GObject                    *object,
+                                                   guint                       param_id,
+                                                   const GValue               *value,
+                                                   GParamSpec                 *pspec);
+static void gtk_cell_renderer_button_init       (GtkCellRendererButton      *celltext);
+static void gtk_cell_renderer_button_class_init (GtkCellRendererButtonClass *class);
+static void gtk_cell_renderer_button_get_size   (GtkCellRenderer            *cell,
+                                                GtkWidget                  *widget,
+                                                GdkRectangle               *cell_area,
+                                                gint                       *x_offset,
+                                                gint                       *y_offset,
+                                                gint                       *width,
+                                                gint                       *height);
+static void gtk_cell_renderer_button_render     (GtkCellRenderer            *cell,
+                                                GdkWindow                  *window,
+                                                GtkWidget                  *widget,
+                                                GdkRectangle               *background_area,
+                                                GdkRectangle               *cell_area,
+                                                GdkRectangle               *expose_area,
+                                                GtkCellRendererState        flags);
+static gboolean gtk_cell_renderer_button_activate  (GtkCellRenderer            *cell,
+                                                   GdkEvent                   *event,
+                                                   GtkWidget                  *widget,
+                                                   const gchar                *path,
+                                                   GdkRectangle               *background_area,
+                                                   GdkRectangle               *cell_area,
+                                                   GtkCellRendererState        flags);
+
+
+enum {
+  TOGGLED,
+  LAST_SIGNAL
+};
+
+enum {
+  PROP_ZERO,
+  PROP_ACTIVATABLE,
+  PROP_ACTIVE,
+/*  PROP_RADIO, */
+  PROP_INCONSISTENT
+};
+
+#define BUTTON_WIDTH 14
+
+static guint button_cell_signals[LAST_SIGNAL] = { 0 };
+
+#define GTK_CELL_RENDERER_BUTTON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_CELL_RENDERER_BUTTON, GtkCellRendererButtonPrivate))
+
+typedef struct _GtkCellRendererButtonPrivate GtkCellRendererButtonPrivate;
+struct _GtkCellRendererButtonPrivate
+{
+  guint inconsistent : 1;
+};
+
+
+GType
+gtk_cell_renderer_button_get_type (void)
+{
+  static GType cell_button_type = 0;
+
+  if (!cell_button_type)
+    {
+      static const GTypeInfo cell_button_info =
+      {
+       sizeof (GtkCellRendererButtonClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+       (GClassInitFunc) gtk_cell_renderer_button_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       sizeof (GtkCellRendererButton),
+       0,              /* n_preallocs */
+       (GInstanceInitFunc) gtk_cell_renderer_button_init,
+      };
+
+      cell_button_type =
+       g_type_register_static (GTK_TYPE_CELL_RENDERER, "GtkCellRendererButton",
+                               &cell_button_info, 0);
+    }
+
+  return cell_button_type;
+}
+
+static void
+gtk_cell_renderer_button_init (GtkCellRendererButton *cellbutton)
+{
+  cellbutton->activatable = TRUE;
+  cellbutton->active = FALSE;
+  GTK_CELL_RENDERER (cellbutton)->mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
+  GTK_CELL_RENDERER (cellbutton)->xpad = 2;
+  GTK_CELL_RENDERER (cellbutton)->ypad = 2;
+}
+
+static void
+gtk_cell_renderer_button_class_init (GtkCellRendererButtonClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
+
+  object_class->get_property = gtk_cell_renderer_button_get_property;
+  object_class->set_property = gtk_cell_renderer_button_set_property;
+
+  cell_class->get_size = gtk_cell_renderer_button_get_size;
+  cell_class->render = gtk_cell_renderer_button_render;
+  cell_class->activate = gtk_cell_renderer_button_activate;
+  
+  g_object_class_install_property (object_class,
+                                  PROP_ACTIVE,
+                                  g_param_spec_boolean ("active",
+                                                        "Button state",
+                                                        "The button state of the button",
+                                                        FALSE,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE));
+
+  g_object_class_install_property (object_class,
+                                  PROP_INCONSISTENT,
+                                  g_param_spec_boolean ("inconsistent",
+                                                        "Inconsistent state",
+                                                        "The inconsistent state of the button",
+                                                        FALSE,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE));
+  
+  g_object_class_install_property (object_class,
+                                  PROP_ACTIVATABLE,
+                                  g_param_spec_boolean ("activatable",
+                                                        "Activatable",
+                                                        "The button button can be activated",
+                                                        TRUE,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE));
+
+  button_cell_signals[TOGGLED] =
+    g_signal_new ("toggled",
+                 G_OBJECT_CLASS_TYPE (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (GtkCellRendererButtonClass, toggled),
+                 NULL, NULL,
+                 /*_gtk_marshal_VOID__STRING ,*/
+                 g_cclosure_marshal_VOID__STRING,
+                 G_TYPE_NONE, 1,
+                 G_TYPE_STRING);
+
+  g_type_class_add_private (object_class, sizeof (GtkCellRendererButtonPrivate));
+}
+
+static void
+gtk_cell_renderer_button_get_property (GObject     *object,
+                                      guint        param_id,
+                                      GValue      *value,
+                                      GParamSpec  *pspec)
+{
+  GtkCellRendererButton *cellbutton = GTK_CELL_RENDERER_BUTTON (object);
+  GtkCellRendererButtonPrivate *priv;
+
+  priv = GTK_CELL_RENDERER_BUTTON_GET_PRIVATE (object);
+  
+  switch (param_id)
+    {
+    case PROP_ACTIVE:
+      g_value_set_boolean (value, cellbutton->active);
+      break;
+    case PROP_INCONSISTENT:
+      g_value_set_boolean (value, priv->inconsistent);
+      break;
+    case PROP_ACTIVATABLE:
+      g_value_set_boolean (value, cellbutton->activatable);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+      break;
+    }
+}
+
+
+static void
+gtk_cell_renderer_button_set_property (GObject      *object,
+                                      guint         param_id,
+                                      const GValue *value,
+                                      GParamSpec   *pspec)
+{
+  GtkCellRendererButton *cellbutton = GTK_CELL_RENDERER_BUTTON (object);
+  GtkCellRendererButtonPrivate *priv;
+
+  priv = GTK_CELL_RENDERER_BUTTON_GET_PRIVATE (object);
+  
+  switch (param_id)
+    {
+    case PROP_ACTIVE:
+      cellbutton->active = g_value_get_boolean (value);
+      g_object_notify (G_OBJECT(object), "active");
+      break;
+    case PROP_INCONSISTENT:
+      priv->inconsistent = g_value_get_boolean (value);
+      g_object_notify (G_OBJECT (object), "inconsistent");
+      break;
+    case PROP_ACTIVATABLE:
+      cellbutton->activatable = g_value_get_boolean (value);
+      g_object_notify (G_OBJECT(object), "activatable");
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+      break;
+    }
+}
+
+/**
+ * gtk_cell_renderer_button_new:
+ * 
+ * Creates a new #GtkCellRendererButton. Adjust rendering
+ * parameters using object properties. Object properties can be set
+ * globally (with g_object_set()). Also, with #GtkTreeViewColumn, you
+ * can bind a property to a value in a #GtkTreeModel. For example, you
+ * can bind the "active" property on the cell renderer to a boolean value
+ * in the model, thus causing the check button to reflect the state of
+ * the model.
+ * 
+ * Return value: the new cell renderer
+ **/
+GtkCellRenderer *
+gtk_cell_renderer_button_new (void)
+{
+  return g_object_new (GTK_TYPE_CELL_RENDERER_BUTTON, NULL);
+}
+
+static void
+gtk_cell_renderer_button_get_size (GtkCellRenderer *cell,
+                                  GtkWidget       *widget,
+                                  GdkRectangle    *cell_area,
+                                  gint            *x_offset,
+                                  gint            *y_offset,
+                                  gint            *width,
+                                  gint            *height)
+{
+  gint calc_width;
+  gint calc_height;
+
+  calc_width = (gint) cell->xpad * 2 + BUTTON_WIDTH;
+  calc_height = (gint) cell->ypad * 2 + BUTTON_WIDTH;
+
+  if (width)
+    *width = calc_width;
+
+  if (height)
+    *height = calc_height;
+
+  if (cell_area)
+    {
+      if (x_offset)
+       {
+         *x_offset = ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
+                      (1.0 - cell->xalign) : cell->xalign) * (cell_area->width - calc_width);
+         *x_offset = MAX (*x_offset, 0);
+       }
+      if (y_offset)
+       {
+         *y_offset = cell->yalign * (cell_area->height - calc_height);
+         *y_offset = MAX (*y_offset, 0);
+       }
+    }
+}
+
+static void
+gtk_cell_renderer_button_render (GtkCellRenderer      *cell,
+                                GdkDrawable          *window,
+                                GtkWidget            *widget,
+                                GdkRectangle         *background_area,
+                                GdkRectangle         *cell_area,
+                                GdkRectangle         *expose_area,
+                                GtkCellRendererState  flags)
+{
+  GtkCellRendererButton *cellbutton = (GtkCellRendererButton *) cell;
+  GtkCellRendererButtonPrivate *priv;
+  gint width, height;
+  gint x_offset, y_offset;
+  GtkShadowType shadow;
+  GtkStateType state = 0;
+
+  priv = GTK_CELL_RENDERER_BUTTON_GET_PRIVATE (cell);
+
+  gtk_cell_renderer_button_get_size (cell, widget, cell_area,
+                                    &x_offset, &y_offset,
+                                    &width, &height);
+  width -= cell->xpad*2;
+  height -= cell->ypad*2;
+
+  if (width <= 0 || height <= 0)
+    return;
+
+  if (priv->inconsistent)
+    shadow = GTK_SHADOW_ETCHED_IN;
+  else
+    shadow = cellbutton->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
+
+  if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
+    {
+      if (GTK_WIDGET_HAS_FOCUS (widget))
+       state = GTK_STATE_SELECTED;
+      else
+       state = GTK_STATE_ACTIVE;
+    }
+  else
+    {
+      if (cellbutton->activatable)
+        state = GTK_STATE_NORMAL;
+      else
+        state = GTK_STATE_INSENSITIVE;
+    }
+
+      gtk_paint_box (widget->style,
+                       window,
+                       state, shadow,
+                       expose_area, widget, "button",
+                       cell_area->x + x_offset + cell->xpad,
+                       cell_area->y + y_offset + cell->ypad,
+                       width - 1, height - 1);
+
+      gtk_paint_hline(
+               widget->style,
+               window,
+               GTK_STATE_NORMAL,
+               expose_area, widget, "label",
+               cell_area->x + x_offset + cell->xpad  + 4 ,
+               cell_area->x + x_offset + width  - 4,
+               cell_area->y + y_offset + cell->ypad/2 + height/2
+       );
+}
+
+static gint
+gtk_cell_renderer_button_activate (GtkCellRenderer      *cell,
+                                  GdkEvent             *event,
+                                  GtkWidget            *widget,
+                                  const gchar          *path,
+                                  GdkRectangle         *background_area,
+                                  GdkRectangle         *cell_area,
+                                  GtkCellRendererState  flags)
+{
+  GtkCellRendererButton *cellbutton;
+  
+  cellbutton = GTK_CELL_RENDERER_BUTTON (cell);
+  if (cellbutton->activatable)
+    {
+      g_signal_emit (cell, button_cell_signals[TOGGLED], 0, path);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+/**
+ * gtk_cell_renderer_button_get_active:
+ * @button: a #GtkCellRendererButton
+ *
+ * Returns whether the cell renderer is active. See
+ * gtk_cell_renderer_button_set_active().
+ *
+ * Return value: %TRUE if the cell renderer is active.
+ **/
+gboolean
+gtk_cell_renderer_button_get_active (GtkCellRendererButton *button)
+{
+  g_return_val_if_fail (GTK_IS_CELL_RENDERER_BUTTON (button), FALSE);
+
+  return button->active;
+}
+
+/**
+ * gtk_cell_renderer_button_set_active:
+ * @button: a #GtkCellRendererButton.
+ * @setting: the value to set.
+ *
+ * Activates or deactivates a cell renderer.
+ **/
+void
+gtk_cell_renderer_button_set_active (GtkCellRendererButton *button,
+                                    gboolean               setting)
+{
+  g_return_if_fail (GTK_IS_CELL_RENDERER_BUTTON (button));
+
+  g_object_set (button, "active", setting ? TRUE : FALSE, NULL);
+}
diff --git a/gtkcellrendererbutton.h b/gtkcellrendererbutton.h
new file mode 100644 (file)
index 0000000..8282191
--- /dev/null
@@ -0,0 +1,75 @@
+/* gtkcellrendererbutton.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CELL_RENDERER_BUTTON_H__
+#define __GTK_CELL_RENDERER_BUTTON_H__
+
+#include <gtk/gtkcellrenderer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_TYPE_CELL_RENDERER_BUTTON                  (gtk_cell_renderer_button_get_type ())
+#define GTK_CELL_RENDERER_BUTTON(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_BUTTON, GtkCellRendererButton))
+#define GTK_CELL_RENDERER_BUTTON_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_BUTTON, GtkCellRendererButtonClass))
+#define GTK_IS_CELL_RENDERER_BUTTON(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_BUTTON))
+#define GTK_IS_CELL_RENDERER_BUTTON_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_RENDERER_BUTTON))
+#define GTK_CELL_RENDERER_BUTTON_GET_CLASS(obj)         (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_RENDERER_BUTTON, GtkCellRendererButtonClass))
+
+typedef struct _GtkCellRendererButton GtkCellRendererButton;
+typedef struct _GtkCellRendererButtonClass GtkCellRendererButtonClass;
+
+struct _GtkCellRendererButton
+{
+  GtkCellRenderer parent;
+
+  /*< private >*/
+  guint active : 1;
+  guint activatable : 1;
+};
+
+struct _GtkCellRendererButtonClass
+{
+  GtkCellRendererClass parent_class;
+
+  void (* toggled) (GtkCellRendererButton *cell_renderer_button,
+                   const gchar                 *path);
+
+  /* Padding for future expansion */
+  void (*_gtk_reserved1) (void);
+  void (*_gtk_reserved2) (void);
+  void (*_gtk_reserved3) (void);
+  void (*_gtk_reserved4) (void);
+};
+
+GType            gtk_cell_renderer_button_get_type  (void);
+GtkCellRenderer *gtk_cell_renderer_button_new       (void);
+
+gboolean        gtk_cell_renderer_button_get_active (GtkCellRendererButton *button);
+void            gtk_cell_renderer_button_set_active (GtkCellRendererButton *button,
+                                                     gboolean               setting);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_CELL_RENDERER_BUTTON_H__ */
diff --git a/menuaction.c b/menuaction.c
new file mode 100644 (file)
index 0000000..f9600ef
--- /dev/null
@@ -0,0 +1,511 @@
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <math.h>
+#include <pthread.h>
+
+#include "tools.h"
+#include "tmalloc.h"
+#include "xgalaxy.h"
+
+void
+galaxy_set_title() {
+       char *buf;
+
+       buf = g_malloc( strlen("Galaxy - ") + (( XGalaxy.filename ) ? strlen(XGalaxy.filename) : 0) + 1);
+       if ( XGalaxy.filename ) {
+               char *ptr = strrchr(XGalaxy.filename,'/');
+               if ( !ptr ) ptr = XGalaxy.filename; else  ptr++;
+               sprintf(buf,"Galaxy - %s", ptr);
+       } else 
+               sprintf(buf,"Galaxy");
+
+       gtk_window_set_title (GTK_WINDOW(XGalaxy.window), buf);
+       g_free(buf); 
+}
+
+void 
+clearData( GtkWidget *w, gpointer   data ) {
+       if ( XGalaxy.runing ) return;
+
+       gtk_label_set_text( GTK_LABEL(XGalaxy.dataEnergyField), "0");
+       gtk_label_set_text( GTK_LABEL(XGalaxy.dataImpulseField), "0");
+       gtk_label_set_text( GTK_LABEL(XGalaxy.dataMomentField), "0");
+
+       gtk_entry_set_text(GTK_ENTRY(XGalaxy.deltaField), "3600");
+       gtk_entry_set_text(GTK_ENTRY(XGalaxy.errorField), "1e-8");
+       gtk_list_store_clear( GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(XGalaxy.dataField))) );
+
+       if ( XGalaxy.filename ) {
+               g_free(XGalaxy.filename);
+               XGalaxy.filename=NULL;
+               galaxy_set_title();
+       }
+       freeStarEntry();
+       XGalaxy.Xangle = XGalaxy.Yangle = XGalaxy.Zangle = 0.0;
+       gtk_range_set_value(GTK_RANGE(XGalaxy.XSlider), 0);
+       gtk_range_set_value(GTK_RANGE(XGalaxy.YSlider), 0);
+       gtk_range_set_value(GTK_RANGE(XGalaxy.ZSlider), 0);
+       memset( &(XGalaxy.angle), 0, sizeof(XGalaxy.angle) );
+       XGalaxy.angle.w=1;
+
+       gtk_clist_clear( GTK_CLIST(XGalaxy.resField) );
+       gtk_label_set_text( GTK_LABEL(XGalaxy.resEnergyField), "0");
+       gtk_label_set_text( GTK_LABEL(XGalaxy.resImpulseField), "0");
+       gtk_label_set_text( GTK_LABEL(XGalaxy.resMomentField), "0");
+       gtk_label_set_text( GTK_LABEL(XGalaxy.resDeltaField), "0");
+       gtk_label_set_text( GTK_LABEL(XGalaxy.resTimeField), "0");
+
+       clearDraw();
+       drawGalaxy();
+}
+
+void
+saveData() {
+       FILE *out = fopen(XGalaxy.filename, "w");
+       u_int32_t i;
+
+       if ( !out ) {
+               GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(XGalaxy.window),
+                                  GTK_DIALOG_DESTROY_WITH_PARENT,
+                                  GTK_MESSAGE_ERROR,
+                                  GTK_BUTTONS_CLOSE,
+                                  "Error saving file '%s': %s",
+                                  XGalaxy.filename, g_strerror (errno));
+               gtk_dialog_run (GTK_DIALOG (dialog));
+               gtk_widget_destroy(dialog);
+               return;
+       }
+
+       fprintf(out, "delta=%G\n", atof( gtk_entry_get_text( GTK_ENTRY(XGalaxy.deltaField) ) ) ); 
+       fprintf(out, "error=%G\n", atof( gtk_entry_get_text( GTK_ENTRY(XGalaxy.errorField) ) ) ); 
+       fprintf(out, "\n");
+       for(i=0;i<XGalaxy.nentry;i++)
+               fprintf(out,"%G\t%G\t%G\t%G\t%G\t%G\t%G\n",
+                       XGalaxy.entry[i].mass,
+                       XGalaxy.entry[i].c.x,
+                       XGalaxy.entry[i].c.y,
+                       XGalaxy.entry[i].c.z,
+                       XGalaxy.entry[i].v.x,
+                       XGalaxy.entry[i].v.y,
+                       XGalaxy.entry[i].v.z
+               );
+
+       fclose(out);
+}
+
+
+void 
+saveAsFile( GtkWidget *w, gpointer   data ) {
+       GtkWidget *dialog = gtk_file_chooser_dialog_new("Save as", GTK_WINDOW(XGalaxy.window), GTK_FILE_CHOOSER_ACTION_SAVE,
+               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+               NULL);
+
+       if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+               if ( XGalaxy.filename ) g_free(XGalaxy.filename);
+               XGalaxy.filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+               galaxy_set_title();
+
+               saveData();
+       }
+
+       gtk_widget_destroy (dialog); 
+}
+
+void 
+saveFile( GtkWidget *w, gpointer   data ) {
+       if ( !XGalaxy.filename ) {
+               saveAsFile(w, data);
+       } else {
+               saveData();
+       }
+}
+
+void
+loadData() {
+       FILE *in;
+       char buf[4096];
+       double  tmp;
+       Star    star;
+        GtkTreeIter   iter;
+        GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(XGalaxy.dataField)));
+
+       if ( XGalaxy.runing )
+               return;
+
+       in = fopen(XGalaxy.filename, "r");
+       if ( !in ) {
+               GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(XGalaxy.window),
+                                  GTK_DIALOG_DESTROY_WITH_PARENT,
+                                  GTK_MESSAGE_ERROR,
+                                  GTK_BUTTONS_CLOSE,
+                                  "Error opening file '%s': %s",
+                                  XGalaxy.filename, g_strerror (errno));
+               gtk_dialog_run (GTK_DIALOG (dialog));
+               gtk_widget_destroy(dialog);
+               g_free(XGalaxy.filename);
+               XGalaxy.filename=NULL;
+               return;
+       }
+
+       while(fgets(buf, 4096, in)) {
+               if ( strncmp("delta", buf, strlen("delta"))==0 ) {
+                       sscanf(buf,"delta=%lG", &tmp);
+                       sprintf(buf,"%G", tmp);
+                       gtk_entry_set_text( GTK_ENTRY(XGalaxy.deltaField), buf );
+               } else if ( strncmp("error", buf, strlen("error"))==0 ) {
+                       sscanf(buf,"error=%lG", &tmp);
+                       sprintf(buf,"%G", tmp);
+                       gtk_entry_set_text( GTK_ENTRY(XGalaxy.errorField), buf );
+               } else if ( strlen(buf)>6 ) {
+                       sscanf(buf,"%lG\t%lG\t%lG\t%lG\t%lG\t%lG\t%lG",
+                               &(star.mass),
+                               &(star.c.x),
+                               &(star.c.y),
+                               &(star.c.z),
+                               &(star.v.x),
+                               &(star.v.y),
+                               &(star.v.z)
+                       );
+                       gtk_list_store_append( store, &iter );
+                       sprintf(buf, "%G", star.mass);
+                       gtk_list_store_set( store, &iter, 1, buf, -1);
+                       sprintf(buf, "%G", star.c.x);
+                       gtk_list_store_set( store, &iter, 2, buf, -1);
+                       sprintf(buf, "%G", star.c.y);
+                       gtk_list_store_set( store, &iter, 3, buf, -1);
+                       sprintf(buf, "%G", star.c.z);
+                       gtk_list_store_set( store, &iter, 4, buf, -1);
+                       sprintf(buf, "%G", star.v.x);
+                       gtk_list_store_set( store, &iter, 5, buf, -1);
+                       sprintf(buf, "%G", star.v.y);
+                       gtk_list_store_set( store, &iter, 6, buf, -1);
+                       sprintf(buf, "%G", star.v.z);
+                       gtk_list_store_set( store, &iter, 7, buf, -1);
+                       addEntry(&star);
+               }
+       }
+       fclose(in);
+       gtk_notebook_set_current_page(GTK_NOTEBOOK(XGalaxy.notebook), 0);
+       cntEntry();
+}
+
+void
+openFile( GtkWidget *w, gpointer   data ) {
+       if ( !XGalaxy.runing ) {
+               GtkWidget *dialog = gtk_file_chooser_dialog_new("Open", GTK_WINDOW(XGalaxy.window), GTK_FILE_CHOOSER_ACTION_OPEN,
+                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+                       NULL);
+
+               if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+                       clearData(w,data);
+                       if ( XGalaxy.filename ) g_free(XGalaxy.filename);
+                       XGalaxy.filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+                       galaxy_set_title();
+                       loadData();
+               }
+
+               gtk_widget_destroy (dialog);
+       }
+}
+
+void
+showAbout(  GtkWidget *w, gpointer   data ) {
+/* 2.6 only
+       GtkWidget *dialog = gtk_about_dialog_new() ;
+
+       gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(dialog), "XGalaxy");
+       gtk_about_dialog_get_comments(GTK_ABOUT_DIALOG(dialog), "Gravity modeling");
+       gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), "1.0");
+       gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog), "COPYRIGHT");
+       gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(dialog),"(X)Galaxy is under BSD license");
+       gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog),"http://www.sigaev.ru");
+       gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(dialog),"Teodor Sigaev");
+
+        gtk_show_about_dialog(GTK_WINDOW(XGalaxy.window),
+               "name", "XGalaxy",
+               "comments", "Gravity modeling",
+               "version", "1.0",
+               "copyright", "COPYRIGHT",
+               "license", "XGalaxy is under BSD license",
+               "website", "http://www.sigaev.ru",
+               "authors", "Teodor Sigaev",
+               NULL);
+*/
+       GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(XGalaxy.window),
+                GTK_DIALOG_MODAL,
+                GTK_MESSAGE_INFO,
+                GTK_BUTTONS_CLOSE,
+               "XGalaxy - gravity modeling software.\nCopyright Teodor Sigaev <teodor@sigaev.ru>, 2004.\nPublished under BSD license"
+       );
+       gtk_dialog_run (GTK_DIALOG (dialog));
+       gtk_widget_destroy(dialog);
+}
+
+void
+fillResult(int clearres) {
+        char buf[128];
+       int     i;
+
+
+       
+       pthread_mutex_lock(&(XGalaxy.mutex));
+        sprintf(buf,"%G",XGalaxy.galaxy.kineticEnergy + XGalaxy.galaxy.potentialEnergy);
+        gtk_label_set_text( GTK_LABEL(XGalaxy.resEnergyField), buf);
+
+        sprintf(buf,"%G",XGalaxy.galaxy.Impulse);
+        gtk_label_set_text( GTK_LABEL(XGalaxy.resImpulseField), buf);
+
+        sprintf(buf,"%G",XGalaxy.galaxy.Moment);
+        gtk_label_set_text( GTK_LABEL(XGalaxy.resMomentField), buf);
+
+        sprintf(buf,"%G",XGalaxy.galaxy.delta);
+        gtk_label_set_text( GTK_LABEL(XGalaxy.resDeltaField), buf);
+
+        sprintf(buf,"%G",XGalaxy.galaxy.elapsedTime);
+        gtk_label_set_text( GTK_LABEL(XGalaxy.resTimeField), buf);
+
+        memcpy( XGalaxy.tmpentry, XGalaxy.galaxy.stars, sizeof(Star)*XGalaxy.galaxy.nstars );
+        pthread_mutex_unlock(&(XGalaxy.mutex));
+
+       gtk_clist_freeze( GTK_CLIST(XGalaxy.resField) );
+       if ( clearres ) {
+               gchar *data[7] = { "", "", "", "", "", "", "" };
+               gtk_clist_clear( GTK_CLIST(XGalaxy.resField) );
+               for(i=0;i<XGalaxy.nentry;i++)
+                       gtk_clist_append(GTK_CLIST(XGalaxy.resField), data);
+       }
+
+
+       for(i=0;i<XGalaxy.nentry;i++) {
+               sprintf(buf,"%G", XGalaxy.tmpentry[i].mass);
+               gtk_clist_set_text(GTK_CLIST(XGalaxy.resField), i, 0, buf);
+               sprintf(buf,"%G", XGalaxy.tmpentry[i].c.x);
+               gtk_clist_set_text(GTK_CLIST(XGalaxy.resField), i, 1, buf);
+               sprintf(buf,"%G", XGalaxy.tmpentry[i].c.y);
+               gtk_clist_set_text(GTK_CLIST(XGalaxy.resField), i, 2, buf);
+               sprintf(buf,"%G", XGalaxy.tmpentry[i].c.z);
+               gtk_clist_set_text(GTK_CLIST(XGalaxy.resField), i, 3, buf);
+               sprintf(buf,"%G", XGalaxy.tmpentry[i].v.x);
+               gtk_clist_set_text(GTK_CLIST(XGalaxy.resField), i, 4, buf);
+               sprintf(buf,"%G", XGalaxy.tmpentry[i].v.y);
+               gtk_clist_set_text(GTK_CLIST(XGalaxy.resField), i, 5, buf);
+               sprintf(buf,"%G", XGalaxy.tmpentry[i].v.z);
+               gtk_clist_set_text(GTK_CLIST(XGalaxy.resField), i, 6, buf);
+               sprintf(buf,"%G", sqrt( XGalaxy.tmpentry[i].v.z*XGalaxy.tmpentry[i].v.z + XGalaxy.tmpentry[i].v.y*XGalaxy.tmpentry[i].v.y + XGalaxy.tmpentry[i].v.x*XGalaxy.tmpentry[i].v.x ) );
+               gtk_clist_set_text(GTK_CLIST(XGalaxy.resField), i, 7, buf);
+       }       
+       gtk_clist_thaw( GTK_CLIST(XGalaxy.resField) );
+}
+
+static u_int32_t cycles=0;
+gboolean
+fillResultByTimeout(gpointer data) {
+       if ( XGalaxy.runing==0 )
+               return FALSE;
+
+
+       if ( !XGalaxy.paused ) {
+               struct timeval begin;
+               XGalaxy.runTime -= ((double)XG_TIME_TICK)/1000.0;
+
+               if ( XGalaxy.runTime >= 0 ) 
+                       return TRUE;
+               
+               gettimeofday( &begin, NULL );
+               switch( XGalaxy.page_active ) {
+                       case 2: 
+                               if ( cycles++ % (int)ceil(200/XG_TIME_TICK) == 0 ) 
+                                       fillResult(FALSE);
+                               if (XGalaxy.trace)
+                                       drawStars(); 
+                               break;
+                       case 1: 
+                               drawGalaxy(); 
+                               break;
+                       default: 
+                               if (XGalaxy.trace)
+                                       drawStars(); 
+                               break;
+               }
+
+               XGalaxy.runTime = elapsedtime( &begin );
+
+               if ( cycles > 2000000000 )
+                       cycles=1;
+       }
+
+       return TRUE;
+} 
+
+gboolean
+show_resCList(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) {
+       g_print("EVENT\n");     
+       return FALSE;
+}
+
+void*
+runingGravity(void *notneed) {
+       while( XGalaxy.request_to_exit == 0 ) {
+               liveGalaxy( &(XGalaxy.galaxy), &(XGalaxy.mutex) );
+               pthread_yield();
+       }
+       XGalaxy.request_to_exit = 0;
+
+       return NULL;
+}
+
+int
+startGravity() {
+       int rc;
+
+       if ( (rc=pthread_create(&(XGalaxy.thread), NULL, runingGravity, NULL))!=0 ) {
+               GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(XGalaxy.window),
+                                  GTK_DIALOG_DESTROY_WITH_PARENT,
+                                  GTK_MESSAGE_ERROR,
+                                  GTK_BUTTONS_CLOSE,
+                                  "Error starting thread': %s",
+                                  g_strerror (errno));
+               gtk_dialog_run (GTK_DIALOG (dialog));
+               gtk_widget_destroy(dialog);
+       }
+       return rc;
+}
+
+void 
+actionStop( GtkWidget *w, gpointer   data ) {
+       if ( !XGalaxy.runing )
+               return;
+
+       if ( !XGalaxy.paused ) {
+               XGalaxy.request_to_exit = 1;
+               /* wait thread */
+               while(XGalaxy.request_to_exit);
+       }        
+
+       fillResult(FALSE);
+       freeGalaxy(&(XGalaxy.galaxy));  
+
+       XGalaxy.locksignal=1;   
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(XGalaxy.buttonPause), FALSE);    
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(XGalaxy.buttonRun), FALSE);      
+       g_object_set(G_OBJECT(XGalaxy.errorField),  "editable", TRUE, NULL);
+       g_object_set(G_OBJECT(XGalaxy.deltaField),  "editable", TRUE, NULL);
+       XGalaxy.locksignal=0;
+
+       XGalaxy.runing = XGalaxy.paused = XGalaxy.request_to_exit = 0;
+       clearDraw();
+       if ( XGalaxy.drawaxis ) drawAxis();
+       drawGalaxy();
+}
+
+void
+actionPause( GtkWidget *w, gpointer   data ) {
+       if (XGalaxy.locksignal)
+               return;
+       XGalaxy.locksignal=1;   
+       if ( !XGalaxy.runing ) {
+               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(XGalaxy.buttonPause), FALSE);    
+               XGalaxy.locksignal=0;   
+               return;
+       }
+
+       if ( XGalaxy.paused ) {
+               if ( startGravity() ) {
+                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(XGalaxy.buttonPause), TRUE);
+                       XGalaxy.locksignal=0;   
+                       return;
+               }
+               XGalaxy.paused=XGalaxy.request_to_exit=0; 
+               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(XGalaxy.buttonPause), FALSE);
+               XGalaxy.locksignal=0;   
+               return;
+       }
+       XGalaxy.request_to_exit = 1;
+       /* wait thread */
+       while(XGalaxy.request_to_exit);
+       
+       XGalaxy.paused = 1;
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(XGalaxy.buttonPause), TRUE);
+       XGalaxy.locksignal=0;   
+       fillResult(FALSE);
+       drawGalaxy();
+}
+
+void
+actionRun( GtkWidget *w, gpointer   data ) {
+       int i;
+
+       if (XGalaxy.locksignal)
+               return;
+
+       XGalaxy.locksignal=1;   
+       if ( XGalaxy.runing ) {
+               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(XGalaxy.buttonRun), TRUE);
+               XGalaxy.locksignal=0;   
+               return;
+       }
+
+       if ( XGalaxy.nentry < 2 ) {
+               GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(XGalaxy.window),
+                                  GTK_DIALOG_DESTROY_WITH_PARENT,
+                                  GTK_MESSAGE_ERROR,
+                                  GTK_BUTTONS_CLOSE,
+                                  "Too small number of entries");
+               gtk_dialog_run (GTK_DIALOG (dialog));
+               gtk_widget_destroy(dialog);
+
+               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(XGalaxy.buttonRun), FALSE);
+               XGalaxy.locksignal=0;   
+               return;
+       }
+
+       for(i=0;i<XGalaxy.nentry;i++) 
+               if ( XGalaxy.entry[i].mass==0.0 || !isfinite(XGalaxy.entry[i].mass) ) {
+                       GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(XGalaxy.window),
+                                  GTK_DIALOG_DESTROY_WITH_PARENT,
+                                  GTK_MESSAGE_ERROR,
+                                  GTK_BUTTONS_CLOSE,
+                                  "Zero mass");
+                       gtk_dialog_run (GTK_DIALOG (dialog));
+                       gtk_widget_destroy(dialog);
+                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(XGalaxy.buttonRun), FALSE);
+                       XGalaxy.locksignal=0;   
+                       return;
+               }
+               
+
+       
+       initGalaxy(&(XGalaxy.galaxy), XGalaxy.nentry);
+       memcpy(XGalaxy.galaxy.stars, XGalaxy.entry, XGalaxy.nentry*sizeof(Star));
+
+       g_object_set(G_OBJECT(XGalaxy.errorField),  "editable", FALSE, NULL);
+       g_object_set(G_OBJECT(XGalaxy.deltaField),  "editable", FALSE, NULL);
+
+       XGalaxy.galaxy.desiredDelta = atof(gtk_entry_get_text(GTK_ENTRY(XGalaxy.deltaField)));
+       XGalaxy.galaxy.errorLimit = atof(gtk_entry_get_text(GTK_ENTRY(XGalaxy.errorField)));
+       initLiveGalaxy( &(XGalaxy.galaxy) );
+       fillResult(TRUE);
+       fitGalaxy();
+       drawGalaxy();
+
+       if ( startGravity() ) { 
+               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(XGalaxy.buttonRun), FALSE);
+               XGalaxy.locksignal=0;   
+               freeGalaxy(&(XGalaxy.galaxy)); 
+               g_object_set(G_OBJECT(XGalaxy.errorField),  "editable", TRUE, NULL);
+               g_object_set(G_OBJECT(XGalaxy.deltaField),  "editable", TRUE, NULL);
+               return; 
+       }
+
+       XGalaxy.runing=1;
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(XGalaxy.buttonRun), TRUE);
+       XGalaxy.paused=XGalaxy.request_to_exit=0;
+       XGalaxy.locksignal=0;   
+       g_timeout_add(XG_TIME_TICK, fillResultByTimeout, NULL);
+}
+
+
diff --git a/quaternions.c b/quaternions.c
new file mode 100644 (file)
index 0000000..1a8752b
--- /dev/null
@@ -0,0 +1,182 @@
+#include <string.h>
+#include <math.h>
+#include "xgalaxy.h"
+
+void
+vector_sub(Vector *res, Vector *a, Vector *b) {
+       res->x =  a->x - b->x;
+       res->y =  a->y - b->y;
+       res->z =  a->z - b->z;
+}
+
+void
+vector_add(Vector *res, Vector *a, Vector *b) {
+       res->x =  a->x + b->x;
+       res->y =  a->y + b->y;
+       res->z =  a->z + b->z;
+}
+
+void
+vector_vmul(Vector *res, Vector *a, Vector *b) {
+        res->x =  a->y * b->z - b->y * a->z;
+        res->y =  a->z * b->x - b->z * a->x;
+        res->z =  a->x * b->y - b->x * a->y;
+}
+
+double
+vector_smul(Vector *a, Vector *b) {
+        double res;
+        res =  a->x * b->x;
+        res+=  a->y * b->y;
+        res+=  a->z * b->z;
+        return res;
+}
+
+double
+vector_length(Vector *v) {
+       return sqrt( v->x*v->x + v->y*v->y + v->z*v->z );
+}
+
+void
+vector_mul(Vector *res, Vector *a, double b) {
+        res->x =  a->x * b;
+        res->y =  a->y * b;
+        res->z =  a->z * b;
+}
+
+void
+quater_vmul(Quaternion *res, Quaternion *a, Quaternion *b) {
+        Vector  aXb, wb, Wa;
+
+        vector_vmul(&aXb, &(a->d), &(b->d));
+        vector_mul(&wb, &(b->d), a->w);
+        vector_mul(&Wa, &(a->d), b->w);
+
+        res->d.x = aXb.x + wb.x + Wa.x;
+        res->d.y = aXb.y + wb.y + Wa.y;
+        res->d.z = aXb.z + wb.z + Wa.z;
+
+                 /*XXX*/
+        res->w = a->w * b->w - vector_smul( &(a->d),  &(b->d) );
+}
+
+double
+quater_length(Quaternion *a) {
+       return sqrt(a->d.x*a->d.x + a->d.y*a->d.y + a->d.z*a->d.z + a->w * a->w);
+}
+
+void
+quater_inverse(Quaternion *res, Quaternion *a) {
+        double norm = a->d.x*a->d.x + a->d.y*a->d.y + a->d.z*a->d.z + a->w * a->w;
+        res->d.x = -a->d.x/norm;
+        res->d.y = -a->d.y/norm;
+        res->d.z = -a->d.z/norm;
+        res->w   =  a->w  /norm;
+}
+
+void
+rotate(Vector *res, Vector *v, Quaternion *q) {
+        Quaternion V, qV, Q, R;
+
+        memcpy( &(V.d), v, sizeof(Vector) );
+        V.w = 0;
+
+        quater_vmul(&qV, q, &V);
+        quater_inverse(&Q, q);
+        quater_vmul(&R, &qV, &Q);
+
+        memcpy( res, &(R.d), sizeof(Vector) );
+}
+
+void
+quater_normalize(Quaternion *q) {
+       double mag;
+
+       mag = sqrt(q->d.x*q->d.x + q->d.y*q->d.y + q->d.z*q->d.z + q->w*q->w);
+       q->d.x /= mag;
+       q->d.y /= mag;
+       q->d.z /= mag;
+       q->w   /= mag;
+}
+
+void
+vector_normal(Vector *v) { 
+       vector_mul( v, v, 1.0/ vector_length(v) ); 
+}
+
+void 
+quater_to_matrix( Quaternion *q, double m[3][3]  ) {
+        double wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
+        double s  = 2.0/(q->d.x*q->d.x + q->d.y*q->d.y + q->d.z*q->d.z + q->w*q->w);  // 4 mul 3 add 1 div
+        x2 = q->d.x * s;    y2 = q->d.y * s;    z2 = q->d.z * s;
+        xx = q->d.x * x2;   xy = q->d.x * y2;   xz = q->d.x * z2;
+        yy = q->d.y * y2;   yz = q->d.y * z2;   zz = q->d.z * z2;
+        wx =   q->w * x2;   wy =   q->w * y2;   wz =   q->w * z2;
+
+        m[0][0] = 1.0 - (yy + zz);
+        m[1][0] = xy - wz;
+        m[2][0] = xz + wy;
+
+        m[0][1] = xy + wz;
+        m[1][1] = 1.0 - (xx + zz);
+        m[2][1] = yz - wx;
+
+        m[0][2] = xz - wy;
+        m[1][2] = yz + wx;
+        m[2][2] = 1.0 - (xx + yy);
+}
+
+void 
+matrix_to_quater(  double m[3][3], Quaternion *q ) {
+        double tr = m[0][0] + m[1][1] + m[2][2]; // trace of martix
+        if (tr > 0.0){     // if trace positive than "w" is biggest component
+               q->d.x = m[1][2] - m[2][1];
+               q->d.y = m[2][0] - m[0][2];
+               q->d.z = m[0][1] - m[1][0];
+               q->w   =  tr+1.0;
+        }else  if( (m[0][0] > m[1][1] ) && ( m[0][0] > m[2][2]) ) {
+               q->d.x = 1.0 + m[0][0] - m[1][1] - m[2][2];
+               q->d.y = m[1][0] + m[0][1];
+               q->d.z = m[2][0] + m[0][2];
+               q->w   = m[1][2] - m[2][1];
+        } else if ( m[1][1] > m[2][2] ){
+               q->d.x = m[1][0] + m[0][1];
+               q->d.y = 1.0 + m[1][1] - m[0][0] - m[2][2];
+               q->d.z = m[2][1] + m[1][2];
+               q->w   = m[2][0] - m[0][2];
+               } else {
+               q->d.x = m[2][0] + m[0][2];
+               q->d.y = m[2][1] + m[1][2];
+               q->d.z = 1.0 + m[2][2] - m[0][0] - m[1][1];
+               q->w   = m[0][1] - m[1][0];
+       }
+       quater_normalize(q);
+}
+
+void
+quater_to_angles( Quaternion *q, Vector *a) {
+       double  m[3][3];
+       double D,C,trx,try;
+
+       quater_to_matrix(q, m);
+
+       D = a->y = asin(m[2][0]);
+       C =  cos( a->y );
+
+       if ( fabs(C) > 1e-8 ) {
+               trx =  m[2][2] / C;
+               try = -m[2][1]  / C;
+               a->x = atan2( try, trx );
+
+               trx =  m[0][0] / C;
+               try = -m[1][0] / C;
+               a->z =  atan2( try, trx );
+       } else {
+               a->x = 0.0;
+               trx      = m[1][1];
+               try      = m[0][1];
+
+               a->z  = atan2( try, trx );
+       }
+}
+
diff --git a/trackball.c b/trackball.c
new file mode 100644 (file)
index 0000000..0fa2a26
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * (c) Copyright 1993, 1994, Silicon Graphics, Inc.
+ * ALL RIGHTS RESERVED
+ * Permission to use, copy, modify, and distribute this software for
+ * any purpose and without fee is hereby granted, provided that the above
+ * copyright notice appear in all copies and that both the copyright notice
+ * and this permission notice appear in supporting documentation, and that
+ * the name of Silicon Graphics, Inc. not be used in advertising
+ * or publicity pertaining to distribution of the software without specific,
+ * written prior permission.
+ *
+ * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
+ * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
+ * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
+ * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
+ * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
+ * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
+ * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
+ * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
+ * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * US Government Users Restricted Rights
+ * Use, duplication, or disclosure by the Government is subject to
+ * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
+ * (c)(1)(ii) of the Rights in Technical Data and Computer Software
+ * clause at DFARS 252.227-7013 and/or in similar or successor
+ * clauses in the FAR or the DOD or NASA FAR Supplement.
+ * Unpublished-- rights reserved under the copyright laws of the
+ * United States.  Contractor/manufacturer is Silicon Graphics,
+ * Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
+ *
+ * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
+ */
+/*
+ * Trackball code:
+ *
+ * Implementation of a virtual trackball.
+ * Implemented by Gavin Bell, lots of ideas from Thant Tessman and
+ *   the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129.
+ *
+ * Vector manip code:
+ *
+ * Original code from:
+ * David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli
+ *
+ * Much mucking with by:
+ * Gavin Bell
+ */
+#include <string.h>
+#include <math.h>
+#include "trackball.h"
+
+/*
+ * This size should really be based on the distance from the center of
+ * rotation to the point on the object underneath the mouse.  That
+ * point would then track the mouse as closely as possible.  This is a
+ * simple example, though, so that is left as an Exercise for the
+ * Programmer.
+ */
+#define TRACKBALLSIZE  (0.8)
+
+/*
+ * Local function prototypes (not defined in trackball.h)
+ */
+static double tb_project_to_sphere(double, double, double);
+
+
+/*
+ * Ok, simulate a track-ball.  Project the points onto the virtual
+ * trackball, then figure out the axis of rotation, which is the cross
+ * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0)
+ * Note:  This is a deformed trackball-- is a trackball in the center,
+ * but is deformed into a hyperbolic sheet of rotation away from the
+ * center.  This particular function was chosen after trying out
+ * several variations.
+ *
+ * It is assumed that the arguments to this routine are in the range
+ * (-1.0 ... 1.0)
+ */
+void
+trackball(Quaternion *q, double p1x, double p1y, double p2x, double p2y)
+{
+    Vector a; /* Axis of rotation */
+    double phi;  /* how much to rotate about axis */
+    Vector p1, p2, d;
+    double t;
+
+    if (p1x == p2x && p1y == p2y) {
+        /* Zero rotation */
+       memset(&(q->d), 0, sizeof(Vector));
+        q->w = 1.0;
+        return;
+    }
+
+    /*
+     * First, figure out z-coordinates for projection of P1 and P2 to
+     * deformed sphere
+     */
+       p1.x = p1x; p1.y = p1y; p1.z = tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y);
+       p2.x = p2x; p2.y = p2y; p2.z = tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y);
+
+    /*
+     *  Now, we want the cross product of P1 and P2
+     */
+    vector_vmul(&a,&p2,&p1);
+
+    /*
+     *  Figure out how much to rotate around that axis.
+     */
+    vector_sub(&d,&p1,&p2);
+    t = vector_length(&d) / (2.0*TRACKBALLSIZE);
+
+    /*
+     * Avoid problems with out-of-control values...
+     */
+    if (t > 1.0) t = 1.0;
+    if (t < -1.0) t = -1.0;
+    phi = 2.0 * asin(t);
+
+    axis_to_quat(&a,phi,q);
+}
+
+/*
+ *  Given an axis and angle, compute quaternion.
+ */
+void
+axis_to_quat(Vector *a, double phi, Quaternion *q) {
+    vector_normal(a);
+    memcpy( &(q->d), a, sizeof(Vector) );
+    vector_mul( &(q->d), &(q->d), -sin(phi/2.0) );
+    q->w = cos(phi/2.0);
+}
+
+/*
+ * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet
+ * if we are away from the center of the sphere.
+ */
+static double
+tb_project_to_sphere(double r, double x, double y)
+{
+    double d, t, z;
+
+    d = sqrt(x*x + y*y);
+    if (d < r * 0.70710678118654752440) {    /* Inside sphere */
+        z = sqrt(r*r - d*d);
+    } else {           /* On hyperbola */
+        t = r / 1.41421356237309504880;
+        z = t*t / d;
+    }
+    return z;
+}
+
+
diff --git a/trackball.h b/trackball.h
new file mode 100644 (file)
index 0000000..f946f5f
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef __TRACKBALL_H__
+#define __TRACKBALL_H__
+/*
+ * (c) Copyright 1993, 1994, Silicon Graphics, Inc.
+ * ALL RIGHTS RESERVED
+ * Permission to use, copy, modify, and distribute this software for
+ * any purpose and without fee is hereby granted, provided that the above
+ * copyright notice appear in all copies and that both the copyright notice
+ * and this permission notice appear in supporting documentation, and that
+ * the name of Silicon Graphics, Inc. not be used in advertising
+ * or publicity pertaining to distribution of the software without specific,
+ * written prior permission.
+ *
+ * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
+ * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
+ * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
+ * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
+ * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
+ * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
+ * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
+ * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
+ * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * US Government Users Restricted Rights
+ * Use, duplication, or disclosure by the Government is subject to
+ * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
+ * (c)(1)(ii) of the Rights in Technical Data and Computer Software
+ * clause at DFARS 252.227-7013 and/or in similar or successor
+ * clauses in the FAR or the DOD or NASA FAR Supplement.
+ * Unpublished-- rights reserved under the copyright laws of the
+ * United States.  Contractor/manufacturer is Silicon Graphics,
+ * Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
+ *
+ * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
+ */
+/*
+ * trackball.h
+ * A virtual trackball implementation
+ * Written by Gavin Bell for Silicon Graphics, November 1988.
+ */
+
+#include "xgalaxy.h"
+
+/*
+ * Pass the x and y coordinates of the last and current positions of
+ * the mouse, scaled so they are from (-1.0 ... 1.0).
+ *
+ * The resulting rotation is returned as a quaternion rotation in the
+ * first paramater.
+ */
+void
+trackball(Quaternion *a, double p1x, double p1y, double p2x, double p2y);
+
+/*
+ * This function computes a quaternion based on an axis (defined by
+ * the given vector) and an angle about which to rotate.  The angle is
+ * expressed in radians.  The result is put into the third argument.
+ */
+void
+axis_to_quat(Vector *a, double phi, Quaternion *q);
+
+#endif
diff --git a/xgalaxy.c b/xgalaxy.c
new file mode 100644 (file)
index 0000000..515e485
--- /dev/null
+++ b/xgalaxy.c
@@ -0,0 +1,714 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <locale.h>
+#include <gtk/gtk.h>
+
+#include "tmalloc.h"
+#include "xgalaxy.h"
+#include "gtkcellrendererbutton.h"
+
+XGlalaxyStruct XGalaxy;
+
+gint 
+delete( GtkWidget *widget, GtkWidget *event, gpointer   data ) {
+        if ( XGalaxy.runing && !XGalaxy.paused ) {
+                XGalaxy.request_to_exit = 1;
+                /* wait thread */
+                while(XGalaxy.request_to_exit);
+        }
+
+       gtk_main_quit();
+       return(FALSE);
+}
+
+gboolean    
+check_number(GtkWidget *widget, GdkEventFocus *event, gpointer user_data) {
+       char buf[128];
+       if ( atof(gtk_entry_get_text(GTK_ENTRY(widget))) <= 0 ) 
+               sprintf(buf, "%G", *(double*)user_data );
+       else  
+               sprintf(buf, "%G", atof(gtk_entry_get_text(GTK_ENTRY(widget))) );
+       gtk_entry_set_text(GTK_ENTRY(widget), buf);     
+       return FALSE;
+}
+
+typedef struct {
+       GtkListStore    *store;
+       int             colnumber;
+} ColInfo;
+
+void 
+set_edited_data(GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, gpointer user_data) {
+       GtkTreeIter   iter;
+       GtkTreePath *path;
+       ColInfo     *info = (ColInfo*)user_data;
+       char buf[128];
+       double val = atof(arg2);
+
+       if ( XGalaxy.runing ) return;
+
+       path = gtk_tree_path_new_from_string (arg1);
+       gtk_tree_model_get_iter(GTK_TREE_MODEL (info->store), &iter, path);
+       gtk_tree_path_free (path);
+
+       if ( info->colnumber==1 && val < 0 )
+               val=-val;
+       sprintf(buf,"%G", val);
+       gtk_list_store_set( info->store, &iter, info->colnumber, buf, -1);
+       editEntry(atoi(arg1), info->colnumber, val);
+       cntEntry();
+}
+
+static int row_to_delete=-1;
+
+void
+cell_toggled(GtkCellRendererButton *cell_renderer, gchar *path, gpointer user_data) {
+       GtkTreePath *treepath;
+       GtkTreeIter   iter;
+       GtkListStore *store = GTK_LIST_STORE(user_data);
+
+       if ( XGalaxy.runing || row_to_delete>=0 ) return;
+
+       row_to_delete = atoi(path);
+       treepath = gtk_tree_path_new_from_string (path);
+       gtk_tree_model_get_iter(GTK_TREE_MODEL (store), &iter, treepath);
+       gtk_tree_path_free (treepath);
+
+       gtk_list_store_set (GTK_LIST_STORE (store), &iter, 0, TRUE, -1);
+}
+
+gboolean    
+button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data) {
+       char path[16];
+       GtkTreePath *treepath;
+       GtkTreeIter   iter;
+       GtkListStore *store = GTK_LIST_STORE(user_data);
+
+       if (row_to_delete<0)
+               return FALSE;
+
+       sprintf(path,"%d", row_to_delete);
+       treepath = gtk_tree_path_new_from_string (path);
+       gtk_tree_model_get_iter(GTK_TREE_MODEL (store), &iter, treepath);
+       gtk_tree_path_free (treepath);
+
+       gtk_list_store_remove(store, &iter);
+       deleteEntry(row_to_delete);
+       cntEntry();
+
+       row_to_delete=-1;
+       
+       return FALSE;
+}
+
+void
+append_row(GtkTreeViewColumn *treeviewcolumn, gpointer user_data) {
+       GtkTreeIter   iter;
+       GtkListStore *store = GTK_LIST_STORE(user_data);
+       int i;
+
+       if ( XGalaxy.runing ) return;
+       gtk_list_store_append( store, &iter );
+       for(i=1;i<8;i++) 
+               gtk_list_store_set( store, &iter, i, "0", -1);
+       addEntry(NULL);
+}
+
+static int size_allocation_treelist_oldwidth=0;
+
+static gint 
+size_allocation_treelist(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data) {
+       /* workaround to prevent size-allocation storm */
+       if ( size_allocation_treelist_oldwidth != allocation->width ) {
+               int i;
+               int colwidth = (allocation->width-18)/7;
+
+               for(i=1;i<8;i++)
+                       gtk_tree_view_column_set_fixed_width( 
+                               gtk_tree_view_get_column(GTK_TREE_VIEW(widget), i),
+                               colwidth
+                       );
+
+               size_allocation_treelist_oldwidth=allocation->width;
+       }
+       return TRUE;
+}
+
+
+static double default_delta=3600;
+static double default_error=1e-8;
+
+GtkWidget*
+inputPage() {
+       GtkWidget *table, *frame;
+       GtkWidget *scrolled;
+       GtkWidget *label, *subtable, *aligment;
+       gchar *titles[8] = { "+", "Mass", "X", "Y", "Z", "Vx", "Vy", "Vz" };
+
+       table = gtk_table_new(3,100,FALSE);
+
+       frame=gtk_frame_new("Options");
+       gtk_container_set_border_width(GTK_CONTAINER (frame), 5);
+       gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 1, 0, 1);  
+       gtk_widget_show (frame);
+
+       subtable = gtk_table_new(3,3,FALSE);
+
+       label = gtk_label_new("Delta T (secs):");
+       gtk_label_set_line_wrap(GTK_LABEL(label), FALSE);
+       gtk_widget_show(label);
+       aligment=gtk_alignment_new(1,1,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), label);
+       gtk_container_set_border_width(GTK_CONTAINER (aligment), 3);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 0,1,0,1);
+
+       XGalaxy.deltaField = gtk_entry_new_with_max_length(15);
+       gtk_entry_set_text(GTK_ENTRY(XGalaxy.deltaField), "3600");
+       gtk_signal_connect (GTK_OBJECT(XGalaxy.deltaField),"focus-out-event", (GtkSignalFunc) check_number, (gpointer)&default_delta);
+       gtk_widget_show(XGalaxy.deltaField);
+       aligment=gtk_alignment_new(0,1,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), XGalaxy.deltaField);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 1,2,0,1);
+       
+       label = gtk_label_new("Quality:");
+       gtk_label_set_line_wrap(GTK_LABEL(label), FALSE);
+       gtk_widget_show(label);
+       aligment=gtk_alignment_new(1,0,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), label);
+       gtk_container_set_border_width(GTK_CONTAINER (aligment), 3);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 0,1,1,2);
+
+       XGalaxy.errorField = gtk_entry_new_with_max_length(15);
+       gtk_entry_set_text(GTK_ENTRY(XGalaxy.errorField), "1e-8");
+       gtk_signal_connect (GTK_OBJECT(XGalaxy.errorField),"focus-out-event", (GtkSignalFunc) check_number, (gpointer)&default_error);
+       gtk_widget_show(XGalaxy.errorField);
+       aligment=gtk_alignment_new(0,0,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), XGalaxy.errorField);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 1,2,1,2);
+
+       gtk_widget_show(subtable);
+       gtk_container_add(GTK_CONTAINER(frame), subtable);
+
+       frame=gtk_frame_new("Summary information");
+       gtk_container_set_border_width(GTK_CONTAINER (frame), 5);
+       gtk_table_attach_defaults(GTK_TABLE(table), frame, 1, 2, 0, 1);  
+       gtk_widget_show (frame);
+
+       subtable = gtk_table_new(4,2,FALSE);
+
+       label = gtk_label_new("Energy:");
+       gtk_label_set_line_wrap(GTK_LABEL(label), FALSE);
+       gtk_widget_show(label);
+       aligment=gtk_alignment_new(1,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), label);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 0,1,0,1);
+
+       XGalaxy.dataEnergyField = gtk_label_new("0");
+       gtk_label_set_line_wrap(GTK_LABEL(XGalaxy.dataEnergyField), FALSE);
+       gtk_widget_show(XGalaxy.dataEnergyField);
+       aligment=gtk_alignment_new(0,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), XGalaxy.dataEnergyField);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 1,2,0,1);
+
+       label = gtk_label_new("Impulse:");
+       gtk_label_set_line_wrap(GTK_LABEL(label), FALSE);
+       gtk_widget_show(label);
+       aligment=gtk_alignment_new(1,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), label);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 0,1,1,2);
+
+       XGalaxy.dataImpulseField = gtk_label_new("0");
+       gtk_label_set_line_wrap(GTK_LABEL(XGalaxy.dataImpulseField), FALSE);
+       gtk_widget_show(XGalaxy.dataImpulseField);
+       aligment=gtk_alignment_new(0,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), XGalaxy.dataImpulseField);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 1,2,1,2);
+
+       label = gtk_label_new("Moment:");
+       gtk_label_set_line_wrap(GTK_LABEL(label), FALSE);
+       gtk_widget_show(label);
+       aligment=gtk_alignment_new(1,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), label);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 0,1,2,3);
+
+       XGalaxy.dataMomentField = gtk_label_new("0");
+       gtk_label_set_line_wrap(GTK_LABEL(XGalaxy.dataMomentField), FALSE);
+       gtk_widget_show(XGalaxy.dataMomentField);
+       aligment=gtk_alignment_new(0,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), XGalaxy.dataMomentField);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 1,2,2,3);
+
+       gtk_widget_show(subtable);
+       gtk_container_add(GTK_CONTAINER(frame), subtable);
+
+       scrolled = gtk_scrolled_window_new(NULL,NULL);
+       gtk_container_set_border_width(GTK_CONTAINER (scrolled), 5);
+       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+       gtk_table_attach_defaults(GTK_TABLE(table), scrolled, 0, 2, 1, 99);  
+       gtk_widget_show (scrolled);
+
+       do {
+               int i;
+               GtkListStore *store = gtk_list_store_new (8, 
+                               G_TYPE_BOOLEAN, /* + */
+                               G_TYPE_STRING,  /* mass */
+                               G_TYPE_STRING,  /* X */
+                               G_TYPE_STRING,  /* Y */
+                               G_TYPE_STRING,  /* Z */
+                               G_TYPE_STRING,  /* Vx */
+                               G_TYPE_STRING,  /* Vy */
+                               G_TYPE_STRING  /* Vz */
+               );
+               XGalaxy.dataField = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+
+               for(i=0;i<8;i++) {
+                       GtkCellRenderer *renderer;
+                       GtkTreeViewColumn *column;
+
+                       if ( i>0 ) {
+                               ColInfo   *info = tmalloc(sizeof(ColInfo));
+
+                               info->store=store;
+                               info->colnumber=i;
+                               renderer = gtk_cell_renderer_text_new ();
+
+                               g_object_set (G_OBJECT (renderer), "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL);
+                               g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
+                               g_object_set (G_OBJECT (renderer), "editable-set", TRUE, NULL);
+                               g_object_set (G_OBJECT (renderer), "single-paragraph-mode", TRUE, NULL);
+                               gtk_signal_connect (GTK_OBJECT(renderer),"edited", (GtkSignalFunc) set_edited_data, info);
+                               column = gtk_tree_view_column_new_with_attributes(
+                                       titles[i],
+                                       renderer,
+                                       "text",
+                                       i,
+                                       NULL
+                               );
+                               gtk_tree_view_column_set_clickable(column, FALSE);
+                               gtk_tree_view_column_set_resizable(column, TRUE);
+                               gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+                               gtk_tree_view_column_set_fixed_width(column, (640-56)/7);
+                       } else {
+                               renderer = gtk_cell_renderer_button_new();
+                               g_object_set (G_OBJECT (renderer), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
+                               g_object_set (G_OBJECT (renderer), "activatable", TRUE, NULL);
+                               gtk_signal_connect (GTK_OBJECT(renderer), "toggled", (GtkSignalFunc) cell_toggled, store);
+                               column = gtk_tree_view_column_new_with_attributes(
+                                       titles[i],
+                                       renderer, "active", 0,
+                                       NULL
+                               );
+                               gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+                               gtk_tree_view_column_set_fixed_width(column, 20);
+                               gtk_tree_view_column_set_clickable(column, TRUE);
+                               gtk_tree_view_column_set_resizable(column, FALSE);
+                               gtk_signal_connect (GTK_OBJECT(column), "clicked", (GtkSignalFunc) append_row, store);
+                       }
+                       gtk_tree_view_append_column (GTK_TREE_VIEW (XGalaxy.dataField), column);
+               }
+               gtk_signal_connect (GTK_OBJECT(XGalaxy.dataField),"button-release-event", (GtkSignalFunc) button_release, store);
+       } while(0);
+       gtk_signal_connect (GTK_OBJECT(XGalaxy.dataField),"size-allocate", (GtkSignalFunc) size_allocation_treelist, NULL);
+       gtk_widget_show(XGalaxy.dataField);
+       gtk_container_add(GTK_CONTAINER(scrolled), XGalaxy.dataField);
+
+       gtk_widget_show(table);
+
+       return table;
+}
+
+static gint 
+size_allocation_clist(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data) {
+       int i;
+       int colwidth = (allocation->width-52)/8;
+
+       for(i=0;i<8;i++) 
+               gtk_clist_set_column_width (GTK_CLIST(widget), i, colwidth);
+
+       return FALSE;
+}
+
+GtkWidget*
+resPage() {
+       GtkWidget *scrolled, *frame, *label, *aligment, *subtable, *vbox;
+       gchar *titles[11] = { "Mass", "X", "Y", "Z", "Vx", "Vy", "Vz", "|V|", "A", "E", "I" };
+
+       vbox = gtk_vbox_new (FALSE, 1);
+       gtk_container_set_border_width (GTK_CONTAINER (vbox), 0);
+       gtk_widget_show (vbox);
+
+       frame=gtk_frame_new("Summary information");
+       gtk_container_set_border_width(GTK_CONTAINER (frame), 5);
+       gtk_widget_show (frame);
+
+       subtable = gtk_table_new(1,10,FALSE);
+
+       label = gtk_label_new("Energy:");
+       gtk_label_set_line_wrap(GTK_LABEL(label), FALSE);
+       gtk_widget_show(label);
+       aligment=gtk_alignment_new(1,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), label);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 0,1,0,1);
+
+       XGalaxy.resEnergyField = gtk_label_new("0");
+       gtk_label_set_line_wrap(GTK_LABEL(XGalaxy.resEnergyField), FALSE);
+       gtk_widget_show(XGalaxy.resEnergyField);
+       aligment=gtk_alignment_new(0.05,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), XGalaxy.resEnergyField);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 1,2,0,1);
+
+       label = gtk_label_new("Impulse:");
+       gtk_label_set_line_wrap(GTK_LABEL(label), FALSE);
+       gtk_widget_show(label);
+       aligment=gtk_alignment_new(1,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), label);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 2,3,0,1);
+
+       XGalaxy.resImpulseField = gtk_label_new("0");
+       gtk_label_set_line_wrap(GTK_LABEL(XGalaxy.resImpulseField), FALSE);
+       gtk_widget_show(XGalaxy.resImpulseField);
+       aligment=gtk_alignment_new(0.05,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), XGalaxy.resImpulseField);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 3,4,0,1);
+
+       label = gtk_label_new("Moment:");
+       gtk_label_set_line_wrap(GTK_LABEL(label), FALSE);
+       gtk_widget_show(label);
+       aligment=gtk_alignment_new(1,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), label);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 4,5,0,1);
+
+       XGalaxy.resMomentField = gtk_label_new("0");
+       gtk_label_set_line_wrap(GTK_LABEL(XGalaxy.resMomentField), FALSE);
+       gtk_widget_show(XGalaxy.resMomentField);
+       aligment=gtk_alignment_new(0.05,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), XGalaxy.resMomentField);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 5,6,0,1);
+
+       label = gtk_label_new("Delta:");
+       gtk_label_set_line_wrap(GTK_LABEL(label), FALSE);
+       gtk_widget_show(label);
+       aligment=gtk_alignment_new(1,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), label);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 6,7,0,1);
+
+       XGalaxy.resDeltaField = gtk_label_new("0");
+       gtk_label_set_line_wrap(GTK_LABEL(XGalaxy.resDeltaField), FALSE);
+       gtk_widget_show(XGalaxy.resDeltaField);
+       aligment=gtk_alignment_new(0.05,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), XGalaxy.resDeltaField);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 7,8,0,1);
+
+       label = gtk_label_new("Elapsed:");
+       gtk_label_set_line_wrap(GTK_LABEL(label), FALSE);
+       gtk_widget_show(label);
+       aligment=gtk_alignment_new(1,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), label);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 8,9,0,1);
+
+       XGalaxy.resTimeField = gtk_label_new("0");
+       gtk_label_set_line_wrap(GTK_LABEL(XGalaxy.resTimeField), FALSE);
+       gtk_widget_show(XGalaxy.resTimeField);
+       aligment=gtk_alignment_new(0.05,0.5,0,0);
+       gtk_container_add(GTK_CONTAINER(aligment), XGalaxy.resTimeField);
+       gtk_widget_show(aligment);
+       gtk_table_attach_defaults(GTK_TABLE(subtable), aligment, 9,10,0,1);
+
+       gtk_widget_show(subtable);
+
+       gtk_container_add(GTK_CONTAINER(frame), subtable);
+       gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0);
+
+       scrolled = gtk_scrolled_window_new(NULL,NULL);
+       gtk_container_set_border_width(GTK_CONTAINER (scrolled), 5);
+       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+       gtk_widget_show (scrolled);
+
+       XGalaxy.resField = gtk_clist_new_with_titles( 8, titles);
+       gtk_signal_connect (GTK_OBJECT(XGalaxy.resField),"size-allocate", (GtkSignalFunc) size_allocation_clist, NULL);
+       gtk_signal_connect (GTK_OBJECT(XGalaxy.resField),"hide", (GtkSignalFunc) show_resCList, NULL);
+       gtk_clist_set_selection_mode(GTK_CLIST(XGalaxy.resField),GTK_SELECTION_SINGLE);
+       gtk_container_add(GTK_CONTAINER(scrolled), XGalaxy.resField);
+
+       gtk_widget_show(XGalaxy.resField);
+       
+       gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
+       
+       return vbox;
+}
+
+static void 
+scale_clicked (GtkButton *button, gpointer user_data) {
+       if ( XGalaxy.trace )
+               return;
+       if ( (int)user_data )
+               XGalaxy.Scale *= 1.5;
+       else
+               XGalaxy.Scale /= 1.5;
+
+        drawGalaxy();
+}
+
+static void 
+trace_clicked (GtkToggleButton *button, gpointer user_data) {
+       XGalaxy.trace = gtk_toggle_button_get_active(button);
+        drawGalaxy();
+}
+
+static void 
+axis_clicked (GtkToggleButton *button, gpointer user_data) {
+       if ( XGalaxy.locksignal )
+               return;
+       if ( XGalaxy.trace ) {
+               XGalaxy.locksignal=1;
+               gtk_toggle_button_set_active( button, !gtk_toggle_button_get_active(button) );
+               XGalaxy.locksignal=0;
+               return;
+       }
+       XGalaxy.drawaxis = gtk_toggle_button_get_active(button); 
+        drawGalaxy();
+}
+
+static gboolean
+mouse_button_press(GtkWidget      *widget, GdkEventButton *event) {
+       XGalaxy.beginx = event->x;
+       XGalaxy.beginy = event->y;
+
+       memset( &(XGalaxy.motion.d), 0, sizeof(Vector));
+       XGalaxy.motion.w=1.0;
+
+       return FALSE;
+}
+
+GtkWidget*
+viewPage() {
+       GtkWidget *vbox, *hbox, *separator, *button, *table;
+       GtkTooltips *button_bar_tips;
+       button_bar_tips = gtk_tooltips_new ();
+       
+       vbox = gtk_vbox_new (FALSE, 1);
+       gtk_container_set_border_width (GTK_CONTAINER (vbox), 0);
+       gtk_widget_show (vbox);
+
+       hbox=gtk_hbutton_box_new();
+       gtk_widget_show (hbox);
+
+       XGalaxy.buttonRun = gtk_toggle_button_new_with_label("Go!");
+       gtk_signal_connect (GTK_OBJECT(XGalaxy.buttonRun),"toggled", (GtkSignalFunc) actionRun, NULL);
+       gtk_container_set_border_width(GTK_CONTAINER (XGalaxy.buttonRun), 5);
+       gtk_widget_show(XGalaxy.buttonRun);
+       gtk_box_pack_start (GTK_BOX (hbox), XGalaxy.buttonRun, FALSE, TRUE, 0);
+       gtk_tooltips_set_tip (GTK_TOOLTIPS (button_bar_tips), XGalaxy.buttonRun, "Start modeling", "");
+
+       XGalaxy.buttonPause = gtk_toggle_button_new_with_label("Pause");
+       gtk_signal_connect (GTK_OBJECT(XGalaxy.buttonPause),"toggled", (GtkSignalFunc) actionPause, NULL);
+       gtk_container_set_border_width(GTK_CONTAINER (XGalaxy.buttonPause), 5);
+       gtk_widget_show(XGalaxy.buttonPause);
+       gtk_box_pack_start (GTK_BOX (hbox), XGalaxy.buttonPause, FALSE, TRUE, 0);
+       gtk_tooltips_set_tip (GTK_TOOLTIPS (button_bar_tips), XGalaxy.buttonPause, "Pause modeling", "");
+
+       button = gtk_button_new_with_label("Stop");
+       gtk_signal_connect (GTK_OBJECT(button),"clicked", (GtkSignalFunc) actionStop, NULL);
+       gtk_container_set_border_width(GTK_CONTAINER (button), 5);
+       gtk_widget_show(button);
+       gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
+       gtk_tooltips_set_tip (GTK_TOOLTIPS (button_bar_tips), button, "Stop modeling", "");
+
+       //button = gtk_button_new_from_stock(GTK_STOCK_ZOOM_IN);
+       button = gtk_button_new_with_label("Scale +");
+       gtk_container_set_border_width(GTK_CONTAINER (button), 5);
+       gtk_widget_show(button);
+       gtk_signal_connect (GTK_OBJECT(button),"clicked", (GtkSignalFunc) scale_clicked, (gpointer)1);
+       gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
+       gtk_tooltips_set_tip (GTK_TOOLTIPS (button_bar_tips), button, "Increase scale", "");
+
+       //button = gtk_button_new_from_stock(GTK_STOCK_ZOOM_OUT);
+       button = gtk_button_new_with_label("Scale -");
+       gtk_container_set_border_width(GTK_CONTAINER (button), 5);
+       gtk_widget_show(button);
+       gtk_signal_connect (GTK_OBJECT(button),"clicked", (GtkSignalFunc) scale_clicked, (gpointer)0);
+       gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
+       gtk_tooltips_set_tip (GTK_TOOLTIPS (button_bar_tips), button, "Decrease scale", "");
+
+       button = gtk_toggle_button_new_with_label("Trace");
+       gtk_container_set_border_width(GTK_CONTAINER (button), 5);
+       gtk_widget_show(button);
+       gtk_signal_connect (GTK_OBJECT(button),"toggled", (GtkSignalFunc) trace_clicked, (gpointer)0);
+       gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
+       gtk_tooltips_set_tip (GTK_TOOLTIPS (button_bar_tips), button, "Turn on/off trace of stars", "");
+
+       button = gtk_toggle_button_new_with_label("Axis");
+       gtk_container_set_border_width(GTK_CONTAINER (button), 5);
+       gtk_widget_show(button);
+       gtk_signal_connect (GTK_OBJECT(button),"toggled", (GtkSignalFunc) axis_clicked, (gpointer)0);
+       gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
+       gtk_tooltips_set_tip (GTK_TOOLTIPS (button_bar_tips), button, "Axis show on/off", "");
+
+       gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+       
+       separator = gtk_hseparator_new();
+       gtk_widget_show(separator);
+       
+       gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, TRUE, 0);
+
+
+       table = gtk_table_new(1000,1000,FALSE);
+
+       XGalaxy.YSlider = gtk_hscale_new_with_range(-180, 180, 1);
+       gtk_scale_set_draw_value(GTK_SCALE(XGalaxy.YSlider), FALSE);
+       gtk_range_set_value(GTK_RANGE(XGalaxy.YSlider), 0);
+       gtk_signal_connect (GTK_OBJECT(XGalaxy.YSlider),"value-changed", (GtkSignalFunc) angle_changed, (gpointer)'Y');
+       gtk_widget_show (XGalaxy.YSlider);
+       gtk_table_attach_defaults(GTK_TABLE(table), XGalaxy.YSlider, 1,999, 999,1000);
+       gtk_tooltips_set_tip (GTK_TOOLTIPS (button_bar_tips), XGalaxy.YSlider, "Turn around Y-axis", "");
+
+       XGalaxy.XSlider = gtk_vscale_new_with_range(-180, 180, 1);
+       gtk_scale_set_draw_value(GTK_SCALE(XGalaxy.XSlider), FALSE);
+       gtk_range_set_value(GTK_RANGE(XGalaxy.XSlider), 0);
+       gtk_signal_connect (GTK_OBJECT(XGalaxy.XSlider),"value-changed", (GtkSignalFunc) angle_changed, (gpointer)'X');
+       gtk_widget_show (XGalaxy.XSlider);
+       gtk_table_attach_defaults(GTK_TABLE(table), XGalaxy.XSlider, 0,1, 0,999);
+       gtk_tooltips_set_tip (GTK_TOOLTIPS (button_bar_tips), XGalaxy.XSlider, "Turn around X-axis", "");
+
+       XGalaxy.ZSlider = gtk_vscale_new_with_range(-180, 180, 1);
+       gtk_scale_set_draw_value(GTK_SCALE(XGalaxy.ZSlider), FALSE);
+       gtk_range_set_value(GTK_RANGE(XGalaxy.ZSlider), 0);
+       gtk_signal_connect (GTK_OBJECT(XGalaxy.ZSlider),"value-changed", (GtkSignalFunc) angle_changed, (gpointer)'Z');
+       gtk_widget_show (XGalaxy.ZSlider);
+       gtk_table_attach_defaults(GTK_TABLE(table), XGalaxy.ZSlider, 999, 1000, 0, 999 );
+       gtk_tooltips_set_tip (GTK_TOOLTIPS (button_bar_tips), XGalaxy.ZSlider, "Turn around Z-axis", "");
+
+       XGalaxy.drawing_area = gtk_drawing_area_new ();
+       gtk_widget_set_events(XGalaxy.drawing_area,
+                        GDK_EXPOSURE_MASK|
+                        GDK_BUTTON_PRESS_MASK|
+                        GDK_BUTTON_RELEASE_MASK|
+                        GDK_POINTER_MOTION_MASK|
+                        GDK_POINTER_MOTION_HINT_MASK);
+       gtk_drawing_area_size (GTK_DRAWING_AREA (XGalaxy.drawing_area), 100,100);
+       gtk_widget_show (XGalaxy.drawing_area);
+       gtk_signal_connect (GTK_OBJECT (XGalaxy.drawing_area), "expose_event", (GtkSignalFunc) expose_event, NULL);
+       gtk_signal_connect (GTK_OBJECT(XGalaxy.drawing_area),"configure_event", (GtkSignalFunc) configure_event, NULL);
+       gtk_signal_connect (GTK_OBJECT(XGalaxy.drawing_area),"button_press_event", (GtkSignalFunc) mouse_button_press, NULL);
+       gtk_signal_connect (GTK_OBJECT(XGalaxy.drawing_area),"motion_notify_event", (GtkSignalFunc) mouse_motion_notify, NULL);
+       gtk_table_attach_defaults(GTK_TABLE(table), XGalaxy.drawing_area, 1,999, 0,999);
+
+       gtk_widget_show(table); 
+
+       gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
+
+       return vbox;
+}
+
+static GtkItemFactoryEntry menu_items[] = {
+  { "/_File",         NULL,         NULL, 0, "<Branch>" },
+  { "/File/_Open",    "<control>O", openFile, 0, NULL },
+  { "/File/_Save",    "<control>S", saveFile, 0, NULL },
+  { "/File/Save _As", NULL,         saveAsFile, 0, NULL },
+  { "/File/sep1",     NULL,         NULL, 0, "<Separator>" },
+  { "/File/Quit",     "<control>Q", gtk_main_quit, 0, NULL },
+  { "/_Options",      NULL,         NULL, 0, "<Branch>" },
+  { "/Options/Clear", NULL,         clearData, 0, NULL },
+  { "/Options/Fill",  NULL,         NULL, 0, NULL },
+  { "/_Help",         NULL,         NULL, 0, "<LastBranch>" },
+  { "/_Help/About",   NULL,         showAbout, 0, NULL },
+};
+
+static  GtkWidget*
+get_main_menu(GtkWidget *window) {
+       GtkItemFactory *item_factory;
+       GtkAccelGroup *accel_group;
+       GtkWidget *menubar;
+       gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
+       
+       accel_group = gtk_accel_group_new ();
+
+       item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", accel_group);
+
+       gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
+
+       gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
+       
+       menubar = gtk_item_factory_get_widget (item_factory, "<main>");
+
+       return menubar;
+}
+
+void
+page_switched(GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer user_data) {
+       XGalaxy.page_active = page_num; 
+}
+
+int
+main( int argc, char *argv[] ) {
+       GtkWidget *window, *notebook, *menubar, *main_vbox;
+       int rc;
+
+       memset(&XGalaxy, 0, sizeof(XGalaxy));
+       XGalaxy.angle.w=1;
+       XGalaxy.motion.w=1.0;
+       XGalaxy.Scale=1.0;
+
+       gtk_init (&argc, &argv);
+
+       if ( (rc=pthread_mutex_init(&(XGalaxy.mutex), NULL)) != 0 ) {
+               g_print("pthread_mutex_init returns %d: %s\n", rc, g_strerror(errno));
+               exit(1);
+       }
+
+       XGalaxy.window = window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+       galaxy_set_title();
+       gtk_window_set_default_size (GTK_WINDOW(window), 640, 480);
+       gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (delete), NULL);
+
+       main_vbox = gtk_vbox_new (FALSE, 1);
+       gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 2);
+       gtk_container_add(GTK_CONTAINER(window), main_vbox);
+       gtk_widget_show (main_vbox);
+
+       menubar = get_main_menu(window);
+       gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, TRUE, 0);
+       gtk_widget_show (menubar);
+
+       notebook = gtk_notebook_new ();
+       gtk_notebook_set_tab_pos(GTK_NOTEBOOK (notebook), GTK_POS_TOP);
+       gtk_widget_show(notebook);
+
+       gtk_notebook_append_page( GTK_NOTEBOOK (notebook), inputPage(), gtk_label_new("Input") );
+       gtk_notebook_append_page( GTK_NOTEBOOK (notebook), viewPage(), gtk_label_new("View") );
+       gtk_notebook_append_page( GTK_NOTEBOOK (notebook), resPage(), gtk_label_new("Data View") );
+       gtk_signal_connect (GTK_OBJECT(notebook),"switch-page", (GtkSignalFunc) page_switched, NULL);
+       XGalaxy.notebook = notebook;
+
+       gtk_box_pack_start (GTK_BOX (main_vbox), notebook, TRUE, TRUE, 0);      
+
+
+       setlocale(LC_NUMERIC, "C");
+       gtk_widget_show(window);
+
+       gtk_main ();
+
+       return 0;
+}
+
diff --git a/xgalaxy.h b/xgalaxy.h
new file mode 100644 (file)
index 0000000..ec0937b
--- /dev/null
+++ b/xgalaxy.h
@@ -0,0 +1,140 @@
+#ifndef __XGALAXY_H__
+#define __XGALAXY_H__
+
+#include <gtk/gtk.h>
+#include <pthread.h>
+#include "galaxy.h"
+
+typedef struct {
+        double  mass;
+        Vector  c; /* coordinate */
+        Vector  v; /* velocity */
+} StarEntry;
+
+typedef struct {
+       Vector  d;
+       double  w;
+} Quaternion;
+
+typedef struct {
+       GtkWidget       *window;
+
+       GtkWidget       *deltaField;
+       GtkWidget       *errorField;
+       GtkWidget       *dataField;
+
+       GtkWidget       *dataEnergyField;
+       GtkWidget       *dataImpulseField;
+       GtkWidget       *dataMomentField;
+
+       GtkWidget       *resEnergyField;
+       GtkWidget       *resImpulseField;
+       GtkWidget       *resMomentField;
+       GtkWidget       *resDeltaField;
+       GtkWidget       *resTimeField;
+       GtkWidget       *resField;
+
+       GtkWidget       *notebook;
+
+       GtkWidget       *XSlider;
+       GtkWidget       *YSlider;
+       GtkWidget       *ZSlider;
+       double          Xangle;
+       double          Yangle;
+       double          Zangle;
+       Quaternion              angle;
+       double          Scale;
+
+       GtkWidget       *drawing_area;
+       GdkPixmap       *pixmap;
+
+       GtkWidget       *buttonRun;
+       GtkWidget       *buttonPause;
+
+       Galaxy          galaxy;
+       
+       char            *filename;
+
+       u_int32_t       nentry; 
+       u_int32_t       lenentry;       
+       Star            *entry;
+       Star            *tmpentry;
+
+
+       /* state */
+       volatile u_int32_t
+               runing:1,
+               paused:1,
+               request_to_exit:1,
+               page_active:4,
+               trace:1,
+               locksignal:1,
+               drawaxis:1,
+               unused:22;
+
+       double  scaleX; 
+       double  scaleY;
+
+       pthread_mutex_t  mutex;
+       pthread_t        thread;        
+
+       double  runTime;
+
+       double beginx;
+       double beginy;
+       Quaternion motion;
+} XGlalaxyStruct;
+
+#define XG_TIME_TICK   (20)
+
+extern XGlalaxyStruct XGalaxy;
+
+/*entry*/
+void freeStarEntry();
+void addEntry(Star *star);
+void deleteEntry(u_int32_t i);
+void editEntry(u_int32_t i, int col, double val);
+void cntEntry();
+
+void galaxy_set_title();
+/* menu action */
+gboolean show_resCList( GtkWidget *w, GdkEventExpose *event, gpointer   data );
+void clearData( GtkWidget *w, gpointer   data );
+void saveAsFile( GtkWidget *w, gpointer   data );
+void saveFile( GtkWidget *w, gpointer   data );
+void openFile( GtkWidget *w, gpointer   data );
+void showAbout(  GtkWidget *w, gpointer   data );
+void actionRun( GtkWidget *w, gpointer   data );
+void actionPause( GtkWidget *w, gpointer   data );
+void actionStop( GtkWidget *w, gpointer   data );
+
+/* graphics */
+gint expose_event( GtkWidget *widget, GdkEventExpose *event );
+gint configure_event( GtkWidget *widget, GdkEventConfigure *event );
+void angle_changed(GtkRange *range, gpointer user_data);
+void fitGalaxy();
+void drawGalaxy();
+void drawStars();
+void drawAxis();
+void clearDraw();
+void DrawDisk(GdkGC   *gc, double CenterX, double CenterY, double Radius, double Feather, double brightness);
+void antialiasedLine(GdkGC   *gc, int x0, int y0, int x1, int y1, GdkColor *color);
+gboolean mouse_motion_notify(GtkWidget      *widget, GdkEventMotion *event);
+
+/*quaternions*/
+void vector_vmul(Vector *res, Vector *a, Vector *b);
+double vector_smul(Vector *a, Vector *b);
+void vector_mul(Vector *res, Vector *a, double b);
+void quater_vmul(Quaternion *res, Quaternion *a, Quaternion *b);
+void quater_inverse(Quaternion *res, Quaternion *a);
+void rotate(Vector *res, Vector *v, Quaternion *q);
+void quater_normalize(Quaternion *q);
+double vector_length(Vector *v);
+void vector_normal(Vector *v);
+void vector_add(Vector *res, Vector *a, Vector *b);
+void vector_sub(Vector *res, Vector *a, Vector *b);
+double quater_length(Quaternion *a);
+void quater_to_matrix( Quaternion *q, double m[3][3]  );
+void matrix_to_quater(  double m[3][3], Quaternion *q );
+void quater_to_angles( Quaternion *q, Vector *a);
+#endif