From: teodor Date: Fri, 21 Jan 2005 16:46:54 +0000 (+0000) Subject: Initial revision X-Git-Tag: start~1 X-Git-Url: http://sigaev.ru/git/gitweb.cgi?p=xgalaxy.git;a=commitdiff_plain;h=89b42321e4719d60b39195ca9b75e0fe8048b65a Initial revision --- 89b42321e4719d60b39195ca9b75e0fe8048b65a diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..7290c45 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2005 Teodor Sigaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2f6293a --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +CC=gcc +#pkg-config gtk+-2.0 +INCLUDE=-I. -I../tedtools `pkg-config gtk+-2.0 --cflags` +CFLAGS=-Wall -g -O2 +DEFINES= +#LIB=-lm -L../tedtools -ltedtools `pkg-config gtk+-2.0 --libs` -lgthread-2.0 +LIB=-lm -L../tedtools -ltedtools `pkg-config gtk+-2.0 --libs` -lpthread +AR=ar rcv +RANLIB=ranlib +LD=ld -x -shared + +OBJ=xgalaxy.o galaxy.o menuaction.o entry.o graphics.o \ + antialias_draw.o gtkcellrendererbutton.o trackball.o \ + quaternions.o + +.SUFFIXES: .o.c + +.c.o: + $(CC) $(CFLAGS) $(DEFINES) $(INCLUDE) -c $< + +all: xgalaxy + +xgalaxy: $(OBJ) + $(CC) -o $@ $(OBJ) $(LIB) + +clean: + rm -rf *core *.o xgalaxy + diff --git a/antialias_draw.c b/antialias_draw.c new file mode 100644 index 0000000..cc3b8b8 --- /dev/null +++ b/antialias_draw.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include +#include +#include + +#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> 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> 8; + xx = xx + dx; + drawPixel(gc, color,x, y, 0); + drawPixel(gc, color,x + 1, y, 255-v); + drawPixel(gc, color,x - 1, y, v); + } + } +} diff --git a/entry.c b/entry.c new file mode 100644 index 0000000..1a0dc1d --- /dev/null +++ b/entry.c @@ -0,0 +1,89 @@ +#include +#include +#include + +#include "tmalloc.h" +#include "galaxy.h" +#include "xgalaxy.h" + + +void +freeStarEntry() { + if ( XGalaxy.entry ) + tfree(XGalaxy.entry); + XGalaxy.entry=NULL; + if ( XGalaxy.tmpentry ) + tfree(XGalaxy.tmpentry); + XGalaxy.tmpentry=NULL; + XGalaxy.nentry = XGalaxy.lenentry = 0; +} + +void +addEntry(Star *star) { + if ( XGalaxy.nentry >= XGalaxy.lenentry ) { + XGalaxy.lenentry = (XGalaxy.lenentry) ? XGalaxy.lenentry*2 : 16; + XGalaxy.entry = (Star*) ((XGalaxy.entry) ? + trealloc(XGalaxy.entry, sizeof(Star)*XGalaxy.lenentry) + : + tmalloc(sizeof(Star)*XGalaxy.lenentry)); + XGalaxy.tmpentry = (Star*) ((XGalaxy.entry) ? + trealloc(XGalaxy.tmpentry, sizeof(Star)*XGalaxy.lenentry) + : + tmalloc(sizeof(Star)*XGalaxy.lenentry)); + } + + if ( star ) + memcpy(XGalaxy.entry + XGalaxy.nentry, star, sizeof(Star)); + else + memset(XGalaxy.entry + XGalaxy.nentry, 0, sizeof(Star)); + + XGalaxy.nentry++; +} + +void +deleteEntry(u_int32_t i) { + if ( i>=XGalaxy.nentry || XGalaxy.nentry==0) + return; + + if ( i!=XGalaxy.nentry-1) + memmove( XGalaxy.entry + i, XGalaxy.entry + i + 1, sizeof(Star) * ( XGalaxy.nentry-i-1) ); + XGalaxy.nentry--; +} + +void +editEntry(u_int32_t i, int col, double val) { + if ( i>=XGalaxy.nentry ) + return; + switch(col) { + case 1: XGalaxy.entry[i].mass = val; break; + case 2: XGalaxy.entry[i].c.x = val; break; + case 3: XGalaxy.entry[i].c.y = val; break; + case 4: XGalaxy.entry[i].c.z = val; break; + case 5: XGalaxy.entry[i].v.x = val; break; + case 6: XGalaxy.entry[i].v.y = val; break; + case 7: XGalaxy.entry[i].v.z = val; break; + default: break; + } +} + +void +cntEntry() { + Galaxy galaxy; + char buf[128]; + + memset(&galaxy,0,sizeof(galaxy)); + + galaxy.stars = XGalaxy.entry; + galaxy.nstars = XGalaxy.nentry; + + EnergyImpulseGalaxy(&galaxy); + + sprintf(buf,"%G",galaxy.kineticEnergy + galaxy.potentialEnergy); + gtk_label_set_text( GTK_LABEL(XGalaxy.dataEnergyField), buf); + + sprintf(buf,"%G",galaxy.Impulse); + gtk_label_set_text( GTK_LABEL(XGalaxy.dataImpulseField), buf); + + sprintf(buf,"%G",galaxy.Moment); + gtk_label_set_text( GTK_LABEL(XGalaxy.dataMomentField), buf); +} diff --git a/examples/doublemoon b/examples/doublemoon new file mode 100644 index 0000000..9be1d20 --- /dev/null +++ b/examples/doublemoon @@ -0,0 +1,5 @@ +delta=3600 +error=1E-10 + +7.4E+22 1E+07 0 0 0 350 0 +7.4E+22 -1E+07 0 0 0 -350 0 diff --git a/examples/doublemoon_doublesputnik b/examples/doublemoon_doublesputnik new file mode 100644 index 0000000..09d8f25 --- /dev/null +++ b/examples/doublemoon_doublesputnik @@ -0,0 +1,7 @@ +delta=3600 +error=1E-11 + +7.4E+22 1E+07 0 0 0 350 0 +7.4E+22 -1E+07 0 0 0 -350 0 +1 2E+07 0 0 0 1120 0 +1 -2E+07 0 0 0 -1120 0 diff --git a/examples/doublemoon_doublesputnik1 b/examples/doublemoon_doublesputnik1 new file mode 100644 index 0000000..22479c9 --- /dev/null +++ b/examples/doublemoon_doublesputnik1 @@ -0,0 +1,7 @@ +delta=3600 +error=1E-11 + +7.4E+22 1E+07 0 0 0 350 0 +7.4E+22 -1E+07 0 0 0 -350 0 +1 1E+06 0 0 0 -1100 0 +1 -1E+06 0 0 0 1100 0 diff --git a/examples/doublemoon_ellipse b/examples/doublemoon_ellipse new file mode 100644 index 0000000..929771e --- /dev/null +++ b/examples/doublemoon_ellipse @@ -0,0 +1,5 @@ +delta=3600 +error=1E-11 + +7.4E+22 7.7E+06 0 0 0 254 0 +7.4E+22 -7.7E+06 0 0 0 -254 0 diff --git a/examples/doublemoon_sputnik b/examples/doublemoon_sputnik new file mode 100644 index 0000000..9b6abbc --- /dev/null +++ b/examples/doublemoon_sputnik @@ -0,0 +1,6 @@ +delta=3600 +error=1E-11 + +7.4E+22 1E+07 0 0 0 350 0 +7.4E+22 -1E+07 0 0 0 -350 0 +1 1E+06 0 0 0 -1100 0 diff --git a/examples/quad_moon b/examples/quad_moon new file mode 100644 index 0000000..fcadbaa --- /dev/null +++ b/examples/quad_moon @@ -0,0 +1,7 @@ +delta=3600 +error=1E-10 + +7.4E+22 7.7E+06 0 0 0 600 0 +7.4E+22 -7.7E+06 0 0 0 -600 0 +7.4E+22 0 7.7E+06 0 -600 0 0 +7.4E+22 0 -7.7E+06 0 600 0 0 diff --git a/examples/three_generator.pl b/examples/three_generator.pl new file mode 100644 index 0000000..b1407eb --- /dev/null +++ b/examples/three_generator.pl @@ -0,0 +1,22 @@ +use strict; +my $x = 0.97000436e0; +my $y = 0.24308753e0; +my $vx =0.93240737e0; +my $vy =0.86473146e0; + +my $R=4.e7; +my $V=176.5e0; +my $M=7.4e22; + +print< +#include + + +#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<1) { + for(i=0;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;iAXIS; \ + \ + 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; instars; i++ ) { + istar = galaxy->stars+i; + jstar = galaxy->stars; + + memcpy( &(istar->pa), &(istar->a), sizeof(Vector) ); + +#ifdef SORT_STAR + for(j=0; jnstars; j++ ) { + if ( jx = ( 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;ja.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;jnstars;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;istars, *jstar; + double potential=0.0; + double kinetic=0.0; + double impulseX=0.0, impulseY=0.0, impulseZ=0.0, momentX=0.0, momentY=0.0, momentZ=0.0; +#ifdef SORT_ENERGYSTAR + double pe[ maxN2(CNT_FARRAY(galaxy->nstars)) ], *ptrp; + + ptrp=pe; + memset(pe, 0, sizeof(double)*maxN2(CNT_FARRAY(galaxy->nstars))); + +#endif + while( istar - galaxy->stars < galaxy->nstars ) { + +#ifndef SORT_ENERGYSTAR + impulseX = impulseX + istar->mass * istar->v.x; + impulseY = impulseY + istar->mass * istar->v.y; + impulseZ = impulseZ + istar->mass * istar->v.z; + + momentX += istar->mass * ( istar->v.y * istar->c.z - istar->c.y * istar->v.z ); + momentY += istar->mass * ( istar->v.z * istar->c.x - istar->c.z * istar->v.x ); + momentZ += istar->mass * ( istar->v.x * istar->c.y - istar->c.x * istar->v.y ); + + kinetic += istar->mass * (istar->v.x * istar->v.x + istar->v.y * istar->v.y + istar->v.z * istar->v.z) / 2.0; +#endif + + jstar=istar+1; + while( jstar - galaxy->stars < galaxy->nstars ) { +#ifdef SORT_ENERGYSTAR + *ptrp++ = +#else + potential += +#endif + G * istar->mass * jstar->mass / sqrt( + (jstar->c.x - istar->c.x)*(jstar->c.x - istar->c.x) + + (jstar->c.y - istar->c.y)*(jstar->c.y - istar->c.y) + + (jstar->c.z - istar->c.z)*(jstar->c.z - istar->c.z) + ); + jstar++; + } + istar++; + } + +#ifdef SORT_ENERGYSTAR + potential = safeSumArray(pe, maxN2(ptrp-pe)); + impulseX = safeSumStar(galaxy->stars, galaxy->nstars, getImpulseX); + impulseY = safeSumStar(galaxy->stars, galaxy->nstars, getImpulseY); + impulseZ = safeSumStar(galaxy->stars, galaxy->nstars, getImpulseZ); + momentX = safeSumStar(galaxy->stars, galaxy->nstars, getMomentX); + momentY = safeSumStar(galaxy->stars, galaxy->nstars, getMomentY); + momentZ = safeSumStar(galaxy->stars, galaxy->nstars, getMomentZ); + kinetic = safeSumStar(galaxy->stars, galaxy->nstars, getKinetic); +#endif + galaxy->Impulse = sqrt(impulseX*impulseX + impulseY*impulseY + impulseZ*impulseZ); + galaxy->Moment = sqrt(momentX *momentX + momentY *momentY + momentZ *momentZ ); + galaxy->kineticEnergy = kinetic; + if ( !isfinite(potential) ) potential=0; + galaxy->potentialEnergy = -potential; +} + +void +initLiveGalaxy(Galaxy *galaxy) { + if ( galaxy->errorLimit<=0) galaxy->errorLimit = 1e-8; + if ( galaxy->desiredDelta<=0 ) galaxy->desiredDelta=3600; + + galaxy->error = galaxy->errorLimit; + galaxy->delta=galaxy->desiredDelta; + galaxy->elapsedTime=0.0; + +#ifdef TAYLOR3 + galaxy->firsttime=1; +#endif + EnergyImpulseGalaxy(galaxy); +} + + +#define iszero(x) ( fpclassify(x) & FP_ZERO ) +#define CHK_ERRORVAL( what, err, val, prevval ) \ +if ( isfinite(val) && isfinite(prevval) ) { \ + err = fabs( (val)/(prevval) - 1.0 ); \ + if ( !isfinite(err) ) \ + err = galaxy->error; \ +} else { \ + fprintf(stderr, "%s is not finite: val:%G prevval:%G\n", what, (val), (prevval)); \ + err = galaxy->error; \ +} + +void +liveGalaxy(Galaxy *galaxy, pthread_mutex_t *mutex) { + double prevEnergy = galaxy->kineticEnergy + galaxy->potentialEnergy; + double prevImpulse = galaxy->Impulse; + double prevMoment = galaxy->Moment; + double errorI, errorE, errorM, error, prevError; + + accelerationGalaxy(galaxy); + prevError = galaxy->error; + + if ( mutex ) pthread_mutex_lock(mutex); + while(1) { + stepGalaxy(galaxy, galaxy->delta); + EnergyImpulseGalaxy(galaxy); + + CHK_ERRORVAL("Energy", errorE, galaxy->kineticEnergy + galaxy->potentialEnergy, prevEnergy); + CHK_ERRORVAL("Impulse",errorI, galaxy->Impulse, prevImpulse); + CHK_ERRORVAL("Moment", errorM, galaxy->Moment, prevMoment); + + /*error=fmax(errorE, fmax(errorI, errorM));*/ + error = errorE; + + if ( error > galaxy->errorLimit ) { + unstepGalaxy(galaxy, galaxy->delta); + galaxy->delta /= 2.0; + } else if ( error*1e2 < galaxy->errorLimit && prevError < galaxy->errorLimit ) { + unstepGalaxy(galaxy, galaxy->delta); + galaxy->delta *= 2.0; + } else { + galaxy->error = error; + break; + } + prevError=error; + } + if ( mutex ) pthread_mutex_unlock(mutex); + + galaxy->elapsedTime += galaxy->delta; +} + +void +printStar(Star *star) { + printf("\tMass: %e\n", star->mass); + printf("\tPosit: x:%e y:%e z:%e\n", star->c.x, star->c.y, star->c.z); + printf("\tSpeed: x:%e y:%e z:%e\n", star->v.x, star->v.y, star->v.z); +} diff --git a/galaxy.h b/galaxy.h new file mode 100644 index 0000000..a43950e --- /dev/null +++ b/galaxy.h @@ -0,0 +1,77 @@ +#ifndef __GALAXY_H__ +#define __GALAXY_H__ + +#include +#include + +/* 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<=iforces + CNT_FARRAY(n) - CNT_FARRAY(n-i) + (j) - (i) - 1) +*/ +#define GET_FORCE(g, i, j) ( (g)->forces + (((j)*((j)+1))>>1) + i ) + + +void initGalaxy(Galaxy *galaxy, u_int32_t nstars); +void forceGalaxy(Galaxy *galaxy); +void accelerationGalaxy(Galaxy *galaxy); +void stepGalaxy(Galaxy *galaxy, double delta); +void unstepGalaxy(Galaxy *galaxy, double delta); +void freeGalaxy(Galaxy *galaxy); +void EnergyImpulseGalaxy(Galaxy *galaxy); +void initLiveGalaxy(Galaxy *galaxy); +void liveGalaxy(Galaxy *galaxy, pthread_mutex_t *mutex); +void printStar(Star *star); + +#define G (6.742e-11) +#endif diff --git a/graphics.c b/graphics.c new file mode 100644 index 0000000..1daa205 --- /dev/null +++ b/graphics.c @@ -0,0 +1,316 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 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 1.0 ) brightness = 1.0; + DrawDisk( + gc, + ((double)XGalaxy.drawing_area->allocation.width)/2.0 + (minsize*XGalaxy.Scale*XGalaxy.tmpentry[i].a.x), + ((double)XGalaxy.drawing_area->allocation.height)/2.0 - (minsize*XGalaxy.Scale*XGalaxy.tmpentry[i].a.y), + r, + 1.0, + brightness + ); + } + } + g_object_unref(G_OBJECT(gc)); +} + +void +drawGalaxy() { + GdkRectangle update_rect; + + if (!XGalaxy.pixmap) + return; + update_rect.x=0; + update_rect.y=0; + update_rect.width = XGalaxy.drawing_area->allocation.width; + update_rect.height = XGalaxy.drawing_area->allocation.height; + + gdk_window_invalidate_rect (XGalaxy.drawing_area->window, &update_rect, FALSE); +} + +void +angle_changed(GtkRange *range, gpointer user_data) { + double angle = gtk_range_get_value(range)*M_PI/180.0; + Quaternion old, newquaternion; + + if (XGalaxy.locksignal) + return; + + if (XGalaxy.trace) { + XGalaxy.locksignal=1; + switch((int)user_data) { + case 'X': gtk_range_set_value(range, XGalaxy.Xangle*180.0/M_PI); break; + case 'Y': gtk_range_set_value(range, XGalaxy.Yangle*180.0/M_PI); break; + case 'Z': gtk_range_set_value(range, XGalaxy.Zangle*180.0/M_PI); break; + default: break; + } + XGalaxy.locksignal=0; + return; + } + + memcpy(&old, &(XGalaxy.angle), sizeof(old)); + memset(&newquaternion, 0 , sizeof(newquaternion)); + switch( (int)user_data ) { + case 'X': + newquaternion.d.x = sin( (angle-XGalaxy.Xangle)/2.0 ); + newquaternion.w = cos( (angle-XGalaxy.Xangle)/2.0 ); + XGalaxy.Xangle=angle; + break; + case 'Y': + newquaternion.d.y = sin( (angle-XGalaxy.Yangle)/2.0 ); + newquaternion.w = cos( (angle-XGalaxy.Yangle)/2.0 ); + XGalaxy.Yangle=angle; + break; + case 'Z': + newquaternion.d.z = sin( (angle-XGalaxy.Zangle)/2.0 ); + newquaternion.w = cos( (angle-XGalaxy.Zangle)/2.0 ); + XGalaxy.Zangle=angle; + break; + default: g_print("Unknown axis: %c\n", (char)(int)user_data); + } + + //quater_vmul( &(XGalaxy.angle), &old, &newquaternion ); + quater_vmul( &(XGalaxy.angle), &newquaternion, &old ); + + drawGalaxy(); +} + +void +clearDraw() { + if ( !XGalaxy.pixmap ) + return; + gdk_draw_rectangle (XGalaxy.pixmap, + XGalaxy.drawing_area->style->white_gc, + TRUE, + 0, 0, + XGalaxy.drawing_area->allocation.width, + XGalaxy.drawing_area->allocation.height); + if ( XGalaxy.drawaxis ) drawAxis(); +} + +gint +expose_event( GtkWidget *widget, GdkEventExpose *event ) { + /* draw only last expose */ + if ( event->count > 0) + return TRUE; + + drawStars(); + gdk_draw_pixmap(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + XGalaxy.pixmap, + event->area.x, event->area.y, + event->area.x, event->area.y, + event->area.width, event->area.height); + + return FALSE; +} + +gint +configure_event( GtkWidget *widget, GdkEventConfigure *event ) { + if (XGalaxy.pixmap) + g_object_unref(XGalaxy.pixmap); + + XGalaxy.pixmap = gdk_pixmap_new(widget->window, + widget->allocation.width, + widget->allocation.height, + -1); + + clearDraw(); + + return TRUE; +} + +gboolean +mouse_motion_notify(GtkWidget *widget, GdkEventMotion *event) { + int x = 0; + int y = 0; + GdkModifierType state = 0; + + if (XGalaxy.locksignal || XGalaxy.trace ) + return TRUE; + + if (event->is_hint) + gdk_window_get_pointer(event->window, &x, &y, &state); + else { + x = event->x; + y = event->y; + state = event->state; + } + if (state & GDK_BUTTON1_MASK) { + double width, height; + Quaternion old; + Vector angles; + + width = widget->allocation.width; + height = widget->allocation.height; + trackball( &(XGalaxy.motion), + (2.0*XGalaxy.beginx - width) / width, + ( height - 2.0*XGalaxy.beginy) / height, + ( 2.0*x - width) / width, + ( height - 2.0*y) / height ); + + memcpy(&old, &(XGalaxy.angle), sizeof(old)); + //quater_vmul( &(XGalaxy.angle), &old, &(XGalaxy.motion) ); + quater_vmul( &(XGalaxy.angle), &(XGalaxy.motion), &old ); + + XGalaxy.locksignal=1; + quater_to_angles( &(XGalaxy.angle), &angles); + XGalaxy.Xangle = angles.x; + gtk_range_set_value(GTK_RANGE(XGalaxy.XSlider), angles.x*180.0/M_PI); + XGalaxy.Yangle = angles.y; + gtk_range_set_value(GTK_RANGE(XGalaxy.YSlider), angles.y*180.0/M_PI); + XGalaxy.Zangle = angles.z; + gtk_range_set_value(GTK_RANGE(XGalaxy.ZSlider), angles.z*180.0/M_PI); + XGalaxy.locksignal=0; + + XGalaxy.beginx = x; + XGalaxy.beginy = y; + + drawGalaxy(); + } + return TRUE; +} diff --git a/gtkcellrendererbutton.c b/gtkcellrendererbutton.c new file mode 100644 index 0000000..403f562 --- /dev/null +++ b/gtkcellrendererbutton.c @@ -0,0 +1,412 @@ +/* gtkcellrendererbutton.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * 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 +#include +#include "gtkcellrendererbutton.h" +//#include "gtkintl.h" +//#include "gtkmarshalers.h" +//#include "gtktreeprivate.h" + +static void gtk_cell_renderer_button_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_button_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_button_init (GtkCellRendererButton *celltext); +static void gtk_cell_renderer_button_class_init (GtkCellRendererButtonClass *class); +static void gtk_cell_renderer_button_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height); +static void gtk_cell_renderer_button_render (GtkCellRenderer *cell, + GdkWindow *window, + GtkWidget *widget, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GdkRectangle *expose_area, + GtkCellRendererState flags); +static gboolean gtk_cell_renderer_button_activate (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const gchar *path, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GtkCellRendererState flags); + + +enum { + TOGGLED, + LAST_SIGNAL +}; + +enum { + PROP_ZERO, + PROP_ACTIVATABLE, + PROP_ACTIVE, +/* PROP_RADIO, */ + PROP_INCONSISTENT +}; + +#define BUTTON_WIDTH 14 + +static guint button_cell_signals[LAST_SIGNAL] = { 0 }; + +#define GTK_CELL_RENDERER_BUTTON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_CELL_RENDERER_BUTTON, GtkCellRendererButtonPrivate)) + +typedef struct _GtkCellRendererButtonPrivate GtkCellRendererButtonPrivate; +struct _GtkCellRendererButtonPrivate +{ + guint inconsistent : 1; +}; + + +GType +gtk_cell_renderer_button_get_type (void) +{ + static GType cell_button_type = 0; + + if (!cell_button_type) + { + static const GTypeInfo cell_button_info = + { + sizeof (GtkCellRendererButtonClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_cell_renderer_button_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkCellRendererButton), + 0, /* n_preallocs */ + (GInstanceInitFunc) gtk_cell_renderer_button_init, + }; + + cell_button_type = + g_type_register_static (GTK_TYPE_CELL_RENDERER, "GtkCellRendererButton", + &cell_button_info, 0); + } + + return cell_button_type; +} + +static void +gtk_cell_renderer_button_init (GtkCellRendererButton *cellbutton) +{ + cellbutton->activatable = TRUE; + cellbutton->active = FALSE; + GTK_CELL_RENDERER (cellbutton)->mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE; + GTK_CELL_RENDERER (cellbutton)->xpad = 2; + GTK_CELL_RENDERER (cellbutton)->ypad = 2; +} + +static void +gtk_cell_renderer_button_class_init (GtkCellRendererButtonClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); + + object_class->get_property = gtk_cell_renderer_button_get_property; + object_class->set_property = gtk_cell_renderer_button_set_property; + + cell_class->get_size = gtk_cell_renderer_button_get_size; + cell_class->render = gtk_cell_renderer_button_render; + cell_class->activate = gtk_cell_renderer_button_activate; + + g_object_class_install_property (object_class, + PROP_ACTIVE, + g_param_spec_boolean ("active", + "Button state", + "The button state of the button", + FALSE, + G_PARAM_READABLE | + G_PARAM_WRITABLE)); + + g_object_class_install_property (object_class, + PROP_INCONSISTENT, + g_param_spec_boolean ("inconsistent", + "Inconsistent state", + "The inconsistent state of the button", + FALSE, + G_PARAM_READABLE | + G_PARAM_WRITABLE)); + + g_object_class_install_property (object_class, + PROP_ACTIVATABLE, + g_param_spec_boolean ("activatable", + "Activatable", + "The button button can be activated", + TRUE, + G_PARAM_READABLE | + G_PARAM_WRITABLE)); + + button_cell_signals[TOGGLED] = + g_signal_new ("toggled", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkCellRendererButtonClass, toggled), + NULL, NULL, + /*_gtk_marshal_VOID__STRING ,*/ + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + + g_type_class_add_private (object_class, sizeof (GtkCellRendererButtonPrivate)); +} + +static void +gtk_cell_renderer_button_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererButton *cellbutton = GTK_CELL_RENDERER_BUTTON (object); + GtkCellRendererButtonPrivate *priv; + + priv = GTK_CELL_RENDERER_BUTTON_GET_PRIVATE (object); + + switch (param_id) + { + case PROP_ACTIVE: + g_value_set_boolean (value, cellbutton->active); + break; + case PROP_INCONSISTENT: + g_value_set_boolean (value, priv->inconsistent); + break; + case PROP_ACTIVATABLE: + g_value_set_boolean (value, cellbutton->activatable); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + + +static void +gtk_cell_renderer_button_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererButton *cellbutton = GTK_CELL_RENDERER_BUTTON (object); + GtkCellRendererButtonPrivate *priv; + + priv = GTK_CELL_RENDERER_BUTTON_GET_PRIVATE (object); + + switch (param_id) + { + case PROP_ACTIVE: + cellbutton->active = g_value_get_boolean (value); + g_object_notify (G_OBJECT(object), "active"); + break; + case PROP_INCONSISTENT: + priv->inconsistent = g_value_get_boolean (value); + g_object_notify (G_OBJECT (object), "inconsistent"); + break; + case PROP_ACTIVATABLE: + cellbutton->activatable = g_value_get_boolean (value); + g_object_notify (G_OBJECT(object), "activatable"); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +/** + * gtk_cell_renderer_button_new: + * + * Creates a new #GtkCellRendererButton. Adjust rendering + * parameters using object properties. Object properties can be set + * globally (with g_object_set()). Also, with #GtkTreeViewColumn, you + * can bind a property to a value in a #GtkTreeModel. For example, you + * can bind the "active" property on the cell renderer to a boolean value + * in the model, thus causing the check button to reflect the state of + * the model. + * + * Return value: the new cell renderer + **/ +GtkCellRenderer * +gtk_cell_renderer_button_new (void) +{ + return g_object_new (GTK_TYPE_CELL_RENDERER_BUTTON, NULL); +} + +static void +gtk_cell_renderer_button_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height) +{ + gint calc_width; + gint calc_height; + + calc_width = (gint) cell->xpad * 2 + BUTTON_WIDTH; + calc_height = (gint) cell->ypad * 2 + BUTTON_WIDTH; + + if (width) + *width = calc_width; + + if (height) + *height = calc_height; + + if (cell_area) + { + if (x_offset) + { + *x_offset = ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ? + (1.0 - cell->xalign) : cell->xalign) * (cell_area->width - calc_width); + *x_offset = MAX (*x_offset, 0); + } + if (y_offset) + { + *y_offset = cell->yalign * (cell_area->height - calc_height); + *y_offset = MAX (*y_offset, 0); + } + } +} + +static void +gtk_cell_renderer_button_render (GtkCellRenderer *cell, + GdkDrawable *window, + GtkWidget *widget, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GdkRectangle *expose_area, + GtkCellRendererState flags) +{ + GtkCellRendererButton *cellbutton = (GtkCellRendererButton *) cell; + GtkCellRendererButtonPrivate *priv; + gint width, height; + gint x_offset, y_offset; + GtkShadowType shadow; + GtkStateType state = 0; + + priv = GTK_CELL_RENDERER_BUTTON_GET_PRIVATE (cell); + + gtk_cell_renderer_button_get_size (cell, widget, cell_area, + &x_offset, &y_offset, + &width, &height); + width -= cell->xpad*2; + height -= cell->ypad*2; + + if (width <= 0 || height <= 0) + return; + + if (priv->inconsistent) + shadow = GTK_SHADOW_ETCHED_IN; + else + shadow = cellbutton->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + + if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED) + { + if (GTK_WIDGET_HAS_FOCUS (widget)) + state = GTK_STATE_SELECTED; + else + state = GTK_STATE_ACTIVE; + } + else + { + if (cellbutton->activatable) + state = GTK_STATE_NORMAL; + else + state = GTK_STATE_INSENSITIVE; + } + + gtk_paint_box (widget->style, + window, + state, shadow, + expose_area, widget, "button", + cell_area->x + x_offset + cell->xpad, + cell_area->y + y_offset + cell->ypad, + width - 1, height - 1); + + gtk_paint_hline( + widget->style, + window, + GTK_STATE_NORMAL, + expose_area, widget, "label", + cell_area->x + x_offset + cell->xpad + 4 , + cell_area->x + x_offset + width - 4, + cell_area->y + y_offset + cell->ypad/2 + height/2 + ); +} + +static gint +gtk_cell_renderer_button_activate (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const gchar *path, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellRendererButton *cellbutton; + + cellbutton = GTK_CELL_RENDERER_BUTTON (cell); + if (cellbutton->activatable) + { + g_signal_emit (cell, button_cell_signals[TOGGLED], 0, path); + return TRUE; + } + + return FALSE; +} + +/** + * gtk_cell_renderer_button_get_active: + * @button: a #GtkCellRendererButton + * + * Returns whether the cell renderer is active. See + * gtk_cell_renderer_button_set_active(). + * + * Return value: %TRUE if the cell renderer is active. + **/ +gboolean +gtk_cell_renderer_button_get_active (GtkCellRendererButton *button) +{ + g_return_val_if_fail (GTK_IS_CELL_RENDERER_BUTTON (button), FALSE); + + return button->active; +} + +/** + * gtk_cell_renderer_button_set_active: + * @button: a #GtkCellRendererButton. + * @setting: the value to set. + * + * Activates or deactivates a cell renderer. + **/ +void +gtk_cell_renderer_button_set_active (GtkCellRendererButton *button, + gboolean setting) +{ + g_return_if_fail (GTK_IS_CELL_RENDERER_BUTTON (button)); + + g_object_set (button, "active", setting ? TRUE : FALSE, NULL); +} diff --git a/gtkcellrendererbutton.h b/gtkcellrendererbutton.h new file mode 100644 index 0000000..8282191 --- /dev/null +++ b/gtkcellrendererbutton.h @@ -0,0 +1,75 @@ +/* gtkcellrendererbutton.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * 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 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GTK_TYPE_CELL_RENDERER_BUTTON (gtk_cell_renderer_button_get_type ()) +#define GTK_CELL_RENDERER_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_BUTTON, GtkCellRendererButton)) +#define GTK_CELL_RENDERER_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_BUTTON, GtkCellRendererButtonClass)) +#define GTK_IS_CELL_RENDERER_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_BUTTON)) +#define GTK_IS_CELL_RENDERER_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_RENDERER_BUTTON)) +#define GTK_CELL_RENDERER_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_RENDERER_BUTTON, GtkCellRendererButtonClass)) + +typedef struct _GtkCellRendererButton GtkCellRendererButton; +typedef struct _GtkCellRendererButtonClass GtkCellRendererButtonClass; + +struct _GtkCellRendererButton +{ + GtkCellRenderer parent; + + /*< private >*/ + guint active : 1; + guint activatable : 1; +}; + +struct _GtkCellRendererButtonClass +{ + GtkCellRendererClass parent_class; + + void (* toggled) (GtkCellRendererButton *cell_renderer_button, + const gchar *path); + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); +}; + +GType gtk_cell_renderer_button_get_type (void); +GtkCellRenderer *gtk_cell_renderer_button_new (void); + +gboolean gtk_cell_renderer_button_get_active (GtkCellRendererButton *button); +void gtk_cell_renderer_button_set_active (GtkCellRendererButton *button, + gboolean setting); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_CELL_RENDERER_BUTTON_H__ */ diff --git a/menuaction.c b/menuaction.c new file mode 100644 index 0000000..f9600ef --- /dev/null +++ b/menuaction.c @@ -0,0 +1,511 @@ +#include +#include +#include +#include +#include +#include +#include + +#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;i6 ) { + 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 , 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= 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 +#include +#include "xgalaxy.h" + +void +vector_sub(Vector *res, Vector *a, Vector *b) { + res->x = a->x - b->x; + res->y = a->y - b->y; + res->z = a->z - b->z; +} + +void +vector_add(Vector *res, Vector *a, Vector *b) { + res->x = a->x + b->x; + res->y = a->y + b->y; + res->z = a->z + b->z; +} + +void +vector_vmul(Vector *res, Vector *a, Vector *b) { + res->x = a->y * b->z - b->y * a->z; + res->y = a->z * b->x - b->z * a->x; + res->z = a->x * b->y - b->x * a->y; +} + +double +vector_smul(Vector *a, Vector *b) { + double res; + res = a->x * b->x; + res+= a->y * b->y; + res+= a->z * b->z; + return res; +} + +double +vector_length(Vector *v) { + return sqrt( v->x*v->x + v->y*v->y + v->z*v->z ); +} + +void +vector_mul(Vector *res, Vector *a, double b) { + res->x = a->x * b; + res->y = a->y * b; + res->z = a->z * b; +} + +void +quater_vmul(Quaternion *res, Quaternion *a, Quaternion *b) { + Vector aXb, wb, Wa; + + vector_vmul(&aXb, &(a->d), &(b->d)); + vector_mul(&wb, &(b->d), a->w); + vector_mul(&Wa, &(a->d), b->w); + + res->d.x = aXb.x + wb.x + Wa.x; + res->d.y = aXb.y + wb.y + Wa.y; + res->d.z = aXb.z + wb.z + Wa.z; + + /*XXX*/ + res->w = a->w * b->w - vector_smul( &(a->d), &(b->d) ); +} + +double +quater_length(Quaternion *a) { + return sqrt(a->d.x*a->d.x + a->d.y*a->d.y + a->d.z*a->d.z + a->w * a->w); +} + +void +quater_inverse(Quaternion *res, Quaternion *a) { + double norm = a->d.x*a->d.x + a->d.y*a->d.y + a->d.z*a->d.z + a->w * a->w; + res->d.x = -a->d.x/norm; + res->d.y = -a->d.y/norm; + res->d.z = -a->d.z/norm; + res->w = a->w /norm; +} + +void +rotate(Vector *res, Vector *v, Quaternion *q) { + Quaternion V, qV, Q, R; + + memcpy( &(V.d), v, sizeof(Vector) ); + V.w = 0; + + quater_vmul(&qV, q, &V); + quater_inverse(&Q, q); + quater_vmul(&R, &qV, &Q); + + memcpy( res, &(R.d), sizeof(Vector) ); +} + +void +quater_normalize(Quaternion *q) { + double mag; + + mag = sqrt(q->d.x*q->d.x + q->d.y*q->d.y + q->d.z*q->d.z + q->w*q->w); + q->d.x /= mag; + q->d.y /= mag; + q->d.z /= mag; + q->w /= mag; +} + +void +vector_normal(Vector *v) { + vector_mul( v, v, 1.0/ vector_length(v) ); +} + +void +quater_to_matrix( Quaternion *q, double m[3][3] ) { + double wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; + double s = 2.0/(q->d.x*q->d.x + q->d.y*q->d.y + q->d.z*q->d.z + q->w*q->w); // 4 mul 3 add 1 div + x2 = q->d.x * s; y2 = q->d.y * s; z2 = q->d.z * s; + xx = q->d.x * x2; xy = q->d.x * y2; xz = q->d.x * z2; + yy = q->d.y * y2; yz = q->d.y * z2; zz = q->d.z * z2; + wx = q->w * x2; wy = q->w * y2; wz = q->w * z2; + + m[0][0] = 1.0 - (yy + zz); + m[1][0] = xy - wz; + m[2][0] = xz + wy; + + m[0][1] = xy + wz; + m[1][1] = 1.0 - (xx + zz); + m[2][1] = yz - wx; + + m[0][2] = xz - wy; + m[1][2] = yz + wx; + m[2][2] = 1.0 - (xx + yy); +} + +void +matrix_to_quater( double m[3][3], Quaternion *q ) { + double tr = m[0][0] + m[1][1] + m[2][2]; // trace of martix + if (tr > 0.0){ // if trace positive than "w" is biggest component + q->d.x = m[1][2] - m[2][1]; + q->d.y = m[2][0] - m[0][2]; + q->d.z = m[0][1] - m[1][0]; + q->w = tr+1.0; + }else if( (m[0][0] > m[1][1] ) && ( m[0][0] > m[2][2]) ) { + q->d.x = 1.0 + m[0][0] - m[1][1] - m[2][2]; + q->d.y = m[1][0] + m[0][1]; + q->d.z = m[2][0] + m[0][2]; + q->w = m[1][2] - m[2][1]; + } else if ( m[1][1] > m[2][2] ){ + q->d.x = m[1][0] + m[0][1]; + q->d.y = 1.0 + m[1][1] - m[0][0] - m[2][2]; + q->d.z = m[2][1] + m[1][2]; + q->w = m[2][0] - m[0][2]; + } else { + q->d.x = m[2][0] + m[0][2]; + q->d.y = m[2][1] + m[1][2]; + q->d.z = 1.0 + m[2][2] - m[0][0] - m[1][1]; + q->w = m[0][1] - m[1][0]; + } + quater_normalize(q); +} + +void +quater_to_angles( Quaternion *q, Vector *a) { + double m[3][3]; + double D,C,trx,try; + + quater_to_matrix(q, m); + + D = a->y = asin(m[2][0]); + C = cos( a->y ); + + if ( fabs(C) > 1e-8 ) { + trx = m[2][2] / C; + try = -m[2][1] / C; + a->x = atan2( try, trx ); + + trx = m[0][0] / C; + try = -m[1][0] / C; + a->z = atan2( try, trx ); + } else { + a->x = 0.0; + trx = m[1][1]; + try = m[0][1]; + + a->z = atan2( try, trx ); + } +} + diff --git a/trackball.c b/trackball.c new file mode 100644 index 0000000..0fa2a26 --- /dev/null +++ b/trackball.c @@ -0,0 +1,156 @@ +/* + * (c) Copyright 1993, 1994, Silicon Graphics, Inc. + * ALL RIGHTS RESERVED + * Permission to use, copy, modify, and distribute this software for + * any purpose and without fee is hereby granted, provided that the above + * copyright notice appear in all copies and that both the copyright notice + * and this permission notice appear in supporting documentation, and that + * the name of Silicon Graphics, Inc. not be used in advertising + * or publicity pertaining to distribution of the software without specific, + * written prior permission. + * + * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" + * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, + * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY + * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, + * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF + * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE + * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE. + * + * US Government Users Restricted Rights + * Use, duplication, or disclosure by the Government is subject to + * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph + * (c)(1)(ii) of the Rights in Technical Data and Computer Software + * clause at DFARS 252.227-7013 and/or in similar or successor + * clauses in the FAR or the DOD or NASA FAR Supplement. + * Unpublished-- rights reserved under the copyright laws of the + * United States. Contractor/manufacturer is Silicon Graphics, + * Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311. + * + * OpenGL(TM) is a trademark of Silicon Graphics, Inc. + */ +/* + * Trackball code: + * + * Implementation of a virtual trackball. + * Implemented by Gavin Bell, lots of ideas from Thant Tessman and + * the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129. + * + * Vector manip code: + * + * Original code from: + * David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli + * + * Much mucking with by: + * Gavin Bell + */ +#include +#include +#include "trackball.h" + +/* + * This size should really be based on the distance from the center of + * rotation to the point on the object underneath the mouse. That + * point would then track the mouse as closely as possible. This is a + * simple example, though, so that is left as an Exercise for the + * Programmer. + */ +#define TRACKBALLSIZE (0.8) + +/* + * Local function prototypes (not defined in trackball.h) + */ +static double tb_project_to_sphere(double, double, double); + + +/* + * Ok, simulate a track-ball. Project the points onto the virtual + * trackball, then figure out the axis of rotation, which is the cross + * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0) + * Note: This is a deformed trackball-- is a trackball in the center, + * but is deformed into a hyperbolic sheet of rotation away from the + * center. This particular function was chosen after trying out + * several variations. + * + * It is assumed that the arguments to this routine are in the range + * (-1.0 ... 1.0) + */ +void +trackball(Quaternion *q, double p1x, double p1y, double p2x, double p2y) +{ + Vector a; /* Axis of rotation */ + double phi; /* how much to rotate about axis */ + Vector p1, p2, d; + double t; + + if (p1x == p2x && p1y == p2y) { + /* Zero rotation */ + memset(&(q->d), 0, sizeof(Vector)); + q->w = 1.0; + return; + } + + /* + * First, figure out z-coordinates for projection of P1 and P2 to + * deformed sphere + */ + p1.x = p1x; p1.y = p1y; p1.z = tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y); + p2.x = p2x; p2.y = p2y; p2.z = tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y); + + /* + * Now, we want the cross product of P1 and P2 + */ + vector_vmul(&a,&p2,&p1); + + /* + * Figure out how much to rotate around that axis. + */ + vector_sub(&d,&p1,&p2); + t = vector_length(&d) / (2.0*TRACKBALLSIZE); + + /* + * Avoid problems with out-of-control values... + */ + if (t > 1.0) t = 1.0; + if (t < -1.0) t = -1.0; + phi = 2.0 * asin(t); + + axis_to_quat(&a,phi,q); +} + +/* + * Given an axis and angle, compute quaternion. + */ +void +axis_to_quat(Vector *a, double phi, Quaternion *q) { + vector_normal(a); + memcpy( &(q->d), a, sizeof(Vector) ); + vector_mul( &(q->d), &(q->d), -sin(phi/2.0) ); + q->w = cos(phi/2.0); +} + +/* + * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet + * if we are away from the center of the sphere. + */ +static double +tb_project_to_sphere(double r, double x, double y) +{ + double d, t, z; + + d = sqrt(x*x + y*y); + if (d < r * 0.70710678118654752440) { /* Inside sphere */ + z = sqrt(r*r - d*d); + } else { /* On hyperbola */ + t = r / 1.41421356237309504880; + z = t*t / d; + } + return z; +} + + diff --git a/trackball.h b/trackball.h new file mode 100644 index 0000000..f946f5f --- /dev/null +++ b/trackball.h @@ -0,0 +1,65 @@ +#ifndef __TRACKBALL_H__ +#define __TRACKBALL_H__ +/* + * (c) Copyright 1993, 1994, Silicon Graphics, Inc. + * ALL RIGHTS RESERVED + * Permission to use, copy, modify, and distribute this software for + * any purpose and without fee is hereby granted, provided that the above + * copyright notice appear in all copies and that both the copyright notice + * and this permission notice appear in supporting documentation, and that + * the name of Silicon Graphics, Inc. not be used in advertising + * or publicity pertaining to distribution of the software without specific, + * written prior permission. + * + * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" + * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, + * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY + * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, + * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF + * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE + * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE. + * + * US Government Users Restricted Rights + * Use, duplication, or disclosure by the Government is subject to + * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph + * (c)(1)(ii) of the Rights in Technical Data and Computer Software + * clause at DFARS 252.227-7013 and/or in similar or successor + * clauses in the FAR or the DOD or NASA FAR Supplement. + * Unpublished-- rights reserved under the copyright laws of the + * United States. Contractor/manufacturer is Silicon Graphics, + * Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311. + * + * OpenGL(TM) is a trademark of Silicon Graphics, Inc. + */ +/* + * trackball.h + * A virtual trackball implementation + * Written by Gavin Bell for Silicon Graphics, November 1988. + */ + +#include "xgalaxy.h" + +/* + * Pass the x and y coordinates of the last and current positions of + * the mouse, scaled so they are from (-1.0 ... 1.0). + * + * The resulting rotation is returned as a quaternion rotation in the + * first paramater. + */ +void +trackball(Quaternion *a, double p1x, double p1y, double p2x, double p2y); + +/* + * This function computes a quaternion based on an axis (defined by + * the given vector) and an angle about which to rotate. The angle is + * expressed in radians. The result is put into the third argument. + */ +void +axis_to_quat(Vector *a, double phi, Quaternion *q); + +#endif diff --git a/xgalaxy.c b/xgalaxy.c new file mode 100644 index 0000000..515e485 --- /dev/null +++ b/xgalaxy.c @@ -0,0 +1,714 @@ +#include +#include +#include +#include +#include +#include + +#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, "" }, + { "/File/_Open", "O", openFile, 0, NULL }, + { "/File/_Save", "S", saveFile, 0, NULL }, + { "/File/Save _As", NULL, saveAsFile, 0, NULL }, + { "/File/sep1", NULL, NULL, 0, "" }, + { "/File/Quit", "Q", gtk_main_quit, 0, NULL }, + { "/_Options", NULL, NULL, 0, "" }, + { "/Options/Clear", NULL, clearData, 0, NULL }, + { "/Options/Fill", NULL, NULL, 0, NULL }, + { "/_Help", NULL, NULL, 0, "" }, + { "/_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, "
", 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, "
"); + + return menubar; +} + +void +page_switched(GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer user_data) { + XGalaxy.page_active = page_num; +} + +int +main( int argc, char *argv[] ) { + GtkWidget *window, *notebook, *menubar, *main_vbox; + int rc; + + memset(&XGalaxy, 0, sizeof(XGalaxy)); + XGalaxy.angle.w=1; + XGalaxy.motion.w=1.0; + XGalaxy.Scale=1.0; + + gtk_init (&argc, &argv); + + if ( (rc=pthread_mutex_init(&(XGalaxy.mutex), NULL)) != 0 ) { + g_print("pthread_mutex_init returns %d: %s\n", rc, g_strerror(errno)); + exit(1); + } + + XGalaxy.window = window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + galaxy_set_title(); + gtk_window_set_default_size (GTK_WINDOW(window), 640, 480); + gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (delete), NULL); + + main_vbox = gtk_vbox_new (FALSE, 1); + gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 2); + gtk_container_add(GTK_CONTAINER(window), main_vbox); + gtk_widget_show (main_vbox); + + menubar = get_main_menu(window); + gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, TRUE, 0); + gtk_widget_show (menubar); + + notebook = gtk_notebook_new (); + gtk_notebook_set_tab_pos(GTK_NOTEBOOK (notebook), GTK_POS_TOP); + gtk_widget_show(notebook); + + gtk_notebook_append_page( GTK_NOTEBOOK (notebook), inputPage(), gtk_label_new("Input") ); + gtk_notebook_append_page( GTK_NOTEBOOK (notebook), viewPage(), gtk_label_new("View") ); + gtk_notebook_append_page( GTK_NOTEBOOK (notebook), resPage(), gtk_label_new("Data View") ); + gtk_signal_connect (GTK_OBJECT(notebook),"switch-page", (GtkSignalFunc) page_switched, NULL); + XGalaxy.notebook = notebook; + + gtk_box_pack_start (GTK_BOX (main_vbox), notebook, TRUE, TRUE, 0); + + + setlocale(LC_NUMERIC, "C"); + gtk_widget_show(window); + + gtk_main (); + + return 0; +} + diff --git a/xgalaxy.h b/xgalaxy.h new file mode 100644 index 0000000..ec0937b --- /dev/null +++ b/xgalaxy.h @@ -0,0 +1,140 @@ +#ifndef __XGALAXY_H__ +#define __XGALAXY_H__ + +#include +#include +#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