/*
 *
 * symmetry.c - Nina Amenta, Aug. 1989
 * rewritten by Ed H. Chi summer 1994
 
 $Id: symmetry.c,v 1.11 1996/10/07 15:26:31 slevy Exp $
 $Log: symmetry.c,v $
 * Revision 1.11  1996/10/07  15:26:31  slevy
 * Remove all conditional code for SGI & VOGL -- we're now pure X,
 * using only XForms.
 *
 * Revision 1.10  1996/06/04  17:28:04  slevy
 * Convert to new DRAWER-based calling sequences.
 * Picking is no longer a special case.
 *
 * Revision 1.9  1994/09/12  23:14:41  chi
 * version 3.0 changes
 * header file recreated
 *
 * Revision 1.8  1994/08/26  16:29:02  chi
 * rewritten by Ed H. Chi
 * start of port to X11
 *
 * Revision 1.2  1994/08/15  20:05:38  chi
 * significant code cleanup.
 * header files created.
 * main.h created
 * changed #defines in main.h from DRAW to KALIDRAW, CUT to
 .... etc
 *
 
 */

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



#include "main.h"
#include "symmetry.h"
#include "io.h"


/* Grids - a grid is just a list of lines */

/* two translation lines */
/*  p1 */
LINE GRID0 [2] = {  
  { {0.0, 0.0, 0.0, 1.0},0,0,&(GRID0[1])},
  { {0.0, 0.0, 1.0, 0.0},0,0,NULL}
};

/* one lattice, one off-lattice */
/* p4 */
LINE GRID1 [2] = {  
  { {0.0, 0.0, 1.0, 0.0},0,0,&(GRID1[1])},
  { {0.0, 0.0, 1.0, 1.0},0,0,NULL}
};

/* One part-way off-lattice */
/* p3,p6 */
LINE GRID2 [1] = {  
  { {0.0, 0.0, 1.333333, 0.666667},0,0,NULL}
};

/* One lattice, one off-lattice */
/* p6m */
LINE GRID3 [2] = {  
  { {0.0, 0.0, 0.0, 1.0},0,0,&GRID3[1]},
  { {0.0, 0.0, 1.0, 0.5},0,0,NULL}
};

/* Two off-lattice  */
/* two lattice */
/* cmm */
LINE GRID4 [4] = {
  { {0.0, 0.0, 1.0, 1.0},0,0,&(GRID4[1])},
  { {0.0, 0.0, 1.0, -1.0}, 0,0,&(GRID4[2])},
  { {0.0, 0.0, 0.0, 1.0},0,0,&(GRID4[3])},
  { {0.0, 0.0, 1.0, 0.0},0,0,NULL}
};


/* two off-lattice */
/* cm */
LINE GRID5 [3] = {
  { {0.0, 0.0, 1.0, 1.0},0,0,&(GRID5[1])},
  { {0.0, 0.0, -1.0, 1.0},0,0,NULL}
};

/* one half translation line */
/* two mirrors, one halfway across */
/* pm, pg, p2 */
LINE GRID6 [3] = {
  { {0.0, 0.0, 0.5, 0.0},0,0,&GRID6[1]},
  { {0.0, 0.0, 0.0, 1.0},0,0,&GRID6[2]},
  { {0.5, 0.0, 0.5, 1.0},0,0,NULL}
};

/* two half glide mirrors, strategicly placed */
/* pgg */
LINE GRID7 [2] = {
  { {0.0, 0.0, 0.5, 0.0},0,0,&GRID7[1]},
  { {0.25, 0.25, 0.25, -0.25},0,0,NULL}
};

/* four mirrors */
/* pmm */
LINE GRID8 [4] = {
  { {0.0, 0.0, 0.5, 0.0},0,0,&GRID8[1]},
  { {0.0, 0.0, 0.0, 0.5},0,0,&GRID8[2]},
  { {0.5, 0.0, 0.5, 0.5},0,0,&GRID8[3]},
  { {0.0, 0.5, 0.5, 0.5},0,0,NULL},
};

