/*
 * Copyright (c) 1992, The Geometry Center
 *                     University of Minnesota 
 *                     1300 South Second Street
 *                     Minneapolis, MN  55454
 *
 * email address: software@geom.umn.edu
 *
 * This software is copyrighted as noted above.  It may be freely copied,
 * modified, and redistributed, provided that the copyright notice is
 * preserved on all copies.
 *
 * There is no warranty or other guarantee of fitness for this software,
 * it is provided solely "as is".  Bug reports or fixes may be sent
 * to the authors, who may or may not act on them as they desire.
 *
 * You may not include this software in a program or other software product
 * without supplying the source, or without informing the end-user that the
 * source is available for no extra charge.
 *
 * If you modify this software, you should include a notice giving the
 * name of the person performing the modification, the date of modification,
 * and the reason for such modification.
 *
 *     The National Science and Technology Research Center for
 *      Computation and Visualization of Geometric Structures
 */


/* Generated by Interface Builder */

#import <appkit/Pasteboard.h>
#import <objc/objc.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "link_types.h"
#include "link_edit_types.h"
#include "link_edit_global.h"
#include "link.h"
#import <dpsclient/wraps.h>
#import <dpsclient/dpsclient.h>
#import "DisplayView.h"
#import <streams/streams.h>
#import <appkit/Panel.h>
#import <appkit/Font.h>
#import <appkit/Application.h>
#import "Link.h"
#import "PrefObj.h"
#import <appkit/color.h>

#define NUMROPES 3
#define PI	3.141592653589
#ifdef THREE_D
#define	SIDE_LENGTH	.2
#else
#define SIDE_LENGTH	.05
#endif

#define tpx(xxx)  ((.33* (xxx) + .5))
#define tpy(yyy)  ((.33* (-(yyy)) + .5))
#define labelx(m,n) (.5)   
#define labely(m,n) .98

	struct rect {
		float xmin, xmax, ymin, ymax;
		} ; 

	/* phase is in terms of direction N0: other directions are given by table */
	static double phase_offset[2][4] = 
		{0, PI, PI/2.0, -PI/2,	
		0, PI, -PI/2.0, PI/2};	/* positive orientation */
	extern char *machine_name, devicenames[][30];
	extern int which_dvc;
	extern struct rect cnvs;
	extern FILE *psfp;
        extern char *outfile;
        int  xsegs = 1, ysegs = 1;
	static int linkdrawcnt = 0;
        static int panel_num = 0;
        int cyid_ascii = -1, cyid_live = -1, cid = -1;







void drawL(float x1,float y1,float x2,float y2,float c);

/* Determines orientation (left or right) of crossing with up strand going
   from (x1, y1) to (x2, y2), and the down strand beginning at (p,q) and
   crossing over to the other side */
char orient(double x1, double y1, double x2, double y2, double p, double q)
{
  double a = (y1 - y2) / (x1 - x2);  /* a = slope of up strand */
  double b, ainv, bovera;

  /* Take cases on slope of up strand, then subcases on direction */
  if (-1 > a || a > 1)
    {
      ainv = (x1 - x2) / (y1 - y2);
      bovera = (x1*y2 - x2*y1) / (y1 - y2);
      if (y2 > y1)        /* going up */
	{
	  if (p < ainv*q - bovera)
	    return 'r';
	  else return 'l';
	}
      else                /* going down */
	{
	  if (p > ainv*q - bovera)
	    return 'r';
	  else return 'l';
	}
    }
  else
    {
      b = (x1*y2 - x2*y1) / (x1 - x2);
      if (x1 < x2)       /* going right */
	{
	  if (q > a*p + b) return 'r';
	  else return 'l';
	}
      else               /* going left */
	{
	  if (q < a*p + b) return 'r';
	  else return 'l';
	}
    }
}

void getperp(double z, double w, double slope, double distance, double *points);

