--- /dev/null
+/*
+ * 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.
+ */
+
--- /dev/null
+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
+
--- /dev/null
+#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);
+ }
+ }
+}
--- /dev/null
+#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);
+}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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";
+
+
--- /dev/null
+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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+#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);
+}
+
+
--- /dev/null
+#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 );
+ }
+}
+
--- /dev/null
+/*
+ * (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;
+}
+
+
--- /dev/null
+#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
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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