/* two mirrors, also strategicly placed */
/* p4g */
LINE GRID9 [2] = {
  { {0.0, 0.5, 0.5, 0.0},0,0,&GRID9[1]},
  { {0.0, 0.5, 0.5, 0.5},0,0,NULL}
};

  
/* lattice, half-lattice, and off lattice! */
/* p4m */
LINE GRID10 [3] = {
  { {0.0, 0.0, 1.0, 0.0},0,0,&GRID10[1]},
  { {0.5, 0.5, 0.0, 0.5},0,0,&GRID10[2]},
  { {0.0, 0.0, 1.0, 1.0},0,0,NULL}
};

/* lattice, and a quarter down (wierd)! */
/* pmg */
LINE GRID11 [3] = {
  { {0.0, 0.0, 0.0, 1.0},0,0,&GRID11[1]},
  { {0.0, 0.0, 1.0, 0.0},0,0,&GRID11[2]},
  { {0.0, 0.25, 1.0, 0.25},0,0,NULL}
};

/* One off-lattice */
/* p3m1 */
LINE GRID12 [1] = {  
  { {0.0, 0.0, 2.0, 1.0},0,0,NULL}
};

/* One off-lattice */
/* p31m */
LINE GRID13 [2] = {  
  { {0.0, 0.0, 0.666667, 0.333333},0,0,&GRID13[1]},
  { {0.0, 0.0, 0.0, 1.0},0,0,NULL}
};

/* grids for frieze groups */
/* the y-values get changed by SetUpSymmetry*/

/* long vertical */
/* hop */
LINE GRID14 [1] = {
  { {0.0, -1.0, 0.0, 1.0},0,0,NULL}
};

/* two long verticals */
/* sidle */
LINE GRID15 [2] = {
  { {0.0, -1.0, 0.0, 1.0},0,0,&GRID15[1]},
  { {0.5, -1.0, 0.5, 1.0},0,0,NULL}
};

/* one long vertical, offset, with horizontal */
/* spinsidle */
LINE GRID16 [2] = {
  { {0.0, 0.0, 1.0, 0.0},0,0,&GRID16[1]},
  { {0.25, -1.0, 0.25, 1.0},0,0,NULL},
};

/* two short verticals and a horizontal */
/* spinsidle, spinjump */
LINE GRID17 [3] = {
  { {0.0, 0.0, 0.0, 1.0},0,0,&GRID17[1]},
  { {0.5, 0.0, 0.5, 1.0},0,0,&GRID17[2]},
  { {0.0, 0.0, 1.0, 0.0},0,0,NULL}
};

/* one short vertical and a horizontal */
/* jump, spinhop */
LINE GRID18 [2] = {
  { {0.0, 0.0, 0.0, 1.0},0,0,&GRID18[1]},
  { {0.0, 0.0, 1.0, 0.0},0,0,NULL}
};


/*----------------------------------------------------------
    symmetry =
              admissible transformations
	      #translations
              translation vector1 (initial screen basis)
              translation vector2 (initial screen basis)
	      #reflections
	      reflection vector1  (standard basis)
	      reflection vector2  (standard basis)
	      glide reflection flag
	      #rotations
	      old label strings
	      pointer to grid
    
    NOTE: the SCREEN basis is the lattice as displayed, ie under user
    control.  Internally, things assume the STANDARD {1,0},{0,1} basis
    for the lattice.
    Which is why the length of a glide reflection vector is always 0.5.
    
---------------------------------------------------------------*/