/* The following is a "half sin" function, periodically (with period 1)
   spouting sin from PI/4 to 7PI/4 from domain input 0 to 1 */
double halfsin(double x)
{
  /* Get x in [0,1) */
  x = fabs(x);
  x = x - floor(x);

  /* Return appropriate value of sin */
  return sin(x * (3*PI/2) + PI/4);
}

/* The following parametrizes the line segment from (x1,y1) to (x2,y2) between
   a and b.  It returns the point on the segment corresponding to c in
   *lastx & *lasty. */
void ensin (double x1, double y1, double x2, double y2, double a, double b,
	    float amplitude, float seg, float *lastx, float *lasty, int which)
{
  static int isfirstime = 1;
  static double prevc[NUMROPES];
  int i;
  double c, z, w, scale, points[4], slope, step, dist;
  slope = (y1-y2) / (x1-x2);
  step = (b-a) / (2*seg);

  if (isfirstime)
    {
      for (i=0; i<NUMROPES; i++)
	prevc[i] = -1;
      isfirstime = 0;
    }

  if (prevc[which] == -1) prevc[which] = a;
  /* The segment from (x1,y1) to (x2,y2) is paremetrized by c */
  for (c = a; c <= b; c += step)
    {
      scale = (c-a)/(b-a);
      z = scale*(x2-x1) + x1;
      w = scale*(y2-y1) + y1;   /* c is at (z,w) */
      dist = amplitude*halfsin(c);
      getperp(z, w, slope, (double) dist, points);
      /* Choose correct point and return */
      if (orient(x1, y1, x2, y2, points[0], points[1]) == 'l')
	{
	  if (dist > 0)
	    {
	      points[0] = points[2];
	      points[1] = points[3];
	    }
	}
      else if (dist < 0)
	{
	  points[0] = points[2];
	  points[1] = points[3];
	}
      if (c == a)                           /* If first time, start path */
	{
	  PSnewpath();
	  if (*lastx == -1)
	    PSmoveto((float) points[0], (float) points[1]);
	  else PSmoveto(*lastx, *lasty);
	}
      if (floor(prevc[which]) != floor(c))  /* If new halfsin to start here...*/
	{
	  if (c != a)
	    {
	      PSstroke();                   /* Draw old path */
	    }
	  PSnewpath();                      /* Start new path */
	  PSmoveto((float) points[0], (float) points[1]);
	}
      else
	PSlineto((float) points[0], (float) points[1]);
      prevc[which] = c;
    }
  PSstroke();
  *lastx = points[0];
  *lasty = points[1];
}

void enlin (double x1, double y1, double x2, double y2, float linesep,
	    float lastx[2], float lasty[2])
{
  double firstpnts[4], lastpnts[4], tmp1, tmp2, slope;
  slope = (y1-y2) / (x1-x2);

  getperp(x1, y1, slope, (double) linesep, firstpnts);
  if (orient(x1, y1, x2, y2, firstpnts[0], firstpnts[1]) == 'l')
    {
      tmp1 = firstpnts[0]; tmp2 = firstpnts[1];
      firstpnts[0] = firstpnts[2];
      firstpnts[1] = firstpnts[3];
      firstpnts[2] = tmp1; firstpnts[3] = tmp2;
    }

  getperp(x2, y2, slope, (double) linesep, lastpnts);
  if (orient(x1, y1, x2, y2, lastpnts[0], lastpnts[1]) == 'l')
    {
      tmp1 = lastpnts[0]; tmp2 = lastpnts[1];
      lastpnts[0] = lastpnts[2];
      lastpnts[1] = lastpnts[3];
      lastpnts[2] = tmp1; lastpnts[3] = tmp2;
    }
  if (lastx[0] == -1)
    {
      lastx[0] = (float) firstpnts[0]; lasty[0] = (float) firstpnts[1];
      lastx[1] = (float) firstpnts[2]; lasty[1] = (float) firstpnts[3];
    }
  PSnewpath();
  PSmoveto(lastx[0], lasty[0]);
  PSlineto((float) firstpnts[0], (float) firstpnts[1]);
  PSlineto((float) lastpnts[0], (float) lastpnts[1]);
  PSstroke();
  PSnewpath();
  PSmoveto(lastx[1], lasty[1]);
  PSlineto((float) firstpnts[2], (float) firstpnts[3]);
  PSlineto((float) lastpnts[2], (float) lastpnts[3]);
  PSstroke();
  lastx[0] = (float) lastpnts[0]; lasty[0] = (float) lastpnts[1];
  lastx[1] = (float) lastpnts[2]; lasty[1] = (float) lastpnts[3];
}

