
#include <math.h>
#include <stdio.h>

#define SCALE_DOWN .8
#define MIN_D .045
#define MIN_T .2

#define abs(x) (((x) > 0) ? (x) : (-(x)))
#define sign(x) (((x) > 0) ? 1 : (((x) < 0) ? -1 : 0))

void makescale(FILE *fp, char *name, double (*fcn)(double), double x0,
	       double y0, double height, double val0, double val1, double inc,
	       void (*text)(FILE *, double, double, double, double));
int makedashes(FILE *fp, double (*fcn)(double), double x0, double y0, 
	       double height, double val0, double val1, double inc,
	       void (*text)(FILE *, double, double, double, double));

double xlog10(double x) {
  return 10. * log10(x);
}

double nxlog10(double x) {
  return 10. + 10. * log10(1./x);
}

double x2log10(double x) {
  return 5. * log10(x);
}

double x3log10(double x) {
  return (10. / 3.) * log10(x);
}

double id(double x) {
  return x;
}

double xinv(double x) {
  return 10. / x;
}

void simpletext(FILE *fp, double val, double x, double y, double inc) {
  fprintf(fp, "%f cm %f cm moveto\n", x, y);
  fprintf(fp, "(%.0f) show\n", fmod(val / abs(inc), 10.));
}

int main() {
  FILE *fp = fopen("window.ps", "wt");

  fprintf(fp, "%%!\n");
  fprintf(fp, "/cm {28.34645669291338582677 mul} def\n");
  fprintf(fp, "0 setlinewidth\n");
  fprintf(fp, "/Times-Roman findfont\n");
  fprintf(fp, "5 scalefont\n");
  fprintf(fp, "setfont\n");

  makescale(fp, "log10", xlog10, 1., 15., 1., 1., 10., 1., simpletext);
  makescale(fp, "2log10", x2log10, 1., 14., 1., 10., 100., 10., simpletext);
  makescale(fp, "", x2log10, 1., 14., 1., 1., 10., 1., simpletext);
  makescale(fp, "3log10", x3log10, 1., 13., 1., 100., 1000., 100., simpletext);
  makescale(fp, "", x3log10, 1., 13., 1., 10., 100., 10., simpletext);
  makescale(fp, "", x3log10, 1., 13., 1., 1., 10., 1., simpletext);
  makescale(fp, "-log10", nxlog10, 1., 16., 1., 10., 1., -1., simpletext);
  makescale(fp, "x", id, 1., 17., 1., 0., 10., 1., simpletext);
  /*makescale(fp, "1/x", xinv, 1., 7., 1., 10., 1., -1., simpletext);*/

  fprintf(fp, "showpage\n");
}

/* Makes a scale with vertical lines from left to right */
/* Have function, want to put a marking as often as possible, but
 different sizes based on importance */
/* Ah! recursive calls to makescale! */
void makescale(FILE *fp, char *name, double (*fcn)(double), double x0,
	       double y0, double height, double val0, double val1, double inc,
	       void (*text)(FILE *, double, double, double, double)) {
  fprintf(fp, "gsave\n");
  fprintf(fp, "%f cm %f cm translate\n", fcn(val1) + x0, y0 - MIN_T);
  fprintf(fp, "-90 rotate\n");
  fprintf(fp, "newpath\n");
  fprintf(fp, "0 0 moveto\n");
  fprintf(fp, "(%s) true charpath\n", name);
  fprintf(fp, "stroke\n");
  fprintf(fp, "grestore\n");

  fprintf(fp, "newpath\n");

  printf("Drawing %s...\n", name);

  makedashes(fp, fcn, x0, y0, height, val0, val1, inc, text);

  fprintf(fp, "closepath\nstroke\n");
}

int makedashes(FILE *fp, double (*fcn)(double), double x0, double y0,
	       double height, double val0, double val1, double inc,
	       void (*text)(FILE *, double, double, double, double)) {
  double val, x;
  double textmiss = 0;

  if (abs(fcn(val0 + inc) - fcn(val0)) < MIN_D ||
      abs(fcn(val1) - fcn(val1 - inc)) < MIN_D)
    return 0;

  for (val = val0; sign(inc) * val <= sign(inc) * val1; val += inc) {
    x = fcn(val) + x0;
    fprintf(fp, "%f cm %f cm moveto\n", x, y0);
    fprintf(fp, "%f cm %f cm lineto\n", x, y0 - height);
    textmiss += abs(fcn(val + inc) - fcn(val));
    textmiss /= 1.5;
    if (textmiss >= MIN_T) {
      text(fp, val, x, y0 - height, inc);
      textmiss = 0;
    }
    if (abs(val1 - val) >= MIN_D)
      if (!makedashes(fp, fcn, x0, y0, height * SCALE_DOWN, val, val + inc,
		      inc / 10., text))
	if (!makedashes(fp, fcn, x0, y0, height * SCALE_DOWN, val, val + inc,
			inc / 5., text))
	  makedashes(fp, fcn, x0, y0, height * SCALE_DOWN, val, val + inc,
		     inc / 2., text);
  }

  return 1;
}
