/*****************************************************************************
  FILE           : o_graph.c
  SHORTNAME      : o_graph.c
  SNNS VERSION   : 3.2

  PURPOSE        : Contains all functions to run GRAPH.
  NOTES          : You may wonder why all functions have the praefix "o_". 
		   That is easy to explaine: The original name of the progam 
		   was oszi.c but we had to change the name because it was 
		   not English enough.
             	   Small parts of graph.c are in the programs ui_remoteP.c 
		   and ui_fileP.c

  AUTHOR         : Markus Heuttel and Michael Schmalzl
  DATE           : 3.4.1992

  CHANGED BY     :
  IDENTIFICATION : @(#)o_graph.c	1.15 3/30/94
  SCCS VERSION   : 1.15
  LAST CHANGE    : 3/30/94

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG
             
******************************************************************************/


#include <stdio.h>
#include <malloc.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/cursorfont.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Simple.h>
#include <X11/Xaw/Grip.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/SmeLine.h>
#include <X11/Xaw/Viewport.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Toggle.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Cardinals.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Scrollbar.h>

#include "ui.h"
#include "ui_xWidgets.h"
#include "ui_textP.h"
#include "ui_main.h"
#include "ui_mainP.h"


#include "done.xbm"
#include "clear.xbm"
#include "prev.xbm"
#include "next.xbm"

#include "o_graph.ph"


	/***************** Begin: Performance Functions *********************/


/*****************************************************************************
  FUNCTION : o_createOszi

  PURPOSE  : creates the  window GRAPH
  RETURNS  : void
  NOTES    :

  UPDATE   : 20.12.1991
******************************************************************************/

void o_createOszi (void)

{
 Widget       form,w1,w2;
 Widget       Done_oszi, clear_oszi;
 Arg          arg[5];
 Cardinal     n;
 char         buf[40];
 unsigned long foreground,background;
 int          curr_unit,test_unit,
              count_unit = 0;


 /* test whether there are output units defined. Since no error can be 
    computed in the opposit case, the graph tool can not be used */
 curr_unit  = krui_getCurrentUnit();
 test_unit  = krui_getFirstUnit();
 do{
     if((n=krui_getUnitTType(test_unit)) == 2) count_unit = 1;
 }while((test_unit  = krui_getNextUnit()) && (count_unit == 0));
 curr_unit  = krui_setCurrentUnit(curr_unit);
 
 if(count_unit == 0){
    ui_confirmOk("No Output units defined!\nAn error can neither be\ncomputed nor displayed!");
   return;
 }


 if(!o_open) {
   o_open = 1; 
   o_init();
    
   sprintf (buf, "graph");
   n = 0;  

   XtSetArg(arg[n],XtNminHeight,200); n++;
   XtSetArg(arg[n],XtNminWidth,400); n++;  
   XtSetArg(arg[n],XtNheight,o_WindowHeight+29); n++;
   XtSetArg(arg[n],XtNwidth,o_WindowWidth+10); n++;

   o_displayMainWidget = XtCreatePopupShell (buf, topLevelShellWidgetClass, ui_toplevel, arg, n); 
   n = 0;  
   form = XtCreateManagedWidget ("form", formWidgetClass, o_displayMainWidget, arg, n);

    Done_oszi = o_xCreateButtonItem ("Done",form,NULL,NULL);
       XtAddCallback(Done_oszi,XtNcallback,(XtCallbackProc) o_DoneProc,NULL);

    clear_oszi = o_xCreateButtonItem ("Clear",form,Done_oszi,NULL);
       XtAddCallback(clear_oszi,XtNcallback,(XtCallbackProc) o_clearProc,NULL);

    w1 = ui_xCreateLabelItem ("Scale X: ",form,8*8,clear_oszi,NULL); 
    w2 = o_xCreateButtonItem ("prev",form,w1,NULL);
      XtAddCallback(w2,XtNcallback,(XtCallbackProc) o_XForwardProc,NULL);
    w1 = o_xCreateButtonItem ("next",form,w2,NULL);
      XtAddCallback(w1,XtNcallback,(XtCallbackProc) o_XBackProc,NULL);

    w2 = ui_xCreateLabelItem ("  Scale Y: ",form,10*8,w1,NULL); 
    w1 = o_xCreateButtonItem ("prev",form,w2,NULL); 
      XtAddCallback(w1,XtNcallback,(XtCallbackProc) o_YBackProc,NULL);
    w2 = o_xCreateButtonItem ("next",form,w1,NULL);
      XtAddCallback(w2,XtNcallback,(XtCallbackProc) o_YForwardProc,NULL);

    o_DisplayWidget = o_xCreateScreenItem("screen",form,o_WindowWidth,o_WindowHeight,NULL,Done_oszi);
       XtAddEventHandler(o_DisplayWidget,StructureNotifyMask | ExposureMask, 
		FALSE,(XtEventHandler) o_eventProc,o_display);

    ui_checkWindowPosition(o_displayMainWidget);
    XtPopup (o_displayMainWidget, XtGrabNone);

    o_display = XtDisplay (o_DisplayWidget); 
    o_window = XtWindow (o_DisplayWidget);
    o_fontStruct = XLoadQueryFont(o_display, "6x12");

    o_gc = XCreateGC (o_display, o_window, ZERO, NULL);
    XSetFont(o_display,o_gc,(*o_fontStruct).fid);
    o_screen = DefaultScreen (o_display);

    o_depth = DisplayPlanes(o_display,o_screen);

    background = WhitePixel (o_display, o_screen);
    foreground = BlackPixel (o_display, o_screen); 
   /* XSetWindowBackground(o_display,o_window,background); */
    XSetBackground (o_display, o_gc, background);
    XSetForeground (o_display, o_gc, foreground); 

    XSetGraphicsExposures(o_display,o_gc,0);

    o_Pixmap = XCreatePixmap(o_display,o_window,(unsigned int) o_PixmapWidth, 
	(unsigned int) o_PixmapHeight, (unsigned int) o_depth);
    o_ClearPixmap(o_display,o_Pixmap,o_gc,o_screen,0,0,o_PixmapWidth,o_PixmapHeight); 
    XClearArea(o_display,o_window,o_OsziXPos,o_OsziYPos, 
	(unsigned int) o_OsziWidth-1, (unsigned int) o_OsziHeight,1);
   } 
}