/* This returns the two points which make a line perpendicular to the line
   through (z,w) with slope slope, and which are distance away from (z,w).
   The points are returned in (points[0], points[1]) and (points[2], points[3]) */
void getperp(double z, double w, double slope, double distance, double *points)
{
  double disc = 2*fabs(distance) * sqrt(1 / (slope*slope + 1));
  if (slope < 1 && slope > -1)
    {
      points[1] = w + disc/2;
      points[3] = w - disc/2;
      points[0] = (w - points[1]) * slope + z;
      points[2] = (w - points[3]) * slope + z;
    }
  else if (slope > 100000000000000.0 || slope < -100000000000000.0)
    {
      points[0] = z - distance;
      points[2] = z + distance;
      points[1] = points[3] = w;
    }
  else
    {
      disc *= fabs(slope);
      points[0] = z + disc/2;
      points[2] = z - disc/2;
      points[1] = (z - points[0])/slope + w;
      points[3] = (z - points[2])/slope + w;
    }
}
  
@implementation DisplayView

- (BOOL) acceptsFirstResponder
{
  return YES;
}

/* Highlights entire view, so user knows he can copy it */
void fillR(float,float,float,float,float);
- mouseDown: (NXEvent *) event
{
  float x = bounds.origin.x;
  float y = bounds.origin.y;
  float w = bounds.size.width;
  float h = bounds.size.height;
  [self lockFocus];
  fillR(x, y, 4, 4, .33);
  fillR(x, y + h/2 -2, 4, 4, .33);
  fillR(x, y+h-4, 4, 4, .33);
  fillR(x+w-4, y, 4, 4, .33);
  fillR(x+w-4, y + h/2 -2, 4, 4, .33);
  fillR(x+w-4, y+h-4, 4, 4, .33);
  fillR(x + w/2 - 2, y, 4, 4, .33);
  fillR(x + w/2 - 2, y+h-4, 4, 4, .33);
  [self unlockFocus];
  [window flushWindow];
  return self;
}

- copy:sender
{
  id pb = [Pasteboard new];
  NXStream *stream;
  char *data;
  int length, maxLength;

  [pb declareTypes:&NXPostScriptPboardType num:1 owner:self];
  stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  [self copyPSCodeInside:NULL to:stream];
  NXGetMemoryBuffer(stream, &data, &length, &maxLength);
  [pb writeType:NXPostScriptPboardType data:data length:length];
  NXCloseMemory(stream, NX_FREEBUFFER);
  return self;
}

- drawSelf: (NXRect *)dRects:(int)dCount
{
  [linkObj doDraw:self];
  return self;
}

LinkList *newLink(LinkStatus *gnrc);

