
/*
   File:            agbox.c
   Author:          Andrew Moore
   Created:         9th Feb 1990
   Modified:        4 Feb 96

   Description:     Andrews Graphics
*/

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "agbox.h"

agbox *mk_agbox(double xlo,double ylo,double xhi,double yhi)
{
  agbox *agb = AM_MALLOC(agbox);
  agb -> xlo = xlo;
  agb -> ylo = ylo;
  agb -> xhi = xhi;
  agb -> yhi = yhi;
  return agb;
}

void free_agbox(agbox *agb)
{
  AM_FREE(agb,agbox);
}

agbox *mk_copy_agbox(agbox *agb)
{
  agbox *newagb = AM_MALLOC(agbox);
  newagb -> xlo = agb -> xlo;
  newagb -> ylo = agb -> ylo;
  newagb -> xhi = agb -> xhi;
  newagb -> yhi = agb -> yhi;
  return newagb;
}

void fprintf_agbox(FILE *s,char *m1,agbox *x,char *m2)
{
  fprintf(s,"%s -> xlo = %g%s\n",m1,x->xlo,m2);
  fprintf(s,"%s -> ylo = %g%s\n",m1,x->ylo,m2);
  fprintf(s,"%s -> xhi = %g%s\n",m1,x->xhi,m2);
  fprintf(s,"%s -> yhi = %g%s\n",m1,x->yhi,m2);
}

double break_agval(double lo,double hi,double frac)
{
  return lo + frac * (hi - lo);
}

double vertical_break_agval(agbox *agb,double frac)
{
  return break_agval(agb->ylo,agb->yhi,frac);
}

double horizontal_break_agval(agbox *agb,double frac)
{
  return break_agval(agb->xlo,agb->xhi,frac);
}

void agbox_piebar(agbox *agb,dyv *widths
                  ,string_array *left,string_array *right
                  ,ivec *colors){
  int num_segs = dyv_size(widths);
  int num_colors = ivec_size(colors);
  agbox *i_agb, *i_agb_right, *i_agb_left;
  double x = 0, dx, xx;
  double width = dyv_sum(widths);
  char *lstr, *rstr;
  int i,count=0;
  bool alternate_colors = num_colors<num_segs;

  for(i=0;i<num_segs;i++){
    if(dyv_ref(widths,i)>0){
      dx = dyv_ref(widths,i);
      lstr = string_array_ref(left,i);
      rstr = string_array_ref(right,i);
      i_agb = mk_agbox_corresponding_to_time(agb,0,width,x,x+dx);

      agbox_color_fill(i_agb,ivec_ref(colors,(count%num_colors)));
      ag_set_pen_color(AG_BLACK);
      x += dx;

      if(((strlen(rstr)+4)*AG_CHAR_WIDTH) < agbox_width(i_agb,XCOMP))
      {
        xx = i_agb->xhi-(strlen(rstr)+1)*AG_CHAR_WIDTH;
        i_agb_right = mk_vertical_stripe_absolute(i_agb,xx,i_agb->xhi);
        i_agb_left = mk_vertical_stripe_absolute(i_agb,i_agb->xlo,xx);
        agbox_render_string(i_agb_right,rstr);
      } else {
        i_agb_right = NULL;
        i_agb_left = mk_copy_agbox(i_agb);
      }
      agbox_render_string_if_enough_room(i_agb_left,lstr);
      agbox_border(i_agb);

      if(i_agb_right) free_agbox(i_agb_right);
      free_agbox(i_agb_left);
      free_agbox(i_agb);
      count++;
    }
    else if(!alternate_colors) count++;
  }
}

double ag_value_corresponding_to_time(agbox *parent,
				      double parent_start_time,
				      double parent_end_time,
				      double local_time)
{
  double z = (local_time - parent_start_time) / 
             (parent_end_time - parent_start_time);
  return vertical_break_agval(parent,z);
}

/*
  300secs          400secs       500secs                      700secs
   +------------------------------------------------------------+
   |                |              |                            |
   |                |              |                            |
   +------------------------------------------------------------+

   Suppose parent was the large box above. Suppose you were told that parent
   covers the time period 300secs--700secs. And suppose you wanted to 
   draw in a subregion corresponding to 400-500 secs. What should your agbox
   be? The answer is returned by this function.
*/
agbox *mk_agbox_corresponding_to_time(agbox *parent,
				      double parent_start_time,
				      double parent_end_time,
				      double local_start_time,
				      double local_end_time)
{
  double d = parent_end_time - parent_start_time;
  double fracstart = (local_start_time - parent_start_time) / d;
  double fracend = (local_end_time - parent_start_time) / d;
  return mk_vertical_stripe_relative(parent,fracstart,fracend);
}