SYMMETRY SYMTAB[NUM_SYM] = {
  {0,2,{60.0,-103.923},{60.0,103.923},0,
     {{0.0,0.0},{0.0,0.0}},0,3,"p3 - 3-way rotation",GRID2},
  {ANG | RAT,2,{100.0,0.0},{0.0,100.0},0,
     {{0.0,0.0},{0.0,0.0}},0,2,"p2 - 2 way rotation",GRID6},
  {ANG | RAT,2,{100.0,-100.0},{100.0,100.0},0,
     {{0.0,0.0},{0.0,0.0}},0,0,"p1 - translation",GRID0},
  {RAT,2,{100.0,0.0},{0.0,100.0},1,
     {{0.0,0.5},{0.0,0.0}},GLIDE,0,"pg - 1 glide reflection",GRID6},
  {RAT,2,{125.0,0.0},{0.0,125.0},2,
     {{0.5,0.0},{0.0,0.5}},GLIDE,0,"pgg - 2 glide reflections",GRID7},
  {RAT,2,{125.0,0.0},{0.0,125.0},1,
     {{0.0,0.5},{0.0,0.0}},GLIDE,2,"pmg - glide reflection w/ 2-way rotation",GRID11},
  {RAT,2,{125.0,0.0},{0.0,125.0},1,
     {{0.0,0.5},{0.0,0.0}},0,0,"pm - 1 lattice reflection",GRID6},
  {ANG,2,{60.0,-103.923},{60.0,103.923},1,
     {{0.5,0.5},{0.0,0.0}},0,0,"cm - 1 off-lattice reflection",GRID5},
  {RAT,2,{125.0,0.0},{0.0,125.0},1,
     {{0.0,0.5},{0.0,0.0}},0,2,"pmm - 2 lattice reflections",GRID8},
  {ANG,2,{100.0,-100.0},{100.0,100.0},1,
     {{0.5,0.5},{0.0,0.0}},0,2,"cmm - 2 off-lattice reflections",GRID4},
  {0,2,{90.0,-155.884},{90.0,155.884},1,
     {{0.0,0.5},{0.0,0.0}},0,3,"p31m - 3 lattice reflections",GRID13},
  {0,2,{90.0,-155.884},{90.0,155.884},1,
     {{1.0,2.0},{0.0,0.0}},0,3,"p3m1 - 3 off-lattice reflections",GRID12},
  {0,2,{100.0,-100.0},{100.0,100.0},0,
     {{0.0,0.0},{0.0,0.0}},0,4,"p4 - 4-way rotation",GRID1},
  {0,2,{100.0,-100.0},{100.0,100.0},1,
     {{0.5,0.5},{0.0,0.0}},GLIDE,4,"p4g - glide reflection w/ 4-way rotation",GRID9},
  {0,2,{100.0,-100.0},{100.0,100.0},1,
     {{0.5,0.5},{0.0,0.0}},0,4,"p4m - reflection w/ 4-way rotation",GRID10},
  {0,2,{90.0,-155.884},{90.0,155.884},0,
     {{0.0,0.0},{0.0,0.0}},0,6,"p6 - 6-way rotation",GRID2},
  {0,2,{90.0,-155.884},{90.0,155.884},1,
     {{0.0,0.5},{0.0,0.0}},0,6,"p6m - reflection w/ 6-way rotation",GRID3},
  
  /* Frieze groups */
  
  {RAT | KALIANGLE,1,{100.00,0.0},{0.0,100.0},0,
     {{0.0,0.0},{0.0,0.0}},0,0,"f1 - hop",GRID14},
  {RAT | ANG,1,{100.00,0.0},{0.0,100.0},0,
     {{0.0,0.0},{0.0,0.0}},0,2,"f2 - spinhop",GRID18},
  {RAT,1,{100.00,0.0},{0.0,100.0},1,
     {{0.5,0.0},{0.0,0.0}},0,0,"f3 - jump",GRID18},
  {RAT,1,{100.00,0.0},{0.0,100.0},1,
     {{0.5,0.0},{0.0,0.0}},0,2,"f4 - spinjump",GRID17},
  {RAT,1,{100.00,0.0},{0.0,100.0},1,
     {{0.0,0.5},{0.0,0.0}},0,0,"f5 - sidle",GRID15},
  {RAT,1,{100.00,0.0},{0.0,100.0},1,
     {{0.5,0.0},{0.0,0.0}},GLIDE,2,"f6 - spinsidle",GRID16},
  {RAT,1,{100.00,0.0},{0.0,100.0},1,
     {{0.5,0.0},{0.0,0.0}},GLIDE,0,"f7 - step",GRID18}
};

