📄 gview.c
字号:
void recenter_disp(Point c){ transform tr = cur_trans(); fpoint cc, off; do_untransform(&cc, &tr, &c); off.x = cc.x - .5*(univ.disp.min.x + univ.disp.max.x); off.y = cc.y - .5*(univ.disp.min.y + univ.disp.max.y); univ.disp.min.x += off.x; univ.disp.min.y += off.y; univ.disp.max.x += off.x; univ.disp.max.y += off.y;}/* Find the upper-left and lower-right corners of the bounding box of the parallelogram formed by untransforming the rectangle rminx, rminy, ... (given in screen coordinates), and return the height of the parallelogram (negated if it slopes downward).*/double untransform_corners(double rminx, double rminy, double rmaxx, double rmaxy, fpoint *ul, fpoint *lr){ fpoint r_ur, r_ul, r_ll, r_lr; /* corners of the given recangle */ fpoint ur, ll; /* untransformed versions of r_ur, r_ll */ transform tr = cur_trans(); double ht; r_ur.x=rmaxx; r_ur.y=rminy; r_ul.x=rminx; r_ul.y=rminy; r_ll.x=rminx; r_ll.y=rmaxy; r_lr.x=rmaxx; r_lr.y=rmaxy; do_untransform(ul, &tr, &r_ul); do_untransform(lr, &tr, &r_lr); do_untransform(&ur, &tr, &r_ur); do_untransform(&ll, &tr, &r_ll); ht = ur.y - lr->y; if (ll.x < ul->x) ul->x = ll.x; if (ur.y > ul->y) ul->y = ur.y; else ht = -ht; if (ur.x > lr->x) lr->x = ur.x; if (ll.y < lr->y) lr->y = ll.y; return ht;}void disp_dozoom(double rminx, double rminy, double rmaxx, double rmaxy){ fpoint ul, lr; double sh = untransform_corners(rminx, rminy, rmaxx, rmaxy, &ul, &lr); if (ul.x==lr.x || ul.y==lr.y) return; univ.slant_ht = sh; univ.disp.min.x = ul.x; univ.disp.max.y = ul.y; univ.disp.max.x = lr.x; univ.disp.min.y = lr.y; nontrivial_interval(&univ.disp.min.x, &univ.disp.max.x); nontrivial_interval(&univ.disp.min.y, &univ.disp.max.y);}void disp_zoomin(Rectangle r){ disp_dozoom(r.min.x, r.min.y, r.max.x, r.max.y);}void disp_zoomout(Rectangle r){ double qminx, qminy, qmaxx, qmaxy; double scx, scy; Rectangle s = screen->r; if (r.min.x==r.max.x || r.min.y==r.max.y) return; s.min.x += lft_border; s.min.y += top_border; s.max.x -= rt_border; s.max.y -= bot_border; scx = (s.max.x - s.min.x)/(r.max.x - r.min.x); scy = (s.max.y - s.min.y)/(r.max.y - r.min.y); qminx = s.min.x + scx*(s.min.x - r.min.x); qmaxx = s.max.x + scx*(s.max.x - r.max.x); qminy = s.min.y + scy*(s.min.y - r.min.y); qmaxy = s.max.y + scy*(s.max.y - r.max.y); disp_dozoom(qminx, qminy, qmaxx, qmaxy);}void expand2(double* a, double* b, double f){ double mid = .5*(*a + *b); *a = mid + f*(*a - mid); *b = mid + f*(*b - mid);}void disp_squareup(void){ double dx = univ.disp.max.x - univ.disp.min.x; double dy = univ.disp.max.y - univ.disp.min.y; dx /= screen->r.max.x - lft_border - screen->r.min.x - rt_border; dy /= screen->r.max.y - bot_border - screen->r.min.y - top_border; if (dx > dy) expand2(&univ.disp.min.y, &univ.disp.max.y, dx/dy); else expand2(&univ.disp.min.x, &univ.disp.max.x, dy/dx); univ.slant_ht = univ.disp.max.y - univ.disp.min.y;}/* Slant so that p and q appear at the same height on the screen and the screen contains the smallest possible superset of what its previous contents.*/void slant_disp(fpoint p, fpoint q){ double yll, ylr, yul, yur; /* corner y coords of displayed parallelogram */ double sh, dy; if (p.x == q.x) return; sh = univ.slant_ht; if (sh > 0) { yll=yul=univ.disp.min.y; yul+=sh; ylr=yur=univ.disp.max.y; ylr-=sh; } else { yll=yul=univ.disp.max.y; yll+=sh; ylr=yur=univ.disp.min.y; yur-=sh; } dy = (univ.disp.max.x-univ.disp.min.x)*(q.y - p.y)/(q.x - p.x); dy -= ylr - yll; if (dy > 0) {yll-=dy; yur+=dy;} else {yul-=dy; ylr+=dy;} if (ylr > yll) { univ.disp.min.y = yll; univ.disp.max.y = yur; univ.slant_ht = yur - ylr; } else { univ.disp.max.y = yul; univ.disp.min.y = ylr; univ.slant_ht = ylr - yur; }}/******************************** Ascii input ********************************/void set_fbb(fpolygon* fp){ fpoint lo=fp->p[0], hi=fp->p[0]; const fpoint *q, *qtop; for (qtop=(q=fp->p)+fp->n; ++q<=qtop;) { if (q->x < lo.x) lo.x=q->x; if (q->y < lo.y) lo.y=q->y; if (q->x > hi.x) hi.x=q->x; if (q->y > hi.y) hi.y=q->y; } fp->bb.min = lo; fp->bb.max = hi;}char* mystrdup(char* s){ char *r, *t = strrchr(s,'"'); if (t==0) { t = s + strlen(s); while (t>s && (t[-1]=='\n' || t[-1]=='\r')) t--; } r = malloc(1+(t-s)); memcpy(r, s, t-s); r[t-s] = 0; return r;}int is_valid_label(char* lab){ char* t; if (lab[0]=='"') return (t=strrchr(lab,'"'))!=0 && t!=lab && strspn(t+1," \t\r\n")==strlen(t+1); return strcspn(lab," \t")==strlen(lab);}/* Read a polyline and update the number of lines read. A zero result indicates bad syntax if *lineno increases; otherwise it indicates end of file.*/fpolygon* rd_fpoly(FILE* fin, int *lineno){ char buf[256], junk[2]; fpoint q; fpolygon* fp; int allocn; if (!fgets(buf,256,fin)) return 0; (*lineno)++; if (sscanf(buf,"%lg%lg%1s",&q.x,&q.y,junk) != 2) return 0; fp = malloc(sizeof(fpolygon)); allocn = 16; fp->p = malloc(allocn*sizeof(fpoint)); fp->p[0] = q; fp->n = 0; fp->nam = ""; fp->thick = 0; fp->clr = clr_im(DBlack); while (fgets(buf,256,fin)) { (*lineno)++; if (sscanf(buf,"%lg%lg%1s",&q.x,&q.y,junk) != 2) { if (!is_valid_label(buf)) {free(fp->p); free(fp); return 0;} fp->nam = (buf[0]=='"') ? buf+1 : buf; break; } if (++(fp->n) == allocn) fp->p = realloc(fp->p, (allocn<<=1)*sizeof(fpoint)); fp->p[fp->n] = q; } fp->nam = mystrdup(fp->nam); set_fbb(fp); fp->link = 0; return fp;}/* Read input into *fps and return 0 or a line number where there's a syntax error */int rd_fpolys(FILE* fin, fpolygons* fps){ fpolygon *fp, *fp0=fps->p; int lineno=0, ok_upto=0; while ((fp=rd_fpoly(fin,&lineno)) != 0) { ok_upto = lineno; fp->link = fps->p; fps->p = fp; grow_bb(&fps->bb, &fp->bb); } set_default_clrs(fps, fp0); return (ok_upto==lineno) ? 0 : lineno;}/* Read input from file fnam and return an error line no., -1 for "can't open" or 0 for success.*/int doinput(char* fnam){ FILE* fin = strcmp(fnam,"-")==0 ? stdin : fopen(fnam, "r"); int errline_or0; if (fin==0) return -1; errline_or0 = rd_fpolys(fin, &univ); fclose(fin); return errline_or0;}/******************************** Ascii output ********************************/fpolygon* fp_reverse(fpolygon* fp){ fpolygon* r = 0; while (fp!=0) { fpolygon* q = fp->link; fp->link = r; r = fp; fp = q; } return r;}void wr_fpoly(FILE* fout, const fpolygon* fp){ char buf[256]; int i; for (i=0; i<=fp->n; i++) fprintf(fout,"%.12g\t%.12g\n", fp->p[i].x, fp->p[i].y); fprintf(fout,"\"%s\"\n", nam_with_thclr(fp->nam, fp->thick, fp->clr, buf, 256));}void wr_fpolys(FILE* fout, fpolygons* fps){ fpolygon* fp; fps->p = fp_reverse(fps->p); for (fp=fps->p; fp!=0; fp=fp->link) wr_fpoly(fout, fp); fps->p = fp_reverse(fps->p);}int dooutput(char* fnam){ FILE* fout = fopen(fnam, "w"); if (fout==0) return 0; wr_fpolys(fout, &univ); fclose(fout); return 1;}/************************ Clipping to screen rectangle ************************//* Find the t values, 0<=t<=1 for which x0+t*(x1-x0) is between xlo and xhi, or return 0 to indicate no such t values exist. If returning 1, set *t0 and *t1 to delimit the t interval.*/int do_xory(double x0, double x1, double xlo, double xhi, double* t0, double* t1){ *t1 = 1.0; if (x0<xlo) { if (x1<xlo) return 0; *t0 = (xlo-x0)/(x1-x0); if (x1>xhi) *t1 = (xhi-x0)/(x1-x0); } else if (x0>xhi) { if (x1>xhi) return 0; *t0 = (xhi-x0)/(x1-x0); if (x1<xlo) *t1 = (xlo-x0)/(x1-x0); } else { *t0 = 0.0; if (x1>xhi) *t1 = (xhi-x0)/(x1-x0); else if (x1<xlo) *t1 = (xlo-x0)/(x1-x0); else *t1 = 1.0; } return 1;}/* After mapping y to y-slope*x, what initial fraction of the *p to *q edge is outside of *r? Note that the edge could start outside *r, pass through *r, and wind up outside again.*/double frac_outside(const fpoint* p, const fpoint* q, const frectangle* r, double slope){ double t0, t1, tt0, tt1; double px=p->x, qx=q->x; if (!do_xory(px, qx, r->min.x, r->max.x, &t0, &t1)) return 1; if (!do_xory(p->y-slope*px, q->y-slope*qx, r->min.y, r->max.y, &tt0, &tt1)) return 1; if (tt0 > t0) t0 = tt0; if (t1<=t0 || tt1<=t0) return 1; return t0;}/* Think of p0..pn as piecewise-linear function F(t) for t=0..pn-p0, and find the maximum tt such that F(0..tt) is all inside of r, assuming p0 is inside. Coordinates are transformed by y=y-x*slope before testing against r.*/double in_length(const fpoint* p0, const fpoint* pn, frectangle r, double slope){ const fpoint* p = p0; double px, py; do if (++p > pn) return pn - p0; while (r.min.x<=(px=p->x) && px<=r.max.x && r.min.y<=(py=p->y-slope*px) && py<=r.max.y); return (p - p0) - frac_outside(p, p-1, &r, slope);}/* Think of p0..pn as piecewise-linear function F(t) for t=0..pn-p0, and find the maximum tt such that F(0..tt) is all outside of *r. Coordinates are transformed by y=y-x*slope before testing against r.*/double out_length(const fpoint* p0, const fpoint* pn, frectangle r, double slope){ const fpoint* p = p0; double fr; do { if (p->x < r.min.x) do if (++p>pn) return pn-p0; while (p->x <= r.min.x); else if (p->x > r.max.x) do if (++p>pn) return pn-p0; while (p->x >= r.max.x); else if (p->y-slope*p->x < r.min.y) do if (++p>pn) return pn-p0; while (p->y-slope*p->x <= r.min.y); else if (p->y-slope*p->x > r.max.y) do if (++p>pn) return pn-p0; while (p->y-slope*p->x >= r.max.y); else return p - p0; } while ((fr=frac_outside(p-1,p,&r,slope)) == 1); return (p - p0) + fr-1;}/*********************** Drawing frame and axis labels ***********************/#define Nthous 7#define Len_thous 30 /* bound on strlen(thous_nam[i]) */char* thous_nam[Nthous] = { "one", "thousand", "million", "billion", "trillion", "quadrillion", "quintillion",};typedef struct lab_interval { double sep; /* separation between tick marks */ double unit; /* power of 1000 divisor */ int logunit; /* log base 1000 of of this divisor */ double off; /* offset to subtract before dividing */} lab_interval;char* abbrev_num(double x, const lab_interval* iv){ static char buf[16]; double dx = x - iv->off; dx = iv->sep * floor(dx/iv->sep + .5); sprintf(buf,"%g", dx/iv->unit); return buf;}double lead_digits(double n, double r) /* n truncated to power of 10 above r */{ double rr = pow(10, ceil(log10(r))); double nn = (n<rr) ? 0.0 : rr*floor(n/rr); if (n+r-nn >= digs10pow) { rr /= 10; nn = (n<rr) ? 0.0 : rr*floor(n/rr); } return nn;}lab_interval next_larger(double s0, double xlo, double xhi){ double nlo, nhi; lab_interval r; r.logunit = (int) floor(log10(s0) + LOG2); r.unit = pow(10, r.logunit); nlo = xlo/r.unit; nhi = xhi/r.unit; if (nhi >= digs10pow) r.off = r.unit*lead_digits(nlo, nhi-nlo); else if (nlo <= -digs10pow) r.off = -r.unit*lead_digits(-nhi, nhi-nlo); else r.off = 0; r.sep = (s0<=r.unit) ? r.unit : (s0<2*r.unit ? 2*r.unit : 5*r.unit); switch (r.logunit%3) { case 1: r.unit*=.1; r.logunit--; break; case -1: case 2: r.unit*=10; r.logunit++; break; case -2: r.unit*=100; r.logunit+=2; } r.logunit /= 3; return r;}double min_hsep(const transform* tr){ double s = (2+labdigs)*sdigit.x; double ss = (univ.disp.min.x<0) ? s+sdigit.x : s; return dxuntransform(tr, ss);}lab_interval mark_x_axis(const transform* tr){ fpoint p = univ.disp.min; Point q, qtop, qbot, tmp; double x0=univ.disp.min.x, x1=univ.disp.max.x; double seps0, nseps, seps; lab_interval iv = next_larger(min_hsep(tr), x0, x1); set_unslanted_y(&univ, &p.y, 0); q.y = ytransform(tr, p.y) + .5; qtop.y = q.y - tick_len; qbot.y = q.y + framewd + framesep; seps0 = ceil(x0/iv.sep); for (seps=0, nseps=floor(x1/iv.sep)-seps0; seps<=nseps; seps+=1) { char* num = abbrev_num((p.x=iv.sep*(seps0+seps)), &iv); Font* f = display->defaultfont; q.x = qtop.x = qbot.x = xtransform(tr, p.x); line(screen, qtop, q, Enddisc, Enddisc, 0, axis_color, q); tmp = stringsize(f, num); qbot.x -= tmp.x/2; string(screen, qbot, display->black, qbot, f, num); } return iv;}lab_interval mark_y_axis(const transform* tr){ Font* f = display->defaultfont; fpoint p = univ.disp.min; Point q, qrt, qlft; double y0, y1, seps0, nseps, seps; lab_interval iv; set_unslanted_y(&univ, &y0, &y1); iv = next_larger(dyuntransform(tr,-f->height), y0, y1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -