/* * GRAPH, Version 1.00 - 4 August 1989 * * Copyright 1989, David Gay. All Rights Reserved. * This software is freely redistrubatable. */ #include #include #include #include #include #include "object.h" #include "object/function.h" #include "file.h" #include "graph.h" #include "uio.h" #include "coords.h" #include "list.h" #include "grph.h" #include "user/eval.h" #include "user/gadgets.h" #include "tracker.h" #include #include #include struct x_y { struct function f; char x[EXPRLEN], y[EXPRLEN]; value x_t, y_t, dx, dy; }; struct point_x_y { point p; double t; }; typedef struct point_x_y point_x_y; /*-------------------------------------------------------------------------*/ /* x_y class implementation */ /*-------------------------------------------------------------------------*/ /* Is the function displayable ? */ static int x_y_ok(const struct x_y *this) { return this->f.min != NOVAL && this->f.max != NOVAL && this->f.min < this->f.max && (this->f.steps == INOVAL || this->f.steps >= 3); } /* Free resources */ static void destroy_x_y(struct x_y *this) { free_var_list(&this->f.used); if (this->f.calc) free_list(&this->f.pts, this->f.sizept); this->f.calc = FALSE; if (this->x_t) free_expr(this->x_t); if (this->dx) free_expr(this->dx); if (this->y_t) free_expr(this->y_t); if (this->dy) free_expr(this->dy); this->dx = this->dy = this->x_t = this->y_t = NULL; } /* Init dependant parts of function */ static int create_x_y(struct x_y *this) { this->f.calc = FALSE; this->f.var.name = this->f.vname; this->x_t = compile(this->x); if (eval_error != 0) { message(this->f.o.g, "Compilation error in x(t):", eval_messages[eval_e rror], (char *)NULL); return FALSE; } this->dx = differentiate(this->x_t, this->f.vname); if (eval_error != NOT_DIFFERENTIABLE && eval_error != 0) { message(this->f.o.g, "Differentiation error (x):", eval_messages[eval_e rror], (char *)NULL); return FALSE; } this->y_t = compile(this->y); if (eval_error != 0) { message(this->f.o.g, "Compilation error in y(t):", eval_messages[eval_e rror], (char *)NULL); return FALSE; } this->dy = differentiate(this->y_t, this->f.vname); if (eval_error != NOT_DIFFERENTIABLE && eval_error != 0) { message(this->f.o.g, "Differentiation error(y):", eval_messages[eval_er ror], (char *)NULL); return FALSE; } if (!make_var_list(this->x_t, &this->f.used) || !make_var_list(this->y_t, & this->f.used)) init_var_list(&this->f.used); return TRUE; } /* Allow user to define function */ static int edit_x_y(struct x_y *this, struct Region **ref) { struct Requester *req; struct Memory *m; struct Gadget *gl = NULL, *sd, *nd; char from[NBLEN], to[NBLEN], steps[INTLEN], x[EXPRLEN], y[EXPRLEN], tname[V ARLEN], colour[INTLEN]; int ret = FALSE; *ref = NULL; /* Create requester */ double2str(from, this->f.min); double2str(to, this->f.max); int2str(steps, this->f.steps); int2str(colour, this->f.colour); strcpy(x, this->x); strcpy(y, this->y); strcpy(tname, this->f.vname); if ((m = NewMemory()) && (req = InitReq(50, 20, 255, 165, m)) && SetReqBorder(req, 1, m) && AddIntuiText(&req->ReqText, "Function", 95, 6, m) && AddText(&gl, 0, "x(", FALSE, tname, VARLEN, TRUE, 0, RELVERIFY, 25, 20, 24, 10, TRUE, m) && AddText(&gl, 0, ")=", FALSE, x, EXPRLEN, TRUE, 0, RELVERIFY, 81, 20, 16 0, 10, TRUE, m) && AddText(&gl, 0, "y( )=", FALSE, y, EXPRLEN, TRUE, 0, RELVERIFY, 81, 4 0, 160, 10, TRUE, m) && AddText(&gl, 0, "from ", FALSE, from, NBLEN, TRUE, 0, RELVERIFY, 49, 60 , 80, 10, TRUE, m) && AddText(&gl, 0, "to ", FALSE, to, NBLEN, TRUE, 0, RELVERIFY, 167, 60, 8 0, 10, TRUE, m) && AddText(&gl, 0, "steps ", FALSE, steps, INTLEN, TRUE, 0, RELVERIFY, 57, 80, 32, 10, TRUE, m) && AddText(&gl, 0, "colour ", FALSE, colour, INTLEN, TRUE, 0, RELVERIFY, 1 56, 80, 32, 10, TRUE, m) && (sd = AddOption(&gl, 0, "Show discontinuities", TRUE, this->f.showdisc * SELECTED, 0, 9, 100, 10, 10, m)) && (nd = AddOption(&gl, 0, "Allow flat discontinuities", TRUE, this->f.nic edisc * SELECTED, 0, 9, 120, 10, 10, m)) && AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 40, 140, 65, 15, FALS E, m) && AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 145, 140, 65, 15 , FALSE, m)) { SetReqGadgets(req, gl); if (ret = DoRequest(req, this->f.o.g, std_ghandler)) { *ref = full_refresh(this->f.o.g); /* Extract info */ this->f.min = str2double(from); this->f.max = str2double(to); this->f.steps = str2int(steps); if ((this->f.colour = str2int(colour)) == INOVAL) this->f.colour = 1; this->f.showdisc = (sd->Flags & SELECTED) != 0; this->f.nicedisc = (nd->Flags & SELECTED) != 0; strcpy(this->x, x); strcpy(this->y, y); strcpy(this->f.vname, tname); /* Create function */ destroy_x_y(this); if (this->f.o.ok = x_y_ok(this)) this->f.o.ok = create_x_y(this); } } Free(m); return ret; } /* Calculate points of function */ static int calc_x_y(struct x_y *this, int allow_mes) { double t; int i; struct graph *const g = this->f.o.g; double const tmin = this->f.min; double const tmax = this->f.max; int const steps = this->f.steps == INOVAL ? DEFSTEPS : this->f.steps; double const step = (tmax - tmin) / (steps - 1); static char func[FNAMELEN + 30]; strcpy(func, "Can't calculate points for "); strcat(func, this->f.o.name); strcat(func, ":"); new_list(&this->f.pts); if (!create_quick(&this->f.var)) { if (allow_mes) message(g, func, "Couldn't create variable", (char *)NUL L); else alert(g->io.win, func, "Couldn't create variable"); return FALSE; } /* Calculate steps points, spread evenly from min to max */ for (i = 0, t = tmin; i < steps; i++, t += step) { point_x_y *pt = alloc_node(this->f.sizept); if (!pt) { /* No mem */ free_list(&this->f.pts, this->f.sizept); free_quick(&this->f.var); if (allow_mes) message(g, func, "No memory", (char *)NULL); else alert(g->io.win, func, "No memory"); return FALSE; } add_tail(&this->f.pts, pt); set_quick(&this->f.var, t); pt->t = t; pt->p.x = quick_eval(this->x_t); pt->p.state = (eval_error == 0) ? EXISTS : 0; if (pt->p.state == EXISTS) { pt->p.y = quick_eval(this->y_t); pt->p.state = (eval_error == 0) ? EXISTS : 0; } } free_quick(&this->f.var); return TRUE; } /* Try to improve look of function by adding points. If fails, decides that there is a discontinuity */ /* see f_of_x.c for details */ static int improve_x_y(struct x_y *this) { struct graph *const g = this->f.o.g; point_x_y *pt, *next; int ok = FALSE, iter, abort = FALSE; double flatx = FLAT * (g->a.y.max - g->a.y.min) / g->io.win->Height; double flaty = FLAT * (g->a.x.max - g->a.x.min) / g->io.win->Width; char msg[FNAMELEN + 30]; char pass[20]; struct Requester *req; struct Region *full = NULL; /* Flat has no meaning when graph incorrect */ if (!this->f.o.g->ok) flatx = flaty = 0.0; if (!this->f.calc) { strcpy(msg, this->f.o.name); strcpy(msg, "not calculated!"); message(g, msg, (char *)NULL); return NULL; } if (!this->dx || !this->dy) { strcpy(msg, this->f.o.name); strcat(msg, " wasn't differentiable"); message(g, msg, (char *)NULL); return NULL; } if (!create_quick(&this->f.var)) { message(g, "Couldn't create variable", (char *)NULL); return NULL; } if (!(req = abort_request(g, "Improve: Pass 1"))) message(g, "No Memory !", (char *)NULL); else { full = full_refresh(this->f.o.g); for (iter = 1; iter <= MAXITER && !ok && !abort; iter++) { sprintf(pass, "Improve: Pass %d", iter); set_abort_msg(req, pass); ok = TRUE; for (pt = first(&this->f.pts); succ(next = succ(pt)); pt = next) { if ((pt->p.state & (EXISTS | OK)) == EXISTS) /* Only exists */ { double dx, dy; pt->p.state |= OK; pt->p.state &= ~DISC; set_quick(&this->f.var, pt->t); dx = quick_eval(this->dx); if (eval_error == 0) { dy = quick_eval(this->dy); if (eval_error == 0) { double ecartx = next->p.x - pt->p.x; double errorx = fabs(ecartx - (next->t - pt->t) * d x); double ecarty = next->p.y - pt->p.y; double errory = fabs(ecarty - (next->t - pt->t) * d y); /* Check both axes */ if ((errorx > fabs(ecartx) * MAXERROR && (!this->f. nicedisc || errorx > flatx)) || (errory > fabs(ecarty) * MAXERROR && (!this->f. nicedisc || errory > flaty))) { pt->p.state &= ~OK; ok = FALSE; if (iter == MAXITER) pt->p.state |= DISC; else /* cut interval in 2 */ { point_x_y *newpt = alloc_node(this->f.sizep t); if (!newpt) { nomem(g->io.win); abort = TRUE; break; } newpt->t = (pt->t + next->t) / 2; set_quick(&this->f.var, newpt->t); newpt->p.x = quick_eval(this->x_t); newpt->p.state = (eval_error == 0) ? EXISTS : 0; if (newpt->p.state == EXISTS) { newpt->p.y = quick_eval(this->y_t); newpt->p.state = (eval_error == 0) ? EX ISTS : 0; } insert(&this->f.pts, newpt, pt); } } } } } } } end_abort_request(req); } free_quick(&this->f.var); return full; } /* String representation of function */ static char *f2str_x_y(struct x_y *this, char *buf, int maxlen) { buf[maxlen - 1] = '\0'; strncpy(buf, this->f.o.name, maxlen - 1); strncat(buf, ": x(", maxlen - strlen(buf) - 1); strncat(buf, this->f.vname, maxlen - strlen(buf) - 1); strncat(buf, ")=", maxlen - strlen(buf) - 1); strncat(buf, this->x, maxlen - strlen(buf) - 1); strncat(buf, ", y(", maxlen - strlen(buf) - 1); strncat(buf, this->f.vname, maxlen - strlen(buf) - 1); strncat(buf, ")=", maxlen - strlen(buf) - 1); strncat(buf, this->y, maxlen - strlen(buf) - 1); return buf; } /* Save local data to file */ static int save_x_y(struct x_y *this, FILE *f) { short tag = X_Y_TAG; short end = X_Y_END; return WRITE(f, tag) && WRITE(f, this->x) && WRITE(f, this->y) && WRITE(f, end); } /* free function */ static struct Region *delete_x_y(struct x_y *this) { struct Region *full = full_refresh(this->f.o.g); destroy_x_y(this); FreeMem(this, sizeof(struct x_y)); return full; } /* Create a new function */ struct x_y *new_x_y(struct graph *g, char *name) { struct x_y *this = AllocMem(sizeof(struct x_y), MEMF_CLEAR); if (this) { /* Standard init */ init_function(&this->f, g, name); /* Local methods */ this->f.o.delete = (void *)delete_x_y; this->f.o.edit = (void *)edit_x_y; this->f.o.improve = (void *)improve_x_y; this->f.o.f2str = (void *)f2str_x_y; this->f.calcf = (void *)calc_x_y; this->f.save = (void *)save_x_y; this->f.sizept = sizeof(point_x_y); return this; } message(g, "Couldn't create function:", "No memory", (char *)NULL); return NULL; } /* load from file */ struct x_y *load_x_y(struct graph *g, FILE *f) { struct x_y *this = new_x_y(g, ""); if (this) { short end; /* Read local data */ if (READ(f, this->x) && READ(f, this->y) && READ(f, end) && end == X_Y_END) { load_rest(&this->f, f); /* Read standard data */ if (this->f.o.ok = x_y_ok(this)) this->f.o.ok = create_x_y(this); return this; } delete_x_y(this); } return NULL; }