int SetUpSymmetry(SYMMETRY *s, POINT **pointpointer, MATRIX xforms[],
		  RECTANGLE *s_rec, RECTANGLE *w_rec)  {
  
  int i=0; int j;
  VECTOR vec1,vec2;
  RECTANGLE virtual;
  POINT *pts;
  int maxpts;
  int done_flag = 0;


  /* both vectors point right; if there's two, vec1 goes down and
     vec2 goes up */
  RearrangeVectors(s,&vec1,&vec2);

  /* the drawing rectangle */
  /* is scaled and starts at 0,0 */
  virtual.x = virtual.y = 0;
  virtual.width = s_rec->width;
  virtual.height = s_rec->height;

  /* get matrix */
  if (*pointpointer != NULL) free(*pointpointer);
  /* how many points do we need? Take a good first guess...*/
  if (s->trans == 2) {
    maxpts =  2 * ((virtual.width * virtual.height) /
		  ((vec1.x*vec2.y) - (vec2.x*vec1.y)) ) + 10 ; 
    /* That last expression on the bottom is the area of the */
    /* parallelogram spanned by vec1 and vec2. The *2 at the */
    /* begining and the +10 at the end is just for good luck */
  }
  else 
    maxpts = 2 * (virtual.width/(vec1.x+vec2.x) + 10);

  pts = (POINT *)malloc(maxpts*sizeof(POINT));

  /* start at bottom left for wallpaper; middle for frieze */
  if (s->trans == 2) 
    { pts[0].x = 0.0; pts[0].y = 0.0; }
  else 
    { pts[0].x = virtual.width/2; pts[0].y = virtual.height/2; }

  /* lay down points of symmetry */
  /* row by row - only rows may be skewed in relation to rectangle */
  /* a row is a line of points parallel to vec1 */

  for (;;)
    {
      /* feel left for begining of row */
      while (! PointLeftOfRect(pts[i],virtual) &&
	    (! PointAboveRect(pts[i],virtual)) &&
	    (! PointBelowRect(pts[i],virtual)))
	bump(pts+i,pts+i,-1,&vec1);
      if (PointAboveRect(pts[i],virtual)) done_flag = 1;
      /* go right till you're inside it (might be far off the top) */
      while (!RectIncludesPoint(virtual,pts[i]) &&
	     !PointRightOfRect(pts[i],virtual))
	bump(pts+i,pts+i,1,&vec1);
      if (PointAboveRect(pts[i],virtual) && (done_flag == 1)) break;
      else done_flag = 0;

      j = i; /* remember begining of this row */
      /* fill in the row */
      while (RectIncludesPoint(virtual,pts[i]))
	{
	  bump(pts+i,pts+i+1,1,&vec1);
	  i++;
	  if (i >= maxpts-1) {
	    maxpts *= 2;
	    pts = (POINT *) realloc(pts,maxpts*sizeof(POINT));
	  }
	}
      /* the last one was off the screen */
      /* for frieze grops we're done */
      if (s->trans == 1) break;
      /* for wallpaper, move up a row */
      bump(pts+j,pts+i,1,&vec2);
      /* if off the right side, we're done */
/*      if (PointRightOfRect(pts[i],virtual)) break; */
    }
  /* return the pointer to the array */
  (*pointpointer) = pts;

  SetUpParameterization(&(s->v1),&(s->v2),xforms[3],xforms[4]);
  if (s->rot>1)
    SetUpRot(2.0*M_PI/(s->rot),xforms[0]);
  if (s->refl!=0)
    for (j=0; j<s->refl; j++)
      SetUpRefl(&(s->reflections[j]),xforms[j+1],xforms[3]);

  /* freize groups - adjust grid height to go off screen */
  if (s->trans == 1) { 
    float gridadj;
    LINE *cur;
    int i;
    float m,h;

    m = GetMax(xforms[4][2],xforms[4][3]);
    h = (GetMax(s_rec->height,s_rec->width));
    gridadj = 2.0 * h / m;

    for (cur = s->grid; cur != NULL; cur = cur->next)
      {
	/* make all non-zero y's long */
	if (cur->m[1] > 0) cur->m[1] = gridadj;
	else if (cur->m[1] < 0) cur->m[1] = -gridadj;
	if (cur->m[3] > 0) cur->m[3] = gridadj;
	else if (cur->m[3] < 0) cur->m[3] = -gridadj;
      }
  }

  /* return number of points */
  return(i);
}

