/* * 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 /* (private) class r_t, inherits from function */ struct r_t { struct function f; char r[EXPRLEN], theta[EXPRLEN]; value r_t, theta_t, dr, dtheta; }; /* (private) class, inherits from point */ struct point_r_t { point p; double r, theta, t; }; typedef struct point_r_t point_r_t; /*-------------------------------------------------------------------------*/ /* r_t class implementation */ /*-------------------------------------------------------------------------*/ /* Is the function displayable ? */ static int r_t_ok(const struct r_t *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_r_t(struct r_t *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->r_t) free_expr(this->r_t); if (this->dr) free_expr(this->dr); if (this->theta_t) free_expr(this->theta_t); if (this->dtheta) free_expr(this->dtheta); this->theta_t = this->r_t = this->dtheta = this->dr = NULL; } /* Init dependant parts of function */ static int create_r_t(struct r_t *this) { this->f.calc = FALSE; this->f.var.name = this->f.vname; this->r_t = compile(this->r); if (eval_error != 0) { message(this->f.o.g, "Compilation error in x(t):", eval_messages[eval_e rror], (char *)NULL); return FALSE; } this->dr = differentiate(this->r_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->theta_t = compile(this->theta); if (eval_error != 0) { message(this->f.o.g, "Compilation error in y(t):", eval_messages[eval_e rror], (char *)NULL); return FALSE; } this->dtheta = differentiate(this->theta_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->r_t, &this->f.used) || !make_var_list(this->theta_ t, &this->f.used)) init_var_list(&this->f.used); return TRUE; } /* Allow user to define function */ static int edit_r_t(struct r_t *this, struct Region **ref) { struct Requester *req; struct Memory *m; struct Gadget *gl = NULL, *sd, *nd; char from[NBLEN], to[NBLEN], steps[INTLEN], r[EXPRLEN], theta[EXPRLEN], tna me[VARLEN], 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(r, this->r); strcpy(theta, this->theta); 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, "r(", FALSE, tname, VARLEN, TRUE, 0, RELVERIFY, 27, 20, 24, 10, TRUE, m) && AddText(&gl, 0, ")=", FALSE, r, EXPRLEN, TRUE, 0, RELVERIFY, 75, 20, 16 0, 10, TRUE, m) && AddText(&gl, 0, "theta()=", FALSE, theta, EXPRLEN, TRUE, 0, RELVERIFY, 75, 40, 160, 10, TRUE, m) && AddText(&gl, 0, "from ", FALSE, from, NBLEN, TRUE, 0, RELVERIFY, 51, 60 , 80, 10, TRUE, m) && AddText(&gl, 0, "to ", FALSE, to, NBLEN, TRUE, 0, RELVERIFY, 169, 60, 8 0, 10, TRUE, m) && AddText(&gl, 0, "steps ", FALSE, steps, INTLEN, TRUE, 0, RELVERIFY, 59, 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, 11, 100, 10, 10, m)) && (nd = AddOption(&gl, 0, "Allow flat discontinuities", TRUE, this->f.nic edisc * SELECTED, 0, 11, 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->r, r); strcpy(this->theta, theta); strcpy(this->f.vname, tname); /* Create function */ destroy_r_t(this); if (this->f.o.ok = r_t_ok(this)) this->f.o.ok = create_r_t(this); } } Free(m); return ret; } /* Calculate points of function */ static int calc_r_t(struct r_t *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_r_t *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->r = quick_eval(this->r_t); pt->p.state = (eval_error == 0) ? EXISTS : 0; if (pt->p.state == EXISTS) { pt->theta = quick_eval(this->theta_t); pt->p.state = (eval_error == 0) ? EXISTS : 0; /* Polar -> Rect conversion */ pt->p.x = fabs(pt->r) * cos(pt->theta); pt->p.y = fabs(pt->r) * sin(pt->theta); } } 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 struct Region *improve_r_t(struct r_t *this) { struct graph *const g = this->f.o.g; point_r_t *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->dr || !this->dtheta) { 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 dr, dtheta, dt; pt->p.state |= OK; pt->p.state &= ~DISC; dt = next->t - pt->t; set_quick(&this->f.var, pt->t); dr = quick_eval(this->dr); if (eval_error == 0) { dtheta = quick_eval(this->dtheta); if (eval_error == 0) { double c = cos(pt->theta); double s = sin(pt->theta); /* A little elementary calculus ... */ double dx = dr * c - pt->r * dtheta * s; double dy = dr * s + pt->r * dtheta * c; double ecartx = next->p.x - pt->p.x; double errorx = fabs(ecartx - dt * dx); double ecarty = next->p.y - pt->p.y; double errory = fabs(ecarty - dt * dy); /* 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_r_t *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->r = quick_eval(this->r_t); newpt->p.state = (eval_error == 0) ? EXISTS : 0; if (newpt->p.state == EXISTS) { newpt->theta = quick_eval(this->theta_t ); newpt->p.state = (eval_error == 0) ? EX ISTS : 0; newpt->p.x = fabs(newpt->r) * cos(newpt ->theta); newpt->p.y = fabs(newpt->r) * sin(newpt ->theta); } insert(&this->f.pts, newpt, pt); } } } } } } } end_abort_request(req); } free_quick(&this->f.var); return full; } /* String representation of function */ static char *f2str_r_t(struct r_t *this, char *buf, int maxlen) { buf[maxlen - 1] = '\0'; strncpy(buf, this->f.o.name, maxlen - 1); strncat(buf, ": r(", maxlen - strlen(buf) - 1); strncat(buf, this->f.vname, maxlen - strlen(buf) - 1); strncat(buf, ")=", maxlen - strlen(buf) - 1); strncat(buf, this->r, maxlen - strlen(buf) - 1); strncat(buf, ", theta(", maxlen - strlen(buf) - 1); strncat(buf, this->f.vname, maxlen - strlen(buf) - 1); strncat(buf, ")=", maxlen - strlen(buf) - 1); strncat(buf, this->theta, maxlen - strlen(buf) - 1); return buf; } /* Save local data to file */ static int save_r_t(struct r_t *this, FILE *f) { short tag = R_T_TAG; short end = R_T_END; return WRITE(f, tag) && WRITE(f, this->r) && WRITE(f, this->theta) && WRITE(f, end); } /* free function */ static struct Region *delete_r_t(struct r_t *this) { struct Region *full = full_refresh(this->f.o.g); destroy_r_t(this); FreeMem(this, sizeof(struct r_t)); return full; } /* Create a new function */ struct r_t *new_r_t(struct graph *g, char *name) { struct r_t *this = AllocMem(sizeof(struct r_t), MEMF_CLEAR); if (this) { /* Standard init */ init_function(&this->f, g, name); /* Local methods */ this->f.o.delete = (void *)delete_r_t; this->f.o.edit = (void *)edit_r_t; this->f.o.improve = (void *)improve_r_t; this->f.o.f2str = (void *)f2str_r_t; this->f.calcf = (void *)calc_r_t; this->f.save = (void *)save_r_t; this->f.sizept = sizeof(point_r_t); return this; } message(g, "Couldn't create function:", "No memory", (char *)NULL); return NULL; } /* load from file */ struct r_t *load_r_t(struct graph *g, FILE *f) { struct r_t *this = new_r_t(g, ""); if (this) { short end; /* Read local data */ if (READ(f, this->r) && READ(f, this->theta) && READ(f, end) && end == R_T_END) { load_rest(&this->f, f); /* Read standard data */ if (this->f.o.ok = r_t_ok(this)) this->f.o.ok = create_r_t(this); return this; } delete_r_t(this); } return NULL; }