/*****************************************************************************
  FUNCTION : o_eventProc

  PURPOSE  : Manages the events (resize and exposure)
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_eventProc (Widget w, Display *display, XEvent *event)

{
 switch (event->type) {
   case Expose: {
     if(event->xexpose.count == 0) {
         XCopyArea(o_display,o_Pixmap,o_window,o_gc,0,0,(unsigned int) o_OsziWidth, (unsigned int) o_OsziHeight,o_OsziXPos,o_OsziYPos);
         XDrawRectangle(o_display,o_window,o_gc,o_OsziFrameXPos,o_OsziFrameYPos, (unsigned int) o_OsziWidth+1, (unsigned int) o_OsziHeight);
         o_ScaleY(o_display,o_window,o_gc);
         o_ScaleX(o_display,o_window,o_gc);
     }
     break;
   }
   case ConfigureNotify: {
     o_ResizeOszi(event->xconfigure.width,event->xconfigure.height);
     break;
   }
 }     
}


/*****************************************************************************
  FUNCTION : o_clearProc

  PURPOSE  : callback function of the clear-buttom. 
             Clears all curves of the display.
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_clearProc (void)

{
 if(o_PressPossible){
   o_ClearPixmap(o_display,o_Pixmap,o_gc,o_screen,0,0,o_PixmapWidth,o_PixmapHeight);
   XClearArea(o_display,o_window,o_OsziXPos,o_OsziYPos,(unsigned int) o_OsziWidth,(unsigned int) o_OsziHeight-1,1);
   o_ClearCurves();
   o_LearnStepCount = 0;
 }
}


/*****************************************************************************
  FUNCTION : o_DoneProc

  PURPOSE  : callback function of the done-buttom. You are leaving bignet.A
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_DoneProc (void)

{
 if(o_PressPossible){
   o_clearProc();
   XtDestroyWidget(o_displayMainWidget);
   o_open = 0;
 }
}


/*****************************************************************************
  FUNCTION : o_xCreateButtonItem

  PURPOSE  : create a command widget
  RETURNS  : Widget
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static Widget o_xCreateButtonItem (char *name, Widget parent, Widget left, Widget top)

{
    int n;
    Widget   w;
    Arg      arg[15];
    
    n = 0;

    if (strcmp (name, "Done") == 0)
      {  
        XtSetArg(arg[n], XtNbitmap, 
                  (Pixmap) XCreateBitmapFromData (ui_display,
                   XDefaultRootWindow (ui_display), 
                   done_bits, done_width, done_height)); n++;
      } 

    if (strcmp (name, "Clear") == 0)
      {  
        XtSetArg(arg[n], XtNbitmap, 
                  (Pixmap) XCreateBitmapFromData (ui_display,
                   XDefaultRootWindow (ui_display), 
                   clear_bits, clear_width, clear_height)); n++;
      } 

    if (strcmp (name, "prev") == 0)
      {  
        XtSetArg(arg[n], XtNbitmap, 
                  (Pixmap) XCreateBitmapFromData (ui_display,
                   XDefaultRootWindow (ui_display), 
                   prev_bits, prev_width, prev_height)); n++;
      } 

    if (strcmp (name, "next") == 0)
      {  
        XtSetArg(arg[n], XtNbitmap, 
                  (Pixmap) XCreateBitmapFromData (ui_display,
                   XDefaultRootWindow (ui_display), 
                   next_bits, next_width, next_height)); n++;
      } 

    XtSetArg(arg[n], XtNborderWidth, 0); n++;
    XtSetArg(arg[n], XtNinternalHeight, 1); n++;
    XtSetArg(arg[n], XtNinternalWidth , 1); n++;

    XtSetArg(arg[n], XtNfromVert , top);  n++;
    XtSetArg(arg[n], XtNfromHoriz, left);  n++;
    XtSetArg(arg[n], XtNleft  , XtChainLeft); n++;
    XtSetArg(arg[n], XtNright , XtChainLeft); n++;
    XtSetArg(arg[n], XtNtop   , XtChainTop); n++;
    XtSetArg(arg[n], XtNbottom, XtChainTop); n++;

    w = XtCreateManagedWidget(name, commandWidgetClass, parent, arg, 
	(unsigned int) n);
    return(w);
}


/*****************************************************************************
  FUNCTION : o_xCreateScreenItem

  PURPOSE  : Creates the widget on which the curves are drawn.
  RETURNS  : Widget
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static Widget   o_xCreateScreenItem (char *name, Widget parent, Dimension width, 
	Dimension height, Widget left, Widget top)

{ 
    Cardinal n;
    Widget   w;
    Arg      args[15];

    n = 0;
    XtSetArg(args[n], XtNheight,height); n++;
    XtSetArg(args[n], XtNwidth,width); n++;
    XtSetArg(args[n], XtNfromVert , top);  n++;

    XtSetArg(args[n], XtNleft  , XtChainLeft); n++;
    XtSetArg(args[n], XtNright , XtChainRight); n++;
    XtSetArg(args[n], XtNtop   , XtChainTop); n++;
    XtSetArg(args[n], XtNbottom, XtChainBottom); n++;

   XtSetArg(args[n],XtNresizable,True); n++;

    w = XtCreateManagedWidget(name, formWidgetClass, parent, args, n);

    return(w);
}


/*****************************************************************************
  FUNCTION : o_XBackProc

  PURPOSE  : callback function which changes the scala of the x-axis. The scala
             is enlarged. (You press the right x-buttom)
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_XBackProc (void)

{
 if(o_PressPossible){
   o_XBack();
 }
}


/*****************************************************************************
  FUNCTION : o_XForwardProc

  PURPOSE  : callback function which changes the scala of the x-axis. The scala
             is lessened. (You press the left x-buttom)
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_XForwardProc (void)

{
 if(o_PressPossible){
   o_XForward();
 }
}


/*****************************************************************************
  FUNCTION : o_YBackProc

  PURPOSE  : callback function which changes the scala of the y-axis. The scala
             is lessened. (You press the left y-buttom)
  RETURNS  : void
  NOTES    : That's just the other way round as o_XBackProc!

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_YBackProc (void)

{
 if(o_PressPossible){
   o_YBack();
 }
}


/*****************************************************************************
  FUNCTION : o_YForwardProc

  PURPOSE  : callback function which changes the scala of the y-axis. The scala
             is enlarged. (You press the right y-buttom)
  RETURNS  : void
  NOTES    : That's just the other way round as o_XForwardProc!

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_YForwardProc (void)

{
 if(o_PressPossible){
   o_YForward();
 }
}

 
	/***************** End: Performance Functions *********************/