double get_time_corresponding_to_agbox(agbox *agb,double start,double end,double x)
{
  double agb_x = (x - agb->xlo) / agbox_width(agb,XCOMP);
  return start + agb_x * (end - start);
}

/* TODO:  This function should replace the function above!   -- Dan */
double transform_agbox_coord(agbox *agb,double x,double a0,double a1,int comp)
{
  return (x-agbox_ref(agb,comp,LO))*(a1-a0)/agbox_width(agb,comp);
}

/* Prints horizontally at top left of box */
void agbox_render_string_horizontal(agbox *agb,char *string)
{
  ag_print(agb->xlo+0.4*AG_CHAR_WIDTH,
	   agb->yhi-AG_CHAR_HEIGHT-1.0,string);
}

/* Prints vertically at top left of box */
void agbox_render_string_vertical(agbox *agb,char *string)
{
  ag_print_vertical(agb->xlo+0.5*AG_CHAR_WIDTH,
		    agb->yhi-AG_CHAR_HEIGHT-1.0,string);
}

/* Prints horizontally at top left of box, unless it can't fit the string 
   in horizontally, and it can fit more letters in vertically, in which 
   case prints vertically.  It does clip letters.*/
void agbox_render_string(agbox *agb,char *string)
{
  int numchars = (int)strlen(string);
  int numchars_h = int_min(numchars,(int)(agbox_width(agb,YCOMP)/(AG_CHAR_HEIGHT+1.0)));
  int numchars_w = int_min(numchars,(int)(agbox_width(agb,XCOMP)/AG_CHAR_WIDTH));
  char *buff = AM_MALLOC_ARRAY(char,numchars+1);

  sprintf(buff,"%s",string);

  if ( numchars_w < numchars && numchars_h > numchars_w ) {
    buff[numchars_h] = '\0';
    agbox_render_string_vertical(agb,buff);
  } else {
    buff[numchars_w] = '\0';
    agbox_render_string_horizontal(agb,buff);
  }

  AM_FREE_ARRAY(buff,char,numchars+1);
}

bool enough_room_for_string_in_agbox(agbox *agb,char *string)
{
  int len = (int)strlen(string);
  double box_width = agbox_width(agb,XCOMP);
  double box_height = agbox_width(agb,YCOMP);
  bool ok = box_width >= AG_CHAR_WIDTH && 
            box_height >= AG_CHAR_HEIGHT;
  if ( ok )
  {
    double real_width = (len+.4) * (double)AG_CHAR_WIDTH;
    double real_height =  (len+.5) * (double)AG_CHAR_HEIGHT;
    ok = real_width < box_width || real_height < box_height;
  }

  return ok;
}

/* As above, except only renders if enough room */
void agbox_render_string_if_enough_room(agbox *agb,char *string)
{
  if ( enough_room_for_string_in_agbox(agb,string) )
    agbox_render_string(agb,string);
}

/* As above, except returns the leftover horizontal space*/
bool agbox_render_string_if_enough_room_and_resize(agbox **agb,char *string)
{
  double width = AG_CHAR_WIDTH*((double) (strlen(string)+1));
  agbox *agb_old;
  bool ret = FALSE;

  if ( width<agbox_width(*agb,XCOMP) && 
       AG_CHAR_HEIGHT < agbox_width(*agb,YCOMP) )
  {
    ret = TRUE;
    agbox_render_string_horizontal(*agb,string);
    agb_old = *agb;
    *agb = mk_vertical_stripe_absolute(agb_old,agb_old->xlo+width,agb_old->xhi);
    free_agbox(agb_old);
  }
  return ret;
}