- linkdraw :(link *) linkptr :(FILE *) ofp :(LinkStatus *) gnrc
{
  crossing *xingptr, *firstxing;
  int i, j, k, segs = 8, m, n, dir, tdir, firstdir;
  double angle, oangle, c,s, oc, os, p[500][2], d,
  radius, oradius, ex, ey, ex1, ey1, ex2, ey2, deriv0, deriv1,
  ox, oy, xx, yy, fff, d2, x0[2], x1[2], x2[2], x3[2], scale, oscale, deriv[2];
  double rope[NUMROPES];   /* Next value to call halfsin with for each fiber */
  float lastx[NUMROPES], lasty[NUMROPES];
  float x[500], y[500];
  int which;
  locamplitude = amp/ ((xsegs > ysegs) ? xsegs : ysegs);
  locpixelsPerCycle = ppc/ ((xsegs > ysegs) ? xsegs : ysegs);

/*  [self drawCircle];
  return self;*/
  
  if (ofp == NULL && gnrc == NULL)
    [self clearScreen];

  linkdrawcnt = 0;
  panel_num = 0;


  n = m = 0;
  
  for (j = 0; j<linkptr->nstrands; j++)
    {
      /* Set up rope */
      for (i=0; i<NUMROPES; i++)
	{
	  rope[i] = 0;
	  lastx[i] = lasty[i] = -1;
	}      
      rope[1] = rope[0] + 0.33333;
      rope[2] = rope[1] + 0.33333;
      
      if (gnrc)          /* Update current editor link if necessary */
	{
	  if (gnrc->current_link == NULL)
	    /* First strand */
	    gnrc->current_link = gnrc->link.next = newLink(gnrc);
	  else
	    /* Not first strand */
	    gnrc->current_link = gnrc->current_link->next = newLink(gnrc);
	}
      
      /* Search for an under crossing */
      xingptr = linkptr->strandlist[j];
      dir = linkptr->dirlist[j];
      if (gnrc == NULL && ([linkObj get_tangle] == 0 || xingptr->n > 3)
	  && xingptr->type != LOOSE_END)     /* Only search if this isn't a loose end */
	{
	  for (i=0;
	       !((i > 0 && xingptr == linkptr->strandlist[j]
		  && (dir == linkptr->dirlist[j] || xingptr->type == ANCHOR))
		 || (xingptr->orient == TERMINUS));
	       ++i)
	    {
	      if (xingptr->type == NORMAL && dir == N1)        /* Ignore anchors */
		break;                                         /* The under is found */
	      tdir = (xingptr->ud[dir] == UP) ? N0 : N1;       /* always move forward*/
	      xingptr = xingptr->nhbr[dir];
	      dir = tdir;
	    }
	}
      firstxing = xingptr;
      firstdir = dir;
      oangle = xingptr->phase + phase_offset[xingptr->orient == RIGHT][dir];
      oc = cos(oangle);	os = sin(oangle);
/*      oradius = (xingptr->sep > .20) ? sep : xingptr->sep*xsepScale;*/
      oradius = sep;
      if (xingptr->type == ANCHOR) oradius = 0;
      
      for (i=0;
	   !((i > 0 && xingptr == firstxing &&
	      (dir == firstdir || firstxing->type == ANCHOR))
	     || (xingptr->orient == TERMINUS));
	   ++i)
	{
	  if (dir == N0 && xingptr->type == NORMAL)	/* overstrand */
	    {
	      if (ofp)
		fprintf(ofp,"%f %f %f\n",xingptr->x, xingptr->y, oradius);
	      else if (gnrc)
		{
		  LinkPointList *pnt;
		  if (oradius < 0.06) oradius = 0.06;
		  if (LinkAddPointWorldCoords
		      (gnrc,
		       tpx(xingptr->x + oradius * oc),
		       1-tpy(xingptr->y + oradius * os), YES, NO) != -1)
		    gnrc->current_link->num++;
		  if (LinkAddPointWorldCoords
		      (gnrc,
		       tpx(xingptr->x - oradius * oc),
		       1-tpy(xingptr->y - oradius * os), YES, NO) != -1)
		    gnrc->current_link->num++;
		  /* Fix crossing */
		  pnt = gnrc->current_link->point.next;
		  while (pnt->next != NULL) pnt = pnt->next;
		  pnt = pnt->previous;
		  if (pnt->crossing.next)
		    {
		      pnt->crossing.next->z = 1;
		      pnt->crossing.next->crossing->z = -1;
		    }
		}
	      else
		[self drawLine :tpx(xingptr->x + oradius * oc)
		 :tpy(xingptr->y + oradius * os)
		 :tpx(xingptr->x - oradius * oc)
		 :tpy(xingptr->y - oradius * os)
		 :rope :lastx :lasty :j :n];
	    }
	  else if (dir == N1)	/* understrand */
	    {
	      LinkPointList *pnt;
	      if (ofp)
		fprintf(ofp,"%f %f %f\n",xingptr->x, xingptr->y, -oradius);
	      else if (gnrc)
		{
		  if (oradius < 0.06) oradius = 0.06;
		  if (LinkAddPointWorldCoords
		      (gnrc,
		       tpx(xingptr->x + oradius * oc),
		       1-tpy(xingptr->y + oradius * os), YES, NO) != -1)
		    gnrc->current_link->num++;
		  if (LinkAddPointWorldCoords
		      (gnrc,
		       tpx(xingptr->x - oradius * oc),
		       1-tpy(xingptr->y - oradius * os), YES, NO) != -1)
		    gnrc->current_link->num ++;
		  /* Fix crossing */
		  pnt = gnrc->current_link->point.next;
		  while (pnt->next != NULL) pnt = pnt->next;
		  pnt = pnt->previous;
		  if (pnt->crossing.next)
		    {
		      pnt->crossing.next->z = -1;
		      pnt->crossing.next->crossing->z = 1;
		    }
		}
	      for (k=0; k<NUMROPES; k++)
		lastx[k] = lasty[k] = -1;
	    }
/*	  else perror("linkdraw","bad direction in following strand");*/
	  
	  ox = xingptr->x;		oy = xingptr->y;
	  /* advance to next node */
	  which = (dir == N0) ? UP : DOWN;
	  ex = xingptr->edgeX[which]; ey = xingptr->edgeY[which];
	  tdir = (xingptr->ud[dir] == UP) ? N0 : N1; 	/* always move forward*/
	  xingptr = xingptr->nhbr[dir];
	  dir = tdir;
	  angle = xingptr->phase + phase_offset[xingptr->orient == RIGHT][dir];
	  
/*	  radius = (xingptr->sep > .20) ? sep : xingptr->sep*xsepScale;*/
	  radius = sep;
	  if (xingptr->type == ANCHOR) radius = 0;
	  
	  xx = xingptr->x;		yy = xingptr->y;
	  c = cos(angle);	        s = sin(angle);
	  d = sqrt ( (xx-ox)*(xx-ox) + (yy-oy)*(yy-oy))/2; 
	  if (ofp)
	    segs = d/(.2) + 4;
	  else
	    segs = d*segScale + 4;
	  if (segs > segMax)	segs = segMax;

	  /* Do two splines: out-xing to edge, then edge to in-xing */
	  /* ZERO make estimate of divergence of edges */
	  
	  x1[0] = ox - d * oc; 		x1[1] = oy - d*os;
	  x2[0] = xx + d*c; 		x2[1] = yy + d*s;
	  d2 = sqrt ( (x1[0]-x2[0])*(x1[0]-x2[0]) + (x1[1]-x2[1])*(x1[1]-x2[1])); 
	  if (d) fff = fffb*d2 + fffa*d*2;
	  else fff = 1;
	  scale =  fff + radius; 
	  oscale = fff + oradius; 
#ifdef DEBUGR
	  printf("angle, scale: %f %f\n",angle,scale);
#endif
	  /* store beginning point, and directional point, for drawing spline */
	  x0[0] = ox - oradius * oc; 	x0[1] = oy - oradius * os;
	  x1[0] = ox - oscale * oc; 	x1[1] = oy - oscale*os;
	  x2[0] = xx + scale*c; 	x2[1] = yy + scale*s;
	  x3[0] = xx + radius * c; 	x3[1] = yy + radius * s;

	  splineval(x0,x1,x2,x3,p[0],deriv,0.5);
	  deriv0 = deriv[0]/sqrt(deriv[0]*deriv[0] + deriv[1]*deriv[1]);
	  deriv1 = deriv[1]/sqrt(deriv[0]*deriv[0] + deriv[1]*deriv[1]);
	  ex1 = ex - deriv0*(fff/2); ey1 = ey - deriv1*(fff/2);
	  ex2 = ex + deriv0*(fff/2); ey2 = ey + deriv1*(fff/2);

	  if (redraw == EVOLUTION || redraw == ANCHORS)
	    [self splineEmUp :segs :ofp :gnrc :(double *) rope :(float *) lastx
	     :(float *) lasty :j :n :p :(float *) x :(float *) y :x0 :x1 :x2 :x3];

	  if (redraw == EVOLUTION_FULL)
	    {
	      /* ONE make estimate of divergence of edges */
	      x1[0] = ox - (d/2) * oc; 		x1[1] = oy - (d/2)*os;
	      x2[0] = ex1; 		                x2[1] = ey1;
	      d2 = sqrt ( (x1[0]-x2[0])*(x1[0]-x2[0]) + (x1[1]-x2[1])*(x1[1]-x2[1])); 
	      if (d) fff = fffb*d2 + fffa*d;
	      else fff = 1;
	      scale =  fff + radius; 
	      oscale = fff/2 + oradius; 
#ifdef DEBUGR
	      printf("angle, scale: %f %f\n",angle,scale);
#endif
	      /* store beginning point, and directional point, for drawing spline */
	      x0[0] = ox - oradius * oc; 	x0[1] = oy - oradius * os;
	      x1[0] = ox - oscale * oc;	x1[1] = oy - oscale*os;
	      x2[0] = ex1; 	x2[1] = ey1;
	      x3[0] = ex; 	x3[1] = ey;
	      
	      
	      [self splineEmUp :segs :ofp :gnrc :(double *) rope :(float *) lastx
	       :(float *) lasty :j :n :p :(float *) x :(float *) y :x0 :x1 :x2 :x3];
	      
	      /* TWO make estimate of divergence of edges */
	      x1[0] = ex2; 		x1[1] = ey2;
	      x2[0] = xx + (d/2)*c; 		x2[1] = yy + (d/2)*s;
	      d2 = sqrt ( (x1[0]-x2[0])*(x1[0]-x2[0]) + (x1[1]-x2[1])*(x1[1]-x2[1])); 
	      if (d) fff = fffb*d2 + fffa*d;
	      else fff = 1;
	      scale =  fff/2 + radius; 
	      oscale = fff + oradius; 
#ifdef DEBUGR
	      printf("angle, scale: %f %f\n",angle,scale);
#endif
	      /* store beginning point, and directional point, for drawing spline */
	      x0[0] = ex; 	x0[1] = ey;
	      x1[0] = ex2; 	x1[1] = ey2;
	      x2[0] = xx + scale*c; 	x2[1] = yy + scale*s;
	      x3[0] = xx + radius * c; 	x3[1] = yy + radius * s;
	      
	      [self splineEmUp :segs :ofp :gnrc :(double *) rope :(float *) lastx
	       :(float *) lasty :j :n :p :(float *) x :(float *) y :x0 :x1 :x2 :x3];
	    }
	  oc = c;	os = s;
	  oangle = angle;
	  oradius = radius;
	}
    }
  printf("\n");
  if (gnrc) gnrc->current_link->next = NULL;
  return self;
}

