home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff280.lzh / Graph / object / x_y.c < prev   
C/C++ Source or Header  |  1989-11-20  |  14KB  |  446 lines

  1. /*
  2.  *                 GRAPH, Version 1.00 - 4 August 1989
  3.  *
  4.  *            Copyright 1989, David Gay. All Rights Reserved.
  5.  *            This software is freely redistrubatable.
  6.  */
  7.  
  8. #include <exec/types.h>
  9. #include <intuition/intuition.h>
  10. #include <graphics/text.h>
  11. #include <math.h>
  12. #include <string.h>
  13.  
  14. #include "object.h"
  15. #include "object/function.h"
  16. #include "file.h"
  17. #include "graph.h"
  18. #include "uio.h"
  19. #include "coords.h"
  20. #include "list.h"
  21. #include "grph.h"
  22. #include "user/eval.h"
  23. #include "user/gadgets.h"
  24. #include "tracker.h"
  25.  
  26. #include <proto/exec.h>
  27. #include <proto/intuition.h>
  28. #include <proto/graphics.h>
  29.  
  30. struct x_y {
  31.     struct function f;
  32.     char x[EXPRLEN], y[EXPRLEN];
  33.     value x_t, y_t, dx, dy;
  34. };
  35.  
  36. struct point_x_y {
  37.     point p;
  38.     double t;
  39. };
  40.  
  41. typedef struct point_x_y point_x_y;
  42.  
  43. /*-------------------------------------------------------------------------*/
  44. /*                        x_y class implementation                         */
  45. /*-------------------------------------------------------------------------*/
  46.  
  47. /* Is the function displayable ? */
  48. static int x_y_ok(const struct x_y *this)
  49. {
  50.     return this->f.min != NOVAL && this->f.max != NOVAL &&
  51.            this->f.min < this->f.max &&
  52.            (this->f.steps == INOVAL || this->f.steps >= 3);
  53. }
  54.  
  55. /* Free resources */
  56. static void destroy_x_y(struct x_y *this)
  57. {
  58.     free_var_list(&this->f.used);
  59.     if (this->f.calc) free_list(&this->f.pts, this->f.sizept);
  60.     this->f.calc = FALSE;
  61.     if (this->x_t) free_expr(this->x_t);
  62.     if (this->dx) free_expr(this->dx);
  63.     if (this->y_t) free_expr(this->y_t);
  64.     if (this->dy) free_expr(this->dy);
  65.     this->dx = this->dy = this->x_t = this->y_t = NULL;
  66. }
  67.  
  68. /* Init dependant parts of function */
  69. static int create_x_y(struct x_y *this)
  70. {
  71.     this->f.calc = FALSE;
  72.     this->f.var.name = this->f.vname;
  73.     this->x_t = compile(this->x);
  74.     if (eval_error != 0)
  75.     {
  76.         message(this->f.o.g, "Compilation error in x(t):", eval_messages[eval_e
  77. rror], (char *)NULL);
  78.         return FALSE;
  79.     }
  80.     this->dx = differentiate(this->x_t, this->f.vname);
  81.     if (eval_error != NOT_DIFFERENTIABLE && eval_error != 0)
  82.     {
  83.         message(this->f.o.g, "Differentiation error (x):", eval_messages[eval_e
  84. rror], (char *)NULL);
  85.         return FALSE;
  86.     }
  87.     this->y_t = compile(this->y);
  88.     if (eval_error != 0)
  89.     {
  90.         message(this->f.o.g, "Compilation error in y(t):", eval_messages[eval_e
  91. rror], (char *)NULL);
  92.         return FALSE;
  93.     }
  94.     this->dy = differentiate(this->y_t, this->f.vname);
  95.     if (eval_error != NOT_DIFFERENTIABLE && eval_error != 0)
  96.     {
  97.         message(this->f.o.g, "Differentiation error(y):", eval_messages[eval_er
  98. ror], (char *)NULL);
  99.         return FALSE;
  100.     }
  101.     if (!make_var_list(this->x_t, &this->f.used) || !make_var_list(this->y_t, &
  102. this->f.used))
  103.         init_var_list(&this->f.used);
  104.     return TRUE;
  105. }
  106.  
  107. /* Allow user to define function */
  108. static int edit_x_y(struct x_y *this, struct Region **ref)
  109. {
  110.     struct Requester *req;
  111.     struct Memory *m;
  112.     struct Gadget *gl = NULL, *sd, *nd;
  113.     char from[NBLEN], to[NBLEN], steps[INTLEN], x[EXPRLEN], y[EXPRLEN], tname[V
  114. ARLEN], colour[INTLEN];
  115.     int ret = FALSE;
  116.  
  117.     *ref = NULL;
  118.  
  119.     /* Create requester */
  120.     double2str(from, this->f.min);
  121.     double2str(to, this->f.max);
  122.     int2str(steps, this->f.steps);
  123.     int2str(colour, this->f.colour);
  124.     strcpy(x, this->x);
  125.     strcpy(y, this->y);
  126.     strcpy(tname, this->f.vname);
  127.  
  128.     if ((m = NewMemory()) &&
  129.         (req = InitReq(50, 20, 255, 165, m)) &&
  130.         SetReqBorder(req, 1, m) &&
  131.         AddIntuiText(&req->ReqText, "Function", 95, 6, m) &&
  132.         AddText(&gl, 0, "x(", FALSE, tname, VARLEN, TRUE, 0, RELVERIFY, 25, 20,
  133.  24, 10, TRUE, m) &&
  134.         AddText(&gl, 0, ")=", FALSE, x, EXPRLEN, TRUE, 0, RELVERIFY, 81, 20, 16
  135. 0, 10, TRUE, m) &&
  136.         AddText(&gl, 0, "y(   )=", FALSE, y, EXPRLEN, TRUE, 0, RELVERIFY, 81, 4
  137. 0, 160, 10, TRUE, m) &&
  138.         AddText(&gl, 0, "from ", FALSE, from, NBLEN, TRUE, 0, RELVERIFY, 49, 60
  139. , 80, 10, TRUE, m) &&
  140.         AddText(&gl, 0, "to ", FALSE, to, NBLEN, TRUE, 0, RELVERIFY, 167, 60, 8
  141. 0, 10, TRUE, m) &&
  142.         AddText(&gl, 0, "steps ", FALSE, steps, INTLEN, TRUE, 0, RELVERIFY, 57,
  143.  80, 32, 10, TRUE, m) &&
  144.         AddText(&gl, 0, "colour ", FALSE, colour, INTLEN, TRUE, 0, RELVERIFY, 1
  145. 56, 80, 32, 10, TRUE, m) &&
  146.         (sd = AddOption(&gl, 0, "Show discontinuities", TRUE, this->f.showdisc
  147. * SELECTED, 0, 9, 100, 10, 10, m)) &&
  148.         (nd = AddOption(&gl, 0, "Allow flat discontinuities", TRUE, this->f.nic
  149. edisc * SELECTED, 0, 9, 120, 10, 10, m)) &&
  150.         AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 40, 140, 65, 15, FALS
  151. E, m) &&
  152.         AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 145, 140, 65, 15
  153. , FALSE, m))
  154.     {
  155.         SetReqGadgets(req, gl);
  156.         if (ret = DoRequest(req, this->f.o.g, std_ghandler))
  157.         {
  158.             *ref = full_refresh(this->f.o.g);
  159.  
  160.             /* Extract info */
  161.             this->f.min = str2double(from);
  162.             this->f.max = str2double(to);
  163.             this->f.steps = str2int(steps);
  164.             if ((this->f.colour = str2int(colour)) == INOVAL) this->f.colour =
  165. 1;
  166.             this->f.showdisc = (sd->Flags & SELECTED) != 0;
  167.             this->f.nicedisc = (nd->Flags & SELECTED) != 0;
  168.             strcpy(this->x, x);
  169.             strcpy(this->y, y);
  170.             strcpy(this->f.vname, tname);
  171.  
  172.             /* Create function */
  173.             destroy_x_y(this);
  174.             if (this->f.o.ok = x_y_ok(this)) this->f.o.ok = create_x_y(this);
  175.         }
  176.     }
  177.     Free(m);
  178.  
  179.     return ret;
  180. }
  181.  
  182. /* Calculate points of function */
  183. static int calc_x_y(struct x_y *this, int allow_mes)
  184. {
  185.     double t;
  186.     int i;
  187.     struct graph *const g = this->f.o.g;
  188.     double const tmin = this->f.min;
  189.     double const tmax = this->f.max;
  190.     int const steps = this->f.steps == INOVAL ? DEFSTEPS : this->f.steps;
  191.     double const step = (tmax - tmin) / (steps - 1);
  192.     static char func[FNAMELEN + 30];
  193.  
  194.     strcpy(func, "Can't calculate points for ");
  195.     strcat(func, this->f.o.name);
  196.     strcat(func, ":");
  197.  
  198.     new_list(&this->f.pts);
  199.  
  200.     if (!create_quick(&this->f.var))
  201.     {
  202.         if (allow_mes) message(g, func, "Couldn't create variable", (char *)NUL
  203. L);
  204.         else alert(g->io.win, func, "Couldn't create variable");
  205.         return FALSE;
  206.     }
  207.  
  208.     /* Calculate steps points, spread evenly from min to max */
  209.     for (i = 0, t = tmin; i < steps; i++, t += step)
  210.     {
  211.         point_x_y *pt = alloc_node(this->f.sizept);
  212.  
  213.         if (!pt)
  214.         { /* No mem */
  215.             free_list(&this->f.pts, this->f.sizept);
  216.             free_quick(&this->f.var);
  217.             if (allow_mes) message(g, func, "No memory", (char *)NULL);
  218.             else alert(g->io.win, func, "No memory");
  219.             return FALSE;
  220.         }
  221.         add_tail(&this->f.pts, pt);
  222.  
  223.         set_quick(&this->f.var, t);
  224.         pt->t = t;
  225.  
  226.         pt->p.x = quick_eval(this->x_t);
  227.         pt->p.state = (eval_error == 0) ? EXISTS : 0;
  228.         if (pt->p.state == EXISTS)
  229.         {
  230.             pt->p.y = quick_eval(this->y_t);
  231.             pt->p.state = (eval_error == 0) ? EXISTS : 0;
  232.         }
  233.     }
  234.     free_quick(&this->f.var);
  235.     return TRUE;
  236. }
  237.  
  238. /* Try to improve look of function by adding points. If fails, decides that
  239.    there is a discontinuity */
  240. /* see f_of_x.c for details */
  241. static int improve_x_y(struct x_y *this)
  242. {
  243.     struct graph *const g = this->f.o.g;
  244.     point_x_y *pt, *next;
  245.     int ok = FALSE, iter, abort = FALSE;
  246.     double flatx = FLAT * (g->a.y.max - g->a.y.min) / g->io.win->Height;
  247.     double flaty = FLAT * (g->a.x.max - g->a.x.min) / g->io.win->Width;
  248.     char msg[FNAMELEN + 30];
  249.     char pass[20];
  250.     struct Requester *req;
  251.     struct Region *full = NULL;
  252.  
  253.     /* Flat has no meaning when graph incorrect */
  254.     if (!this->f.o.g->ok) flatx = flaty = 0.0;
  255.  
  256.     if (!this->f.calc)
  257.     {
  258.         strcpy(msg, this->f.o.name);
  259.         strcpy(msg, "not calculated!");
  260.         message(g, msg, (char *)NULL);
  261.         return NULL;
  262.     }
  263.     if (!this->dx || !this->dy)
  264.     {
  265.         strcpy(msg, this->f.o.name);
  266.         strcat(msg, " wasn't differentiable");
  267.         message(g, msg, (char *)NULL);
  268.         return NULL;
  269.     }
  270.     if (!create_quick(&this->f.var))
  271.     {
  272.         message(g, "Couldn't create variable", (char *)NULL);
  273.         return NULL;
  274.     }
  275.  
  276.     if (!(req = abort_request(g, "Improve: Pass 1")))
  277.         message(g, "No Memory !", (char *)NULL);
  278.     else
  279.     {
  280.         full = full_refresh(this->f.o.g);
  281.  
  282.         for (iter = 1; iter <= MAXITER && !ok && !abort; iter++)
  283.         {
  284.             sprintf(pass, "Improve: Pass %d", iter);
  285.             set_abort_msg(req, pass);
  286.             ok = TRUE;
  287.  
  288.             for (pt = first(&this->f.pts); succ(next = succ(pt)); pt = next)
  289.             {
  290.                 if ((pt->p.state & (EXISTS | OK)) == EXISTS) /* Only exists */
  291.                 {
  292.                     double dx, dy;
  293.  
  294.                     pt->p.state |= OK;
  295.                     pt->p.state &= ~DISC;
  296.  
  297.                     set_quick(&this->f.var, pt->t);
  298.                     dx = quick_eval(this->dx);
  299.                     if (eval_error == 0)
  300.                     {
  301.                         dy = quick_eval(this->dy);
  302.                         if (eval_error == 0)
  303.                         {
  304.                             double ecartx = next->p.x - pt->p.x;
  305.                             double errorx = fabs(ecartx - (next->t - pt->t) * d
  306. x);
  307.                             double ecarty = next->p.y - pt->p.y;
  308.                             double errory = fabs(ecarty - (next->t - pt->t) * d
  309. y);
  310.  
  311.                             /* Check both axes */
  312.                             if ((errorx > fabs(ecartx) * MAXERROR && (!this->f.
  313. nicedisc || errorx > flatx)) ||
  314.                                 (errory > fabs(ecarty) * MAXERROR && (!this->f.
  315. nicedisc || errory > flaty)))
  316.                             {
  317.                                 pt->p.state &= ~OK;
  318.                                 ok = FALSE;
  319.  
  320.                                 if (iter == MAXITER) pt->p.state |= DISC;
  321.                                 else /* cut interval in 2 */
  322.                                 {
  323.                                     point_x_y *newpt = alloc_node(this->f.sizep
  324. t);
  325.  
  326.                                     if (!newpt)
  327.                                     {
  328.                                         nomem(g->io.win);
  329.                                         abort = TRUE;
  330.                                         break;
  331.                                     }
  332.  
  333.                                     newpt->t = (pt->t + next->t) / 2;
  334.                                     set_quick(&this->f.var, newpt->t);
  335.                                     newpt->p.x = quick_eval(this->x_t);
  336.                                     newpt->p.state = (eval_error == 0) ? EXISTS
  337.  : 0;
  338.                                     if (newpt->p.state == EXISTS)
  339.                                     {
  340.                                         newpt->p.y = quick_eval(this->y_t);
  341.                                         newpt->p.state = (eval_error == 0) ? EX
  342. ISTS : 0;
  343.                                     }
  344.                                     insert(&this->f.pts, newpt, pt);
  345.                                 }
  346.                             }
  347.                         }
  348.                     }
  349.                 }
  350.             }
  351.         }
  352.         end_abort_request(req);
  353.     }
  354.     free_quick(&this->f.var);
  355.     return full;
  356. }
  357.  
  358. /* String representation of function */
  359. static char *f2str_x_y(struct x_y *this, char *buf, int maxlen)
  360. {
  361.     buf[maxlen - 1] = '\0';
  362.     strncpy(buf, this->f.o.name, maxlen - 1);
  363.     strncat(buf, ": x(", maxlen - strlen(buf) - 1);
  364.     strncat(buf, this->f.vname, maxlen - strlen(buf) - 1);
  365.     strncat(buf, ")=", maxlen - strlen(buf) - 1);
  366.     strncat(buf, this->x, maxlen - strlen(buf) - 1);
  367.     strncat(buf, ", y(", maxlen - strlen(buf) - 1);
  368.     strncat(buf, this->f.vname, maxlen - strlen(buf) - 1);
  369.     strncat(buf, ")=", maxlen - strlen(buf) - 1);
  370.     strncat(buf, this->y, maxlen - strlen(buf) - 1);
  371.  
  372.     return buf;
  373. }
  374.  
  375. /* Save local data to file */
  376. static int save_x_y(struct x_y *this, FILE *f)
  377. {
  378.     short tag = X_Y_TAG;
  379.     short end = X_Y_END;
  380.  
  381.     return WRITE(f, tag) &&
  382.            WRITE(f, this->x) &&
  383.            WRITE(f, this->y) &&
  384.            WRITE(f, end);
  385. }
  386.  
  387. /* free function */
  388. static struct Region *delete_x_y(struct x_y *this)
  389. {
  390.     struct Region *full = full_refresh(this->f.o.g);
  391.  
  392.     destroy_x_y(this);
  393.     FreeMem(this, sizeof(struct x_y));
  394.  
  395.     return full;
  396. }
  397.  
  398. /* Create a new function */
  399. struct x_y *new_x_y(struct graph *g, char *name)
  400. {
  401.     struct x_y *this = AllocMem(sizeof(struct x_y), MEMF_CLEAR);
  402.  
  403.     if (this)
  404.     {
  405.         /* Standard init */
  406.         init_function(&this->f, g, name);
  407.         /* Local methods */
  408.         this->f.o.delete = (void *)delete_x_y;
  409.         this->f.o.edit = (void *)edit_x_y;
  410.         this->f.o.improve = (void *)improve_x_y;
  411.         this->f.o.f2str = (void *)f2str_x_y;
  412.         this->f.calcf = (void *)calc_x_y;
  413.         this->f.save = (void *)save_x_y;
  414.         this->f.sizept = sizeof(point_x_y);
  415.         return this;
  416.     }
  417.     message(g, "Couldn't create function:", "No memory", (char *)NULL);
  418.     return NULL;
  419. }
  420.  
  421. /* load from file */
  422. struct x_y *load_x_y(struct graph *g, FILE *f)
  423. {
  424.     struct x_y *this = new_x_y(g, "");
  425.  
  426.     if (this)
  427.     {
  428.         short end;
  429.  
  430.         /* Read local data */
  431.         if (READ(f, this->x) &&
  432.             READ(f, this->y) &&
  433.             READ(f, end) &&
  434.             end == X_Y_END)
  435.         {
  436.             load_rest(&this->f, f); /* Read standard data */
  437.             if (this->f.o.ok = x_y_ok(this)) this->f.o.ok = create_x_y(this);
  438.  
  439.             return this;
  440.         }
  441.         delete_x_y(this);
  442.     }
  443.     return NULL;
  444. }
  445.  
  446.