int render_string_array_horizontal_as_far_as_possible(agbox *agb,string_array *sa){
  string_array *used_sa = mk_string_array(0);
  double width = agbox_width(agb,XCOMP);
  double size = 0.0;
  char *str = NULL;
  agbox *agb_next;
  int rows_used = 0;

  if(agbox_width(agb,YCOMP)>=AG_CHAR_HEIGHT)
  {
    while(size<=width && string_array_size(sa)!=0){
      str = string_array_ref(sa,0);
      size += (strlen(str)+1)*AG_CHAR_WIDTH;
      if(size<=width){    
        add_to_string_array(used_sa,str);
        string_array_remove(sa,0);
      }
    }

    if(string_array_size(used_sa)>0)
    {
      rows_used++;
      if(size>width && agbox_width(agb,YCOMP)>(AG_CHAR_HEIGHT*2)){
        agb_next = mk_horizontal_stripe_absolute(agb,agb->ylo,agb->yhi-AG_CHAR_HEIGHT);
        rows_used += render_string_array_horizontal_as_far_as_possible(agb_next,sa);
        free_agbox(agb_next);
      }

      str = mk_string_from_string_array(used_sa);
      agbox_render_string_horizontal(agb,str);
      free_string(str);
    }
  }

  free_string_array(used_sa);
  return rows_used;
}

int render_string_array_horizontal_if_possible(agbox *agb,string_array *sa){
  string_array *used_sa = mk_string_array(0);
  double width = agbox_width(agb,XCOMP);
  double size = 0.0;
  char *str = NULL;
  agbox *agb_next;
  int ok = 0;

  while(size<=width && string_array_size(sa)!=0){
    str = string_array_ref(sa,0);
    size += (strlen(str)+1)*AG_CHAR_WIDTH;
    if(size<=width){    
      add_to_string_array(used_sa,str);
      string_array_remove(sa,0);
    }
  }

  if(string_array_size(used_sa)>0 && size>width){
    if(agbox_width(agb,YCOMP)>(AG_CHAR_HEIGHT*1)){
      agb_next = mk_horizontal_stripe_absolute(agb,agb->ylo,agb->yhi-AG_CHAR_HEIGHT);
      ok = render_string_array_horizontal_if_possible(agb_next,sa);
      free_agbox(agb_next);
    } else {
      ok = 0;
    }
  }

  if(size<=width || ok){
    str = mk_string_from_string_array(used_sa);
    agbox_render_string_horizontal(agb,str);
    free_string(str);
    free_string_array(used_sa);
    return ok+1;
  }
  free_string_array(used_sa);
  return 0;
}

int render_string_array_vertical_if_possible(agbox *agb,string_array *sa){
  string_array *used_sa = mk_string_array(0);
  double height = agbox_width(agb,YCOMP);
  double size = 0.0;
  char *str;
  agbox *agb_next;
  int ok = 0;

  while(size<=height && string_array_size(sa)!=0){
    str = string_array_ref(sa,0);
    size += (strlen(str)+1)*AG_CHAR_HEIGHT;
    if(size<=height){    
      add_to_string_array(used_sa,str);
      string_array_remove(sa,0);
    }
  }

  if(string_array_size(used_sa)>0 && size>height){
    if(agbox_width(agb,XCOMP)>(AG_CHAR_WIDTH*2)){
      agb_next = mk_vertical_stripe_absolute(agb,agb->xlo+(AG_CHAR_WIDTH*2.0),agb->xhi);
      ok = render_string_array_vertical_if_possible(agb_next,sa);
      free_agbox(agb_next);
    } else {
      ok = 0;
    }
  }

  if(size<=height || ok){
    str = mk_string_from_string_array(used_sa);
    agbox_render_string_vertical(agb,str);
    free_string(str);
    free_string_array(used_sa);
    return ok+1;
  }
  free_string_array(used_sa);
  return 0;
}

/*Fits each string into the box the best way it knows how.  Returns how many rows/cols it 
  needed to fit them all, or 0 if it couldn't do it.*/
int agbox_render_string_array(agbox *agb,string_array *sa){
  string_array *copy_sa = NULL;
  int ok;

  copy_sa = mk_copy_string_array(sa);
  ok = render_string_array_horizontal_if_possible(agb,copy_sa);
  free_string_array(copy_sa);
  if(!ok){
    copy_sa = mk_copy_string_array(sa);
    ok = render_string_array_vertical_if_possible(agb,copy_sa);
    free_string_array(copy_sa);
  }
  return ok;
}