/* Put the basis vectors in a standardized form */
/* Both pointing right, vec1 pointing down and vec2 pointing up */
void RearrangeVectors(SYMMETRY *s, VECTOR *vec1,VECTOR *vec2) {
  int problem_flag = FALSE;
  VECTOR tmp;
  
  if (s->trans == 2) {

    problem_flag = PickTwo(&(s->v1),&(s->v2),vec1,vec2);
    if (problem_flag) {
      /* didn't work; try the difference with the originals */
      AddVector(&(s->v1),&(s->v2),-1,&tmp);
      problem_flag = PickTwo(&(s->v1),&tmp,vec1,vec2);
      /* hmm, maybe the other one? */
      if (problem_flag) 
	problem_flag = PickTwo(&(s->v2),&tmp,vec1,vec2);
    }

 /* now we in trouble! */
    if (problem_flag)
      printf("Bad! (%f,%f),(%f,%f),(%f,%f) \n",
	     (s->v1).x, (s->v1).y,
	     (s->v2).x, (s->v2).y,
	     tmp.x, tmp.y );

  /* they both point right */
    if (vec1->x < 0.0) VectorScalarMult(vec1,-1.0);
    if (vec2->x < 0.0) VectorScalarMult(vec2,-1.0);

  }
  else {
    /* frieze groups */
    *vec1 = (s->v1);
    *vec2 = (s->v2);
    /* bug fix for bizzare fenom of minute negative x's being 
       neither < nor == to 0 */
    if (!((vec1->x > 0.0) || (vec1->x == 0.0))) 
      VectorScalarMult(vec1,-1.0);
    if (!((vec2->x > 0.0) || (vec2->x == 0.0))) 
      VectorScalarMult(vec2,-1.0);

      
  }

}

int PickTwo(VECTOR *in1, VECTOR *in2,
	    VECTOR *vec1, VECTOR *vec2)
{
  int flag1=FALSE,flag2=FALSE;
  int i;
  VECTOR tmp[4];

    /* take the input vectors */
    tmp[0] = *in1;
    tmp[1] = *in2;

    /* find the sum & difference */
    AddVector(&tmp[0],&tmp[1],1,&tmp[2]);
    AddVector(&tmp[0],&tmp[1],-1,&tmp[3]);
  
    /* try and pick vec1 to have differing signs */
    /* and vec2 to have same signs */
    for (i=3; i >= 0; i--) {
      if ( ((tmp[i].x > 0.0) && (tmp[i].y <= 0.0)) ||
	  ((tmp[i].x <= 0.0) && (tmp[i].y > 0.0)) )   {
	*vec1 = tmp[i];
	flag1 = TRUE;
	}
      else {
	*vec2 = tmp[i];
	flag2 = TRUE;
      }
    }
    /* indicate sucsess or not */
    if (!flag1) return(1);
    if (!flag2) return(2);
    return(0);
}



void SetUpParameterization(VECTOR *vec1, VECTOR *vec2,
			  MATRIX std2scr, MATRIX scr2std)
{
  std2scr[0] = vec1->x;
  std2scr[1] = vec1->y;
  std2scr[2] = vec2->x;
  std2scr[3] = vec2->y;
  InvertMatrix(std2scr,scr2std);
}


