/*
 *
 * dof.c - Generate a Depth of Field table (In LaTeX)
 *
 * Copyright (C) 1990 Lars Bo Nielsen and Steen F. Kroyer.
 *
 * Author:
 *	Lars Bo Nielsen
 *	Aalborg University
 *	Strandvejen 19
 *	DK-9000 Aalborg
 *	DENMARK
 *	Email: lbn@iesd.auc.dk
 *
 * With help from Steen F. Kroyer, who figured out the math
 * behind it all.
 * 
 * Compilation: cc -o dof dof.c
 * Run the program without argument to get a usage message.
 *
 * Created On      : Wed Apr 18 14:09:31 1990
 * Last Modified On: Mon May  7 12:37:15 1990
 From: lbn@iesd.auc.dk (Lars Bo Nielsen)
 Newsgroups: rec.photo
 Subject: Depth of Field in LaTeX (Program incl)
 Date: 7 May 90 11:59:49 GMT
 Organization: Mathematics and Computer Science, University of Aalborg

 Here is the first release of my Depth of Field (dof) program. It
 generates dof tables in LaTeX. 
 There is a lot of different options, so compile it and play a little
 around with it (run it without arguments to get a usage message).

 Please don't hessitate to send me suggestions, idears and bugs. I am
 sure some of you wants additional features put into the program.

 To compile just type: cc -o dof dof.c

  Lars Bo Nielsen @ Aalborg University - Denmark
  lbn@iesd.auc.dk  -**-  {...}!mcvax!dkuug!auc!iesd!lbn
 *  
 */


/*
 *  SOME BACKGROUND: 
 *
 *   Written by Steen F. Kroyer, previously posted to usenet (rec.photo)
 *
 *   I have a couple of lenses with no (or useless) depth-of-field (dof)
 *   scale. For the purpose of developing tables for these lenses, I used
 *   some info found in Ansel Adams' "The Camera" to develop 2 formulas to
 *   calculate near and far dof-limits. Others might find them useful, so
 *   here they are.
 *
 *   1) Near limit: Uc = (F^2*U)/(F^2+c*f*(U-F)) , for U > 2*F
 *
 *   2) Far limit : Uf = (F^2*U)/(F^2-c*f*(U-F)) , for U <= F^2/(c*f)+F
 *
 *   where U = focusing distance.
 *         F = lens focal length.
 *         f = (relative) aperture, i.e f-stop.
 *         c = maximum circle of confusion, typically 1/1000 inch.
 *
 *   F, U and c must be specified in the same units (mm, inches, lightyears
 *   or whatever). f is measured in the usual f-stops, e.g 1.4, 8, 11 etc.
 *
 *   Camera            Object in exact focus
 *    .---.                      v
 *    |   |]      {--------------O-------------------------} << field in focus
 *    `---'
 *        |-------|--------------|-------------------------|----- > infinity
 *        0       Uc             U                         Uf
 *
 *   **********************************************************************
 *   Some details:
 *
 *   A) The maximum circle of confusion perhaps needs some explanation.
 *   Imagine focusing on a single point of light (this will not appeal to
 *   mr. Bush ;-). If the point is exactly in focus, the image on the film
 *   plane should also be a single point. If, however, the light source is
 *   out of focus, it will be rendered as a somewhat larger diffused circle
 *   instead. The largest diameter of this circle one is going to tolerate,
 *   is the maximum circle of confusion. Adams' suggests 1/750th to
 *   1/1000th of an inch as typical values for 35mm lenses. Using 1/1000" I
 *   have been able to (almost exact) replicate the manufacturers dof-scale
 *   on a 24mm wideangle (the only lens with a decent dof-scale I have).
 *
 *   B) The formulas assume that light travels in simple, straight
 *   geometric lines.  Diffraction, lens aberrations and the like are
 *   ignored. They should be plenty accurate for practical work, though.
 *
 *   C) I have quoted the lowest usable limit for U as 2*F, corresponding
 *   to 1:1 magnification (i.e, 1:1 macro). Inspecting 1) one will see that
 *   it will produce a "sensible" value even if U creeps down towards F.
 *   I'm no optical expert, however, and I'm not sure what happens
 *   imagewise when F < U < 2*F. Theoretically I guess we're then talking
 *   microscopy since the film plane image will be larger than the object.
 *
 *   D) Note that, by "solving" for Uf = infinity in 2), the hyperfocal
 *   distance (H) for a lens is found. (Hyperfocal distance = near limit of
 *   dof when far limit is at infinity). H turns out to be the quoted upper
 *   limit for U in 2). This limit makes sense, since Uf will be at
 *   infinity for all U
's further out than H. When focusing at exactly H,
 *   the dof extends from 1/2*H to infinity.
 *   ----
 *   NB: I'm actually a bit at odds with Adams' here (prophanity strikes !
 *   ;-).  Adams' defines H as "near limit of dof when lens is FOCUSED at
 *   infinity". The difference is subtle, and not significant. Example: for
 *   a 24mm lens at f/22 using c = 1/1000", "my" H = 1054.78 mm & Adams' H
 *   = 1030.78 mm. The definition I use makes the whole cahoot dovetail
 *   nicely, but then again, the accuracy of the formulas probably doesn't
 *   support such a detailed scrutiny.
 *
 *   E) For closeup work it might be worth noting that U is measured from
 *   the object to the lens rear nodal plane. And what's that ? Well, one
 *   way of putting it is: for a lens with focal length F, the lens rear
 *   nodal plane is located F (whatever units) in front of the film plane *)
 *   In some cases that means that the rear nodal plane will be located
 *   in front of the entire lens, or behind the lens' rear element.
 *   That's okay and caused by the optical design of the lens.
 *   ----
 *   *) Actually the rear nodal plane of a (complex, i.e consisting of more
 *   than one element) lens is the plane in which one could substitute the
 *   complex lens with a single ideal lens without changing the image on
 *   the film plane.
 *
 ***************************************************************************/
   

