add .gitignore
[xgalaxy.git] / graphics.c
1 #include <stdio.h>
2 #include <errno.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <gtk/gtk.h>
6 #include <math.h>
7 #include <pthread.h>
8
9 #include "tmalloc.h"
10 #include "xgalaxy.h"
11 #include "trackball.h"
12
13 typedef struct {
14         Vector  v;
15         Vector  V;
16         GdkColor color;
17         char    name[2];
18 } Axis3D;
19
20 static Axis3D axises[]={
21         {
22                 { 1.0, 0.0, 0.0 },
23                 { 0.0, 0.0, 0.0 },
24                 { 0,  8480, 57570, 22360 },
25                 "X"
26         },
27         {
28                 { 0.0, 1.0, 0.0 },
29                 { 0.0, 0.0, 0.0 },
30                 { 0,  8480, 47300, 57570 },
31                 "Y"
32         },
33         {
34                 { 0.0, 0.0, 1.0 },
35                 { 0.0, 0.0, 0.0 },
36                 { 0,  57570, 8480, 8480 },
37                 "Z"
38         }
39 };
40
41 static int
42 cmpAxis3D(const void *a, const void *b) {
43         return ( ((Axis3D*)a)->V.z > ((Axis3D*)b)->V.z ) ? 1 : -1;
44 }
45
46 void
47 drawAxis() {
48         double          minsize = fmin( XGalaxy.drawing_area->allocation.width, XGalaxy.drawing_area->allocation.height );
49         double Length = 0.4*minsize;
50         GdkGC   *gc = gdk_gc_new(XGalaxy.drawing_area->window);
51         PangoLayout *pl = gtk_widget_create_pango_layout(XGalaxy.drawing_area, NULL);
52         int i;
53
54         for(i=0;i<3;i++) {
55                 rotate( &(axises[i].V), &(axises[i].v), &(XGalaxy.angle) );
56                 vector_mul( &(axises[i].V),  &(axises[i].V), Length );
57         }
58
59         qsort(axises, 3, sizeof(Axis3D), cmpAxis3D); 
60                 
61
62         for(i=0;i<3;i++) {
63                 antialiasedLine(
64                         gc, 
65                         XGalaxy.drawing_area->allocation.width/2,
66                         XGalaxy.drawing_area->allocation.height/2,
67                         XGalaxy.drawing_area->allocation.width/2  + axises[i].V.x,
68                         XGalaxy.drawing_area->allocation.height/2 - axises[i].V.y,
69                         &(axises[i].color)
70                 );
71                 gdk_gc_set_rgb_fg_color(gc, &(axises[i].color));
72                 pango_layout_set_text(pl, axises[i].name, -1);
73                 gdk_draw_layout(
74                         XGalaxy.pixmap,
75                         gc,
76                         XGalaxy.drawing_area->allocation.width/2 + axises[i].V.x,
77                         XGalaxy.drawing_area->allocation.height/2 - axises[i].V.y,
78                         pl
79                 );
80         }
81
82         g_object_unref(G_OBJECT(pl));
83         g_object_unref(G_OBJECT(gc));
84 }
85
86
87 void
88 fitGalaxy() {
89         double max=-1.0;
90         int i;
91
92         for(i=0;i<XGalaxy.nentry;i++) {
93                 rotate( &(XGalaxy.entry[i].a), &(XGalaxy.entry[i].c), &(XGalaxy.angle) );
94                 if ( fabs(XGalaxy.entry[i].a.x) > max )
95                         max = fabs(XGalaxy.entry[i].a.x); 
96                 if ( fabs(XGalaxy.entry[i].a.y) > max )
97                         max = fabs(XGalaxy.entry[i].a.y); 
98                 if ( fabs(XGalaxy.entry[i].a.z) > max )
99                         max = fabs(XGalaxy.entry[i].a.z);
100         }
101
102         XGalaxy.Scale = 1.0/(3.0*max); 
103 }
104
105 static int
106 cmpStar(const void *a, const void *b) {
107         return ( ((Star*)a)->a.z > ((Star*)b)->a.z ) ? 1 : -1;
108 }
109
110 #define MIN_BRIGHT (0.4)
111
112
113
114 void
115 drawStars() {
116         int i;
117         double          minsize = fmin( XGalaxy.drawing_area->allocation.width, XGalaxy.drawing_area->allocation.height );
118         double  r, brightness;
119         GdkGC   *gc = gdk_gc_new(XGalaxy.drawing_area->window);
120
121         if ( !XGalaxy.trace ) {
122                 gdk_draw_rectangle (XGalaxy.pixmap,
123                       XGalaxy.drawing_area->style->white_gc,
124                       TRUE,
125                       0, 0,
126                       XGalaxy.drawing_area->allocation.width,
127                       XGalaxy.drawing_area->allocation.height
128                 );
129                 if ( XGalaxy.drawaxis) drawAxis();
130         }
131
132         if ( XGalaxy.runing ) {
133                 pthread_mutex_lock(&(XGalaxy.mutex));
134                 memcpy( XGalaxy.tmpentry, XGalaxy.galaxy.stars, sizeof(Star)*XGalaxy.galaxy.nstars );
135                 pthread_mutex_unlock(&(XGalaxy.mutex));
136
137                 for(i=0;i<XGalaxy.nentry;i++)
138                         rotate( &(XGalaxy.tmpentry[i].a), &(XGalaxy.tmpentry[i].c), &(XGalaxy.angle) );
139         
140                 qsort(XGalaxy.tmpentry, XGalaxy.nentry, sizeof(Star), cmpStar); 
141
142                 for(i=0;i<XGalaxy.nentry;i++) {
143                         r = (10 + (10*XGalaxy.Scale*XGalaxy.tmpentry[i].a.z))/2.0;
144                         if (r<1.5) r = 1.5;
145                         brightness = MIN_BRIGHT + (1.0-MIN_BRIGHT)*(1.0 + XGalaxy.Scale*XGalaxy.tmpentry[i].a.z) / 2.0;
146                         if ( brightness < MIN_BRIGHT ) brightness = MIN_BRIGHT;
147                         if ( brightness > 1.0 )     brightness = 1.0;
148                         DrawDisk(
149                                 gc, 
150                                 ((double)XGalaxy.drawing_area->allocation.width)/2.0 + (minsize*XGalaxy.Scale*XGalaxy.tmpentry[i].a.x),
151                                 ((double)XGalaxy.drawing_area->allocation.height)/2.0 - (minsize*XGalaxy.Scale*XGalaxy.tmpentry[i].a.y),
152                                 r, 
153                                 1.0,
154                                 brightness
155                         );
156                 }
157         }
158         g_object_unref(G_OBJECT(gc));
159 }
160
161 void
162 drawGalaxy() {
163         GdkRectangle update_rect;
164
165         if (!XGalaxy.pixmap)
166                 return;
167         update_rect.x=0;        
168         update_rect.y=0;
169         update_rect.width = XGalaxy.drawing_area->allocation.width;     
170         update_rect.height = XGalaxy.drawing_area->allocation.height;
171
172         gdk_window_invalidate_rect (XGalaxy.drawing_area->window, &update_rect, FALSE);
173 }
174
175 void
176 angle_changed(GtkRange *range, gpointer user_data) {
177         double angle = gtk_range_get_value(range)*M_PI/180.0;
178         Quaternion      old, newquaternion;
179
180         if (XGalaxy.locksignal)
181                 return;
182
183         if (XGalaxy.trace) {
184                 XGalaxy.locksignal=1;
185                 switch((int)user_data) {
186                         case 'X': gtk_range_set_value(range, XGalaxy.Xangle*180.0/M_PI); break;
187                         case 'Y': gtk_range_set_value(range, XGalaxy.Yangle*180.0/M_PI); break;
188                         case 'Z': gtk_range_set_value(range, XGalaxy.Zangle*180.0/M_PI); break;
189                         default: break;
190                 }
191                 XGalaxy.locksignal=0;
192                 return;
193         }
194
195         memcpy(&old, &(XGalaxy.angle), sizeof(old));
196         memset(&newquaternion, 0 , sizeof(newquaternion));
197         switch( (int)user_data ) {
198                 case 'X': 
199                         newquaternion.d.x = sin( (angle-XGalaxy.Xangle)/2.0 ); 
200                         newquaternion.w = cos( (angle-XGalaxy.Xangle)/2.0 );
201                         XGalaxy.Xangle=angle; 
202                         break; 
203                 case 'Y': 
204                         newquaternion.d.y = sin( (angle-XGalaxy.Yangle)/2.0 ); 
205                         newquaternion.w = cos( (angle-XGalaxy.Yangle)/2.0 );
206                         XGalaxy.Yangle=angle; 
207                         break; 
208                 case 'Z': 
209                         newquaternion.d.z = sin( (angle-XGalaxy.Zangle)/2.0 ); 
210                         newquaternion.w = cos( (angle-XGalaxy.Zangle)/2.0 );
211                         XGalaxy.Zangle=angle; 
212                         break;
213                 default: g_print("Unknown axis: %c\n", (char)(int)user_data);
214         }
215
216         //quater_vmul( &(XGalaxy.angle), &old, &newquaternion );
217         quater_vmul( &(XGalaxy.angle), &newquaternion, &old );
218
219         drawGalaxy();
220 }
221
222 void
223 clearDraw() {
224         if ( !XGalaxy.pixmap )
225                 return;
226         gdk_draw_rectangle (XGalaxy.pixmap,
227                 XGalaxy.drawing_area->style->white_gc,
228                 TRUE,
229                 0, 0,
230                 XGalaxy.drawing_area->allocation.width,
231                 XGalaxy.drawing_area->allocation.height);
232         if ( XGalaxy.drawaxis ) drawAxis();
233 }
234
235 gint
236 expose_event( GtkWidget      *widget, GdkEventExpose *event ) {
237         /* draw only last expose */
238         if ( event->count > 0) 
239                 return TRUE;
240
241         drawStars();
242         gdk_draw_pixmap(widget->window,
243                   widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
244                   XGalaxy.pixmap,
245                   event->area.x, event->area.y,
246                   event->area.x, event->area.y,
247                   event->area.width, event->area.height);
248
249         return FALSE;
250 }
251
252 gint
253 configure_event( GtkWidget         *widget, GdkEventConfigure *event ) {
254         if (XGalaxy.pixmap)
255                 g_object_unref(XGalaxy.pixmap);
256         
257         XGalaxy.pixmap = gdk_pixmap_new(widget->window,
258                           widget->allocation.width,
259                           widget->allocation.height,  
260                           -1);
261
262         clearDraw();
263  
264         return TRUE;
265 }
266
267 gboolean 
268 mouse_motion_notify(GtkWidget      *widget, GdkEventMotion *event) {
269         int x = 0;
270         int y = 0;
271         GdkModifierType state = 0;
272         
273         if (XGalaxy.locksignal || XGalaxy.trace )
274                 return TRUE;
275
276         if (event->is_hint) 
277                 gdk_window_get_pointer(event->window, &x, &y, &state);
278         else {
279                 x = event->x;
280                 y = event->y;
281                 state = event->state;
282         }
283         if (state & GDK_BUTTON1_MASK) {
284                 double width, height;
285                 Quaternion old;
286                 Vector angles;
287
288                 width = widget->allocation.width;
289                 height = widget->allocation.height;
290                 trackball( &(XGalaxy.motion),
291                         (2.0*XGalaxy.beginx -            width) / width,
292                         (          height - 2.0*XGalaxy.beginy) / height,
293                         (           2.0*x -            width) / width,
294                         (          height -            2.0*y) / height );
295
296                 memcpy(&old, &(XGalaxy.angle), sizeof(old));
297                 //quater_vmul( &(XGalaxy.angle), &old, &(XGalaxy.motion) );
298                 quater_vmul( &(XGalaxy.angle), &(XGalaxy.motion), &old );
299
300                 XGalaxy.locksignal=1;
301                 quater_to_angles( &(XGalaxy.angle), &angles);
302                 XGalaxy.Xangle = angles.x;
303                 gtk_range_set_value(GTK_RANGE(XGalaxy.XSlider), angles.x*180.0/M_PI);
304                 XGalaxy.Yangle = angles.y;
305                 gtk_range_set_value(GTK_RANGE(XGalaxy.YSlider), angles.y*180.0/M_PI);
306                 XGalaxy.Zangle = angles.z;
307                 gtk_range_set_value(GTK_RANGE(XGalaxy.ZSlider), angles.z*180.0/M_PI);
308                 XGalaxy.locksignal=0;
309
310                 XGalaxy.beginx = x;
311                 XGalaxy.beginy = y;
312
313                 drawGalaxy();
314         }
315         return TRUE;
316 }