void SetUpRefl(VECTOR *v, MATRIX xform, MATRIX std2scr) {
  
  MATRIX temp;
  double angle;
  VECTOR w;

  /* reflection vector is in standard basis, convert to screen basis */
  VectorMatrixMult(v,std2scr,&w);
  angle=atan2((double)w.y,(double)w.x);
  /* reflection across the x axis */
  temp[0] = 1;
  temp[1] = temp[2] = 0;
  temp[3] = -1; 
  /* rotate to x axis */
  SetUpRot(-angle,xform);
  /* reflect */
  MatrixMultiply(xform,temp,temp);
  /* rotate back */
  SetUpRot(angle,xform);
  MatrixMultiply(temp,xform,xform);
}

void SetUpRot(double angle, float *xform)
{
  xform[1] = sin(angle);
  xform[2] = -sin(angle);
  xform[0] = xform[3] = cos(angle);
}

LINE *MakeCurrentObject(LINE *Lines, SYMMETRY *sym, MATRIX xforms[],
			RECTANGLE *bounds)
{
  int i,j,pos_count;
  LINE cur,temp,*line_ptr,*obj;
  VECTOR glide[2];

  obj = NULL;
  bounds->x = bounds->y = bounds->width = bounds->height = 0.0;
  /* transform glide reflection vectors from standard to current basis */
  if (sym->glide == GLIDE) {
    VectorMatrixMult(&(sym->reflections[0]),xforms[3],&(glide[0]));
    VectorMatrixMult(&(sym->reflections[1]),xforms[3],&(glide[1]));
  }
  else {
    glide[0].x = glide[1].x = glide[0].y = glide[0].y = 0.0;
  }

  for (line_ptr=Lines; line_ptr != NULL; line_ptr = line_ptr->next)    {
    CopyLine(line_ptr,&cur);
    /* Convert from standard basis to screen coordinate basis */
    MatrixMultiply(cur.m,xforms[3],cur.m);
    pos_count = 0;      /* used in picking */
    obj = AllRotations(&cur,xforms[0],sym,obj,bounds,&pos_count);
    if (sym->refl > 0)	{
	for (i=1; i<=sym->refl; i++)	  {
	  temp.id = cur.id;
	  MatrixMultiply(cur.m,xforms[i],temp.m);
	  TranslateLine(&temp,&(glide[i-1]),1,&temp);
	  obj = AllRotations(&temp,xforms[0],sym,obj,bounds,&pos_count);
	  if (i==2)	    {
	    MatrixMultiply(temp.m,xforms[1],temp.m);
	    TranslateLine(&temp,&(glide[0]),1,&temp);
	    obj = AllRotations(&temp,xforms[0],sym,obj,bounds,&pos_count);
	  }
	  
	}
      }
    }
  return(obj);
}

void FreeObject(LINE *obj)
{
    LINE *cur;
    while (obj != NULL)
    {
	cur = obj;
	obj = obj->next;
	free(cur);
    }
}


LINE *AllRotations(LINE *l, MATRIX xform, SYMMETRY *sym, LINE*obj,
		   RECTANGLE *bounds, int *pos_count)
{

  int j;
  LINE temp;

  CopyLine(l,&temp);
  obj = AddLineToObject(obj,&temp,bounds,pos_count);
  for (j=1; j<sym->rot; j++)    {
    MatrixMultiply(temp.m,xform,temp.m);
    obj = AddLineToObject(obj,&temp,bounds,pos_count);
  }
  return(obj);
}

LINE *AddLineToObject(LINE *obj, LINE *l, RECTANGLE *r,int *pos_count)
{
    LINE *new;
    new = NewLine(obj);
    new->obj_pos = (*pos_count)++;
    CopyLine(l,new);    
    AdjustBoundingBox(new,r);
    return(new);
}

void AdjustBoundingBox(LINE *l, RECTANGLE *r)
{
    float min,max;
    GetMinMax(r->x,r->width,l->m[EX],l->m[SX],&min,&max);
    r->x = min;
    r->width = max;
    GetMinMax(r->y,r->height,l->m[EY],l->m[SY],&min,&max);
    r->y = min;
    r->height = max;
}