/*****************************************************************************
  FUNCTION : o_init

  PURPOSE  : Initializes all variables of GRAPH. 
  RETURNS  : void
  NOTES    :

  UPDATE   : 20.12.1991
******************************************************************************/

static void o_init (void)

{
 int i;

 o_WindowWidth = 490;
 o_WindowHeight = 250;

 o_LearnStepCount = 0;
 o_YCounter = 3;
 o_XCounter = 9;

 o_PixelsOfOneLearningStep = o_XScaleValues[o_XCounter].ScreenEinheit/o_XScaleValues[o_XCounter].Einheit; 
 o_PixelsOfOneLearningStep = ((o_PixelsOfOneLearningStep == 0) ? (1) : (o_PixelsOfOneLearningStep));
 o_LearningStepsOfOnePixel = o_XScaleValues[o_XCounter].Einheit/o_XScaleValues[o_XCounter].ScreenEinheit;
 o_LearningStepsOfOnePixel = ((o_LearningStepsOfOnePixel == 1) ? (0) : (o_LearningStepsOfOnePixel));

 o_AbsoluteScale = (float)o_YScaleValues[o_YCounter].ScreenEinheit/(float)o_YScaleValues[o_YCounter].Einheit;

 o_RightOffset      = o_PixelsOfOneLearningStep;
 o_OsziXPos         = o_SpaceLeft;
 o_OsziYPos         = o_SpaceAbove;
 o_OsziHeight       = 200;  
 o_OsziWidth        = 400; 
 o_PixmapWidth      = o_OsziWidth+o_RightOffset;
 o_PixmapHeight     = o_OsziHeight;
 o_OsziFrameXPos   = o_OsziXPos - 1; 
 o_OsziFrameYPos   = o_OsziYPos - 1;
 o_OsziFrameWidth  = o_OsziWidth + 2;
 o_OsziFrameHeight = o_OsziHeight + 2;
 o_MaxYDrawPos      = o_OsziHeight-1;

 o_CurveNo = 0;
 for(i=0;i<MAX_CURVE_NO;i++) {
   o_CurveLengths[i] = -1;
 }
 o_InitCurve();
}


