1 /*
2  * InputDevice.h
3  *
4  * Copyright (C) 2001 J. "MUFTI" Scheurich
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (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 (see the file "COPYING" for details); if
18  * not, write to the Free Software Foundation, Inc., 675 Mass Ave,
19  * Cambridge, MA 02139, USA.
20  */
21 
22 #pragma once
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include "stdafx.h"
27 #include "MyString.h"
28 #include "StringArray.h"
29 #include "swt.h"
30 #include "TransformMode.h"
31 #include "EulerAngles.h"
32 #include "Quaternion.h"
33 #include "Map.h"
34 
35 class InputDeviceApp;
36 
37 // class to access several joystick like devices via a unified interface
38 
39 // Example for such devices are spaceballs, magellan-devices,
40 // thumbsticks of gamepads and of course joysticks 8-)
41 
42 // This can also be used for Xinput devices, that not deliver zero,
43 // if you take your hand away from the device like dialbox, mouse or
44 // graphics tablett.
45 
46 class InputDevice
47    {
48 public:
49    // true if there is a new event from the joystick
50    virtual bool readInputDevice(void) = 0;
51 
isValid(void)52    virtual bool isValid(void) { return valid; }
53 
54    Quaternion& get_quaternion(TransformMode* tm, bool local, bool check_only);
55 
56    virtual Quaternion& get_quaternion(TransformMode* tm=NULL,
57                                       bool check_only=false);
58    virtual Quaternion& get_localQuaternion(TransformMode* tm=NULL,
59                                            bool check_only=false);
60    EulerAngles& get_eulerAngles(TransformMode* tm,bool check_only,bool current);
61 
62    Vec3f& get_vector(TransformMode* tm);
63 
get_number_axes(void)64    int get_number_axes(void) { return number_max_axis - number_ignored_axes; }
get_number_max_axis(void)65    int get_number_max_axis(void) { return number_max_axis; }
66 
get_number_buttons(void)67    int get_number_buttons(void) { return number_buttons; }
68 
69    virtual const char *getDeviceOption(void) const = 0;
getDeviceName(void)70    const char *getDeviceName(void) { return device_name; }
71 
isWand(void)72    bool isWand(void) { return wandflag; }
isHead(void)73    bool isHead(void) { return headflag; }
sendalways(void)74    virtual bool sendalways(void) { return alwaysflag; }
75 
76    virtual bool allzero(void);
77 
isTracker(void)78    virtual bool isTracker(void) { return false; }
79 
hasReadDelay(void)80    virtual bool hasReadDelay(void) { return false; }
81 
prepareRead(void)82    virtual void prepareRead(void) { return; }
83 
84    virtual float maxvalue(int) const=0;
85 
useCurrent(void)86    virtual bool useCurrent(void) { return false; }
87 
setHeadNavigation(void)88    virtual void setHeadNavigation(void) { }
getHeadNavigation(void)89    virtual bool getHeadNavigation(void) { return true; }
90 
getxyzfactor(void)91    float getxyzfactor(void) { return xyzfactor; }
getrotfactor(void)92    float getrotfactor(void) { return rotfactor; }
93 
setxyzfactor(float f)94    void setxyzfactor(float f) { xyzfactor = f; }
setrotfactor(float f)95    void setrotfactor(float f) { rotfactor = f; }
96 
97    void set_firstflag(void);
98 
99    virtual void guessInputDeviceNames(StringArray *names) const = 0;
100 
101    static InputDevice *createInputDevice(const char* device_option,
102                                          const char* device_name);
103 
InputDevice(const char * device)104    InputDevice(const char *device)
105       {
106       initialize();
107       valid=true;
108       if (device==NULL)
109           device_name=NULL;
110       else
111           device_name=strdup(device);
112       }
113 
initialize(void)114    void initialize(void)
115       {
116       valid=false;
117       // initialise joystick class default data
118       button_pressed=-1;
119       button_released=-1;
120       number_buttons=0;
121       number_max_axis=0;
122       number_max_axes=7;
123       number_ignored_axes=0;
124       for (int i=0;i<number_max_axes;i++)
125          value[i]=0;
126       headflag=false;
127       wandflag=false;
128       alwaysflag=false;
129       zero_on_release=NULL;
130       sign_swap=NULL;
131       factor=NULL;
132       acceleration=NULL;
133       zero_size_fraction=NULL;
134       num_axis_x=0;
135       num_axis_y=1;
136       num_axis_z=2;
137       num_axis_xrot=3;
138       num_axis_yrot=4;
139       num_axis_zrot=5;
140       num_axis_angle=6;
141       static const char* axesinfodata[6]= {
142          "xrot",
143          "yrot",
144          "zrot",
145          "x",
146          "y",
147          "z"
148       };
149       for (int i=0;i<6;i++)
150          axesinfo[i]=(char *)axesinfodata[i];
151       for (int i=0;i<7;i++)
152          firstflag[i]=true;
153       for (int i=0;i<7;i++)
154          oldvalue[i]=0;
155       for (int i=0;i<7;i++)
156          ignore[i]=false;
157       zero_on_release=new bool[number_max_axes];
158       for (int i=0;i<number_max_axes;i++)
159          zero_on_release[i]=true;
160       alwaysflag=false;
161       xyzfactor=1;
162       rotfactor=1;
163       device_name=NULL;
164       error_message="";
165       }
~InputDevice()166    virtual ~InputDevice()
167       {
168       if (zero_on_release!=NULL)
169          delete zero_on_release;
170       if (acceleration!=NULL)
171          delete acceleration;
172       }
173    void setAxisInformation(const char* info_string);
174    void setNumberAxes(const char* number);
setSendAlways(bool flag)175    void setSendAlways(bool flag) {alwaysflag=flag;}
176 
get_ignore(int axis)177    bool get_ignore(int axis) { return ignore[axis]; }
178    int get_sign(int axis_number);
179    int getAxisFromInformation(const char* axesinfo);
180    float get_factor(int axis_number);
181    float get_acceleration(int axis_number);
182    bool get_zero_on_release(int axis_number);
183    float get_zero_size_fraction(int axis_number);
184 
getAxesInfo(int i)185    char *getAxesInfo(int i) { return axesinfo[i >= 3 ? i - 3 : i + 3]; }
186 
getErrorMessage(void)187    const char *getErrorMessage(void) { return error_message; }
188 
getNumAxis(int i)189    int getNumAxis(int i) {
190       switch(i) {
191         case 0:
192           return num_axis_x;
193         case 1:
194           return num_axis_y;
195         case 2:
196           return num_axis_z;
197         case 3:
198           return num_axis_xrot;
199         case 4:
200           return num_axis_yrot;
201         case 5:
202           return num_axis_zrot;
203       }
204       return -1;
205    }
206 
207 protected:
208    bool valid;
209 
210    float get_x(TransformMode* tm=NULL,bool check_only=false,bool curr=false);
211    float get_y(TransformMode* tm=NULL,bool check_only=false,bool curr=false);
212    float get_z(TransformMode* tm=NULL,bool check_only=false,bool curr=false);
213    float get_xrot(TransformMode* tm=NULL,bool check_only=false,bool curr=false);
214    float get_yrot(TransformMode* tm=NULL,bool check_only=false,bool curr=false);
215    float get_zrot(TransformMode* tm=NULL,bool check_only=false,bool curr=false);
216    float get_angle(TransformMode* tm=NULL,bool check_only=false,bool curr=false);
217 
218    // is device a headtracker ?
219    bool headflag;
220    // is device a wand ?
221    bool wandflag;
222    // do device send (amost) always values ?
223    bool alwaysflag;
224 
225    // axes of device
226    int number_max_axis;
227    // number of axes used in account (7 (including angle axis))
228    int number_max_axes;
229    // number of ignored axes of device
230    int number_ignored_axes;
231 
232    // 0-2 offset and 3-7 rotation values from joystick (if available)
233    float value[7];
234 
235    // buttons of joystick
236    int number_buttons;
237    bool *button;
238    // last pressed/released button
239    int button_pressed;
240    int button_released;
241 
242    // number of axis for values
243    int num_axis_x;
244    int num_axis_y;
245    int num_axis_z;
246    int num_axis_xrot;
247    int num_axis_yrot;
248    int num_axis_zrot;
249    int num_axis_angle;
250 
251    // true if axis number deliver zero when device released
252    void set_zero_on_release(int axis_number,bool value);
253    void set_zero_on_release(const char* axis_string,bool value);
254 
255    // formula to accelerate values from axis
256    float accelerate(float value,int num_axis);
257 
258    // signswap of value from axis
259    void set_sign_swap(int axis_number,bool value);
260 
261    // factor: multiplcated to value from axis
262    void set_factor(int axis_number,float value);
263    void set_factor(const char* axis_string,float value);
264 
265    // acceleration: additional acceleration parameter
266    void set_acceleration(int axis_number,float value);
267    void set_acceleration(const char* axis_string,float value);
268 
269    // percent (relative to max) of value which will be ignored
270    // "insensitivity of device"
271    void set_zero_size_fraction(int axis_number,float value);
272    void set_zero_size_fraction(const char* string,float value);
273 
274    MyString error_message;
275 
276 protected:
277 
278    // function to read/set configuration data like
279    //    axisnumbers, zero_on_release, acceleration
280    void setAxisOfInformation(const char* axesinfo,int axis_number);
281 
282    bool max_value(int index,TransformMode* tm);
283    float get_value(int index,bool check_only,int num_axis);
284    float get_value(TransformMode* tm,
285                    int index, bool check_only, int num_axis, bool current);
286 
287    float get_current_value(int index,int num_axis);
288 
289    // array of flags for first get from device
290    bool firstflag[7];
291 
292    // flags if the value of a axis should be ignored
293    bool ignore[7];
294 
295    // array of old values from device
296    float oldvalue[7];
297 
298    // array of flags if axis number arrayindex deliver zero when device is
299    // released
300    bool* zero_on_release;
301 
302    // array of nullsize in percent
303    float* zero_size_fraction;
304 
305    // array of flags, if sign of result is swapped
306    bool* sign_swap;
307 
308    // array of factor to multiply with value of axis
309    float* factor;
310 
311    // array of acceleration per axis
312    float* acceleration;
313 
314    // helper functions for setAxisInformation
315    bool scan_argument(char *string,const char* argument);
316    bool argument_is_axis(const char* string);
317    char* axesinfo[6];
318 
319    float xyzfactor;
320    float rotfactor;
321 
322    Quaternion old_quat;
323    Quaternion old_local_quat;
324 
325    const char *device_name;
326    };
327 
328 # ifdef LINUX_JOYSTICK
329 
330 // using Linux Joystick interface
331 
332 #  define NAME_LENGTH 128
333 #  include <linux/joystick.h>
334 
335 class linux_joystick : public InputDevice
336    {
337 public:
338    linux_joystick(const char* device);
339    ~linux_joystick();
340    bool readInputDevice(void);
getDeviceOption(void)341    const char *getDeviceOption(void) const { return "-joystick"; }
342    void guessInputDeviceNames(StringArray *names) const;
343 private:
344    int fd;
345    int version;
346    char name[NAME_LENGTH];
347    struct js_event js;
maxvalue(int i)348    float maxvalue(int i) const {return(32767.0);}
349    float linux_js_factor;
350    };
351 
352 # endif
353 
354 # ifdef DIRECTX_JOYSTICK
355 
356 // using M$Windows Joystick interface from DirectInput API
357 
358 #  include <Windows.h>
359 #include <commctrl.h>
360 #include <basetsd.h>
361 #include <dinput.h>
362 
363 class windows_joystick : public InputDevice
364    {
365 public:
366    windows_joystick(const char* device);
windows_joystick()367    windows_joystick() : InputDevice() {}
368    ~windows_joystick();
369    bool readInputDevice(void);
getDeviceOption(void)370    const char *getDeviceOption(void) const { return "-joystick"; }
371    void guessInputDeviceNames(StringArray *names) const;
372 private:
373    int number_of_joystick;
374    int version;
375    char name[MAXPNAMELEN];
376    DIJOYSTATE2 js;
377    DIPROPRANGE diprg;
378    int *max_value;
379    int *null_value;
maxvalue(int i)380    float maxvalue(int i) const {return max_value[i];}
381    float factor;
382 
383    HRESULT InitDirectInput();
384    LPDIRECTINPUT8 g_pDI;
385    LPDIRECTINPUTDEVICE8 g_pJoystick;
386    };
387 
388 # endif
389 
390 # ifdef WINDOWS_JOYSTICK
391 
392 // using older M$Windows Joystick API interface ("JOYCAPS" etc.)
393 
394 #  include <Windows.h>
395 
396 class windows_joystick : public InputDevice
397    {
398 public:
399    windows_joystick(const char* device);
400    ~windows_joystick();
401    bool readInputDevice(void);
getDeviceOption(void)402    const char *getDeviceOption(void) const { return "-joystick"; }
403    void guessInputDeviceNames(StringArray *names) const;
404 private:
405    int number_of_joystick;
406    int version;
407    char name[MAXPNAMELEN];
408    JOYCAPS joycapabilities;
409    JOYINFOEX joyinfo_ex;
410    int *maximum;
411    int *null_value;
maxvalue(int i)412    float maxvalue(int i) const {return maximum[i];}
413    float windows_js_factor;
414    };
415 
416 # endif
417 
418 # ifdef HAVE_SDL_JOYSTICK
419 
420 // using modified Simple DirectMedia Layer joystick interface
421 # include "SDLjoystick/SDL_joystick.h"
422 
423 class SDL_joystick : public InputDevice
424    {
425 public:
426    SDL_joystick(const char* device);
427    ~SDL_joystick();
428    bool readInputDevice(void);
getDeviceOption(void)429    const char *getDeviceOption(void) const { return "-SDLjoystick"; }
430    void guessInputDeviceNames(StringArray *names) const;
431 private:
432    SDL_Joystick* js;
433    int number_of_joystick;
434    int *maximum;
435    int *null_value;
maxvalue(int i)436    float maxvalue(int i) const {return maximum[i];}
437    float sdl_factor;
438    };
439 
440 # endif
441 
442 # ifdef HAVE_XINPUT
443 
444 // Xi X11 XInput devices (e.g. spaceball, magellan)
445 
446 #  include <X11/extensions/XInput.h>
447 extern "C"
448    {
449 #  include "swtxinput.h"
450    }
451 
452 extern Display* swGetDisplay(void);
453 extern Window* swGetWindow(void);
454 
455 class xinput : public InputDevice
456    {
457 public:
458    xinput(const char* device);
459    ~xinput();
460    bool readInputDevice(void);
getDeviceOption(void)461    const char *getDeviceOption(void) const { return "-xinput"; }
462    void guessInputDeviceNames(StringArray *names) const;
463 private:
464    char* name;
465    swXinput* swxinput;
466    float xinput_factor;
467    float *maximum;
maxvalue(int i)468    float maxvalue(int i) const {return maximum[i];}
469    int nullsize;
470    };
471 
472 # endif
473 
474 # ifdef HAVE_LIBSBALL
475 
476 // using libsball library for SpaceTec/LabTec Spaceball devices
477 
478 #  include <sball.h>
479 
480 class spaceball : public InputDevice
481    {
482 public:
483    spaceball(const char* device);
484    ~spaceball();
485    bool readInputDevice(void);
getDeviceOption(void)486    const char *getDeviceOption(void) const { return "-spaceball"; }
isValid(void)487    virtual bool isValid(void) { return valid && (sball != NULL); }
488    void guessInputDeviceNames(StringArray *names) const;
489 private:
490    const char* name;
491    SBallHandle sball;
maxvalue(int i)492    float maxvalue(int i) const {return maximum;}
493    float maximum;
494    float sball_factor;
495    int nullsize;
496    };
497 
498 # endif
499 
500 #ifdef HAVE_NXT_DIALS
501 
502 #define USB_ID_VENDOR_LEGO 0x0694
503 #define USB_ID_PRODUCT_NXT 0x0002
504 #define USB_OUT_ENDPOINT   0x01
505 #define USB_IN_ENDPOINT    0x82
506 #define USB_TIMEOUT        1000
507 #define USB_INTERFACE      0
508 #define USB_NXT_CONFIG     1
509 
510 class nxtDials : public InputDevice
511    {
512 public:
513    nxtDials(const char* device);
514    virtual ~nxtDials();
515    bool readInputDevice(void);
getDeviceOption(void)516    const char *getDeviceOption(void) const { return "-nxtdials"; }
517    void guessInputDeviceNames(StringArray *names) const;
518 private:
519    const char* name;
maxvalue(int i)520    float maxvalue(int i) const {return maximum;}
521    float maximum;
522    float factor;
523    const char* errorString;
524    int getRotationCount(int motorport);
525 
526    // USB functions in this class from on libnxtusb.cpp of
527    // http://jgraef.bplaced.de/libnxtusb-0.1b.zip
528    /****************************************************************************
529     *                               LibNXTUSB                                  *
530     ****************************************************************************
531     *                                                                          *
532     *  Name:     LibNXTUSB 0.1                                                 *
533     *  Auhor:    Janosch Gräf <jgraef@users.sf.ne t>                          *
534     *  License:  GNU/GPL                                                           *
535     *                                                                              *
536     ********************************************************************************
537     *                                                                              *
538     *  LibNXTUSB - Use your NXT with USB under Linux                               *
539     *  Copyright (C) 2007  Janosch Gräf                                           *
540     *                                                                              *
541     *  This program is free software: you can redistribute it and/or modify        *
542     *  it under the terms of the GNU General Public License as published by        *
543     *  the Free Software Foundation, either version 3 of the License, or           *
544     *  (at your option) any later version.                                         *
545     *                                                                              *
546     *  This program is distributed in the hope that it will be useful,             *
547     *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
548     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
549     *  GNU General Public License for more details.                                *
550     *                                                                              *
551     *  You should have received a copy of the GNU General Public License           *
552     *  along with this program.  If not, see <http://www.gnu.org/licenses/>.       *
553     *                                                                              *
554     ********************************************************************************/
555 
556    struct usb_dev_handle *nxt_handle;
557 
558    void shutdown();
559    usb_dev_handle *locate_nxt(int index);
560    int usb_sendpacket(char *buffer,int size);
561    int usb_recvpacket(char *buffer,int size);
562    void abortprog();
563    };
564 
565 #endif
566 
567 # ifdef HAVE_WINDOWS_P5
568 
569 // using P5 library for P5 devices on M$Windows
570 
571 #include "p5dll.h"
572 #include "p5bend.h"
573 #include "P5Motion.h"
574 
575 class windowsp5 : public InputDevice
576    {
577 public:
578    windowsp5(const char* device);
579    ~windowsp5();
sendalways(void)580    bool sendalways(void) { return true; }
581    bool readInputDevice(void);
getDeviceOption(void)582    const char *getDeviceOption(void) const { return "-p5"; }
583    void guessInputDeviceNames(StringArray *names) const;
584 private:
585    CP5DLL P5;
586    bool bP5Present;
587    char* name;
588    int number_p5;
maxvalue(int i)589    float maxvalue(int i) const {return maximum;}
590    float maximum;
591    float p5_factor;
592    int nullsize;
593    };
594 
595 # endif
596 
597 # ifdef HAVE_WINDOWS_SPACEBALL
598 
599 // using modern driver for SpaceNavigator devices on M$Windows
600 
601 class windowsspaceball : public InputDevice
602    {
603 public:
604    windowsspaceball(const char* device);
605    ~windowsspaceball();
getDeviceOption(void)606    const char *getDeviceOption(void) const { return "-spaceball"; }
607    bool readInputDevice(void);
608    void guessInputDeviceNames(StringArray *names) const;
609 private:
610    bool initialized;
611    char* name;
maxvalue(int i)612    float maxvalue(int i) const {return maximum;}
613    float maximum;
614    float windows_sball_factor;
615    int nullsize;
616    };
617 
618 # endif
619 
620 # ifdef HAVE_AFLOCK
621 
622 // using aFlock program from VR Juggler library for
623 // Ascention Flock of birds devices
624 
625 #  include "Aflock.h"
626 
627 class AflockDevice
628    {
629 public:
630    AflockDevice(const char* device);
631    ~AflockDevice();
isValid()632    bool isValid() { return opened; }
633    Aflock* getAflock(void);
getDeviceName(void)634    const char *getDeviceName(void) { return name; }
635    void close();
getErrorMessage(void)636    const char *getErrorMessage(void) { return errorMessage; }
637    // parameters
638    void setBaud(char* baudrate_string);
639    void setSync(char* sync_string);
640    void setBlock(char* block_string);
641    void setNumBrds(char* numBrds_string);
642    void setTransmit(char* transmit_string);
643    void setHemi(char* hemi_string);
644    void setFilt(char* filt_string);
645    void setSuddenChangeLock(char* sudden_string);
646    void setReport(char* report_string);
647    void setCalfile(char* calfile_string);
648    void setIgnoreSize(const char* ignoresize_string);
649 
start(void)650    void start(void) { if (flock) flock->start(); }
stop(void)651    void stop(void) { if (flock) flock->stop(); }
652 
getBaudrate(void)653    int getBaudrate(void) { return baudrate; }
getNumBrds(void)654    int getNumBrds(void) { return numBrds; }
getBlock(void)655    int getBlock(void) { return block; }
getTransmit(void)656    int getTransmit(void) { return transmit; }
657    const char* getHemi(void);
658    const char* getFilt(void);
getCalfile(void)659    const char* getCalfile(void) { return calfile; }
getSudden_change_lock(void)660    bool getSudden_change_lock(void) { return sudden_change_lock; }
getReport(void)661    char getReport(void) { return report; }
getSync(void)662    int getSync(void) { return sync; }
getIgnoreSize()663    float getIgnoreSize() { return ignoreSize; }
664 
665 private:
666    char* name;
667    Aflock* flock;
668    bool opened;
669    MyString errorMessage;
670 
671    const char* device;
672    int baudrate;
673    int sync;
674    int block;
675    int numBrds;
676    int transmit;
677    BIRD_HEMI hemi;
678    BIRD_FILT filt;
679    bool sudden_change_lock;
680    char report;
681    char* calfile;
682    float ignoreSize;
683    };
684 
685 class aflock : public InputDevice
686    {
687 public:
688    aflock(AflockDevice* device,char* receiver_string,bool headflag);
aflock(const char * checkDev)689    aflock(const char *checkDev) : InputDevice("")
690        {
691        aflockDevice = NULL;
692        checkDevice = checkDev;
693        }
694    ~aflock();
695    bool readInputDevice(void);
696    bool isValid();
sendalways(void)697    bool sendalways(void) { return true; }
hasReadDelay(void)698    bool hasReadDelay(void) { return true; }
699    void prepareRead(void);
setHeadNavigation(void)700    void setHeadNavigation(void) { headNavigation=true; }
getHeadNavigation(void)701    bool getHeadNavigation(void) { return isTracker() && headNavigation; }
useCurrent(void)702    bool useCurrent(void) { return true; }
getDeviceOption(void)703    const char *getDeviceOption(void) const
704       {
705       if (head)
706          return "-tracker";
707       else
708          return "-wand";
709       }
710    void guessInputDeviceNames(StringArray *names) const;
711 private:
712    int receiver;
713    char* name;
714    bool head;
715    bool wand;
716    bool headNavigation;
isTracker(void)717    bool isTracker(void) { return head; }
isWand(void)718    bool isWand(void) { return wand; }
719    Aflock* flock;
720    AflockDevice* aflockDevice;
721    const char *checkDevice;
maxvalue(int i)722    float maxvalue(int i) const {return maximum;}
723    float maximum;
724    float aflock_factor;
725    float degreefactor;
726    bool aflockFirstFlag;
727    float old_value[3];
728    };
729 # endif
730 
731 class dummyInputDevice : public InputDevice
732    {
733 public:
dummyInputDevice(const char * device)734    dummyInputDevice(const char* device) : InputDevice(device) {};
~dummyInputDevice()735    ~dummyInputDevice() {};
readInputDevice(void)736    bool readInputDevice(void) { return false; }
getDeviceOption(void)737    const char *getDeviceOption(void) const { return "-dummy"; }
738    void guessInputDeviceNames(StringArray *names) const;
739 private:
maxvalue(int axis)740    float maxvalue(int axis) const { return 0.0; }
741    };
742 
743