home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / irit / drawfuns.arc / DRAWFUNC.C < prev    next >
Text File  |  1989-09-10  |  17KB  |  511 lines

  1. /*****************************************************************************
  2. *   Program to draw a function given as Y = f(X) or X = f(t), Y = f(t) .     *
  3. * The function is drawn with its derivatives, up to any level...             *
  4. *                                                                            *
  5. *   Written be :  Gershon Elber                          Ver 0.2, Apr. 1989  *
  6. *****************************************************************************/
  7.  
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <graphics.h>
  11. #include <math.h>
  12. #include <string.h>
  13. #include <conio.h>
  14. #include <dos.h>
  15. #include <dir.h>
  16. #include <setjmp.h>
  17. #include "Program.h"
  18. #include "Expr2TrG.h"
  19. #include "GraphGnG.h"
  20. #include "MouseDrv.h"
  21. #include "Config.h"
  22. #include "MathErr.h"
  23.  
  24. int MouseExists = FALSE,  /* Set according to autotest and config enforcment */
  25.     GraphDriver = DETECT;                        /* " */
  26. char *VersionStr = "DrawFunc    IBMPC version 1.0,    Gershon Elber,    "
  27.         __DATE__ ",   " __TIME__ "\n"
  28.         "(C) Copyright 1989 Gershon Elber, Non commercial use only.";
  29. static int
  30.     FuncDrawFlags[4] = { TRUE, FALSE, FALSE, FALSE },    /* What Der. to draw */
  31.     AutoScaleFlag = TRUE,
  32.     NumOfSamples = 100;           /* Number of samples per function drawing */
  33. static double
  34.     LocalXmin = 1.0, LocalXmax = -1.0,              /* For autoscaling */
  35.     LocalYmax = 1.0;
  36. static jmp_buf LongJumpBuffer;               /* Used in error trapping */
  37. static char CurrentWorkingDir[LINE_LEN];/* Save start CWD to recover on exit */
  38.  
  39. extern unsigned int  _stklen = 16384;
  40.  
  41. /* And here is the configuration module data structure: */
  42. static ConfigStruct SetUp[] =
  43. { { "NumOfSamples",    (void *) &NumOfSamples,    SU_INTEGER_TYPE },
  44.   { "GraphDriver",    (void *) &GraphDriver,  SU_INTEGER_TYPE },
  45.   { "Mouse",        (void *) &MouseExists,    SU_BOOLEAN_TYPE } };
  46.  
  47.  /* Number of entries in SetUp structure: */
  48. #define NUM_SET_UP  sizeof(SetUp) / sizeof(ConfigStruct)
  49.  
  50. static void UpdateMainFlags(MenuItem *MainMenu, int *FuncDrawFlags);
  51. void RedrawScreen(ExprNode *PFuncX[], ExprNode *PFuncY[],
  52.     char SFuncX[][LINE_LEN_LONG], char SFuncY[][LINE_LEN_LONG],
  53.     double *Xmin, double *Xmax, double *Ymax,
  54.     double Tmin, double Tmax, int InputKind);
  55. static void DrawFunction(ExprNode *PFuncX, ExprNode *PFuncY,
  56.     char *SFuncX, char *SFuncY, int Color, double Tmin, double Tmax,
  57.     double Xmin, double Xmax, double Ymax, double PosX, double PosY,
  58.     int InputKind, char *FuncName);
  59. static double MapXcoord(double x, double Xmin, double Xmax);
  60. static double MapYcoord(double y, double Ymax);
  61. static void DrawAxes(double Xmin, double Xmax, double Ymax);
  62.  
  63. /*****************************************************************************
  64. * Main routine of function drawing.                                          *
  65. *****************************************************************************/
  66. void main(int argc, char **argv)
  67. {
  68.     static struct MenuItem MainMenu[] = {          /* Main Menu selection */
  69.     YELLOW,    "Function Drawing",
  70.         FUNC_COLOR, "Get Function",
  71.         FUNC_DER1, "            ",  /* Used as F'    ON/OFF */
  72.         FUNC_DER2, "            ",  /* Used as F''   ON/OFF */
  73.         FUNC_DER3, "            ",  /* Used as F'''  ON/OFF */
  74.         CYAN,    "Parameters Set",
  75.         CYAN,    "Redraw",
  76.         CYAN,    "Help",
  77.         MAGENTA, "",
  78.         BLUE,    "Exit"
  79.     };
  80.     int i, select, InputKind = 0, color;
  81.     char SFuncX[4][LINE_LEN_LONG], SFuncY[4][LINE_LEN_LONG], *FuncName,
  82.     *ErrorMsg;
  83.     double Xmin = -1.0, Xmax = 1.0, Ymax = 1.0,
  84.     TFuncMin = -1.0, TFuncMax = 1.0;
  85.     /* Used to save input func. as binary tree */
  86.     ExprNode *PFuncX[4], *PFuncY[4];
  87.     /* Used to save func. as string */
  88.  
  89.     /* If math error occurs - long jump to given place: */
  90.     MathErrorSetUp(ME_KILL, NULL);
  91.  
  92.     getcwd(CurrentWorkingDir, LINE_LEN-1);
  93.  
  94.     MouseExists = MouseDetect();        /* Automatic mouse detection routine */
  95.  
  96.     if (_osmajor <= 2)    /* No argv[0] is given (prgm name) - allways NULL! */
  97.      Config("DrawFunc", SetUp, NUM_SET_UP);/*Read config. file if exists */
  98.     else Config(*argv, SetUp, NUM_SET_UP);    /* Read config. file if exists */
  99.  
  100.     while (argc-- > 1) {
  101.     if (strcmp(*++argv, "-z") == 0) {
  102.         fprintf(stderr, "\n%s\n", VersionStr);
  103.         fprintf(stderr, "\nUsage: drawfunc [-z]\n");
  104.         ConfigPrint(SetUp, NUM_SET_UP);
  105.         MyExit(0);
  106.     }
  107.     }
  108.  
  109.     GGInitGraph();
  110.     GGClearMenuArea();
  111.     GGClearViewArea();
  112.  
  113.     if (MouseExists)   /* Must be initialized AFTER graph mode was selected. */
  114.     if ((ErrorMsg = MouseInit()) != NULL) {
  115.         /* Must be called before any usage! */
  116.         fprintf(stderr, "\n%s\n\n\tPress any key to continue:", ErrorMsg);
  117.         MouseExists = FALSE;
  118.         getch();
  119.     }
  120.  
  121.     for (i=0; i<4; i++) {
  122.     PFuncX[i] = PFuncY[i] = NULL;                   /* Clear data */
  123.     SFuncX[i][0] = SFuncY[i][0] = 0;
  124.     }
  125.  
  126.     while (TRUE) {
  127.     UpdateMainFlags(MainMenu, FuncDrawFlags);
  128.     GGMenuDraw(9, MainMenu, TRUE);                /* Draw MainMenu */
  129.     select = GGMenuPick();
  130.  
  131.     switch (select) {
  132.         case 1:                 /* Get New function to draw */
  133.         GGClearViewArea();             /* New data - clear all */
  134.         DoGetFunc(PFuncX, PFuncY, SFuncX, SFuncY, &InputKind
  135.                  , &Xmin, &Xmax, &Ymax, &TFuncMin, &TFuncMax);
  136.                 if (InputKind == XY_FUNC_T) {
  137.              LocalXmin = Xmin = -1.0;           /* Default as a start */
  138.                      LocalXmax = Xmax =  1.0;
  139.                      LocalYmax = Ymax =  1.0;
  140.                 }
  141.         else LocalYmax = Ymax = 1.0;           /* Default as a start */
  142.         RedrawScreen(PFuncX, PFuncY, SFuncX, SFuncY,
  143.                  &Xmin, &Xmax, &Ymax,
  144.                  TFuncMin, TFuncMax, InputKind);
  145.                 break;
  146.         case 2:              /* Toggle first derivative drawing */
  147.         case 3:             /* Toggle second derivative drawing */
  148.         case 4:              /* Toggle third derivative drawing */
  149.         if (FuncDrawFlags[select-1]) {
  150.             FuncDrawFlags[select-1] = !FuncDrawFlags[select-1];
  151.             RedrawScreen(PFuncX, PFuncY, SFuncX, SFuncY,
  152.                  &Xmin, &Xmax, &Ymax, TFuncMin,
  153.                  TFuncMax, InputKind);
  154.                 }
  155.                 else {
  156.             switch (select) {
  157.             case 2:
  158.                 color = FUNC_DER1;
  159.                 switch (InputKind) {
  160.                 case XY_FUNC_T:
  161.                     FuncName = "?'(t)   =";
  162.                     break;
  163.                 case Y_FUNC_X:
  164.                     FuncName = "Y'(x)   =";
  165.                     break;
  166.                 }
  167.                 break;
  168.             case 3:
  169.                 color = FUNC_DER2;
  170.                 switch (InputKind) {
  171.                 case XY_FUNC_T:
  172.                     FuncName = "?''(t)  =";
  173.                     break;
  174.                 case Y_FUNC_X:
  175.                     FuncName = "Y''(x)  =";
  176.                     break;
  177.                 }
  178.                 break;
  179.             case 4:
  180.                 color = FUNC_DER3;
  181.                 switch (InputKind) {
  182.                 case XY_FUNC_T:
  183.                     FuncName = "?'''(t) =";
  184.                     break;
  185.                 case Y_FUNC_X:
  186.                     FuncName = "Y'''(x) =";
  187.                     break;
  188.                 }
  189.                 break;
  190.             }
  191.             GGViewPortViewArea();
  192.             DrawFunction(PFuncX[select-1], PFuncY[select-1],
  193.                  SFuncX[select-1], SFuncY[select-1],
  194.             color, TFuncMin, TFuncMax, Xmin, Xmax, Ymax,
  195.             FUNC_POS_X, FUNC_POS_Y-FUNC_DIF_Y*(select-1),
  196.             InputKind, FuncName);
  197.             PrintMathError();         /* If was error - put error msg */
  198.             FuncDrawFlags[select-1] = !FuncDrawFlags[select-1];
  199.                 }
  200.                 break;
  201.         case 5:                        /* Set Scale */
  202.         switch(InputKind) {
  203.             case 0:          /* No function yet - put error message */
  204.             GGPutErrorMsg("No Function defined yet");
  205.             break;
  206.             case XY_FUNC_T:
  207.             DoSetScale(&Xmin, &Xmax, &Ymax, &NumOfSamples,
  208.                    &AutoScaleFlag, &TFuncMin, &TFuncMax);
  209.             break;
  210.             case Y_FUNC_X:
  211.             DoSetScale(&Xmin, &Xmax, &Ymax, &NumOfSamples,
  212.                        &AutoScaleFlag, &Xmin, &Xmax);
  213.             break;
  214.             default:
  215.             break;
  216.         }
  217.         LocalXmin = Xmin;        /* Disable local autoscaling */
  218.                 LocalXmax = Xmax;
  219.                 LocalYmax = Ymax;
  220.                 /* break; - redraw the screen after scalings */
  221.         if (!InputKind) break;   /* If no function is defined - quit */
  222.  
  223.         case 6:                           /* Redraw */
  224.         RedrawScreen(PFuncX, PFuncY, SFuncX, SFuncY,
  225.                  &Xmin, &Xmax, &Ymax,
  226.                  TFuncMin, TFuncMax, InputKind);
  227.                 break;
  228.  
  229.         case 7:                             /* Help */
  230.         GGPrintHelpMenu("DrawFunc.hlp", "MAINMENU");
  231.                 break;
  232.  
  233.         case 9:                             /* Exit */
  234.         if (GGConfirm("Exit Program")) {
  235.             MyExit(0);
  236.                 }
  237.                 break;
  238.         }
  239.     }
  240. }
  241.  
  242. /*****************************************************************************
  243. *   Routine to update the EditMenu status according to flags :               *
  244. *****************************************************************************/
  245. static void UpdateMainFlags(MenuItem *MainMenu, int *FuncDrawFlags)
  246. {
  247.     static char *Der1On  = "First Der. ON";
  248.     static char *Der1Off = "First Der. OFF";
  249.     static char *Der2On  = "Second Der. ON";
  250.     static char *Der2Off = "Second Der. OFF";
  251.     static char *Der3On  = "Third Der. ON";
  252.     static char *Der3Off = "Third Der. OFF";
  253.  
  254.     if (FuncDrawFlags[1])   strcpy(MainMenu[2].string, Der1On);
  255.     else                    strcpy(MainMenu[2].string, Der1Off);
  256.  
  257.     if (FuncDrawFlags[2])   strcpy(MainMenu[3].string, Der2On);
  258.     else                    strcpy(MainMenu[3].string, Der2Off);
  259.  
  260.     if (FuncDrawFlags[3])   strcpy(MainMenu[4].string, Der3On);
  261.     else                    strcpy(MainMenu[4].string, Der3Off);
  262. }
  263.  
  264. /*****************************************************************************
  265. *   Routines to clear the all screen and redraw axes and functions:          *
  266. *****************************************************************************/
  267. void RedrawScreen(ExprNode *PFuncX[], ExprNode *PFuncY[],
  268.     char SFuncX[][LINE_LEN_LONG], char SFuncY[][LINE_LEN_LONG],
  269.     double *Xmin, double *Xmax, double *Ymax,
  270.     double Tmin, double Tmax, int InputKind)
  271. {
  272.     int color, i;
  273.     char *FuncName;
  274.  
  275.     if (AutoScaleFlag) {
  276.         if (InputKind == XY_FUNC_T) {
  277.         *Xmin = LocalXmin;
  278.         *Xmax = LocalXmax;
  279.     }
  280.     *Ymax = LocalYmax;   /* might be improved by function drawing itself */
  281.     }
  282.     if (ABS(*Ymax) < EPSILON) *Ymax = EPSILON;
  283.  
  284.     LocalXmin = 1.0;
  285.     LocalXmax = -1.0;
  286.     LocalYmax = 0;
  287.  
  288.     GGClearViewArea();                   /* Clear old graphic data */
  289.     DrawAxes(*Xmin, *Xmax, *Ymax);
  290.  
  291.     for (i=0; i<=3; i++) if (FuncDrawFlags[i]) {       /* Draw the functions */
  292.         switch (i) {
  293.             case 0:
  294.             color = FUNC_COLOR;
  295.         switch (InputKind) {
  296.             case XY_FUNC_T:
  297.             FuncName = "?(t)    =";
  298.             break;
  299.             case Y_FUNC_X:
  300.             FuncName = "Y(x)    =";
  301.             break;
  302.         }
  303.                 break;
  304.             case 1:
  305.             color = FUNC_DER1;
  306.         switch (InputKind) {
  307.             case XY_FUNC_T:
  308.             FuncName = "?'(t)   =";
  309.             break;
  310.             case Y_FUNC_X:
  311.             FuncName = "Y'(x)   =";
  312.             break;
  313.         }
  314.                 break;
  315.             case 2:
  316.             color = FUNC_DER2;
  317.         switch (InputKind) {
  318.             case XY_FUNC_T:
  319.             FuncName = "?''(t)  =";
  320.             break;
  321.             case Y_FUNC_X:
  322.             FuncName = "Y''(x)  =";
  323.             break;
  324.         }
  325.                 break;
  326.             case 3:
  327.             color = FUNC_DER3;
  328.         switch (InputKind) {
  329.             case XY_FUNC_T:
  330.             FuncName = "?'''(t) =";
  331.             break;
  332.             case Y_FUNC_X:
  333.             FuncName = "Y'''(x) =";
  334.             break;
  335.         }
  336.                 break;
  337.         }
  338.     DrawFunction(PFuncX[i], PFuncY[i], SFuncX[i], SFuncY[i], color,
  339.              Tmin, Tmax, *Xmin, *Xmax, *Ymax, FUNC_POS_X,
  340.              FUNC_POS_Y - i * FUNC_DIF_Y, InputKind, FuncName);
  341.     PrintMathError();             /* If was error - put error msg */
  342.     }
  343. }
  344.  
  345. /*****************************************************************************
  346. * Routine to draw the given function pfunc scaled according to max. vals.    *
  347. * Method: Linear interpolation between the sampled points.                   *
  348. *****************************************************************************/
  349. static void DrawFunction(ExprNode *PFuncX, ExprNode *PFuncY,
  350.     char *SFuncX, char *SFuncY, int Color, double Tmin, double Tmax,
  351.     double Xmin, double Xmax, double Ymax, double PosX, double PosY,
  352.     int InputKind, char *FuncName)
  353. {
  354.     double Dt, t, Dx, x, y;
  355.     int i;
  356.  
  357.     if (setjmp(LongJumpBuffer) != 0) return;
  358.     /* If math error occurs - long jump to given place: */
  359.     MathErrorSetUp(ME_LONGJMP, &LongJumpBuffer);
  360.  
  361.     GGMySetColor(Color);
  362.     GGViewPortViewArea();
  363.  
  364.     switch (InputKind) {
  365.     case XY_FUNC_T:
  366.         SetParamValue(Tmin, PARAMETER_T);
  367.         x = EvalTree(PFuncX);
  368.         y = EvalTree(PFuncY);
  369.         t = Tmin;
  370.         Dt = (Tmax - Tmin) / NumOfSamples;
  371.         break;
  372.         case Y_FUNC_X:
  373.         x = Xmin;
  374.         Dx = (Xmax - Xmin) / NumOfSamples;
  375.         SetParamValue(Xmin, PARAMETER_X);
  376.         y = EvalTree(PFuncY);
  377.         break;
  378.     }
  379.     if (InputKind == XY_FUNC_T) {
  380.         if (x > LocalXmax) LocalXmax = x;
  381.         if (x < LocalXmin) LocalXmin = x;
  382.     }
  383.     if (ABS(y) > LocalYmax) LocalYmax = ABS(y);
  384.  
  385.     GGMyMove(MapXcoord(x, Xmin, Xmax), MapYcoord(y, Ymax));
  386.     for (i=1; i<=NumOfSamples; i++) {
  387.         switch (InputKind) {
  388.             case XY_FUNC_T:
  389.         t = t + Dt;
  390.         SetParamValue(t, PARAMETER_T);
  391.         x = EvalTree(PFuncX);
  392.         y = EvalTree(PFuncY);
  393.         break;
  394.             case Y_FUNC_X:
  395.         x = x + Dx;
  396.         SetParamValue(x, PARAMETER_X);
  397.         y = EvalTree(PFuncY);
  398.         break;
  399.         }
  400.         if (InputKind == XY_FUNC_T) {
  401.             if (x > LocalXmax) LocalXmax = x;
  402.             if (x < LocalXmin) LocalXmin = x;
  403.         }
  404.     if (ABS(y) > LocalYmax) LocalYmax = ABS(y);
  405.     GGMyDraw(MapXcoord(x, Xmin, Xmax), MapYcoord(y, Ymax));
  406.     }
  407.  
  408.     switch (InputKind) {
  409.         case XY_FUNC_T:
  410.         FuncName[0] = 'X';
  411.         GGPutMsgXY(FuncName, PosX , PosY);
  412.         GGPutMsgXY(SFuncX, PosX+0.35, PosY);
  413.             FuncName[0] = 'Y';
  414.         GGPutMsgXY(FuncName, PosX, PosY-FUNC_DIF_Y*0.5);
  415.         GGPutMsgXY(SFuncY, PosX+0.35, PosY-FUNC_DIF_Y*0.5);
  416.             break;
  417.         case Y_FUNC_X:
  418.         GGPutMsgXY(FuncName, PosX, PosY);
  419.         GGPutMsgXY(SFuncY, PosX+0.35, PosY);
  420.             break;
  421.     }
  422.  
  423.     MathErrorSetUp(ME_KILL, NULL);
  424. }
  425.  
  426. /*****************************************************************************
  427. * Routine to map the Xmin..Xmax range into normelizes -1..1 window range:    *
  428. *****************************************************************************/
  429. static double MapXcoord(double x, double Xmin, double Xmax)
  430. {
  431.     x = ((x - Xmin) / (Xmax - Xmin) - 0.5) * MAIN_SCALE;
  432.     return BOUND(x, -0.99, 0.99);        /* Force it between -1 and 1 */
  433. }
  434.  
  435. /*****************************************************************************
  436. * Routine to map the -Ymax..Ymax range into normelizes -1..1 window range:   *
  437. *****************************************************************************/
  438. static double MapYcoord(double y, double Ymax)
  439. {
  440.     y = (y / (2*Ymax)) * MAIN_SCALE;
  441.     return BOUND(y, -0.99, 0.99);
  442. }
  443.  
  444. /****************************************************************************
  445. * Routine to Draw the axes according to given X range:                      *
  446. ****************************************************************************/
  447. static void DrawAxes(double Xmin, double Xmax, double Ymax)
  448. {
  449.     double Xaxis;
  450.     char s[LINE_LEN];
  451.  
  452.     GGMySetColor(AXES_COLOR);
  453.     GGMyMove(-1.0, 0.0);                      /* Draw X axis */
  454.     GGMyDraw(1.0, 0.0);
  455.     GGPutMsgXY("X", 0.85, 0.05);
  456.  
  457.     if (((Xaxis=MapXcoord(0.0, Xmin, Xmax)) > -1.0) && (Xaxis < 1.0)) {
  458.         /* Draw Y axis only of in range */
  459.     GGMyMove(Xaxis, -1.0);                      /* Draw Y axis */
  460.     GGMyDraw(Xaxis,  1.0);
  461.     GGPutMsgXY("Y", Xaxis, 0.95);
  462.     }
  463.  
  464.     sprintf(s, "Xmin = %10lf", Xmin);
  465.     GGPutMsgXY(s, -0.75, 0.93);
  466.     sprintf(s, "Xmax = %10lf", Xmax);
  467.     GGPutMsgXY(s, -0.75, 0.86);
  468.     sprintf(s, "Ymax = %10lf", Ymax);
  469.     GGPutMsgXY(s, -0.75, 0.79);
  470. }
  471.  
  472. /****************************************************************************
  473. * Routine to print math    error according    MathErr trapping module or        *
  474. * evaluations in extr2tre module - function evaltree(),    which might be        *
  475. * retrieved via    EvalError() function:                        *
  476. ****************************************************************************/
  477. void PrintMathError(void)
  478. {
  479.     int    EvalErr;
  480.     char *p = NULL;
  481.  
  482.     if ((EvalErr = EvalError()) != 0) switch (EvalErr) {
  483.     case E_ERR_DivByZero:
  484.         p = "Div by zero";
  485.         break;
  486.     default:
  487.         p = "Undef. math error";
  488.         break;
  489.     }
  490.     else p = MathErrorGet();
  491.  
  492.     if (p != NULL) {
  493.     GGPutMsgXY("Math Error:", -0.9, -0.8);
  494.     GGPutMsgXY(p, -0.9, -0.9);
  495.     }
  496. }
  497.  
  498. /*****************************************************************************
  499. * My Routine to exit the program - do some closing staff before calling exit *
  500. *****************************************************************************/
  501. void MyExit(int ExitCode)
  502. {
  503.     GGCloseGraph();                    /* Recover text mode */
  504.     MouseClose();                 /* Recover mouse interrupts */
  505.  
  506.     chdir(CurrentWorkingDir);       /* Recover original directory before exit */
  507.     setdisk(CurrentWorkingDir[0] - 'A');         /* Move to the old disk */
  508.  
  509.     exit(ExitCode);
  510. }
  511.