/*****************************************************************************
  FUNCTION : o_InitCurve

  PURPOSE  : Allocates space for a new curve.
  RETURNS  : void
  NOTES    :

  UPDATE   : 20.12.1991
******************************************************************************/

void o_InitCurve (void)

{
 char buf[80];
 
 if(o_open && (o_CurveLengths[o_CurveNo] != 0)) {
   if(o_CurveLengths[o_CurveNo] == -1) {
     o_DrawAllowed = 1;
     o_MaxCurveLengths = (int)(o_OsziWidth/o_PixelsOfOneLearningStep);
     o_Curve1[o_CurveNo] = (XPoint *)malloc(sizeof(XPoint)*o_MaxCurveLengths);
     o_Curve2[o_CurveNo]   = (MPoint *)malloc(sizeof(MPoint)*o_MaxCurveLengths);
     o_CurveLengths[o_CurveNo] = 0;
     o_Curve1[o_CurveNo][0].x = -1;
     o_Curve1[o_CurveNo][0].y = -1;
     o_Curve2[o_CurveNo][0].x =  0;
     o_Curve2[o_CurveNo][0].y =  0;
   }
   else {
     o_DrawAllowed = 0;
     sprintf(buf,"The max no of curves you can draw is %d",MAX_CURVE_NO);
     ui_tw_errorMessage(buf);
   }
 }
}


/*****************************************************************************
  FUNCTION : o_ClearCurves

  PURPOSE  : Deletes all curves. (The curves vanish from display and the 
             storage is set free.
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_ClearCurves (void)

{
 /* Old Style: for(o_CurveNo;o_CurveNo>=0;o_CurveNo--) */
 for(;o_CurveNo>=0;o_CurveNo--) 
   {
   o_CurveLengths[o_CurveNo]= -1;
   free(o_Curve1[o_CurveNo]);
   free(o_Curve2[o_CurveNo]);
 }
 o_CurveNo = 0;
 o_InitCurve();
}


/*****************************************************************************
  FUNCTION : o_ResizeOszi

  PURPOSE  : This function is called when there is a risize event by the 
             X-server. The variables are initialized with their new values,
             the storage for the current curve (only for this one) is 
	     reallocated and the curves are drawn again.
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_ResizeOszi (int box_width, int box_height)

{
 int Length,UsedStorage,NeededStorage,FreeStorage,x;
 int i,j;
 
 o_WindowWidth = box_width;
 o_WindowHeight = box_height;

 o_PixelsOfOneLearningStep = o_XScaleValues[o_XCounter].ScreenEinheit/o_XScaleValues[o_XCounter].Einheit;
 o_PixelsOfOneLearningStep = ((o_PixelsOfOneLearningStep == 0) ? (1) : (o_PixelsOfOneLearningStep));
 o_LearningStepsOfOnePixel = o_XScaleValues[o_XCounter].Einheit/o_XScaleValues[o_XCounter].ScreenEinheit;
 o_LearningStepsOfOnePixel = ((o_LearningStepsOfOnePixel == 1) ? (0) : (o_LearningStepsOfOnePixel));

 o_RightOffset      = o_PixelsOfOneLearningStep;
 o_OsziHeight       = o_WindowHeight-o_SpaceAbove-o_SpaceBelow;  
 o_OsziWidth        = o_WindowWidth-o_SpaceLeft-o_SpaceRight;
 o_PixmapWidth      = o_OsziWidth+o_RightOffset;
 o_PixmapHeight     = o_OsziHeight;
 o_OsziFrameWidth   = o_OsziWidth + 2;
 o_OsziFrameHeight  = o_OsziHeight + 2;
 o_MaxYDrawPos      = o_OsziHeight-1;

 for(i=0;i<=o_CurveNo;i++) {
   for(j=0;j<o_CurveLengths[i];j++) {
     o_Curve1[i][j].y = (int) (o_MaxYDrawPos-(o_Curve2[i][j].y*o_AbsoluteScale));
   }
 }

 Length = ((o_CurveLengths[o_CurveNo]-1 == -1)?(0):(o_CurveLengths[o_CurveNo]-1)); 
 x = ((o_Curve1[o_CurveNo][Length].x == -1)?(0):(o_Curve1[o_CurveNo][Length].x)); 
 UsedStorage = o_CurveLengths[o_CurveNo];
 FreeStorage = (int)((o_OsziWidth-x)/o_PixelsOfOneLearningStep);
 NeededStorage = ((FreeStorage <= 0)?(0):(FreeStorage+UsedStorage));

 if(NeededStorage > UsedStorage) {
   o_Curve1[o_CurveNo] = (XPoint *)realloc(o_Curve1[o_CurveNo],NeededStorage*sizeof(XPoint));
   o_Curve2[o_CurveNo] = (MPoint *)realloc(o_Curve2[o_CurveNo],NeededStorage*sizeof(MPoint));
   o_MaxCurveLengths = NeededStorage;
 }

 XFreePixmap(o_display,o_Pixmap);
 o_Pixmap = XCreatePixmap(o_display,o_window,(unsigned int) o_PixmapWidth, (unsigned int) o_PixmapHeight, (unsigned int) o_depth);
 o_ClearPixmap(o_display,o_Pixmap,o_gc,o_screen,0,0,o_PixmapWidth,o_PixmapHeight); 
 for(i=0;i<=o_CurveNo;i++) {
   XDrawLines(o_display,o_Pixmap,o_gc,o_Curve1[i],o_CurveLengths[i],CoordModeOrigin);
 }
 XClearArea(o_display,o_window,0,0,(unsigned int) o_WindowWidth, (unsigned int) o_WindowHeight,0); 
 XCopyArea(o_display,o_Pixmap,o_window,o_gc,0,0,(unsigned int) o_OsziWidth,(unsigned int) o_OsziHeight,o_OsziXPos,o_OsziYPos);
 XDrawRectangle(o_display,o_window,o_gc,o_OsziFrameXPos,o_OsziFrameYPos,
	(unsigned int) o_OsziWidth+1, (unsigned int) o_OsziHeight);
 o_ScaleY(o_display,o_window,o_gc); 
 o_ScaleX(o_display,o_window,o_gc); 
}


/*****************************************************************************
  FUNCTION : o_XScaleCurve

  PURPOSE  : After the size of the x-scala has changed, the pixel position
             of the x-values must be calculated again. (Remember: the array
             Curve1 contains the pixel positions of the curves, while the
             array Curve2 contains the number of learnsteps as x-value and
             the learn-error as y-value.)
  RETURNS  : void
  NOTES    : Is called by o_XBack and by o_XForward.

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_XScaleCurve (void)

{
 int i,j,tmp_Curve1_x;
 double PixelsOfOneLearningStep;
   
 for(i=0;i<=o_CurveNo;i++) {
   PixelsOfOneLearningStep = o_XScaleValues[o_XCounter].ScreenEinheit/o_XScaleValues[o_XCounter].Einheit;
   for(j=0;j<o_CurveLengths[i];j++) {
     tmp_Curve1_x = o_Curve2[i][j].x*PixelsOfOneLearningStep-1;
     /* 
        This is necessery to prevent too large values for o_Curve1[i][j].x, which is of the type "short" (XPoint),
        when scaling downward. 
     */ 
     o_Curve1[i][j].x = (tmp_Curve1_x > 32767) ? (32767) : (tmp_Curve1_x);
   }
 }
}