void agbox_render_string_word_wrap(char *str,agbox *agb)
{
  string_array *lines = mk_broken_string_using_seppers(str,"\n");
  int i,numlines=string_array_size(lines);
  int wordsleft = 0;
  agbox *agb_i = mk_copy_agbox(agb);

  ag_set_pen_color(AG_BLACK);

  for(i=0;i<numlines && !wordsleft;i++)
  {
    char *line = string_array_ref(lines,i);
    string_array *words = mk_broken_string(line);
    int used_rows = render_string_array_horizontal_as_far_as_possible(agb_i,words);
    agbox *newagb = mk_horizontal_stripe_absolute(agb_i,agb_i->ylo,agb_i->yhi-AG_CHAR_HEIGHT*used_rows);
    free_agbox(agb_i);
    agb_i = newagb;
    wordsleft = string_array_size(words);
    free_string_array(words);
  }
  free_agbox(agb_i);
  free_string_array(lines);
}

void agbox_render_string_or_abbrev(agbox *agb,char *name,char *shortname)
{
  if ( enough_room_for_string_in_agbox(agb,name) )
    agbox_render_string(agb,name);
  else if ( shortname != NULL && enough_room_for_string_in_agbox(agb,shortname) )
    agbox_render_string(agb,shortname);
}


void agbox_render_string_at_bottom(agbox *agb,char *string)
{
  agbox *bottom = 
    mk_horizontal_stripe_absolute(agb,agb->ylo,
				  agb->ylo + 1.5 * AG_CHAR_HEIGHT);
  agbox_render_string_horizontal(bottom,string);
  free_agbox(bottom);
}

agbox *mk_horizontal_stripe_absolute(agbox *parent,double aglo,double aghi)
{
  return mk_agbox(parent->xlo,aglo,parent->xhi,aghi);
}

agbox *mk_horizontal_stripe_relative(agbox *parent,double fraclo,double frachi)
{
  return mk_horizontal_stripe_absolute(parent,
				       vertical_break_agval(parent,fraclo),
				       vertical_break_agval(parent,frachi));
}

/* Imagine dividing the first arg (parent) up into "numstrips" horizontal
   strips of equal height, exactly covering and partitioning parent. 
   What would be the agbox corresponding to the stripnum'th of these stripes
   from the top (where stripnum==0 corresponds to the topmost stripe?) */
agbox *mk_agbox_horizontal_substripe(agbox *parent,int stripnum,int numstrips)
{
  double x = (double) numstrips;
  double y = x - stripnum - 1.0;
  return mk_horizontal_stripe_relative(parent,y/x,(y+1)/x);
}

agbox *mk_vertical_stripe_absolute(agbox *parent,double aglo,double aghi)
{
  return mk_agbox(aglo,parent->ylo,aghi,parent->yhi);
}

agbox *mk_vertical_stripe_relative(agbox *parent,double fraclo,double frachi)
{
  return mk_vertical_stripe_absolute(parent,
				     horizontal_break_agval(parent,fraclo),
				     horizontal_break_agval(parent,frachi));
}

agbox *mk_agbox_vertical_substripe(agbox *parent,int stripnum,int numstrips)
{
  return mk_vertical_stripe_relative(parent,
				     stripnum/(double) numstrips,
				     (stripnum+1)/(double) numstrips);
}

/* Shrinks both sides of agb by distance agwidth
   in the comp'th direction of the agb. If comp is XCOMP 
   the box becomes thinner horizontally (less distance left to right).
   If YCOMP becomes less tall. An equal amount is taken off both
   sides.  */
agbox *mk_shrink_both_sides_absolute(agbox *agb,int comp,double agwidth)
{
  agbox *result = mk_copy_agbox(agb);
  double middle = agbox_middle(agb,comp);
  agbox_set(result,comp,LO,real_min(middle,agbox_ref(agb,comp,LO)+agwidth));
  agbox_set(result,comp,HI,real_max(middle,agbox_ref(agb,comp,HI)-agwidth));
  return result;
}

/* Shrinks both sides of agb by fraction "frac" of the entire
   width in the comp'th direction of the agb. If comp is XCOMP 
   the box becomes thinner horizontally (less distance left to right).
   If YCOMP becomes less tall. An equal amount is taken off both
   sides. If frac == 0.25 then the box becomes half the width since
   a quarter is taken off one side and a quarter off the other. */
agbox *mk_shrink_both_sides_relative(agbox *agb,int comp,double frac)
{
  return mk_shrink_both_sides_absolute(agb,comp,frac * agbox_width(agb,comp));
}

