Initial release of Maemo 5 port of gnuplot
[gnuplot] / term / grass.trm
1 /* Hello, Emacs, this is -*-C-*- */
2 /*[
3  * GNUPLOT - grass.trm
4  * $Id:
5  *-
6  * Copyright (C) 1992-1995, 1999, 2004 James Darrell McCauley
7  *
8  * Permission to use, copy, and distribute this software and its
9  * documentation for any purpose with or without fee is hereby granted,
10  * provided that the above copyright notice appear in all copies and
11  * that both that copyright notice and this permission notice appear
12  * in supporting documentation.
13  *
14  * Permission to modify the software is granted, but not the right to
15  * distribute the complete modified source code.  Modifications are to
16  * be distributed as patches to the released version.  Permission to
17  * distribute binaries produced by compiling modified sources is granted,
18  * provided you
19  *   1. distribute the corresponding source modifications from the
20  *    released version in the form of a patch file along with the binaries,
21  *   2. add special version identification to distinguish your version
22  *    in addition to the base release version number,
23  *   3. provide your name and address as the primary contact for the
24  *    support of your modified version, and
25  *   4. retain our contact information in regard to use of the base
26  *    software.
27  * Permission to distribute the released version of the source code along
28  * with corresponding source modifications in the form of a patch file is
29  * granted with same provisions 2 through 4 for binary distributions.
30  *
31  * This software is provided "as is" without express or implied warranty
32  * to the extent permitted by applicable law.
33  *
34  * This software  is provided "as is" without express or implied warranty.
35  *
36  * This file is included by ../term.c.
37  *
38  * This terminal driver supports:
39  *  GRASS graphics driver
40  *
41  * AUTHOR
42  * James Darrell McCauley, PhD        http://soils.ecn.purdue.edu/~mccauley/
43  * Dept of Agricultural Engineering   mccauley@ecn.purdue.edu
44  * Purdue University                  tel: 317.494.1198 fax: 317.496.1115
45  *
46  * 05 Apr 1995 - cleaned up code by adding explicit function declarations.
47  *               compiles clean with 'gcc -Wall'
48  * 14 Apr 1995 - adapted for new layout, added font selection
49  *
50  * 13 Jan 1999 - Copyright statement changed to new gnuplot copyright
51  *               Permission given by orig author in private email (lh)
52  *
53  * send your comments or suggestions to (grassp-list@moon.cecer.army.mil).
54  *
55 ]*/
56
57 #include "driver.h"
58
59 #ifdef TERM_REGISTER
60 register_term(grass)
61 #endif
62
63
64 #ifdef TERM_PROTO
65 TERM_PUBLIC void GRASS_move __PROTO((unsigned int x, unsigned int y));
66 TERM_PUBLIC void GRASS_options __PROTO((void));
67 TERM_PUBLIC void GRASS_init __PROTO((void));
68 TERM_PUBLIC void GRASS_reset __PROTO((void));
69 TERM_PUBLIC void GRASS_graphics __PROTO((void));
70 TERM_PUBLIC void GRASS_text __PROTO((void));
71 TERM_PUBLIC void GRASS_vector __PROTO((unsigned int x, unsigned int y));
72 TERM_PUBLIC void GRASS_linetype __PROTO((int lt));
73 TERM_PUBLIC void GRASS_put_text __PROTO((unsigned int x, unsigned int y, const char *str));
74 TERM_PUBLIC int GRASS_text_angle __PROTO((int ang));
75 TERM_PUBLIC int GRASS_justify_text __PROTO((enum JUSTIFY mode));
76 TERM_PUBLIC void GRASS_point __PROTO((unsigned int x, unsigned int y, int point));
77 TERM_PUBLIC int GRASS_set_font __PROTO((const char *font));
78 /* TERM_PUBLIC void GRASS_set_pointsize __PROTO((double size)); */
79 TERM_PUBLIC void GRASS_arrow __PROTO((unsigned int sx, unsigned int sy, unsigned int ex, unsigned int ey, int head));
80 #endif /* TERM_PROTO */
81
82 #ifndef TERM_PROTO_ONLY
83 #ifdef TERM_BODY
84
85 #include <stdio.h>
86 #include <string.h>
87 /* #include "gis.h" */ /* this causes conflicts with things in term.c */
88
89 #define GRASS_XMAX 1000
90 #define GRASS_YMAX 1000
91 #define GRASS_VCHAR 5
92 #define GRASS_HCHAR 5
93 #define GRASS_VTIC 3
94 #define GRASS_HTIC 3
95
96 #define PNT_SIZE 3
97 #define TYPE_DOT  -1
98 #define TYPE_X  0
99 #define TYPE_PLUS   1
100 #define TYPE_BOX    2
101 #define TYPE_DIAMOND    3       /* need type 4 and 5 */
102 #define TYPE_TRIANGLE 4
103 #define TYPE_OCTO 5
104 #define TYPE_ITRIANGLE 6
105 #define TYPE_FBOX 7
106 #define TYPE_FTRIANGLE 8
107 #define TYPE_FITRIANGLE 9
108 #define TYPE_FOCTO 10
109
110 static void cont_abs __PROTO((int x, int y));
111 static void draw_points_dot __PROTO((int x, int y));
112 static void draw_points_diamond __PROTO((int x, int y));
113 static void draw_points_box __PROTO((int x, int y));
114 static void draw_points_fbox __PROTO((int x, int y));
115 static void draw_points_itriangle __PROTO((int x, int y));
116 static void draw_points_fitriangle __PROTO((int x, int y));
117 static void draw_points_triangle __PROTO((int x, int y));
118 static void draw_points_ftriangle __PROTO((int x, int y));
119 static void draw_points_plus __PROTO((int x, int y));
120 static void draw_points_octo __PROTO((int x, int y));
121 static void draw_points_focto __PROTO((int x, int y));
122 static void draw_points_x __PROTO((int x, int y));
123
124 static int R__curx, R__cury;
125
126 static int grass_yoffset;
127 static int grass_xoffset;
128 static int y_max;
129 static int points_buf_x[PNT_SIZE*PNT_SIZE]; /* for filled point types */
130 static int points_buf_y[PNT_SIZE*PNT_SIZE];
131
132 extern int R_move_abs ();
133 extern int R_cont_abs ();
134
135 TERM_PUBLIC void
136 GRASS_move(unsigned int x, unsigned int y)
137 {
138   /* R_move_abs (grass_xoffset+x, grass_yoffset-y + y_max); */
139   R_move_abs (grass_xoffset+x, grass_yoffset-y);
140 }
141
142 static void
143 cont_abs(int x, int y)
144 {
145   /* R_cont_abs (grass_xoffset+x, grass_xoffset-y + y_max); */
146   R_cont_abs (grass_xoffset+x, grass_yoffset-y);
147 }
148
149 TERM_PUBLIC void
150 GRASS_options()
151 {
152   options_null ();              /* no options to begin with */
153 }
154
155 TERM_PUBLIC void
156 GRASS_init()
157 {
158     /* char buff[128]; */
159     char window_name[64];
160     float size = 3.0;
161     /* int backcolor; */
162     int dots_per_line;
163     int top, b, l, r;
164     /* int textcolor; */
165     struct termentry *t = term;
166
167     extern int G_gisinit();
168     extern int R_open_driver();
169     extern int D_setup();
170     extern int D_get_cur_wind();
171     extern int G_fatal_error();
172     extern int D_set_cur_wind();
173     extern int D_get_screen_window();
174     extern int R_set_window();
175     extern int R_text_size();
176     extern int R_font();
177     extern int R_screen_top();
178     extern int R_screen_bot();
179     extern int D_erase_window();
180
181     G_gisinit ("g.gnuplot");
182
183     R_open_driver ();
184
185     D_setup (0);
186
187     if (D_get_cur_wind (window_name))
188         G_fatal_error ("No current window");
189
190     if (D_set_cur_wind (window_name))
191         G_fatal_error ("Current window not available");
192
193     /* Set up the screen, conversions, and graphics */
194     D_get_screen_window (&top, &b, &l, &r);
195     /* D_set_overlay_mode (1); */
196
197     /* Figure out where to put text */
198
199     R_set_window (top, b, l, r);
200     t->xmax = r-l;
201     t->ymax = b-top;
202     grass_xoffset=l;
203     grass_yoffset=b;
204
205     dots_per_line = (int) (size / 100.0 * (float) (t->ymax));
206     t->v_char = t->h_char = (int) (.8 * (float) dots_per_line);
207     R_text_size (t->h_char, t->v_char);
208     R_font("romans");
209
210     t->v_tic = t->h_tic = 4;
211
212     y_max = t->ymax; /* kludge? */
213
214     R__curx = R_screen_top ();
215     R__cury = R_screen_bot () + grass_yoffset;
216
217     D_erase_window();
218
219     fprintf(stderr,"**********************************************\n");
220     fprintf(stderr,"DIAGNOSTIC TERMINAL SETUP\n");
221     fprintf(stderr,"top = %d\tb = %d\tl = %d\tr = %d\n", top,b,l,r);
222     fprintf(stderr,"name = %s\n", t->name);
223     fprintf(stderr,"description = %s\n", t->description);
224     fprintf(stderr,"xmax = %d\t", (int)t->xmax);
225     fprintf(stderr,"ymax = %d\n", (int)t->ymax);
226     fprintf(stderr,"v_char = %d\t", (int)t->v_char);
227     fprintf(stderr,"h_char = %d\n", (int)t->h_char);
228     fprintf(stderr,"v_tic = %d\t", (int)t->v_tic);
229     fprintf(stderr,"h_tic = %d\n", (int)t->h_tic);
230     fprintf(stderr,"**********************************************\n\n");
231 }
232
233 TERM_PUBLIC void
234 GRASS_reset ()
235 {
236     extern int R_standard_color();
237     extern int D_translate_color();
238     extern int R_flush();
239     extern int R_stabilize();
240     extern int R_close_driver();
241
242     R_standard_color (D_translate_color ("black"));
243     /* D_erase_window(); .* don't clear after g.gnuplot is finished */
244     R_flush ();
245     R_stabilize ();
246     R_close_driver ();
247 }
248
249 TERM_PUBLIC void
250 GRASS_graphics ()
251 {
252     extern int D_erase_window();
253     extern int R_flush();
254     extern int R_stabilize();
255     extern int R_standard_color();
256     extern int D_translate_color();
257
258     R_flush ();
259     R_stabilize ();
260     R_standard_color (D_translate_color ("black"));
261     D_erase_window();
262     return;
263 }
264
265 TERM_PUBLIC void
266 GRASS_text ()
267 {
268     extern int R_flush ();
269     extern int R_stabilize ();
270
271     R_flush ();
272     R_stabilize ();
273     return;                     /* device can't be used as a terminal */
274 }
275
276 TERM_PUBLIC void
277 GRASS_vector(unsigned int x, unsigned int y)
278 {
279     extern int R_flush ();
280     extern int R_stabilize ();
281
282     cont_abs (x, y);
283     R_flush ();
284     R_stabilize ();
285 }
286
287 TERM_PUBLIC void
288 GRASS_linetype(int lt)
289 {
290     extern int R_standard_color();
291     extern int D_translate_color();
292     extern int R_flush ();
293     extern int R_stabilize ();
294
295     while (lt > 10) lt-=10;
296
297     if (lt <= LT_BLACK)
298         R_standard_color (D_translate_color ("gray"));
299     else if (lt == LT_AXIS)
300         R_standard_color (D_translate_color ("white"));
301     else if (lt == 0)
302         R_standard_color (D_translate_color ("red"));
303     else if (lt == 1)
304         R_standard_color (D_translate_color ("green"));
305     else if (lt == 2)
306         R_standard_color (D_translate_color ("magenta"));
307     else if (lt == 3)
308         R_standard_color (D_translate_color ("brown"));
309     else if (lt == 4)
310         R_standard_color (D_translate_color ("orange"));
311     else if (lt == 5)
312         R_standard_color (D_translate_color ("yellow"));
313     else if (lt == 6)
314         R_standard_color (D_translate_color ("blue"));
315     else if (lt == 7)
316         R_standard_color (D_translate_color ("violet"));
317     else if (lt == 8)
318         R_standard_color (D_translate_color ("indigo"));
319     else if (lt == 9)
320         R_standard_color (D_translate_color ("gray"));
321     else /* if (lt == 10) */
322         R_standard_color (D_translate_color ("white"));
323     R_flush ();
324     R_stabilize ();
325     return;
326 }
327
328 /* originally /usr/grass4/src/display/d.label/cmd/label.c */
329
330 TERM_PUBLIC void
331 GRASS_put_text(unsigned int x, unsigned int y, const char *str)
332 {
333     extern int R_text();
334     extern int R_flush ();
335     extern int R_stabilize ();
336
337     if (strlen (str) == 0)
338         return;
339
340     GRASS_move (x, y);
341     /* R_standard_color (D_translate_color ("white")); */
342     R_text (str);
343     R_flush ();
344     R_stabilize ();
345 }
346
347 TERM_PUBLIC int
348 GRASS_text_angle (int ang)
349 {
350   extern int R_text_rotation();
351
352   R_text_rotation((float)ang);
353   return TRUE;                  /* GRASS can (?) rotate text */
354 }
355
356 TERM_PUBLIC int
357 GRASS_justify_text (enum JUSTIFY mode)
358 {
359     return (FALSE);             /* don't mess with this now */
360 }
361
362
363 TERM_PUBLIC void
364 GRASS_point (unsigned int x, unsigned y, int point)
365 {
366     switch (point) {
367     case TYPE_DOT:
368         draw_points_dot (x, y);
369         break;
370     case TYPE_X:
371         draw_points_x (x, y);
372         break;
373     case TYPE_PLUS:
374         draw_points_plus (x, y);
375         break;
376     case TYPE_BOX:
377         draw_points_box (x, y);
378         break;
379     case TYPE_DIAMOND:
380         draw_points_diamond (x, y);
381         break;
382     case TYPE_TRIANGLE:
383         draw_points_triangle (x, y);
384         break;
385     case TYPE_OCTO:
386         draw_points_octo (x, y);
387         break;
388     case TYPE_ITRIANGLE:
389         draw_points_itriangle (x, y);
390         break;
391     case TYPE_FBOX:
392         draw_points_fbox (x, y);
393         break;
394     case TYPE_FTRIANGLE:
395         draw_points_ftriangle (x, y);
396         break;
397     case TYPE_FITRIANGLE:
398         draw_points_fitriangle (x, y);
399         break;
400     case TYPE_FOCTO:
401         draw_points_focto (x, y);
402         break;
403     }
404 }
405
406 /* modified from /usr/grass4/src/display/d.points/cmd/main.c */
407
408 static void
409 draw_points_dot(int x, int y)
410 {
411   GRASS_move (x, y);
412   cont_abs (x, y);
413 }
414
415 static void
416 draw_points_diamond(int x, int y)
417 {
418   GRASS_move (x, y + PNT_SIZE);
419   GRASS_vector (x + PNT_SIZE, y);
420   GRASS_vector (x, y - PNT_SIZE);
421   GRASS_vector (x - PNT_SIZE, y);
422   GRASS_vector (x, y + PNT_SIZE);
423 }
424
425 static void
426 draw_points_box(int x, int y)
427 {
428   GRASS_move (x - PNT_SIZE, y - PNT_SIZE);
429   GRASS_vector (x - PNT_SIZE, y + PNT_SIZE);
430   GRASS_vector (x + PNT_SIZE, y + PNT_SIZE);
431   GRASS_vector (x + PNT_SIZE, y - PNT_SIZE);
432   GRASS_vector (x - PNT_SIZE, y - PNT_SIZE);
433 }
434
435 static void
436 draw_points_fbox(int x, int y)
437 {
438   int R_polygon_abs();
439   points_buf_x[0] = grass_xoffset + x - PNT_SIZE;
440   points_buf_y[0]= grass_yoffset - (y + PNT_SIZE);
441   points_buf_x[1] = grass_xoffset + x + PNT_SIZE;
442   points_buf_y[1]= grass_yoffset - (y + PNT_SIZE);
443   points_buf_x[2] = grass_xoffset + x + PNT_SIZE;
444   points_buf_y[2]= grass_yoffset - (y - PNT_SIZE);
445   points_buf_x[3] = grass_xoffset + x - PNT_SIZE;
446   points_buf_y[3]= grass_yoffset - (y - PNT_SIZE);
447   R_polygon_abs(points_buf_x, points_buf_y, 4 );
448 }
449
450 static void
451 draw_points_itriangle(int x, int y)
452 {
453   GRASS_move (x - PNT_SIZE, y + PNT_SIZE);
454   GRASS_vector (x + PNT_SIZE, y + PNT_SIZE);
455   GRASS_vector (x , y - PNT_SIZE);
456   GRASS_vector (x - PNT_SIZE, y + PNT_SIZE);
457 }
458
459 static void
460 draw_points_fitriangle(int x, int y)
461 {
462   int R_polygon_abs();
463
464   points_buf_x[0] = grass_xoffset + x + PNT_SIZE;
465   points_buf_y[0] = grass_yoffset - (y + PNT_SIZE);
466   points_buf_x[1] = grass_xoffset + x ;
467   points_buf_y[1] = grass_yoffset - (y - PNT_SIZE);
468   points_buf_x[2] = grass_xoffset + x - PNT_SIZE;
469   points_buf_y[2] = grass_yoffset - (y + PNT_SIZE);
470   R_polygon_abs(points_buf_x, points_buf_y, 3 );
471 }
472
473 static void
474 draw_points_triangle(int x, int y)
475 {
476   GRASS_move (x - PNT_SIZE, y - PNT_SIZE);
477   GRASS_vector (x , y + PNT_SIZE);
478   GRASS_vector (x + PNT_SIZE, y - PNT_SIZE);
479   GRASS_vector (x - PNT_SIZE, y - PNT_SIZE);
480 }
481
482 static void
483 draw_points_ftriangle(int x, int y)
484 {
485   int R_polygon_abs();
486
487   points_buf_x[0] = grass_xoffset + x;
488   points_buf_y[0]= grass_yoffset - (y + PNT_SIZE);
489   points_buf_x[1] = grass_xoffset + x + PNT_SIZE;
490   points_buf_y[1]= grass_yoffset - (y - PNT_SIZE);
491   points_buf_x[2] = grass_xoffset + x - PNT_SIZE;
492   points_buf_y[2]= grass_yoffset - (y - PNT_SIZE);
493   R_polygon_abs(points_buf_x, points_buf_y, 3 );
494 }
495
496 static void
497 draw_points_plus(int x, int y)
498 {
499   GRASS_move (x - PNT_SIZE, y);
500   GRASS_vector (x + PNT_SIZE, y);
501   GRASS_move (x, y - PNT_SIZE);
502   GRASS_vector (x, y + PNT_SIZE);
503 }
504
505 /* depends on PNT_SIZE */
506 static void
507 draw_points_octo(int x, int y)
508 {
509   /* CCW */
510   GRASS_move (x - (int) (PNT_SIZE/3), y - PNT_SIZE);   /* 1 */
511   GRASS_vector (x + (int) (PNT_SIZE/3), y - PNT_SIZE); /* 2 */
512   GRASS_vector (x + PNT_SIZE, y - (int) (PNT_SIZE/3)); /* 3 */
513   GRASS_vector (x + PNT_SIZE, y + (int) (PNT_SIZE/3)); /* 4 */
514   GRASS_vector (x + (int) (PNT_SIZE/3), y + PNT_SIZE); /* 5 */
515   GRASS_vector (x - (int) (PNT_SIZE/3), y + PNT_SIZE); /* 6 */
516   GRASS_vector (x - PNT_SIZE, y + (int) (PNT_SIZE/3)); /* 7 */
517   GRASS_vector (x - PNT_SIZE, y - (int) (PNT_SIZE/3)); /* 8 */
518   GRASS_vector (x - (int) (PNT_SIZE/3), y - PNT_SIZE); /* 1 */
519 }
520
521 /* depends on PNT_SIZE */
522 static void
523 draw_points_focto(int x, int y)
524 {
525   int R_polygon_abs();
526
527   /* CCW */
528   points_buf_x[0] = grass_xoffset + x + (int) (PNT_SIZE/3);
529   points_buf_y[0] = grass_yoffset - (y - PNT_SIZE);
530   points_buf_x[1] = grass_xoffset + x + PNT_SIZE;
531   points_buf_y[1] = grass_yoffset - (y - (int) (PNT_SIZE/3));
532   points_buf_x[2] = grass_xoffset + x + PNT_SIZE;
533   points_buf_y[2] = grass_yoffset - (y + (int) (PNT_SIZE/3));
534   points_buf_x[3] = grass_xoffset + x + (int) (PNT_SIZE/3);
535   points_buf_y[3] = grass_yoffset - (y + PNT_SIZE);
536   points_buf_x[4] = grass_xoffset + x - (int) (PNT_SIZE/3);
537   points_buf_y[4] = grass_yoffset - (y + PNT_SIZE);
538   points_buf_x[5] = grass_xoffset + x - PNT_SIZE;
539   points_buf_y[5] = grass_yoffset - (y + (int) (PNT_SIZE/3));
540   points_buf_x[6] = grass_xoffset + x - PNT_SIZE;
541   points_buf_y[6] = grass_yoffset - (y - (int) (PNT_SIZE/3));
542   points_buf_x[7] = grass_xoffset + x - (int) (PNT_SIZE/3);
543   points_buf_y[7] = grass_yoffset - (y - PNT_SIZE);
544   R_polygon_abs(points_buf_x, points_buf_y, 8 );
545 }
546
547 static void
548 draw_points_x(int x, int y)
549 {
550   GRASS_move (x - PNT_SIZE, y - PNT_SIZE);
551   GRASS_vector (x + PNT_SIZE, y + PNT_SIZE);
552   GRASS_move (x + PNT_SIZE, y - PNT_SIZE);
553   GRASS_vector (x - PNT_SIZE, y + PNT_SIZE);
554 }
555
556 TERM_PUBLIC int
557 GRASS_set_font(const char *font)
558 {
559   char name[32];
560   int size,sep, R_font();
561   struct termentry *t = term;
562   int R_text_size (), dots_per_line;
563
564   /* G_warning(font); */
565
566   sep=strcspn(font,",");
567   strncpy(name,font,sep); name[sep]=NUL;
568   sscanf (&(font[sep+1]),"%d",&size);
569
570   if (size==0)
571     size=3;
572   dots_per_line = (int) (size / 100.0 * (float) (t->ymax));
573   t->v_char = t->h_char = (int) (.8 * (float) dots_per_line);
574   R_text_size (t->h_char, t->v_char);
575 /* cyrilc,gothgbt,gothgrt,gothitt,greekc,greekcs,greekp,
576 greeks,italicc,italiccs,italict,romanc,romancs,romand,
577 romans,romant,scriptc,scripts */
578
579   if (strlen(name) > 5 )
580     R_font(name);
581   else
582     R_font("romans");
583   return TRUE;
584 }
585
586 #ifdef GRASS_POINTSIZE
587 /* HBB FIXME 20040619: argument type wrong, but luckily unused anyway... */
588 TERM_PUBLIC void
589 GRASS_set_pointsize(double *size)
590 {
591   return;
592 }
593 #endif /* GRASS_POINTSIZE */
594
595 /* need to fix */
596 TERM_PUBLIC void
597 GRASS_arrow(
598     unsigned int sx, unsigned int sy,
599     unsigned int ex, unsigned int ey,
600     int head)
601 {
602   do_arrow (sx, sy, ex, ey, 1);
603   return;
604 }
605
606 #endif /* TERM_BODY */
607
608 #ifdef TERM_TABLE
609 TERM_TABLE_START(grass_driver)
610     "grass", "GRASS Graphics Monitor",
611     GRASS_XMAX, GRASS_YMAX, GRASS_VCHAR, GRASS_HCHAR,
612     GRASS_VTIC, GRASS_HTIC, GRASS_options, GRASS_init, GRASS_reset,
613     GRASS_text, null_scale, GRASS_graphics, GRASS_move, GRASS_vector,
614     GRASS_linetype, GRASS_put_text, GRASS_text_angle,
615     GRASS_justify_text, GRASS_point, GRASS_arrow, GRASS_set_font
616 TERM_TABLE_END(grass_driver)
617
618
619 #undef LAST_TERM
620 #define LAST_TERM grass_driver
621 #endif /* TERM_TABLE */
622 #endif /* TERM_PROTO_ONLY */
623
624
625 #ifdef TERM_HELP
626 START_HELP(grass)
627 "1 grass",
628 "?commands set terminal grass",
629 "?set terminal grass",
630 "?set term grass",
631 "?terminal grass",
632 "?term grass",
633 "?grass",
634 " The `grass` terminal driver gives `gnuplot` capabilities to users of the ",
635 " GRASS geographic information system.  Contact grassp-list@moon.cecer.army.mil",
636 " for more information.  Pages are written to the current frame of the GRASS",
637 " Graphics Window.  There are no options."
638 END_HELP(grass)
639 #endif