/*****************************************************************************
  FUNCTION : o_XBack

  PURPOSE  : The scala of the x-axis is enlarged. The variables have to be 
             initialized with new values and the storage of the current curve 
             (only for this one) has to be reallocated. Before drawing all 
             visible curves, the function o_XScaleCurve() must be called.
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_XBack (void)

{
 int i,Length;
 double PixelsOfOneLearningStep;
 int UsedStorage,FreeStorage,NeededStorage;
 
 o_XCounter = (--o_XCounter < 0) ? (0) : (o_XCounter);

 o_PixelsOfOneLearningStep = PixelsOfOneLearningStep = o_XScaleValues[o_XCounter].ScreenEinheit/o_XScaleValues[o_XCounter].Einheit;
 o_PixelsOfOneLearningStep = ((o_PixelsOfOneLearningStep == 0) ? (1) : (o_PixelsOfOneLearningStep));
 o_LearningStepsOfOnePixel = o_XScaleValues[o_XCounter].Einheit/o_XScaleValues[o_XCounter].ScreenEinheit;
 o_LearningStepsOfOnePixel = ((o_LearningStepsOfOnePixel == 1) ? (0) : (o_LearningStepsOfOnePixel));

 Length = (o_CurveLengths[o_CurveNo]-1 == -1)?(0):(o_CurveLengths[o_CurveNo]-1);
 UsedStorage = o_CurveLengths[o_CurveNo];
 FreeStorage = (int)((o_OsziWidth-(o_Curve2[o_CurveNo][Length].x*PixelsOfOneLearningStep-1))/o_PixelsOfOneLearningStep);
 NeededStorage = ((FreeStorage <= 0)?(0):(FreeStorage+UsedStorage));

 if(NeededStorage>o_MaxCurveLengths){
   o_Curve1[o_CurveNo] = (XPoint *)realloc(o_Curve1[o_CurveNo],NeededStorage*sizeof(XPoint));
   o_Curve2[o_CurveNo] = (MPoint *)realloc(o_Curve2[o_CurveNo],NeededStorage*sizeof(MPoint));
   o_MaxCurveLengths = NeededStorage;
 }
 o_XScaleCurve();
 XClearArea(o_display,o_window,0,0,(unsigned int) o_WindowWidth,(unsigned int) o_WindowHeight,0); 
 o_ScaleY(o_display,o_window,o_gc); 
 o_ScaleX(o_display,o_window,o_gc); 
 o_ClearPixmap(o_display,o_Pixmap,o_gc,o_screen,0,0,o_PixmapWidth,o_PixmapHeight); 
 for(i=0;i<=o_CurveNo;i++) {
   XDrawLines(o_display,o_Pixmap,o_gc,o_Curve1[i],o_CurveLengths[i],CoordModeOrigin);
 } 
 XCopyArea(o_display,o_Pixmap,o_window,o_gc,0,0,(unsigned int) o_OsziWidth,
	(unsigned int) o_OsziHeight,o_OsziXPos,o_OsziYPos);
 XDrawRectangle(o_display,o_window,o_gc,o_OsziFrameXPos,o_OsziFrameYPos,
	(unsigned int) o_OsziWidth+1,(unsigned int) o_OsziHeight);
 }


/*****************************************************************************
  FUNCTION : o_XForward

  PURPOSE  : The scala of the x-axis is lessened. The variables have to be 
             initialized with new values and the storage of the current curve 
             (only for this one) has to be reallocated. Before drawing all 
             visible curves, the function o_XScaleCurve() must be called. 
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_XForward (void)

{
 int i,Length;
 double PixelsOfOneLearningStep;
 int UsedStorage,FreeStorage,NeededStorage;

 if(o_XCounter != 14){ 
   o_XCounter++;

   o_PixelsOfOneLearningStep = PixelsOfOneLearningStep = o_XScaleValues[o_XCounter].ScreenEinheit/o_XScaleValues[o_XCounter].Einheit;
   o_PixelsOfOneLearningStep = ((o_PixelsOfOneLearningStep == 0) ? (1) : (o_PixelsOfOneLearningStep));
   o_LearningStepsOfOnePixel = o_XScaleValues[o_XCounter].Einheit/o_XScaleValues[o_XCounter].ScreenEinheit;
   o_LearningStepsOfOnePixel = ((o_LearningStepsOfOnePixel == 1) ? (0) : (o_LearningStepsOfOnePixel));

   Length = (o_CurveLengths[o_CurveNo]-1 == -1)?(0):(o_CurveLengths[o_CurveNo]-1);

   UsedStorage = o_CurveLengths[o_CurveNo];
   FreeStorage = (int)((o_OsziWidth-(o_Curve2[o_CurveNo][Length].x*PixelsOfOneLearningStep-1))/o_PixelsOfOneLearningStep);
   NeededStorage = ((FreeStorage <= 0)?(0):(FreeStorage+UsedStorage));

   if(NeededStorage){
     o_Curve1[o_CurveNo] = (XPoint *)realloc(o_Curve1[o_CurveNo],NeededStorage*sizeof(XPoint));
     o_Curve2[o_CurveNo] = (MPoint *)realloc(o_Curve2[o_CurveNo],NeededStorage*sizeof(MPoint));
     o_MaxCurveLengths = NeededStorage;
   }
   o_XScaleCurve();
   XClearArea(o_display,o_window,0,0,(unsigned int) o_WindowWidth,(unsigned int) o_WindowHeight,0); 
   o_ScaleY(o_display,o_window,o_gc); 
   o_ScaleX(o_display,o_window,o_gc); 
   o_ClearPixmap(o_display,o_Pixmap,o_gc,o_screen,0,0,o_PixmapWidth,o_PixmapHeight); 
   for(i=0;i<=o_CurveNo;i++) {
     XDrawLines(o_display,o_Pixmap,o_gc,o_Curve1[i],o_CurveLengths[i],CoordModeOrigin);
   }
   XCopyArea(o_display,o_Pixmap,o_window,o_gc,0,0,(unsigned int) o_OsziWidth,(unsigned int) o_OsziHeight,o_OsziXPos,o_OsziYPos);
   XDrawRectangle(o_display,o_window,o_gc,o_OsziFrameXPos,o_OsziFrameYPos,
	(unsigned int) o_OsziWidth+1,(unsigned int) o_OsziHeight);
 }
}


/*****************************************************************************
  FUNCTION : o_YScaleCurve

  PURPOSE  : After the size of the y-scala has changed, the pixel position
             of the y-values must be calculated again. (Remember: the array
             Curve1 contains the pixel positions of the curves, while the
             array Curve2 contains the number of learnsteps as x-value and
             the learn-error as y-value.)
  RETURNS  : void
  NOTES    : Is called by o_YForwardCurve() and o_YBack().

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_YScaleCurve (void)

{
 int i,j;
   
 for(i=0;i<=o_CurveNo;i++) {
   for(j=0;j<o_CurveLengths[i];j++) {
     o_Curve1[i][j].y = (int) (o_MaxYDrawPos-(o_Curve2[i][j].y*o_AbsoluteScale));
   }
 }
}


/*****************************************************************************
  FUNCTION : o_YForward

  PURPOSE  : The scala of the y-axis is enlarged. The variables have to be 
             initialized with new values and the storage of the current curve 
             (only for this one) has to be reallocated. Before drawing all 
             visible curves, the function o_YScaleCurve() must be called.
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_YForward (void)

{
 int i;

 o_YCounter = (++o_YCounter > 14) ? (14) : (o_YCounter);

 o_AbsoluteScale = (float)o_YScaleValues[o_YCounter].ScreenEinheit/(float)o_YScaleValues[o_YCounter].Einheit;

 o_YScaleCurve();
 XClearArea(o_display,o_window,0,0,(unsigned int) o_WindowWidth,(unsigned int) o_WindowHeight,0);  
 o_ScaleY(o_display,o_window,o_gc); 
 o_ScaleX(o_display,o_window,o_gc);
 o_ClearPixmap(o_display,o_Pixmap,o_gc,o_screen,0,0,o_PixmapWidth,o_PixmapHeight);
 
 for(i=0;i<=o_CurveNo;i++) {
   XDrawLines(o_display,o_Pixmap,o_gc,o_Curve1[i],o_CurveLengths[i],CoordModeOrigin);
 }
 XCopyArea(o_display,o_Pixmap,o_window,o_gc,0,0,(unsigned int) o_OsziWidth, (unsigned int) o_OsziHeight,o_OsziXPos,o_OsziYPos);
 XDrawRectangle(o_display,o_window,o_gc,o_OsziFrameXPos,o_OsziFrameYPos,(unsigned int) o_OsziWidth+1,(unsigned int) o_OsziHeight);
}


/*****************************************************************************
  FUNCTION : o_YBack

  PURPOSE  : The scala of the y-axis is lessened. The variables have to be 
             initialized with new values and the storage of the current curve 
             (only for this one) has to be reallocated. Before drawing all 
             visible curves, the function o_YScaleCurve() must be called.
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_YBack (void)

{
 int i;

 o_YCounter = (--o_YCounter < 0) ? (0) : (o_YCounter);

 o_AbsoluteScale = (float)o_YScaleValues[o_YCounter].ScreenEinheit/(float)o_YScaleValues[o_YCounter].Einheit;

 o_YScaleCurve();
 XClearArea(o_display,o_window,0,0,(unsigned int) o_WindowWidth,(unsigned int) o_WindowHeight,0);  
 o_ScaleY(o_display,o_window,o_gc); 
 o_ScaleX(o_display,o_window,o_gc);
 o_ClearPixmap(o_display,o_Pixmap,o_gc,o_screen,0,0,o_PixmapWidth,o_PixmapHeight);

 for(i=0;i<=o_CurveNo;i++) {
   XDrawLines(o_display,o_Pixmap,o_gc,o_Curve1[i],o_CurveLengths[i],CoordModeOrigin);
 }
 XCopyArea(o_display,o_Pixmap,o_window,o_gc,0,0,(unsigned int) o_OsziWidth,(unsigned int) o_OsziHeight,o_OsziXPos,o_OsziYPos);

 XDrawRectangle(o_display,o_window,o_gc,o_OsziFrameXPos,o_OsziFrameYPos,(unsigned int) o_OsziWidth+1,(unsigned int) o_OsziHeight);
}


/*****************************************************************************
  FUNCTION : o_draw 

  PURPOSE  : draws the current curve and updates the arrays Curve1 and Curve2.
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

void o_draw (float net_error)

{
 double PixelsOfOneLearningStep;
 int error;
 int Length = o_CurveLengths[o_CurveNo];
 int OldX=o_Curve1[o_CurveNo][(Length-1==-1)?(0):(Length-1)].x;
 int OldY=o_Curve1[o_CurveNo][(Length-1==-1)?(0):(Length-1)].y;
 int NewX =  (OldX+o_PixelsOfOneLearningStep);
 
 if(o_DrawAllowed) { 
   o_LearnStepCount++;

   if(NewX >= o_OsziWidth) {
     while((NewX >= o_OsziWidth) && (o_XCounter-- > 0)) {
       PixelsOfOneLearningStep = o_XScaleValues[o_XCounter].ScreenEinheit/o_XScaleValues[o_XCounter].Einheit;
       NewX = o_Curve2[o_CurveNo][o_CurveLengths[o_CurveNo]-1].x*PixelsOfOneLearningStep-1;
     } 
     if(NewX >= o_OsziWidth){
       o_DrawAllowed = 0;
     }
     o_XCounter++; 
     o_XBack();
     OldX=o_Curve1[o_CurveNo][(Length-1==-1)?(0):(Length-1)].x;
     NewX =  (OldX+o_PixelsOfOneLearningStep);
   } 

   if(o_DrawAllowed) {
     if(o_LearningStepsOfOnePixel == 0) { /* One learning step corresponds with at least one pixel */ 
       error = (int) (o_MaxYDrawPos-(net_error*o_AbsoluteScale));
       if(Length == 0) { /* first time */
         XDrawPoint(o_display,o_Pixmap,o_gc,o_Curve1[o_CurveNo][Length].x=NewX,o_Curve1[o_CurveNo][Length].y=error=(error<0)?(-1):(error));
         XDrawPoint(o_display,o_window,o_gc,NewX+o_SpaceLeft,error+o_SpaceAbove);
       }
       else { /* not first time */
         XDrawLine(o_display,o_Pixmap,o_gc,OldX,OldY,o_Curve1[o_CurveNo][Length].x=NewX,o_Curve1[o_CurveNo][Length].y=error=(error<0)?(-1):(error));
         XDrawLine(o_display,o_window,o_gc,OldX+o_SpaceLeft,OldY+o_SpaceAbove,NewX+o_SpaceLeft,error+o_SpaceAbove);
       }
       o_Curve2[o_CurveNo][Length].x = o_LearnStepCount;
       o_Curve2[o_CurveNo][Length].y = (double)net_error;
       o_CurveLengths[o_CurveNo]++;
     }
     else {  /* One pixel corresponds with more than one learning step */
       if(!(o_LearnStepCount%o_LearningStepsOfOnePixel)) {
         error  = (int) (o_MaxYDrawPos-(net_error*o_AbsoluteScale));
         if(Length == 0) { /* first time */
           XDrawPoint(o_display,o_Pixmap,o_gc,o_Curve1[o_CurveNo][Length].x=NewX,o_Curve1[o_CurveNo][Length].y=error=(error<0)?(-1):(error));
           XDrawPoint(o_display,o_window,o_gc,NewX+o_SpaceLeft,error+o_SpaceAbove);
         }
         else { /* not first time */
           XDrawLine(o_display,o_Pixmap,o_gc,OldX,OldY,o_Curve1[o_CurveNo][Length].x=NewX,o_Curve1[o_CurveNo][Length].y=error=(error<0)?(-1):(error));
           XDrawLine(o_display,o_window,o_gc,OldX+o_SpaceLeft,OldY+o_SpaceAbove,NewX+o_SpaceLeft,error+o_SpaceAbove);
         }
         o_Curve2[o_CurveNo][Length].x = o_LearnStepCount;
         o_Curve2[o_CurveNo][Length].y = (double)net_error;
         o_CurveLengths[o_CurveNo]++;
       }
     }
   }
 }/* if(o_open) */
}