/*m must be an inverted slope, i.e. inv_slope = 1 / slope*/
void clip_point_vertical(double *x,double *y,double c,double m){
  *x = *x + (c - *y) * m;
  *y = c;
}

void clip_point_horizontal(double *x,double *y,double c,double m){
  *y = *y + (c - *x) * m;
  *x = c;
}

/* Draws a line segment in absolute coordinates. 
   It DOES clip the line so that only its components INSIDE agb are
   drawn. */
void render_line_segment_absolute(agbox *agb,
				  double x0,double y0,double x1,double y1)
{
  double slope = 0, inv_slope = 0;
  double temp;

  if(x0>x1){
    temp = x0; x0 = x1; x1 = temp;
    temp = y0; y0 = y1; y1 = temp;
  }
  if(y0!=y1){
    inv_slope = (x1 - x0) / (y1 - y0);
    if(y0<agb->ylo) clip_point_vertical(&x0,&y0,agb->ylo,inv_slope);
    else if(y0>agb->yhi) clip_point_vertical(&x0,&y0,agb->yhi,inv_slope);
    if(y1<agb->ylo) clip_point_vertical(&x1,&y1,agb->ylo,inv_slope);
    else if(y1>agb->yhi) clip_point_vertical(&x1,&y1,agb->yhi,inv_slope);
  }
  if(x0!=x1){
    slope = (y1 - y0) / (x1 - x0);
    if(x0<agb->xlo) clip_point_horizontal(&x0,&y0,agb->xlo,slope);
    if(x1>agb->xhi) clip_point_horizontal(&x1,&y1,agb->xhi,slope);
  }

  if((y0!=y1 || (y0>=agb->ylo && y0<=agb->yhi)) && (x0!=x1 || (x0>=agb->xlo && x0<=agb->xhi)))
    ag_line(x0,y0,x1,y1);
}

/* Draws a relative-coordinate line segment. Assume that the
   bottom left of the agbox should be identified with the
   relative coordinate (0,0) and the top right should be identified
   with (1,1).

   It DOES clip the line so that only its components INSIDE agb are
   drawn. */
void render_line_segment_relative(agbox *agb,
				  double x0,double y0,double x1,double y1)
{
  render_line_segment_absolute(agb,
			       horizontal_break_agval(agb,x0),
			       vertical_break_agval(agb,y0),
			       horizontal_break_agval(agb,x1),
			       vertical_break_agval(agb,y1));
}

/* Draws a virtual-coordinate line segment. Assume that the
   bottom left of the agbox should be identified with the
   virtual-coordinates-point (vx0,vy0) and the top right with
   (vx1,vy1). Then this draws the line segment that begins with
   (x0,y0) in virtual coordinates and ends with (x1,y1).

   It DOES clip the line so that only its components INSIDE agb are
   drawn. */
void render_virtual_line_segment(agbox *agb,
				 double vx0,double vy0,double vx1,double vy1,
				 double x0,double y0,double x1,double y1)
{
  render_line_segment_relative(agb,(x0-vx0)/(vx1-vx0),(y0-vy0)/(vy1-vy0),
			           (x1-vx0)/(vx1-vx0),(y1-vy0)/(vy1-vy0));
}


void check_comp(int comp)
{
  if ( comp != XCOMP && comp != YCOMP )
    my_errorf("check_comp: comp = %d (i.e. not XCOMP or YCOMP)",comp);
}

void check_hilo(int hilo)
{
  if ( hilo != LO && hilo != HI )
    my_errorf("check_hilo: hilo = %d (i.e. not HI or LO)",hilo);
}

double agbox_ref(agbox *agb,int comp,int hilo)
{
  double result;
  check_comp(comp);
  check_hilo(hilo);
  if ( comp == XCOMP )
  {
    if ( hilo == LO )
      result = agb->xlo;
    else
      result = agb->xhi;
  }
  else
  {
    if ( hilo == LO )
      result = agb->ylo;
    else
      result = agb->yhi;
  }
  return result;
}

double agbox_lo(agbox *agb,int comp)
{
  return agbox_ref(agb,comp,LO);
}

double agbox_hi(agbox *agb,int comp)
{
  return agbox_ref(agb,comp,HI);
}

double agbox_width(agbox *agb,int comp)
{
  return agbox_hi(agb,comp) - agbox_lo(agb,comp);
}

double agbox_middle(agbox *agb,int comp)
{
  return 0.5 * (agbox_hi(agb,comp) + agbox_lo(agb,comp));
}