#include <stdio.h>

/* Here you define the command used to invoke LaTeX */
#define LATEXCOMMAND "latex"

/* If you would like options to LaTeX' documentstyle command insert it here */
/* #define LATEX_OPTION "a4,12pt" */
#define LATEX_OPTION ""

/* NOTHING BELOW HERE TO CHANGE (unless I made a bug) */

#define ERROR_USAGE    1
#define ERROR_APERTURE 2
#define ERROR_ACCURACY 3

#define VERSION "Version 1.0 (May 1990)"

/* LaTeX commands */

/* REMEMBER, that if you change the LaTeX commands, that \ should be
   written as \\. That is use two instead of one */

/* Latex options gets inserted in here */
char preamble1[] = "\\documentstyle[%s]{report}\n\\begin{document}\n\
\\pagestyle{empty}\n\\let\\M=\\multicolumn\n\\begin{center}\n\
\\begin{tabular}{||r|";

/* Focal length and number of cols to be inserted in here */
char preamble2[] = "|}\n\\hline\n\
\\M{1}{||c|}{\\bf %.0f} & \\M{%d}{c||}{\\bf Apertures}\\\\\\cline{2-%d}\n\
\\M{1}{||c|}{mm}";

char preamble3[] = "\\\\\\hline\\hline\nHyper";

/* Latex postamble */
char postamble[] = "\\end{tabular}\n\\end{center}\n\\end{document}\n";

/* Table of apertures. The ones used, and the real ones (ALLWAYS END IN 0) */
float apertures[] =
{ 1.0 ,  1.2 ,  1.4 ,  1.7 ,  2.0 ,  2.4 ,  2.8 ,  3.5 ,  4.0 ,  4.5 ,  5.6 ,
  6.7 ,  8.0 ,  9.5 , 11.0 , 13.0 , 16.0 , 19.0 , 22.0 , 27.0 , 32.0 , 0};

float apertures_real[] =
{ 1.0 ,  1.19,  1.41,  1.68,  2.00,  2.38,  2.83,  3.36,  4.00,  4.76,  5.66,
  6.73,  8.00,  9.51, 11.31, 13.45, 16.00, 19.03, 22.63, 26.91, 32.00, 0};

/* The table of apertures we print (ALLWAYS END IN 0) */
float apertures_whole[] = {1.4, 2, 2.8, 4, 5.6, 8, 11, 16, 22, 32, 0};

/* Actual number of elements in apertures to print */
int num_of_apertures = 9;

/* Field of confusion. Defaults to 1/1000 inch (Adams) */
float confusion = 25.4/1000/1000; /* meters */

/* Default focal length */
float focal_length;