/*****************************************************************************
  FUNCTION : o_ClearPixmap

  PURPOSE  : clears the pixmap
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_ClearPixmap (Display *display, Drawable pixmap, GC gc, int screen, 
	int x, int y, int width, int height)

{
 XSetForeground (display,gc,WhitePixel(o_display, o_screen));
 XFillRectangle(display,pixmap,gc,x,y,(unsigned int) width,(unsigned int) height);
 XSetForeground(display,gc,BlackPixel(o_display, o_screen));
}


/*****************************************************************************
  FUNCTION : o_ScaleY

  PURPOSE  : draws the y-axis.
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_ScaleY (Display *my_display, Drawable my_window, GC my_gc)

{
 int buflen;
 char buf[10];
 int count=0;
 int y1,x1,x2,x3;
 int FontWriteYPos = (*o_fontStruct).ascent>>1;

 y1 = o_OsziFrameYPos + o_OsziHeight;
 x1 = o_OsziFrameXPos;
 x2 = x1 - 6;
 x3 = x1 - 3; 
 
 while(y1 >= o_OsziFrameYPos) {
     XDrawLine(my_display,my_window,my_gc,x1,y1,x2,y1);   
     sprintf(buf,"%6.2f",(count*(o_YScaleValues[o_YCounter].Einheit)));
     buflen = strlen(buf);
     XDrawString(o_display,o_window,o_gc,
                   x2-XTextWidth(o_fontStruct,buf,buflen),y1+FontWriteYPos,buf,buflen); 
   count++;
   y1 -= o_YScaleValues[o_YCounter].ScreenEinheit;
 }
}


/*****************************************************************************
  FUNCTION : o_ScaleX

  PURPOSE  : draws the x-axix.
  RETURNS  : void
  NOTES    :

  UPDATE   : 3.4.1992
******************************************************************************/

static void o_ScaleX (Display *my_display, Drawable my_window, GC my_gc)

{
 int buflen;
 int count=0;
 int x1,y1,y2,y3;
 char buf[10];
 int o_OsziFrameXPos_AND_o_OsziFrameWidth = o_OsziFrameXPos+o_OsziFrameWidth;
 int FontWriteXPos = (*o_fontStruct).ascent+2;

 x1 = o_OsziFrameXPos;
 y1 = o_OsziFrameYPos+o_OsziHeight;
 y2 = y1 + 5;
 y3 = y1 + 2;

 while(x1 <= o_OsziFrameXPos_AND_o_OsziFrameWidth) {
     XDrawLine(my_display,my_window,my_gc,x1,y1,x1,y2);
     sprintf(buf,"%d",(int)(count*o_XScaleValues[o_XCounter].Einheit));
     buflen = strlen(buf);
     XDrawString(o_display,o_window,o_gc,
                   x1-(XTextWidth(o_fontStruct,buf,buflen)>>1)+1,y2+FontWriteXPos,buf,buflen);  
  x1 += o_XScaleValues[o_XCounter].ScreenEinheit;
  count++;
 }
}