double agbox_abs2rel(agbox *agb,double a,int comp)
{
  return ( a - agbox_lo(agb,comp) ) / agbox_width(agb,comp);
}

double agbox_rel2abs(agbox *agb,double v,int comp)
{
  return agbox_lo(agb,comp) + v * agbox_width(agb,comp);
}

agbox *mk_unit_agbox(void)
{
  return mk_agbox(0.0,0.0,1.0,1.0);
}

agbox *mk_full_screen_agbox(void)
{
  return mk_agbox(0.0,0.0,512.0,512.0);
}

void agbox_set(agbox *agb,int comp,int hilo,double a)
{
  check_comp(comp);
  check_hilo(hilo);
  if ( comp == XCOMP )
  {
    if ( hilo == LO )
      agb->xlo = a;
    else
      agb->xhi = a;
  }
  else
  {
    if ( hilo == LO )
      agb->ylo = a;
    else
      agb->yhi = a;
  }
}

void agbox_increment(agbox *agb,int comp,int hilo,double delta)
{
  agbox_set(agb,comp,hilo,delta + agbox_ref(agb,comp,hilo));
}

void maybe_expand_agbox_comp(agbox *agb,int comp,double a)
{
  if ( a < agbox_lo(agb,comp) )
    agbox_set(agb,comp,LO,a);
  if ( a > agbox_hi(agb,comp) )
    agbox_set(agb,comp,HI,a);
}

void maybe_expand_agbox(agbox *agb,double x,double y)
{
  maybe_expand_agbox_comp(agb,XCOMP,x);
  maybe_expand_agbox_comp(agb,YCOMP,y);
}

double agbox_xlo(agbox *agb)
{
  return agbox_ref(agb,XCOMP,LO);
}

double agbox_ylo(agbox *agb)
{
  return agbox_ref(agb,YCOMP,LO);
}

double agbox_xhi(agbox *agb)
{
  return agbox_ref(agb,XCOMP,HI);
}

double agbox_yhi(agbox *agb)
{
  return agbox_ref(agb,YCOMP,HI);
}

double agbox_min_width(agbox *agb)
{
  return real_min(agbox_width(agb,XCOMP),agbox_width(agb,YCOMP));
}

void agbox_border(agbox *agb)
{
  ag_rectangle(agb->xlo,agb->ylo,agb->xhi,agb->yhi);
}

void agbox_color_border(agbox *agb,int agcolor)
{
  ag_set_pen_color(agcolor);
  agbox_border(agb);
}

void agbox_fill(agbox *agb)
{
  ag_box(agb->xlo,agb->ylo,agb->xhi,agb->yhi);
}

void agbox_color_fill(agbox *agb,int agcolor)
{
  ag_set_pen_color(agcolor);
  agbox_fill(agb);
}

bool is_in_agbox(agbox *agb,double x,double y)
{
  return (x>=agb->xlo && x<=agb->xhi && y>=agb->ylo && y<=agb->yhi);
}

agbox *mk_agbox_surrounding_dyvs(dyv *xarr,dyv *yarr)
{
  int size = dyv_size(xarr);
  agbox *agb;

  if ( size == 0 )
    agb = mk_unit_agbox();
  else
  {
    double x = dyv_ref(xarr,0);
    double y = dyv_ref(yarr,0);
    int i;
    agb = mk_agbox(x,y,x,y);

    for ( i = 1 ; i < size ; i++ )
    {
      x = dyv_ref(xarr,i);
      y = dyv_ref(yarr,i);
      maybe_expand_agbox(agb,x,y);
    }
  }

  return agb;
}
agbox *mk_agbox_surrounding_tics(dyv *xarr,dyv *yarr)
{
  int size = int_max(dyv_size(xarr),dyv_size(yarr));
  agbox *agb;

  if ( size == 0 )
    agb = mk_unit_agbox();
  else
  {
    agb = mk_agbox(dyv_min(xarr),dyv_min(yarr),dyv_max(xarr),dyv_max(yarr));
  }

  return agb;
}

/* agb is updated to a size sufficient to ensure
   it contains x, but no more. */
void maybe_expand_agbox_from_agbox(agbox *agb,agbox *x)
{
  maybe_expand_agbox(agb,agbox_xlo(x),agbox_ylo(x));
  maybe_expand_agbox(agb,agbox_xhi(x),agbox_yhi(x));
}