/* Default starting distance */
float dist = 1;

/* Default step in distance */
float dist_step = 1;

/* Default number of steps */
int number_of_steps = 20;

/* Number of columns (actually one less, col 1 is always there) */
int num_of_cols;

/* Starting aperture */
float start_aperture = 1.4;

/* Offset into apertures array (as start_aperture = 1.4) */
int offset = 0;

/* The accuracy, used in %.#f later */
int accuracy = 1;

/* Do we have to run latex */
int run_latex = 0;

/* The unit we use (either m or ft, default m) */
char *unit = "m";
char *unit_ft = "ft";

/* We use meter as default */
int use_ft = 0;

/* Tarnslation from meter -> ft */
float meter2ft = 3.28084;

/* Where the output goes */
char *output_filename = NULL;

/* File pointer to output file */
FILE *outfile;

/* The name of the program */
char *progname;

/* Forward declaration */
float map_aperture();

/* From getopt */
extern int optind;
extern char *optarg;

extern int errno;
extern char *sys_errlist[];
  

main(argc, argv)
     int argc;
     char **argv;
{
  int c, a, d;			/* Counters */
  float f;			/* Focal distance, and */
  float powf;			/*  f^2 (both to be calculated later) */
  char format[20];		/* To hold "& %.#f" */
  float *app = apertures_whole;	/* The aperture array we use */

  /* Figure out the name of the program, strip of leading path */
  progname = *argv;
  for(c = strlen(*argv); c > 0; c--)
    if ((*argv)[c] == '/')
      {
	progname = *argv + c + 1;
	break;
      }
  
  
  /* The default, may change */
  num_of_cols = num_of_apertures;

  while ((c = getopt(argc, argv, "a:c:d:s:n:o:e:lf")) != EOF)
    {
      switch (c)
	{
	case 'a':
	  if(sscanf(optarg, "%f", &start_aperture) != 1)
	    printerror(ERROR_USAGE);
	  if(!isin(apertures, start_aperture))
	    printerror(ERROR_APERTURE);
	  /* If start aperture < app[0], we just change app[0] to it */
	  if (start_aperture < app[0])
	    {
	      app[0] = start_aperture;
	    }
	  else
	    {
	      for(a = 0; app[a] <= start_aperture && app[a] != 0; a++)
		/* NOTHING */;
	      if(a)
		offset = a - 1;
	      num_of_cols = num_of_apertures - offset;
	      app[offset] = start_aperture;
	    }
	  break;
	case 'f':
	  use_ft = 1;
	  unit = unit_ft;
	  break;
	case 'c':
	  if(sscanf(optarg, "%f", &confusion) != 1)
	    printerror(ERROR_USAGE);
	  break;
	case 'd':
	  if(sscanf(optarg, "%f", &dist_step) != 1)
	    printerror(ERROR_USAGE);
	  break;
	case 's':
	  if(sscanf(optarg, "%f", &dist) != 1)
	    printerror(ERROR_USAGE);
	  break;
	case 'n':
	  if(sscanf(optarg, "%d", &number_of_steps) != 1)
	    printerror(ERROR_USAGE);
	  break;
	case 'e':
	  if(sscanf(optarg, "%d", &accuracy) != 1)
	    printerror(ERROR_USAGE);
	  if(accuracy < 0 || accuracy > 5)
	    printerror(ERROR_ACCURACY);
	  break;
	case 'o':
	  output_filename = (char *) malloc (strlen(optarg) + 1);
	  strcpy(output_filename, optarg);
	  break;
	case 'l':
	  run_latex = 1;
	  break;
	default:
	  printerror(ERROR_USAGE);
	  break;
	}
    }

  /* There is supposed to be a focal length now */
  if(optind == argc)
    printerror(ERROR_USAGE);

  /* Get the focal length */
  c = sscanf(argv[optind], "%f", &focal_length);
  if (c != 1 || focal_length <= 0)
    {
      fprintf(stderr, "%s: Error in specification of focal length: %s\n",
	      progname, argv[optind]);
      exit(-1);
    }
  
  if(output_filename)
    {
      /* A file-name was specified, check if the extension was .tex */

      if(check_extension(output_filename))
	{
	  char *tmp = (char *) malloc(strlen(output_filename) + 5);
	  
	  fprintf(stderr,"%s: File name has to end in '.tex', adding this.\n",
		  progname);
	  strcpy(tmp, output_filename);
	  strcat(tmp, ".tex");
	  free(output_filename);	/* Don't need this any more */
	  output_filename = tmp;
	  fprintf(stderr, "%s: Creating file: %s\n",
		  progname, output_filename);
	  if ((outfile = fopen(output_filename, "w")) == NULL)
	    {
	      fprintf(stderr, "%s: Unable to open %s: %s\n",
		      progname, optarg, sys_errlist[errno]);
	      exit(-1);
	    }
	}
    }
  else
    {
      /* We have to run LaTeX, but we don't have a filename.
	 Construct one from the focal length */
      
      char *tmp = (char *) malloc (50);	/* Should be enough */
	  
      sprintf(tmp,"%.0f.tex", focal_length);
      output_filename = tmp;
      fprintf(stderr, "%s: Creating file: %s\n", progname, output_filename);
      if ((outfile = fopen(output_filename, "w")) == NULL)
	{
	  fprintf(stderr,"%s: Unable to open %s: %s\n",
		  progname, optarg,sys_errlist[errno]);
	  exit(-1);
	}
    }

  /* Make the format to be printed */
  sprintf(format, " & %%.%df", accuracy);
    
  /* If the start aperture is > 2.8 we will add f/32 to the table */
  if(start_aperture > 2.8)
    num_of_cols++;

  /* Now every thing should be in order for us to begin producing
     some output. */

  /* Print out a header indicating what the parameters was */

  fprintf(outfile, "%%\n%% Output from %s - Depth of Field\n%%\n", progname);
  fprintf(outfile, "%%  Focal length        : %.0f mm\n", focal_length);
  fprintf(outfile, "%%  Start aperture      : %.1f\n", start_aperture);
  fprintf(outfile, "%%  Start distance      : %.1f %s\n", dist, unit);
  fprintf(outfile, "%%  Step in distance    : %.1f %s\n", dist_step, unit);
  fprintf(outfile, "%%  Number of steps     : %d\n", number_of_steps);
  fprintf(outfile, "%%  Accuracy            : %d\n", accuracy);
  fprintf(outfile, "%%  Circle of confusion : %.3e %s\n%%\n\n",
	  confusion, unit);

  /* Print preamble1, with the LaTeX-options */
  fprintf(outfile, preamble1, LATEX_OPTION);

  /* Make columns in the \tabular declaration */
  for (a = 0; a < num_of_cols; a++)
    fprintf(outfile, "r|");

  /* Finish the \tabular declaration, and output `Apertures' and
     focal length */ 
  fprintf(outfile, preamble2, focal_length, num_of_cols, num_of_cols+1);

  /* Output the apertures */
  for (a = 0; a < num_of_cols - 1; a++)
    fprintf(outfile, " & \\M{1}{c|}{\\bf %.1f}", app[a + offset]);
  fprintf(outfile, " & \\M{1}{c||}{\\bf %.1f}", app[num_of_cols-1+offset]);

  /* Output `Hyper' */
  fprintf(outfile, preamble3);

  /* Before we go any futher, we have to do some conversions.
     The focal length have to be in the same unit as the distance.
     If we are supposed to use feet instead of meters, the circle of
     confusion have to be changed as well */
  if(use_ft)
    {
      confusion *= meter2ft;	        /* Confusion -> ft */
      f = meter2ft * focal_length/1000;	/* Focal length from mm -> ft */
    }
  else
    {
      f = focal_length/1000;	/* Focal length from mm -> meters */
    }
  powf = f * f;		        /* Focal length ^ 2 */
  
  /* Print out the Hyper focal distances */
  for (a = 0; a < num_of_cols; a++)
    {
      float ap = map_aperture(app[a + offset]);
      float uh = powf / (ap * confusion);
      fprintf(outfile, format, uh);
    }
  fprintf(outfile, "\\\\\\hline\\hline\n");
  
  /* Print out distance and near and far dof for all apertures */ 
  for(d = 0; d<number_of_steps; d++)
    {
      float u_close;		/* Close distance */
      float u_far;		/* Far disstance */
      
      fprintf(outfile, "%.1f", dist);

      for (a = 0; a < num_of_cols; a++)
	{
	  float ap = map_aperture(app[a + offset]);

	  if ( dist > 2 * f)
	    u_close = (powf*dist) / (powf + (confusion * ap * (dist - f)));
	  else
	    u_close = dist;

	  fprintf(outfile, format, u_close);
	}

      fprintf(outfile, "\\\\\n %s", unit);

      for (a = 0; a < num_of_cols; a++)
	{
	  float ap = map_aperture(app[a + offset]);
	  
	  if ( dist < ((powf / (confusion * ap)) + f))
	    u_far = (powf*dist) / (powf - (confusion * ap * (dist - f)));
	  else
	    u_far = -1;

	  if(u_far < 0)
	    fprintf(outfile, " & $\\infty$");
	  else
	    fprintf(outfile, format, u_far);
	}
      fprintf(outfile, "\\\\\\hline\n");
      dist += dist_step;
    }

  /* Finish everything by ending the tabular and document,
     and close the file */     
  fprintf(outfile, postamble);
  fclose(outfile);

  /* Are we supposed to run latex ? */
  if(run_latex)
    {
      char *command = (char *) malloc(strlen(LATEXCOMMAND)
				      + strlen(output_filename) + 2);

      strcpy(command, LATEXCOMMAND);
      strcat(command, " ");
      strcat(command, output_filename);
      fprintf(stderr, "%s: Running: %s\n", progname, command);
      system(command);
    }
}