- splineEmUp :(int) segs :(FILE *) ofp :(LinkStatus *) gnrc
  :(double *) rope :(float *) lastx :(float *) lasty :(int) j :(int) n
  :(double [500][2]) p :(float *) x :(float *) y
  :(double [2]) x0 :(double [2]) x1 :(double [2]) x2 :(double [2]) x3
{
  double deriv[2], u;
  int k;

  for (k =0; k<segs; ++k)
    {
      u = k/((double) (segs-1));
      splineval(x0,x1,x2,x3,p[k],deriv,u);
    }
  if (ofp)
    for (k=0; k<segs; ++k)
      fprintf(ofp,"%f %f 0.0\n",p[k][0], p[k][1]);
  else
    {
      for (k=0; k<segs; ++k)
	{x[k] = tpx(p[k][0]);	y[k] = tpy(p[k][1]); }
      if (gnrc)
	{
	  for (k=0; k<segs-1; k+=sparse)
	    {
	      if (LinkAddPointWorldCoords(gnrc, x[k], 1-y[k], YES, NO) != -1)
		gnrc->current_link->num++;
	    }
	  if (LinkAddPointWorldCoords(gnrc, x[segs-1], 1-y[segs-1], YES, NO) != -1)
	    gnrc->current_link->num++;
	}
      else
	[self drawPolygon :segs :(float *) x :(float *) y :rope
	 :lastx :lasty :j :n];
    }
  return self;
}

