chiark / gitweb /
can resize
[moebius2.git] / project.c
1 /*
2  * Displays a conformation
3  */
4
5 #include <X11/Xlib.h>
6 #include <X11/Xutil.h>
7
8 #include "mgraph.h"
9
10 #define MAXTRIS (N*2)
11
12 typedef struct { double vertex[3][D3]; } Triangle;
13
14 static Triangle trisbuffer[MAXTRIS], *displaylist[MAXTRIS];
15 static int ntris;
16 static Vertices conformation;
17
18 #define STATIC_MATRIX(x)                                        \
19   static double x[D3][D3];                                      \
20   static gsl_matrix x##_gsl= { D3,D3,D3,&x[0][0] };
21
22 STATIC_MATRIX(transform);
23
24 const char *input_filename;
25
26 static void read_input(void) {
27   FILE *f;
28   int r;
29   
30   f= fopen(input_filename, "rb");  if (!f) diee("input file");
31   errno= 0;
32   r= fread(&conformation,sizeof(conformation),1,f);  if (r!=1) diee("fread");
33   fclose(f);
34 }
35
36 static void transform_coordinates(void) {
37   int v;
38
39   FOR_VERTEX(v) {
40   }
41 }
42
43 static void addtriangle(int va, int vb, int vc) {
44   Triangle *t= &trisbuffer[ntris];
45   int k;
46   
47   assert(ntris < MAXTRIS);
48   K {
49     t->vertex[0][k]= conformation[va][k];
50     t->vertex[1][k]= conformation[vb][k];
51     t->vertex[2][k]= conformation[vc][k];
52   }
53   displaylist[ntris++]= t;
54 }
55
56 static void generate_display_list(void) {
57   int vb, ve[3], e;
58
59   ntris= 0;
60   FOR_VERTEX(vb) {
61     /* We use the two triangles in the parallelogram vb, vb+e1, vb+e0, vb+e2.
62      * We go round each triangle clockwise (although our surface is non-
63      * orientable so it shouldn't matter).
64      */
65     for (e=0; e<3; e++) ve[e]= EDGE_END2(vb,e);
66     if (ve[0]>=0) {
67       if (ve[1]>=0) addtriangle(vb,ve[0],ve[1]);
68       if (ve[2]>=0) addtriangle(vb,ve[2],ve[0]);
69     }
70   }
71 }    
72
73 static int dl_compare(const void *tav, const void *tbv) {
74   const Triangle *const *tap= tav, *ta= *tap;
75   const Triangle *const *tbp= tbp, *tb= *tbp;
76   double za= ta->vertex[0][2];
77   double zb= tb->vertex[0][2];
78   return za > zb ? -1 :
79          za < zb ? +1 : 0;
80 }
81
82 static void sort_display_list(void) {
83   qsort(displaylist, ntris, sizeof(*displaylist), dl_compare);
84 }
85
86 /*---------- X stuff ----------*/
87
88 #define WSZ 400
89
90 static Display *display;
91 static Pixmap pixmap, doublebuffers[2];
92 static Window window;
93 static GC linegc, fillgc;
94 static int wwidth=WSZ, wheight=WSZ, wmindim=WSZ, wmaxdim=WSZ;
95 static int ncut, currentbuffer, x11depth, x11screen;
96
97 static double scale= 0.3;
98 static double eye_z= -10, eye_x= 0;
99 static double cut_z= -9;
100
101 static void xdie(int l, const char *str) {
102   fprintf(stderr,"X library call failed, line %d: %s\n", l, str);
103 }
104
105 #define XA(w) ((w) ? (void)0 : xdie(__LINE__, #w))
106
107 static void drawtriangle(const Triangle *t) {
108   XPoint points[4];
109   int i;
110
111   for (i=0; i<3; i++) {
112     double *v= t->vertex[i];
113     double x= v[0];
114     double y= v[1];
115     double z= v[2];
116
117     if (z < cut_z) { ncut++; return; }
118     
119     double zezezp= eye_z / (eye_z - z);
120     points[i].x= scale * wmindim * (zezezp * (x - eye_x) + eye_x) + wwidth/2;
121     points[i].y= scale * wmindim * (zezezp *  y                 ) + wheight/2;
122   }
123   points[3]= points[0];
124
125   XA( XFillPolygon(display,pixmap,fillgc, points,3,Convex,CoordModeOrigin) );
126   XA( XDrawLines(display,pixmap,linegc,points, 4,CoordModeOrigin) );
127 }
128
129 static const unsigned long core_event_mask=
130   ButtonPressMask|ButtonReleaseMask|StructureNotifyMask|ButtonMotionMask;
131
132 static void mkpixmaps(void) {
133   for (currentbuffer=0; currentbuffer<2; currentbuffer++) {
134     XA( pixmap= XCreatePixmap(display,window,wwidth,wheight,x11depth) );
135     doublebuffers[currentbuffer]= pixmap;
136   }
137   currentbuffer= 0;
138 }
139
140 static void display_prepare(void) {
141   XGCValues gcv;
142   XSetWindowAttributes wa;
143   XVisualInfo vinfo;
144   XSizeHints hints;
145   
146   XA( display= XOpenDisplay(0) );
147   x11screen= DefaultScreen(display);
148   x11depth= DefaultDepth(display,x11screen);
149   XA( XMatchVisualInfo(display,x11screen,x11depth, TrueColor,&vinfo) );
150   
151   wa.event_mask= core_event_mask;
152   XA( window= XCreateWindow(display, DefaultRootWindow(display),
153                             0,0, wwidth,wheight, 0,x11depth,
154                             InputOutput, vinfo.visual,
155                             CWEventMask, &wa) );
156
157   hints.flags= USPosition;
158   hints.x= 10;
159   hints.y= 10;
160   XSetWMNormalHints(display,window,&hints);
161
162   mkpixmaps();
163
164   gcv.function= GXcopy;
165   gcv.plane_mask= ~0UL;
166   gcv.foreground= WhitePixel(display,x11screen);
167   linegc= XCreateGC(display,pixmap, GCFunction|GCPlaneMask|GCForeground, &gcv);
168   
169   gcv.function= GXclear;
170   gcv.plane_mask= ~0UL;
171   fillgc= XCreateGC(display,pixmap, GCFunction|GCPlaneMask, &gcv);
172 }
173
174 static void display_conformation(void) {
175   Triangle *const *t;
176   int i;
177   
178   pixmap= doublebuffers[currentbuffer];
179   XA( XFillRectangle(display,pixmap,fillgc,0,0,wwidth,wheight) );
180   for (i=0, t=displaylist, ncut=0; i<ntris; i++, t++)
181     drawtriangle(*t);
182   printf("shown, %d/%d triangles cut\n", ncut, ntris);
183   
184   XA( XSetWindowBackgroundPixmap(display,window,pixmap) );
185   XA( XClearWindow(display,window) );
186   currentbuffer= !currentbuffer;
187 }
188
189 static void show(void) {
190   read_input();
191   transform_coordinates();
192   generate_display_list();
193   sort_display_list();
194   display_conformation();
195 }
196
197 typedef struct {
198   const char *name;
199   void (*start)(void);
200   void (*delta)(double dx, double dy);
201   void (*conclude)(void);
202   void (*abandon)(void);
203 } Drag;
204
205 #define DRAG(x)                                 \
206   static const Drag drag_##x= {                 \
207     #x, drag_##x##_start, drag_##x##_delta,     \
208     drag_##x##_conclude, drag_##x##_abandon     \
209   }
210
211 #define DRAG_SAVING(x, thing)                           \
212   static typeof(thing) original_##thing;                \
213   static void drag_##x##_start(void) {                  \
214     memcpy(&original_##thing, &thing, sizeof(thing));   \
215   }                                                     \
216   static void drag_##x##_conclude(void) { }             \
217   static void drag_##x##_abandon(void) {                \
218     memcpy(&thing, &original_##thing, sizeof(thing));   \
219     show();                                             \
220   }                                                     \
221   DRAG(x)
222
223 static void drag_none_start(void) { }
224 static void drag_none_delta(double dx, double dy) { }
225 static void drag_none_conclude(void) { }
226 static void drag_none_abandon(void) { }
227 DRAG(none);
228
229 static void drag_rotate_delta(double dx, double dy) {
230   /* We multiple our transformation matrix by this matrix:
231    *     [  1   0   0 ]
232    *     [  0   1   0 ]
233    *     [ dx  dy   1 ]
234    * and then renormalise.
235    */
236
237   STATIC_MATRIX(rotateby);
238   STATIC_MATRIX(qr);
239   static double tau[D3];
240   static gsl_vector tau_gsl= { D3,1,&tau[0] };
241   
242   int k;
243   
244   K rotateby[k][k]= 1;
245   rotateby[0][2]= dx;
246   rotateby[1][2]= dy;
247   
248   gsl_blas_dgemm(CblasNoTrans,CblasNoTrans, 1.0,
249                  &rotateby_gsl,&transform_gsl, 0.0,&qr_gsl);
250   gsl_linalg_QR_decomp(&qr_gsl, &tau_gsl);
251   gsl_linalg_QR_unpack(&qr_gsl, &tau_gsl,
252                        &transform_gsl, &rotateby_gsl /*dummy*/);
253
254   printf("drag_rotate_delta...\n");
255   show();
256 }
257 DRAG_SAVING(rotate, transform);
258
259 static void drag_scale_delta(double dx, double dy) {
260   scale *= pow(3.0, dy);
261   show();
262 }
263 DRAG_SAVING(scale, scale);
264
265 static const Drag *drag= &drag_none;
266
267 static int drag_last_x, drag_last_y;
268
269 static void drag_position(int x, int y) {
270   drag->delta((x - drag_last_x) * 1.0 / wmaxdim,
271               (y - drag_last_y) * 1.0 / wmaxdim);
272   drag_last_x= x;
273   drag_last_y= y;
274 }
275
276 static void event_button(XButtonEvent *e) {
277   if (e->window != window || !e->same_screen) return;
278   if (e->type == ButtonPress) {
279     if (e->state || drag != &drag_none) {
280       printf("drag=%s press state=0x%lx abandon\n",
281              drag->name, (unsigned long)e->state);
282       drag->abandon();
283       drag= &drag_none;
284       return;
285     }
286     switch (e->button) {
287     case Button1: drag= &drag_rotate;  break;
288     case Button2: drag= &drag_scale;   break;
289     default: printf("unknown drag start %d\n", e->button);
290     }
291     printf("drag=%s press button=%lu start %d,%d\n",
292            drag->name, (unsigned long)e->button, e->x, e->y);
293     drag_last_x= e->x;
294     drag_last_y= e->y;
295     drag->start();
296   }
297   if (e->type == ButtonRelease) {
298     printf("drag=%s release %d,%d\n", drag->name, e->x, e->y);
299     drag_position(e->x, e->y);
300     drag->conclude();
301     drag= &drag_none;
302   }
303 }
304
305 static void event_motion(int x, int y) {
306   printf("drag=%s motion %d,%d\n", drag->name, x, y);
307   drag_position(x,y);
308 }
309
310 static void event_config(XConfigureEvent *e) {
311   if (e->width == wwidth && e->height == wheight)
312     return;
313
314   wwidth= e->width;  wheight= e->height;
315   wmaxdim= wwidth > wheight ? wwidth : wheight;
316   wmindim= wwidth < wheight ? wwidth : wheight;
317   
318   XA( XSetWindowBackground(display,window,BlackPixel(display,x11screen)) );
319   for (currentbuffer=0; currentbuffer<2; currentbuffer++)
320     XA( XFreePixmap(display, doublebuffers[currentbuffer]) );
321
322   mkpixmaps();
323   show();
324 }
325
326 int main(int argc, const char *const *argv) {
327   XEvent event;
328   int motion_deferred=0, motion_x=-1, motion_y=-1;
329   
330   if (argc != 2 || argv[1][0]=='-') {
331     fputs("need filename\n",stderr); exit(8);
332   }
333   input_filename= argv[1];
334
335   read_input();
336   display_prepare();
337   show();
338
339   XMapWindow(display,window);
340   for (;;) {
341     if (motion_deferred) {
342       int r= XCheckMaskEvent(display,~0UL,&event);
343       if (!r) {
344         event_motion(motion_x, motion_y);
345         motion_deferred=0;
346         continue;
347       }
348     } else {
349       XNextEvent(display,&event);
350     }
351     switch (event.type) {
352
353     case ButtonPress:
354     case ButtonRelease:     event_button(&event.xbutton);     break;
355       
356     case ConfigureNotify:   event_config(&event.xconfigure);  break;
357
358     case MotionNotify:
359       motion_x= event.xmotion.x;
360       motion_y= event.xmotion.y;
361       motion_deferred= 1;
362       continue;
363       
364     default:
365       printf("unknown event type %u 0x%x\n", event.type,event.type);
366     }
367   }
368 }
369