printerror(type)
     int type;
{
  int i;

  switch(type)
    {
    case ERROR_USAGE:
      fprintf(stderr, "Depth of Field. %s\n", VERSION);
      fprintf(stderr, "Usage:\n %s [-s #] [-d #] [-n #] [-a #]", progname);
      fprintf(stderr, " [-e #] [-c #] [-o file] [-f] [-l] focal_length\n");
      fprintf(stderr, " Where:\n");
      fprintf(stderr,
	      "\ts: Starting distance    (default %.1f m/ft)\n", dist);
      fprintf(stderr,
	      "\td: Step in distance     (default %.1f m/ft)\n", dist_step);
      fprintf(stderr,
	      "\tn: Number of steps      (default 20)\n", number_of_steps);
      fprintf(stderr,
	      "\ta: Start aperture       (default %.1f)\n", start_aperture);
      fprintf(stderr,"\te: Accuracy             (default %d)\n", accuracy);
      fprintf(stderr,"\tc: Circle of confusion  (default ");
      fprintf(stderr,"%.3e m / %.3e ft)\n", confusion, meter2ft * confusion);
      fprintf(stderr, "\to: Output on file\n");
      fprintf(stderr, "\tf: Use feet instead of meters\n");
      fprintf(stderr, "\tl: Run latex\n");
      fprintf(stderr," Focal length to be specified in mm.\n");
      fprintf(stderr,
	      " Parameters (except -n and -e) accepts floating point\n");
      fprintf(stderr," Creates 'focal_length'.tex if no file is specified.\n");
      break;
    case ERROR_APERTURE:
      fprintf(stderr, "%s: Aperture option (-a) must be one of:\n\t",
	      progname);
      for(i = 0; apertures[i] != 0; i++)
	{
	  if( i == 11)
	    fprintf(stderr, "\n\t");
	  fprintf(stderr, " %4.1f", apertures[i]);
	}
      fprintf(stderr, "\n");
      break;
    case ERROR_ACCURACY:
      fprintf(stderr, "%s: Accuracy must be in the range 0-5\n", progname);
      break;
    default:
      fprintf(stderr, "%s: Unknown type of error showed up\n", progname);
      break;
    }
  /* Exit the program */
  exit(-1);
}

int isin(arr, num)
     float arr[];
     float num;
{
  int i;

  for(i = 0; arr[i] != 0; i++)
    if(num == arr[i])
      return(i + 1);

  return(0);
}

float map_aperture(app)
     float app;
{
  int i;

  i = isin(apertures, app);

  if(i == 0)
    {
      fprintf(stderr, "Congratulations, you've found a bug\n");
      fprintf(stderr, "You better tell lbn@iesd.auc.dk about it.\n");
      exit(-1);
    }
  return(apertures_real[i-1]);
}
  
check_extension(name)
     char *name;
{
  int i = strlen(name);

  if(i < 4)
    return 1;
  else
    return(strcmp(".tex", name + i - 4));
}


/*
 * Local variables:
 * compile-command: "gcc -g -o dof dof.c"
 * end:
 */