void GetMinMax(float a, float b, float c, float d,
	       float *min, float *max)
{
    float tmp;

    if (b < a) { tmp=a; a=b; b=tmp;}
    if (c < a) { tmp=a; a=c; c=tmp;}
    if (d < a) { tmp=a; a=d; d=tmp;}
    if (b > d) { tmp=b; b=d; d=tmp;}
    if (c > d) { tmp=c; c=d; d=tmp;}
    *min = a;
    *max = d;
}

float GetMax(float a, float b)
{
  if (a>b) return a;
  return b;
}


/* Translate the design to each of the centers of symmetry on the screen,
   and draw it. Call ReplicateOffEdges to draw the pieces of
   designs around the edges of the picture that belong to centers of
   symmetry that are just off the screen. */

void ReplicateObject(RECTANGLE *rect, LINE *obj, POINT *pts,
		     int count, RECTANGLE *bounds, SYMMETRY *s,
		     DRAWER *drawer )
{
  int i;

    for (i=0; i<count; i++)
	{
	   TranslateCoordinates(drawer, pts[i].x, pts[i].y); 
	   DrawObject(drawer, obj);
	   ReplicateOffEdges
	     (rect,obj,&pts[i],bounds,s,drawer);

	   TranslateBack(drawer);
	}
}

/* Rearranges the line so the closest endpoint to the point
is EX,EY */

void ClosestEndpoint(LINE *line, POINT *p)
{
float temp;
    temp = distance(line->m[EX],line->m[EY],p->x,p->y);
    temp = distance(line->m[SX],line->m[SY],p->x,p->y);
  if (distance(line->m[EX],line->m[EY],p->x,p->y) >
      distance(line->m[SX],line->m[SY],p->x,p->y))

    {
      temp = line->m[SX]; line->m[SX] = line->m[EX]; line->m[EX] = temp;
      temp = line->m[SY]; line->m[SY] = line->m[EY]; line->m[EY] = temp;
    }
}

float distance(float qx, float qy, float px, float py)
{
  float temp;
  temp = sqrt((qx-px)*(qx-px) + (qy-py)*(qy-py));
  return(temp);
}

/* Look at all adjacent possible centers of symmetry in the lattice.
   If any of these are off the screen, but the current bounding box of
   the design intersects the screen, draw the design at that center. 
   Then try another step in the same direction. */
void ReplicateOffEdges(RECTANGLE *screen, LINE *obj, POINT *p,
		       RECTANGLE *bounds, SYMMETRY *s,
		       DRAWER *drawer)
{
  VECTOR v, vec1, vec2;
  
  /* use same vectors as you did to generate the lattice */
  RearrangeVectors(s,&vec1,&vec2);
  
  DrawIfNecessary(screen,obj,p,bounds,&vec1,1,drawer,0);
  DrawIfNecessary(screen,obj,p,bounds,&vec1,-1,drawer,0);
  if (s->trans == 2)    {
    DrawIfNecessary(screen,obj,p,bounds,&vec2,1,drawer,0);
    DrawIfNecessary(screen,obj,p,bounds,&vec2,-1,drawer,0);
    AddVector(&vec1,&vec2,1,&v);
    DrawIfNecessary(screen,obj,p,bounds,&v,1,drawer,0);
    DrawIfNecessary(screen,obj,p,bounds,&v,-1,drawer,0);
    AddVector(&vec1,&vec2,-1,&v);
    DrawIfNecessary(screen,obj,p,bounds,&v,1,drawer,0);
    DrawIfNecessary(screen,obj,p,bounds,&v,-1,drawer,0);
  }

}

/* Recursive routine that keeps stepping in whatever direction */
void DrawIfNecessary(RECTANGLE *screen, LINE *obj, POINT *p,
		     RECTANGLE *bounds, VECTOR *vec, int direction,
		     DRAWER *drawer, int depth)
{
  POINT q;
  
  /* limit depth of recursion if faced with very bogus lattice vectors */
  if (depth >= 20) return;
  
  bump(p,&q,direction,vec);
  if (!RectIncludesPoint(*screen,q) &&
      BBoxIntersectsRect(&q,bounds,screen)) {
    TranslateCoordinates(drawer, direction*vec->x,direction*vec->y);
    DrawObject(drawer, obj);
    DrawIfNecessary(screen,obj,&q,bounds,vec,direction,drawer,depth+1);
    TranslateBack(drawer);
  }
}