- setColor: (int) strand
{
  NXColor *color = [linkObj get_colors];
  NXSetColor(color[strand%6]);
  return self;
}

- drawLine :(float) x1 :(float) y1 :(float) x2 :(float) y2
  :(double *) rope :(float *) lastx :(float *) lasty :(int) strand :(int) n
{
  float addx = 0*width + lxoffset;
  float addy = (ysegs-1-n)*height + lyoffset;
  double distance, newrope;
  int i;
  y1 = 1 - y1; y2 = 1 - y2;
  x1 = x1 * lscaleToSquare + addx;
  y1 = y1 * lscaleToSquare + addy;
  x2 = x2 * lscaleToSquare + addx;
  y2 = y2 * lscaleToSquare + addy;

  if (texture == BORDERED_F)
    {
      [self setColor:strand];
      PSsetlinewidth(locamplitude*2);
      PSnewpath();
      PSmoveto(x1, y1);
      PSlineto(x2, y2);
      PSstroke();
    }
  if (texture == BORDERED_H || texture == BORDERED_F)
    {
      PSsetgray(0);
      PSsetlinewidth(ampB);
      enlin((double) x1, (double) y1, (double) x2, (double) y2, (float) locamplitude,
	    lastx, lasty);
    }
  else if (texture == ROPE)
    {
      PSsetgray(0);
      distance = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
      for (i=0; i<NUMROPES; i++)
	{
	  newrope = rope[i] + distance/locpixelsPerCycle;
	  ensin((double) x1, (double) y1, (double) x2, (double) y2,
		rope[i], newrope, locamplitude, 2*distance, &lastx[i], &lasty[i], i);
	  rope[i] = newrope;
	}
    }
  else drawL(x1, y1, x2, y2, 0);
	
  return self;
}