void maybe_expand_agbox_from_dyvs(agbox *agb,dyv *xarr,dyv *yarr)
{
  if ( dyv_size(xarr) > 0 )
  {
    agbox *x = mk_agbox_surrounding_dyvs(xarr,yarr);
    maybe_expand_agbox_from_agbox(agb,x);
    free_agbox(x);
  }
}

void maybe_expand_agbox_from_tics(agbox *agb,dyv *xtics,dyv *ytics)
{
  maybe_expand_agbox(agb,dyv_min(xtics),dyv_min(ytics));
  maybe_expand_agbox(agb,dyv_max(xtics),dyv_max(ytics));
}

void shrink_agbox_for_printing(agbox *agb)
{
  double delta = real_min(AG_CHAR_HEIGHT,agbox_width(agb,YCOMP)/2.0);
  agbox_increment(agb,YCOMP,HI,-delta);
}

double agbox_top_left_print_coord(agbox *agb,int comp)
{
  double v;
  check_comp(comp);
  if ( comp == XCOMP )
    v = agbox_ref(agb,XCOMP,LO) + AG_CHAR_WIDTH/2.0;
  else
    v = agbox_ref(agb,YCOMP,HI) - AG_CHAR_HEIGHT;
  return v;
}

void print_and_shrink(agbox *agb,char *s)
{
  double x = agbox_top_left_print_coord(agb,XCOMP);
  double y = agbox_top_left_print_coord(agb,YCOMP);
  ag_print(x,y,s);
  shrink_agbox_for_printing(agb);
}

void maybe_print_and_shrink(agbox *agb,char *s)
{
  if ( s != NULL ) print_and_shrink(agb,s);
}

/* Returns a copy of ag except the hi/lo component
   of the comp'th direction is changed to v */
agbox *mk_agbox_segment(agbox *ag,double v,int comp,int hilo)
{
  agbox *n = mk_copy_agbox(ag);
  agbox_set(n,comp,hilo,v);
  return n;
}

void split_agbox_vertical_absolute(agbox *ag,double x,agbox **r_ag1,
				   agbox **r_ag2)
{
  *r_ag1 = mk_agbox_segment(ag,x,XCOMP,HI);
  *r_ag2 = mk_agbox_segment(ag,x,XCOMP,LO);
}

void split_agbox_horizontal_absolute(agbox *ag,double y,agbox **r_ag1,
				     agbox **r_ag2)
{
  *r_ag1 = mk_agbox_segment(ag,y,YCOMP,HI);
  *r_ag2 = mk_agbox_segment(ag,y,YCOMP,LO);
}

/* Assuming all four numerical arguments are between zero and one
   makes a smaller sub-box of "agb" inside "agb". */
agbox *mk_sub_agbox(agbox *agb,double xlo,double ylo,double xhi,double yhi)
{
  agbox *hstripe = mk_horizontal_stripe_relative(agb,ylo,yhi);
  agbox *result = mk_vertical_stripe_relative(hstripe,xlo,xhi);
  free_agbox(hstripe);
  return result;
}

/*
+---------+         +---------+
|         |         |    |    |
|         |         |a01 |a11 |
| agb     | -->     +---------+
|         |         |    |    |
|         |         |a00 |a10 |
+---------+         +---------+
*/
void make_quadrant_sub_agboxes(agbox *agb,
			       agbox **r_a00,agbox **r_a01,
			       agbox **r_a10,agbox **r_a11)
{
  *r_a00 = mk_sub_agbox(agb,0.0,0.0,0.5,0.5);
  *r_a01 = mk_sub_agbox(agb,0.0,0.5,0.5,1.0);
  *r_a10 = mk_sub_agbox(agb,0.5,0.0,1.0,0.5);
  *r_a11 = mk_sub_agbox(agb,0.5,0.5,1.0,1.0);
}

/*
                    +---------+
                    |    |    |
                    |a01 |a11 |
  fullscreen-->     +---------+
                    |    |    |
                    |a00 |a10 |
                    +---------+
*/
void make_quadrant_agboxes(agbox **r_a00,agbox **r_a01,
			   agbox **r_a10,agbox **r_a11)
{
  agbox *full = mk_full_screen_agbox();
  make_quadrant_sub_agboxes(full,r_a00,r_a01,r_a10,r_a11);
  free_agbox(full);
}