int BBoxIntersectsRect(POINT *p, RECTANGLE *bounds, RECTANGLE *screen)
{
  if (((p->x+bounds->width > screen->x) &&
      (p->x+bounds->x < screen->x+screen->width)) &&
      ((p->y+bounds->height > screen->y) &&
       (p->y+bounds->y < screen->y+screen->height))) return(TRUE);
  return(FALSE);
}

void TranslateLine(LINE *s, VECTOR *v, int scale, LINE *d)
{
  MATRIX temp;

  temp[0] = s->m[0]+scale*v->x;
  temp[1] = s->m[1]+scale*v->y;
  temp[2] = s->m[2]+scale*v->x;
  temp[3] = s->m[3]+scale*v->y;
  CopyMatrix(temp,d->m);
  d->id = s->id;
}


void CopyMatrix(MATRIX f, MATRIX t) {
  int i;
  for (i=0;i<4;i++) t[i]=f[i];
}

void CopyLine(LINE *f, LINE *t) {
  CopyMatrix(f->m,t->m);
  t->id = f->id;
}

void CopyVector(VECTOR *v, VECTOR *w) {
  w->x = v->x; w->y = v->y;
}

void CopyRectangle(RECTANGLE *f, RECTANGLE *t)  {
    t->x = f->x; t->y = f->y;
    t->width = f->width; t->height = f->height;
}

void AddVector(VECTOR *v, VECTOR *w, int scale, VECTOR *d)  {
  d->x = v->x + scale*w->x;
  d->y = v->y + scale*w->y;
}

void MatrixMultiply(MATRIX s, MATRIX t, MATRIX d)  {
  MATRIX temp;
  int i;
  temp[0] = s[0]*t[0]+s[1]*t[2];
  temp[1] = s[0]*t[1]+s[1]*t[3];
  temp[2] = s[2]*t[0]+s[3]*t[2];
  temp[3] = s[2]*t[1]+s[3]*t[3];
  CopyMatrix(temp,d);
}

void VectorMatrixMult(VECTOR *s, MATRIX m, VECTOR *d) {
  VECTOR temp;
  temp.x = s->x*m[0]+s->y*m[2];
  temp.y = s->x*m[1]+s->y*m[3];
  d->x = temp.x; d->y = temp.y;
}

void VectorScalarMult(VECTOR *v, float f) {
  v->x *= f; v->y *= f;
}

void InvertMatrix(MATRIX s, MATRIX d) {
  float det;
  det = s[0]*s[3] - s[1]*s[2];
  d[0] = s[3]/det;
  d[1] = s[1]/det;
  d[2] = s[2]/det;
  d[3] = s[0]/det;
  if (d[1] != 0) d[1] = -d[1];
  if (d[2] != 0) d[2] = -d[2];
}


void NewId(LINE *line)  {
  static short newid=0;
  line->id = newid++;
}

LINE *NewLine(LINE *Lines)
{
  LINE *new;
  new = (LINE*) malloc(sizeof(LINE));
  new->next = Lines;
  return(new);
}


LINE *ReadPattern(LINE *Lines, FILE *pat)  {
  LINE *cur;
  int id=0;

  for (cur=ReadLine(pat); cur!=NULL; cur=ReadLine(pat))    {
    cur->next = Lines;
    cur->id = id++;
    Lines = cur;
  }
  return(Lines);
}


LINE *ReadLine(FILE *pat)
{
  LINE *l;
  l = NewLine(NULL);
  if (fscanf(pat,"%f %f %f %f",&l->m[0],&l->m[1],&l->m[2],&l->m[3])!=EOF)
    return (l);
  else 
    {
    free(l);
    return(NULL);
    }
}