- drawPolygon :(int) segs :(float *) x :(float *) y : (double *) rope
  :(float *) lastx :(float *) lasty :(int) strand :(int) n
{
  int i, j;
  float prevx,prevy;
  float addx = 0*width + lxoffset;
  float addy = (ysegs-1-n)*height + lyoffset;
  double distance, newrope, x1, y1, x2, y2;
  
  prevx = x[0];
  prevy = y[0];
  PSsetgray(0);
  
  for (i=1; i<segs; i++)
    {
      x1 = prevx * lscaleToSquare + addx;
      y1 = (1 - prevy) * lscaleToSquare + addy;
      x2 = x[i] * lscaleToSquare + addx;
      y2 = (1 - y[i]) * lscaleToSquare + addy;
      if (texture == BORDERED_F)
	{
	  [self setColor:strand];
	  PSsetlinewidth(locamplitude*2);
	  PSnewpath();
	  PSmoveto(x1, y1);
	  PSlineto(x2, y2);
	  PSstroke();
	}
      if (texture == BORDERED_H || texture == BORDERED_F)
	{
	  PSsetlinewidth(ampB);
	  PSsetgray(0);
	  enlin(x1, y1, x2, y2, (float) locamplitude, lastx, lasty);
	}
      else if (texture == ROPE)
	{
	  distance = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
	  for (j=0; j<NUMROPES; j++)
	    {
	      newrope = rope[j] + distance/locpixelsPerCycle;
	      ensin(x1, y1, x2, y2, rope[j], newrope,
		    locamplitude, 2*distance, &lastx[j], &lasty[j], j);
	      rope[j] = newrope;
	    }
	}
      else drawL(x1, y1, x2, y2, 0);

      prevx = x[i];
      prevy = y[i];
    }

  return self;
}
void clrscrn2(void);
- clearScreen
{
  if (NXDrawingStatus == NX_DRAWING)
    clrscrn2();
  else
    {
/*      PSclippath();
      PSsetgray(0.33);
      PSsetalpha(0);
      PSfill();
      PSsetalpha(1); */
    }
  return self;
}

