merge with trunk at r31523
[blender-staging.git] / source / blender / editors / gpencil / editaction_gpencil.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2008, Blender Foundation
21  * This is a new part of Blender
22  *
23  * Contributor(s): Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27  
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <stddef.h>
32 #include <math.h>
33
34 #include "BLI_math.h"
35 #include "BLI_blenlib.h"
36
37 #include "gpencil_intern.h"
38
39 #if 0 // XXX disabled until grease pencil code stabilises again
40
41 /* XXX */
42 static void actdata_filter() {} // is now ANIM_animdata_filter()
43 static void BIF_undo_push() {}
44 static void error() {}
45 static void *get_action_context() {return NULL;}  // is now ANIM_animdata_get_context()
46 /* XXX */
47
48
49 /* ***************************************** */
50 /* NOTE ABOUT THIS FILE:
51  *      This file contains code for editing Grease Pencil data in the Action Editor
52  *      as a 'keyframes', so that a user can adjust the timing of Grease Pencil drawings.
53  *      Therefore, this file mostly contains functions for selecting Grease-Pencil frames.
54  */
55 /* ***************************************** */
56 /* Generics - Loopers */
57
58 /* Loops over the gp-frames for a gp-layer, and applies the given callback */
59 short gplayer_frames_looper (bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPDframe *, Scene *))
60 {
61         bGPDframe *gpf;
62         
63         /* error checker */
64         if (gpl == NULL)
65                 return 0;
66         
67         /* do loop */
68         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
69                 /* execute callback */
70                 if (gpf_cb(gpf, scene))
71                         return 1;
72         }
73                 
74         /* nothing to return */
75         return 0;
76 }
77
78 /* ****************************************** */
79 /* Data Conversion Tools */
80
81 /* make a listing all the gp-frames in a layer as cfraelems */
82 void gplayer_make_cfra_list (bGPDlayer *gpl, ListBase *elems, short onlysel)
83 {
84         bGPDframe *gpf;
85         CfraElem *ce;
86         
87         /* error checking */
88         if (ELEM(NULL, gpl, elems))
89                 return;
90         
91         /* loop through gp-frames, adding */
92         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
93                 if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) {
94                         ce= MEM_callocN(sizeof(CfraElem), "CfraElem");
95                         
96                         ce->cfra= (float)gpf->framenum;
97                         ce->sel= (gpf->flag & GP_FRAME_SELECT) ? 1 : 0;
98                         
99                         BLI_addtail(elems, ce);
100                 }
101         }
102 }
103
104 /* ***************************************** */
105 /* Selection Tools */
106
107 /* check if one of the frames in this layer is selected */
108 short is_gplayer_frame_selected (bGPDlayer *gpl)
109 {
110         bGPDframe *gpf;
111         
112         /* error checking */
113         if (gpl == NULL) 
114                 return 0;
115         
116         /* stop at the first one found */
117         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
118                 if (gpf->flag & GP_FRAME_SELECT)
119                         return 1;
120         }
121         
122         /* not found */
123         return 0;
124 }
125
126 /* helper function - select gp-frame based on SELECT_* mode */
127 static void gpframe_select (bGPDframe *gpf, short select_mode)
128 {
129         switch (select_mode) {
130                 case SELECT_ADD:
131                         gpf->flag |= GP_FRAME_SELECT;
132                         break;
133                 case SELECT_SUBTRACT:
134                         gpf->flag &= ~GP_FRAME_SELECT;
135                         break;
136                 case SELECT_INVERT:
137                         gpf->flag ^= GP_FRAME_SELECT;
138                         break;
139         }
140 }
141
142 /* set all/none/invert select (like above, but with SELECT_* modes) */
143 void select_gpencil_frames (bGPDlayer *gpl, short select_mode)
144 {
145         bGPDframe *gpf;
146         
147         /* error checking */
148         if (gpl == NULL) 
149                 return;
150                 
151         /* handle according to mode */
152         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
153                 gpframe_select(gpf, select_mode);
154         }
155 }
156
157 /* set all/none/invert select */
158 void set_gplayer_frame_selection (bGPDlayer *gpl, short mode)
159 {
160         /* error checking */
161         if (gpl == NULL) 
162                 return;
163                 
164         /* convert mode to select_mode */
165         switch (mode) {
166                 case 2:
167                         mode= SELECT_INVERT;
168                         break;
169                 case 1:
170                         mode= SELECT_ADD;
171                         break;
172                 case 0:
173                         mode= SELECT_SUBTRACT;
174                         break;
175                 default:
176                         return;
177         }
178         
179         /* now call the standard function */
180         select_gpencil_frames (gpl, mode);
181 }
182
183 /* select the frame in this layer that occurs on this frame (there should only be one at most) */
184 void select_gpencil_frame (bGPDlayer *gpl, int selx, short select_mode)
185 {
186         bGPDframe *gpf;
187    
188         /* search through frames for a match */
189         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
190                 /* there should only be one frame with this frame-number */
191                 if (gpf->framenum == selx) {
192                         gpframe_select(gpf, select_mode);
193                         break;
194                 }
195         }
196 }
197
198 /* select the frames in this layer that occur within the bounds specified */
199 void borderselect_gplayer_frames (bGPDlayer *gpl, float min, float max, short select_mode)
200 {
201         bGPDframe *gpf;
202         
203         /* only select those frames which are in bounds */
204         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
205                 if (IN_RANGE(gpf->framenum, min, max))
206                         gpframe_select(gpf, select_mode);
207         }
208 }
209
210
211 /* De-selects or inverts the selection of Layers for a grease-pencil block
212  *      mode: 0 = default behaviour (select all), 1 = test if (de)select all, 2 = invert all 
213  */
214 void deselect_gpencil_layers (void *data, short mode)
215 {
216         ListBase act_data = {NULL, NULL};
217         bActListElem *ale;
218         int filter, sel=1;
219         
220         /* filter data */
221         filter= ACTFILTER_VISIBLE;
222         actdata_filter(&act_data, filter, data, ACTCONT_GPENCIL);
223         
224         /* See if we should be selecting or deselecting */
225         if (mode == 1) {
226                 for (ale= act_data.first; ale; ale= ale->next) {
227                         if (sel == 0) 
228                                 break;
229                         
230                         if (ale->flag & GP_LAYER_SELECT)
231                                 sel= 0;
232                 }
233         }
234         else
235                 sel= 0;
236                 
237         /* Now set the flags */
238         for (ale= act_data.first; ale; ale= ale->next) {
239                 bGPDlayer *gpl= (bGPDlayer *)ale->data;
240                 
241                 if (mode == 2)
242                         gpl->flag ^= GP_LAYER_SELECT;
243                 else if (sel)
244                         gpl->flag |= GP_LAYER_SELECT;
245                 else
246                         gpl->flag &= ~GP_LAYER_SELECT;
247                         
248                 gpl->flag &= ~GP_LAYER_ACTIVE;
249         }
250         
251         /* Cleanup */
252         BLI_freelistN(&act_data);
253 }
254
255 /* ***************************************** */
256 /* Frame Editing Tools */
257
258 /* Delete selected grease-pencil layers */
259 void delete_gpencil_layers (void)
260 {
261         ListBase act_data = {NULL, NULL};
262         bActListElem *ale, *next;
263         void *data;
264         short datatype;
265         int filter;
266         
267         /* determine what type of data we are operating on */
268         data = get_action_context(&datatype);
269         if (data == NULL) return;
270         if (datatype != ACTCONT_GPENCIL) return;
271         
272         /* filter data */
273         filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_CHANNELS | ACTFILTER_SEL);
274         actdata_filter(&act_data, filter, data, datatype);
275         
276         /* clean up grease-pencil layers */
277         for (ale= act_data.first; ale; ale= next) {
278                 bGPdata *gpd= (bGPdata *)ale->owner;
279                 bGPDlayer *gpl= (bGPDlayer *)ale->data;
280                 next= ale->next;
281                 
282                 /* free layer and its data */
283                 if (SEL_GPL(gpl)) {
284                         free_gpencil_frames(gpl);
285                         BLI_freelinkN(&gpd->layers, gpl);
286                 }
287                 
288                 /* free temp memory */
289                 BLI_freelinkN(&act_data, ale);
290         }
291         
292         BIF_undo_push("Delete GPencil Layers");
293 }
294
295 /* Delete selected frames */
296 void delete_gplayer_frames (bGPDlayer *gpl)
297 {
298         bGPDframe *gpf, *gpfn;
299         
300         /* error checking */
301         if (gpl == NULL)
302                 return;
303                 
304         /* check for frames to delete */
305         for (gpf= gpl->frames.first; gpf; gpf= gpfn) {
306                 gpfn= gpf->next;
307                 
308                 if (gpf->flag & GP_FRAME_SELECT)
309                         gpencil_layer_delframe(gpl, gpf);
310         }
311 }
312
313 /* Duplicate selected frames from given gp-layer */
314 void duplicate_gplayer_frames (bGPDlayer *gpl)
315 {
316         bGPDframe *gpf, *gpfn;
317         
318         /* error checking */
319         if (gpl == NULL)
320                 return;
321         
322         /* duplicate selected frames  */
323         for (gpf= gpl->frames.first; gpf; gpf= gpfn) {
324                 gpfn= gpf->next;
325                 
326                 /* duplicate this frame */
327                 if (gpf->flag & GP_FRAME_SELECT) {
328                         bGPDframe *gpfd; 
329                         
330                         /* duplicate frame, and deselect self */
331                         gpfd= gpencil_frame_duplicate(gpf);
332                         gpf->flag &= ~GP_FRAME_SELECT;
333                         
334                         BLI_insertlinkafter(&gpl->frames, gpf, gpfd);
335                 }
336         }
337 }
338
339 /* -------------------------------------- */
340 /* Copy and Paste Tools */
341 /* - The copy/paste buffer currently stores a set of GP_Layers, with temporary
342  *      GP_Frames with the necessary strokes
343  * - Unless there is only one element in the buffer, names are also tested to check for compatability.
344  * - All pasted frames are offset by the same amount. This is calculated as the difference in the times of
345  *      the current frame and the 'first keyframe' (i.e. the earliest one in all channels).
346  * - The earliest frame is calculated per copy operation.
347  */
348  
349 /* globals for copy/paste data (like for other copy/paste buffers) */
350 ListBase gpcopybuf = {NULL, NULL};
351 static int gpcopy_firstframe= 999999999;
352
353 /* This function frees any MEM_calloc'ed copy/paste buffer data */
354 void free_gpcopybuf ()
355 {
356         free_gpencil_layers(&gpcopybuf); 
357         
358         gpcopybuf.first= gpcopybuf.last= NULL;
359         gpcopy_firstframe= 999999999;
360 }
361
362 /* This function adds data to the copy/paste buffer, freeing existing data first
363  * Only the selected GP-layers get their selected keyframes copied.
364  */
365 void copy_gpdata ()
366 {
367         ListBase act_data = {NULL, NULL};
368         bActListElem *ale;
369         int filter;
370         void *data;
371         short datatype;
372         
373         /* clear buffer first */
374         free_gpcopybuf();
375         
376         /* get data */
377         data= get_action_context(&datatype);
378         if (data == NULL) return;
379         if (datatype != ACTCONT_GPENCIL) return;
380         
381         /* filter data */
382         filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL);
383         actdata_filter(&act_data, filter, data, datatype);
384         
385         /* assume that each of these is an ipo-block */
386         for (ale= act_data.first; ale; ale= ale->next) {
387                 bGPDlayer *gpls, *gpln;
388                 bGPDframe *gpf, *gpfn;
389                 
390                 /* get new layer to put into buffer */
391                 gpls= (bGPDlayer *)ale->data;
392                 gpln= MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer");
393                 
394                 gpln->frames.first= gpln->frames.last= NULL;
395                 strcpy(gpln->info, gpls->info);
396                 
397                 BLI_addtail(&gpcopybuf, gpln);
398                 
399                 /* loop over frames, and copy only selected frames */
400                 for (gpf= gpls->frames.first; gpf; gpf= gpf->next) {
401                         /* if frame is selected, make duplicate it and its strokes */
402                         if (gpf->flag & GP_FRAME_SELECT) {
403                                 /* add frame to buffer */
404                                 gpfn= gpencil_frame_duplicate(gpf);
405                                 BLI_addtail(&gpln->frames, gpfn);
406                                 
407                                 /* check if this is the earliest frame encountered so far */
408                                 if (gpf->framenum < gpcopy_firstframe)
409                                         gpcopy_firstframe= gpf->framenum;
410                         }
411                 }
412         }
413         
414         /* check if anything ended up in the buffer */
415         if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last))
416                 error("Nothing copied to buffer");
417         
418         /* free temp memory */
419         BLI_freelistN(&act_data);
420 }
421
422 void paste_gpdata (Scene *scene)
423 {
424         ListBase act_data = {NULL, NULL};
425         bActListElem *ale;
426         int filter;
427         void *data;
428         short datatype;
429         
430         const int offset = (CFRA - gpcopy_firstframe);
431         short no_name= 0;
432         
433         /* check if buffer is empty */
434         if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last)) {
435                 error("No data in buffer to paste");
436                 return;
437         }
438         /* check if single channel in buffer (disregard names if so)  */
439         if (gpcopybuf.first == gpcopybuf.last)
440                 no_name= 1;
441         
442         /* get data */
443         data= get_action_context(&datatype);
444         if (data == NULL) return;
445         if (datatype != ACTCONT_GPENCIL) return;
446         
447         /* filter data */
448         filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_FOREDIT);
449         actdata_filter(&act_data, filter, data, datatype);
450         
451         /* from selected channels */
452         for (ale= act_data.first; ale; ale= ale->next) {
453                 bGPDlayer *gpld= (bGPDlayer *)ale->data;
454                 bGPDlayer *gpls= NULL;
455                 bGPDframe *gpfs, *gpf;
456                 
457                 /* find suitable layer from buffer to use to paste from */
458                 for (gpls= gpcopybuf.first; gpls; gpls= gpls->next) {
459                         /* check if layer name matches */
460                         if ((no_name) || (strcmp(gpls->info, gpld->info)==0))
461                                 break;
462                 }
463                 
464                 /* this situation might occur! */
465                 if (gpls == NULL)
466                         continue;
467                 
468                 /* add frames from buffer */
469                 for (gpfs= gpls->frames.first; gpfs; gpfs= gpfs->next) {
470                         /* temporarily apply offset to buffer-frame while copying */
471                         gpfs->framenum += offset;
472                         
473                         /* get frame to copy data into (if no frame returned, then just ignore) */
474                         gpf= gpencil_layer_getframe(gpld, gpfs->framenum, 1);
475                         if (gpf) {
476                                 bGPDstroke *gps, *gpsn;
477                                 ScrArea *sa;
478                                 
479                                 /* get area that gp-data comes from */
480                                 //sa= gpencil_data_findowner((bGPdata *)ale->owner);    
481                                 sa = NULL;
482                                 
483                                 /* this should be the right frame... as it may be a pre-existing frame, 
484                                  * must make sure that only compatible stroke types get copied over 
485                                  *      - we cannot just add a duplicate frame, as that would cause errors
486                                  *      - need to check for compatible types to minimise memory usage (copying 'junk' over)
487                                  */
488                                 for (gps= gpfs->strokes.first; gps; gps= gps->next) {
489                                         short stroke_ok;
490                                         
491                                         /* if there's an area, check that it supports this type of stroke */
492                                         if (sa) {
493                                                 stroke_ok= 0;
494                                                 
495                                                 /* check if spacetype supports this type of stroke
496                                                  *      - NOTE: must sync this with gp_paint_initstroke() in gpencil.c
497                                                  */
498                                                 switch (sa->spacetype) {
499                                                         case SPACE_VIEW3D: /* 3D-View: either screen-aligned or 3d-space */
500                                                                 if ((gps->flag == 0) || (gps->flag & GP_STROKE_3DSPACE))
501                                                                         stroke_ok= 1;
502                                                                 break;
503                                                                 
504                                                         case SPACE_NODE: /* Nodes Editor: either screen-aligned or view-aligned */
505                                                         case SPACE_IMAGE: /* Image Editor: either screen-aligned or view\image-aligned */
506                                                                 if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DSPACE))
507                                                                         stroke_ok= 1;
508                                                                 break;
509                                                                 
510                                                         case SPACE_SEQ: /* Sequence Editor: either screen-aligned or view-aligned */
511                                                                 if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DIMAGE))
512                                                                         stroke_ok= 1;
513                                                                 break;
514                                                 }
515                                         }
516                                         else
517                                                 stroke_ok= 1;
518                                         
519                                         /* if stroke is ok, we make a copy of this stroke and add to frame */
520                                         if (stroke_ok) {
521                                                 /* make a copy of stroke, then of its points array */
522                                                 gpsn= MEM_dupallocN(gps);
523                                                 gpsn->points= MEM_dupallocN(gps->points);
524                                                 
525                                                 /* append stroke to frame */
526                                                 BLI_addtail(&gpf->strokes, gpsn);
527                                         }
528                                 }
529                                 
530                                 /* if no strokes (i.e. new frame) added, free gpf */
531                                 if (gpf->strokes.first == NULL)
532                                         gpencil_layer_delframe(gpld, gpf);
533                         }
534                         
535                         /* unapply offset from buffer-frame */
536                         gpfs->framenum -= offset;
537                 }
538         }
539         
540         /* free temp memory */
541         BLI_freelistN(&act_data);
542         
543         /* undo and redraw stuff */
544         BIF_undo_push("Paste Grease Pencil Frames");
545 }
546
547 /* -------------------------------------- */
548 /* Snap Tools */
549
550 static short snap_gpf_nearest (bGPDframe *gpf, Scene *scene)
551 {
552         if (gpf->flag & GP_FRAME_SELECT)
553                 gpf->framenum= (int)(floor(gpf->framenum+0.5));
554         return 0;
555 }
556
557 static short snap_gpf_nearestsec (bGPDframe *gpf, Scene *scene)
558 {
559         float secf = (float)FPS;
560         if (gpf->flag & GP_FRAME_SELECT)
561                 gpf->framenum= (int)(floor(gpf->framenum/secf + 0.5f) * secf);
562         return 0;
563 }
564
565 static short snap_gpf_cframe (bGPDframe *gpf, Scene *scene)
566 {
567         if (gpf->flag & GP_FRAME_SELECT)
568                 gpf->framenum= (int)CFRA;
569         return 0;
570 }
571
572 static short snap_gpf_nearmarker (bGPDframe *gpf, Scene *scene)
573 {
574         if (gpf->flag & GP_FRAME_SELECT)
575                 gpf->framenum= (int)find_nearest_marker_time(&scene->markers, (float)gpf->framenum);
576         return 0;
577 }
578
579
580 /* snap selected frames to ... */
581 void snap_gplayer_frames (bGPDlayer *gpl, Scene *scene, short mode)
582 {
583         switch (mode) {
584                 case 1: /* snap to nearest frame */
585                         gplayer_frames_looper(gpl, scene, snap_gpf_nearest);
586                         break;
587                 case 2: /* snap to current frame */
588                         gplayer_frames_looper(gpl, scene, snap_gpf_cframe);
589                         break;
590                 case 3: /* snap to nearest marker */
591                         gplayer_frames_looper(gpl, scene, snap_gpf_nearmarker);
592                         break;
593                 case 4: /* snap to nearest second */
594                         gplayer_frames_looper(gpl, scene, snap_gpf_nearestsec);
595                         break;
596                 default: /* just in case */
597                         gplayer_frames_looper(gpl, scene, snap_gpf_nearest);
598                         break;
599         }
600 }
601
602 /* -------------------------------------- */
603 /* Mirror Tools */
604
605 static short mirror_gpf_cframe (bGPDframe *gpf, Scene *scene)
606 {
607         int diff;
608         
609         if (gpf->flag & GP_FRAME_SELECT) {
610                 diff= CFRA - gpf->framenum;
611                 gpf->framenum= CFRA;
612         }
613         
614         return 0;
615 }
616
617 static short mirror_gpf_yaxis (bGPDframe *gpf, Scene *scene)
618 {
619         int diff;
620         
621         if (gpf->flag & GP_FRAME_SELECT) {
622                 diff= -gpf->framenum;
623                 gpf->framenum= diff;
624         }
625         
626         return 0;
627 }
628
629 static short mirror_gpf_xaxis (bGPDframe *gpf, Scene *scene)
630 {
631         int diff;
632         
633         if (gpf->flag & GP_FRAME_SELECT) {
634                 diff= -gpf->framenum;
635                 gpf->framenum= diff;
636         }
637         
638         return 0;
639 }
640
641 static short mirror_gpf_marker (bGPDframe *gpf, Scene *scene)
642 {
643         static TimeMarker *marker;
644         static short initialised = 0;
645         int diff;
646         
647         /* In order for this mirror function to work without
648          * any extra arguments being added, we use the case
649          * of bezt==NULL to denote that we should find the 
650          * marker to mirror over. The static pointer is safe
651          * to use this way, as it will be set to null after 
652          * each cycle in which this is called.
653          */
654         
655         if (gpf) {
656                 /* mirroring time */
657                 if ((gpf->flag & GP_FRAME_SELECT) && (marker)) {
658                         diff= (marker->frame - gpf->framenum);
659                         gpf->framenum= (marker->frame + diff);
660                 }
661         }
662         else {
663                 /* initialisation time */
664                 if (initialised) {
665                         /* reset everything for safety */
666                         marker = NULL;
667                         initialised = 0;
668                 }
669                 else {
670                         /* try to find a marker */
671                         for (marker= scene->markers.first; marker; marker=marker->next) {
672                                 if (marker->flag & SELECT) {
673                                         initialised = 1;
674                                         break;
675                                 }
676                         }
677                         
678                         if (initialised == 0) 
679                                 marker = NULL;
680                 }
681         }
682         
683         return 0;
684 }
685
686
687 /* mirror selected gp-frames on... */
688 void mirror_gplayer_frames (bGPDlayer *gpl, Scene *scene, short mode)
689 {
690         switch (mode) {
691                 case 1: /* mirror over current frame */
692                         gplayer_frames_looper(gpl, scene, mirror_gpf_cframe);
693                         break;
694                 case 2: /* mirror over frame 0 */
695                         gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis);
696                         break;
697                 case 3: /* mirror over value 0 */
698                         gplayer_frames_looper(gpl, scene, mirror_gpf_xaxis);
699                         break;
700                 case 4: /* mirror over marker */
701                         mirror_gpf_marker(NULL, NULL);
702                         gplayer_frames_looper(gpl, scene, mirror_gpf_marker);
703                         mirror_gpf_marker(NULL, NULL);
704                         break;
705                 default: /* just in case */
706                         gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis);
707                         break;
708         }
709 }
710
711 /* ***************************************** */
712 #endif // XXX disabled until Grease Pencil code stabilises again...