- setScale
{
  double difference;
  height = bounds.size.height;
  width = bounds.size.width;
  height /= ysegs; width /= xsegs;

  if (height < width)
    {
      scaleToSquare = height;
      xoffset = (width-height)/2;
      yoffset = 0;
    }
  else
    {
      scaleToSquare = width;
      yoffset = (height-width)/2;
      xoffset = 0;
    }
  lscaleToSquare = scaleToSquare;
  difference = (scaleToSquare-lscaleToSquare)/2;
  lxoffset = xoffset + difference;
  lyoffset = yoffset + difference;
 
  return self;
}


/* Printing methods */
- (BOOL) knowsPagesFirst: (int *) f last: (int *) l
{
  *f = *l = 1;
  return YES;
}

- (BOOL) getRect: (NXRect *)r forPage :(int) page
{
  
  r->origin.x = 0; r->origin.y = -10;
  r->size.width = bounds.size.width;
  r->size.height = bounds.size.height+10;
  return YES;
}

- initFrame:(const NXRect *)frameRect
{
  static float dummy;
  int i;
  [super initFrame:frameRect];

  /* Set up values to point to display parameters */
  values = (float **) NXZoneMalloc([self zone], NUM_LOCAL_SETTINGS*sizeof(float *));
  values[0] = &amp;
  values[1] = &ampB;
  values[2] = &fffa;
  values[3] = &fffb;
  values[4] = &ppc;
  values[5] = &segMax;
  values[6] = &segScale;
  values[7] = &sep;
  values[8] = (float *) (&texture);
  values[9] = (float *) (&redraw);
  for (i=10; i<15; i++)
    values[i] = &dummy;
  values[15] = (float *) (&sparse);
  return self;
}
  

/* Display parameters */

- (float **) getValues
{
  return values;
}

- drawCircle
{
  int i;
  double domain,last,step;
  double rope[NUMROPES];   /* Next value to call halfsin with for each fiber */
  float lastx[NUMROPES], lasty[NUMROPES];
  locamplitude = amp/ ((xsegs > ysegs) ? xsegs : ysegs);
  locpixelsPerCycle = ppc/ ((xsegs > ysegs) ? xsegs : ysegs);
  last = 0;
  step = 0.01;
  [self lockFocus];
  [self clearScreen];
  /* Set up rope */
  for (i=0; i<NUMROPES; i++)
    {
      rope[i] = 0;
      lastx[i] = lasty[i] = -1;
    }      
  rope[1] = rope[0] + 0.33333;
  rope[2] = rope[1] + 0.33333;
  for (domain=step; domain<2*PI; domain += step)
    {
      [self drawLine :(float) tpx(0 + cos(last)) :(float) tpy(sin(last))
       :(float) tpx(cos(domain))
       :(float) tpy(sin(domain)) :rope :lastx :lasty :0 :0];
      last = domain;
    }
  return self;
}

/* Sets the instance variable that points to the link object */

- setLink:(id) linkO
{
  linkObj = linkO;
  return self;
}

@end
