/* Tesseract   ©1991, Jerry D. Hedden, All Rights Reserved

The Tesseract program, source code, documenation, and icon imagery are
freely redistributable, but may not be modified in any way, or used in part
or in whole without the express written consent of the author.	Images
generated by the Tesseract program may be used for any purpose provided
credit is given to the program's author.

	Jerry D. Hedden
	28 Windemere Dr.
	Sicklerville, NJ  08081
	Phone:	(609)629-3975
	Email:	Hedden@ESDSDF.DNet.GE.com

===========================================================================

This source code REQUIRES Aztec 'C' v3.6a.  It probably cannot be handled
by any other compiler because of the assumed register usage within the
embedded assembly code.  Make with the following:

	as startup
	cc +x3 -e500 -z10000 tess
	ln -m -o Tesseract startup.o tess.o -lc

===========================================================================

The following copyright notice accompanied the source code for Palette
Requester which was used as a model for this program's Color Requester.

Palette Requester -- (c) Copyright 1989 Jonathan Potter
This program is freely redistributable, although all rights to it remain
with the author. It may be used freely in any program as long as this
notice remains intact, however, if you do this, please mention the author
in the program.  If you wish to use this in a commercial program of any
kind, you must register with a $15 donation.
Please send donations, bug reports, comments and suggestions to :
	Jonathan Potter
	3 William Street
	Clarence Park 5034
	South Australia
	Australia

===========================================================================

History:

v1.00 - 10 Aug 1991
  Initial release

**************************************************************************/

#include <intuition/intuition.h>

/* corrects <exec/memory.h> long integer problems */
#define CLEARED_CHIP	0x00010002L

/* required libraries */
struct Library	*IntuitionBase, *GfxBase, *MathBase, *MathTransBase,
		*_OpenLibrary();

/* Motorola FFP math routines */
long  SPFix(), SPTst();
float SPFlt(), SPAdd(), SPSub(), SPMul();
float SPSincos(), SPSqrt();

/* arrays for holding pre-computed, converted "floating point" values */
short cos[720], sin[720];

/* circle constants */
#define PI		(float)3.1415926535897932384626433832795
#define HALF_DEG	(float)(PI/360.0)

/* rotation constants */
#define RESTART        -1
#define STOPPED 	0
#define TEN_DEGREES	20
#define NINETY_DEGREES	180
#define THREE_SIXTY	720

/* the 4 coordinates */
#define X 0L
#define Y 1L
#define Z 2L
#define W 3L

/* viewing angles */
short cos_theta,sin_theta,cos_phi,sin_phi;

/* objects plotted by this program */
#define TESSERACT 0
short t4d[33][4], tseq[33] =
{0,1,3,2,0,8,9,11,10,8,12,14,10,2,6,4,5,7,6,14,15,11,3,7,15,13,9,1,5,13,12,4,0};

#define OCTAHEDRON 1
short o4d[25][4], oseq[25] =
{0,2,1,3,0,4,1,5,2,4,3,5,0,6,1,7,2,6,3,7,4,6,5,7,0};

#define PYRAMID 2
short p4d[11][4], pseq[11] = {0,1,2,3,4,1,3,0,2,4,0};

/* information for display and graphics */
#define WIDTH		656L
#define HEIGHT		430L
#define PLANES		1
#define PLANESIZE	(long)((WIDTH>>3)*HEIGHT)
/* used in mapping to screen */
#asm
H_HEIGHT	equ	215
H_WIDTH 	equ	328
; SCALE_X = SCALE_Y * 7/8
SCALE_X 	equ	65
SCALE_Y 	equ	75
#endasm

struct Screen *screen, *OpenScreen();
struct ViewPort *vp;
struct Window *window, *OpenWindow();
struct RastPort *rp;
short *pointer;
struct IntuiMessage *_GetMsg();
PLANEPTR buffer1[PLANES], buffer2[PLANES], _AllocMem();

struct NewScreen MyScreen = {
  0,0, WIDTH,HEIGHT,PLANES, 0,1, HIRES|LACE,CUSTOMSCREEN,
  NULL,(UBYTE *)"Tesseract",NULL,NULL
};

struct NewWindow MyWindow = {
  0,0, WIDTH,HEIGHT, 0,1, RAWKEY|MOUSEMOVE,
  NOCAREREFRESH|BORDERLESS|ACTIVATE|SIMPLE_REFRESH|RMBTRAP|REPORTMOUSE,
  NULL,NULL,NULL,NULL,NULL, WIDTH,HEIGHT, WIDTH,HEIGHT, CUSTOMSCREEN
};

/* colors */
#define BLACK	0L
#define WHITE	1L

UWORD ColorTables[3][1<<PLANES] = {
  {0x0000,0x0079},
  {0x0FFF,0x0000},
  /* used by color requester */
  {0x0000,0x0079}
};


/* sets up the pre-computed, converted "floating point" arrays */
Initialize()
{
  register long i;
  register float fsin;
  float fcos;

  initial_pos();

  for (i=0; i<720; i++) {
    fsin = SPSincos(&fcos,SPMul(HALF_DEG,SPFlt(i)));
    cos[i] = (SPTst(fcos) >= 0) ? SPFix(SPAdd(0.5,SPMul(fcos,8192.0))) :
				  SPFix(SPSub(0.5,SPMul(fcos,8192.0)));
    sin[i] = (SPTst(fsin) >= 0) ? SPFix(SPAdd(0.5,SPMul(fsin,8192.0))) :
				  SPFix(SPSub(0.5,SPMul(fsin,8192.0)));
  }
}


#define SCALE_O 14500
#define SCALE_P 3000.0

/* sets the objects to their "starting positions" */
initial_pos()
{
  register long i;

  for (i=0; i<33; i++) {
    t4d[i][X] = (((tseq[i]<<1)&2)-1)<<13;
    t4d[i][Y] = (((tseq[i]   )&2)-1)<<13;
    t4d[i][Z] = (((tseq[i]>>1)&2)-1)<<13;
    t4d[i][W] = (((tseq[i]>>2)&2)-1)<<13;
  }
  for (i=0; i<25; i++) {
    o4d[i][X] = (oseq[i]==0 || oseq[i]==1) ? ((oseq[i]&1) ? SCALE_O : -SCALE_O) : 0;
    o4d[i][Y] = (oseq[i]==2 || oseq[i]==3) ? ((oseq[i]&1) ? SCALE_O : -SCALE_O) : 0;
    o4d[i][Z] = (oseq[i]==4 || oseq[i]==5) ? ((oseq[i]&1) ? SCALE_O : -SCALE_O) : 0;
    o4d[i][W] = (oseq[i]==6 || oseq[i]==7) ? ((oseq[i]&1) ? SCALE_O : -SCALE_O) : 0;
  }
  for (i=0; i<11; i++) {
    p4d[i][X] = (pseq[i] < 2) ? ((pseq[i] == 1) ?
			SPFix(SPMul(SCALE_P,SPMul( 3.0,SPSqrt(2.0)))) :
			SPFix(SPMul(SCALE_P,SPMul(-3.0,SPSqrt(2.0)))) ) : 0;
    p4d[i][Y] = (pseq[i] < 3) ? ((pseq[i] == 2) ?
			SPFix(SPMul(SCALE_P,SPMul( 2.0,SPSqrt(6.0)))) :
			SPFix(SPMul(SCALE_P,SPMul(-1.0,SPSqrt(6.0)))) ) : 0;
    p4d[i][Z] = (pseq[i] < 4) ? ((pseq[i] == 3) ?
			SPFix(SPMul(SCALE_P,SPMul( 3.0,SPSqrt(3.0)))) :
			SPFix(SPMul(SCALE_P,SPMul(-1.0,SPSqrt(3.0)))) ) : 0;
    p4d[i][W] = 		 (pseq[i] == 4) ?
			SPFix(SPMul(SCALE_P,SPMul( 2.4,SPSqrt(5.0)))) :
			SPFix(SPMul(SCALE_P,SPMul(-0.6,SPSqrt(5.0))))  ;
  }
}


/* rotates the four-dimensional objects */
rotate_4d(dir1,dir2,angle)
long dir1,dir2;
short angle;
{
  register short cangle, sangle;
  register short *rega2, *rega3;
  register long i;

  if (angle == RESTART)  {
    initial_pos();
    return;
  }

  cangle = cos[angle];	sangle = sin[angle];

  rega2 = &t4d[0][dir1];  rega3 = &t4d[0][dir2];
  for (i=0; i<33; i++) {
#asm
	bsr.s	rotate
#endasm
  }

  rega2 = &o4d[0][dir1];  rega3 = &o4d[0][dir2];
  for (i=0; i<25; i++) {
#asm
	bsr.s	rotate
#endasm
  }

  rega2 = &p4d[0][dir1];  rega3 = &p4d[0][dir2];
  for (i=0; i<11; i++) {
#asm
	bsr.s	rotate
#endasm
  }
  return;

#asm
rotate: move.w	(a2),d0 ; d1' = d1*cos - d2*sin
	muls	d4,d0
	move.w	(a3),d2
	muls	d5,d2
	sub.l	d2,d0
	move.w	(a2),d1 ; d2' = d1*sin + d2*cos
	muls	d5,d1
	move.w	(a3),d2
	muls	d4,d2
	add.l	d2,d1
	moveq	#13,d3	; normalize
	asr.l	d3,d0
	asr.l	d3,d1
	move.w	d0,(a2)
	move.w	d1,(a3)
	addq.l	#8,a2
	addq.l	#8,a3
	rts
#endasm
}


plot_object(object)
short object;
{
  register long i, size;
  short plot_buf[66];
  register short *obj_ptr, *buf_ptr = plot_buf;

  if (object == TESSERACT) {
    obj_ptr = &t4d[0][X];  size = 33;
  } else if (object == OCTAHEDRON) {
    obj_ptr = &o4d[0][X];  size = 25;
  } else {  /* PYRAMID */
    obj_ptr = &p4d[0][X];  size = 11;
  }

  for (i=0; i<size; i++) {
#asm
	move.w	(a2),d0         ; x' = x*cos_theta - z*sin_theta
	muls	_cos_theta,d0
	move.w	4(a2),d2
	muls	_sin_theta,d2
	sub.l	d2,d0

	move.w	(a2),d1         ; z' = x*sin_theta + z*cos_theta
	muls	_sin_theta,d1
	move.w	4(a2),d2
	muls	_cos_theta,d2
	add.l	d2,d1

	moveq	#13,d3		; normalize z'
	asr.l	d3,d1

	move.w	2(a2),d2        ; y' = y*cos_phi - z'*sin_phi
	muls	_cos_phi,d2
	muls	_sin_phi,d1
	sub.l	d1,d2

	asr.l	d3,d0		; normalize x' and y'
	asr.l	d3,d2

	divs	#SCALE_X,d0	; screen_x = WIDTH/2 + x'/SCALE_X
	add.w	#H_WIDTH,d0
	divs	#-SCALE_Y,d2	; screen_y = HEIGHT/2 - y'/SCALE_Y
	add.w	#H_HEIGHT,d2

	move.w	d0,(a3)+        ; store in plotting buffer
	move.w	d2,(a3)+
	addq.l	#8,a2		; move to next point
#endasm
  }

  SetBuffer();
  Move(rp,(long)plot_buf[0],(long)plot_buf[1]);
  PolyDraw(rp,size,plot_buf);
  DisplayBuffer();
}


/* for double buffering */
SetBuffer()
{
  register long i;

  if (rp->BitMap->Planes[0] == buffer1[0])
    for (i=0; i<PLANES; i++)
      rp->BitMap->Planes[i] = buffer2[i];
  else
    for (i=0; i<PLANES; i++)
      rp->BitMap->Planes[i] = buffer1[i];
  SetRast(rp,BLACK);
}


DisplayBuffer()
{
  WaitBOVP(vp);
  WaitTOF();
  ScrollVPort(vp);
  RethinkDisplay();
}


#define ONE_KEY 	1
#define ZERO_KEY	10
#define ESCAPE		69
#define SPACE		64
#define RKEY		19
#define TKEY		20
#define IKEY		23
#define OKEY		24
#define PKEY		25
#define DKEY		34
#define XKEY		50
#define CKEY		51
#define KP8		62
#define KP2		30
#define KP4		45
#define KP6		47
#define UPARROW 	76
#define DOWNARROW	77
#define LEFTARROW	79
#define RIGHTARROW	78
#define HELP		95

#define IMAGINARY	0
#define REAL		1

#define STILL		0
#define MOVED		1

MainLoop()
{
  register struct IntuiMessage *msg;
  register ULONG class;
  register USHORT code;
  register USHORT qualifier;
  register short mouse = MOVED;
  short plot_it, mode;
  short object	  = TESSERACT;
  short step_rate = 5;
  short ColorSet  = BLACK;

  struct {
    long axis1, axis2;
    short angle;
  } rot[2];

  Initialize();
  goto reset;

  for (;;) {
    if (rot[0].angle != STOPPED) {
      rotate_4d(rot[0].axis1,rot[0].axis2,rot[0].angle);
      if (rot[0].angle != step_rate  &&  rot[0].angle != THREE_SIXTY-step_rate)
	rot[0].angle = STOPPED;
      else if (rot[1].angle != STOPPED)
	rotate_4d(rot[1].axis1,rot[1].axis2,rot[1].angle);
      plot_it = TRUE;
    }
    if (plot_it) {
      plot_object(object);
      plot_it = FALSE;
    }
    if (rot[0].angle == STOPPED)
      _WaitPort(window->UserPort);
    while (msg = _GetMsg(window->UserPort)) {
      class  = msg->Class;   code   = msg->Code;    qualifier = msg->Qualifier;
      _ReplyMsg(msg);
      if (class == MOUSEMOVE) {
	mouse = MOVED;
      } else if (class == RAWKEY) {
	switch (code) {
	  case TKEY:
	    object = TESSERACT;  plot_it = TRUE;
	    break;
	  case OKEY:
	    object = OCTAHEDRON;  plot_it = TRUE;
	    break;
	  case PKEY:
	    object = PYRAMID;  plot_it = TRUE;
	    break;
	  case RKEY:
	    mode = REAL;
	    break;
	  case IKEY:
	    mode = IMAGINARY;
	    break;
	  case DKEY:
	    if (qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)) {
	      rot[1].angle = STOPPED;
	    } else {
	      rot[1] = rot[0];
	      rot[0].angle = STOPPED;
	    }
	    break;
	  case XKEY:
reset:
	    mode = REAL;
	    rot[0].axis1 = X;  rot[0].axis2 = Z;  rot[0].angle = RESTART;
	    rot[1].angle = STOPPED;
	    plot_it = TRUE;
	    break;
	  case CKEY:
	    if (qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)) {
	      ColorRequester(ColorSet);
	      plot_it = TRUE;
	    } else {
	      if (ColorSet = 1 - ColorSet)
		SetRGB4(vp,17L,0L,0L,0L);
	      else
		SetRGB4(vp,17L,15L,15L,15L);
	      LoadRGB4(vp, &ColorTables[ColorSet][0], 1L<<PLANES);
	    }
	    break;
	  case SPACE:
	    rot[0].angle = STOPPED;
	    break;
	  case KP4:
	  case KP6:
	    rot[0].axis1 = X;  rot[0].axis2 = (mode) ? Z : W;
	    goto calc_angle;
	  case KP8:
	  case KP2:
	    rot[0].axis1 = Y;  rot[0].axis2 = (mode) ? Z : W;
	    goto calc_angle;
	  case LEFTARROW:
	  case RIGHTARROW:
	    mode = REAL;  rot[0].axis1 = X;  rot[0].axis2 = Y;
	    goto calc_angle;
	  case UPARROW:
	  case DOWNARROW:
	    mode = IMAGINARY;  rot[0].axis1 = Z;  rot[0].axis2 = W;
calc_angle:
	    if (qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
	      rot[0].angle = TEN_DEGREES;
	    else if (qualifier & IEQUALIFIER_CONTROL)
	      rot[0].angle = NINETY_DEGREES;
	    else
	      rot[0].angle = step_rate;
	    if (code == KP8  ||  code == RIGHTARROW  ||
		code == KP6  ||  code == UPARROW)
	      rot[0].angle = THREE_SIXTY - rot[0].angle;
	    break;
	  case ESCAPE:
	    cleanup(0L);
	  case HELP:
	    LoadRGB4(vp, &ColorTables[0][0], 1L<<PLANES);
	    info();
	    LoadRGB4(vp, &ColorTables[ColorSet][0], 1L<<PLANES);
	    plot_it = TRUE;
	    break;
	  default:
	    if (code >= ONE_KEY  &&  code <= ZERO_KEY) {
	      if (rot[0].angle != STOPPED)
		if (rot[0].angle == step_rate)
		  rot[0].angle = code;
		else
		  rot[0].angle = THREE_SIXTY - code;
	      step_rate = code;
	    }
	}
      }
    }
    if (mouse) {
      short x,y;
      x = (screen->MouseX*45)/41;  /* CAUTION: these factors are  */
      y = (screen->MouseY*36)/43;  /*    highly screen-dependent  */
      if (x > 719)  x = 719;
      cos_theta = (-cos[x]);  sin_theta =   sin[x];
      cos_phi	=   sin[y];   sin_phi	= (-cos[y]);
      mouse = STILL;  plot_it = TRUE;
    }
  }
}


main()
{
  long i;

  if (IntuitionBase = _OpenLibrary("intuition.library", 0L))
  if (GfxBase       = _OpenLibrary("graphics.library", 0L))
  if (MathBase      = _OpenLibrary("mathffp.library", 0L))
  if (MathTransBase = _OpenLibrary("mathtrans.library", 0L))
  if (screen = OpenScreen(&MyScreen)) {
    vp = &screen->ViewPort;
    LoadRGB4(vp,&ColorTables[0][0],1L<<PLANES);
    MyWindow.Screen = screen;
  if (window = OpenWindow(&MyWindow)) {
    rp = window->RPort;
    SetDrMd(rp,(long)JAM1);
    SetAPen(rp,WHITE);
    for (i=0; i<PLANES; i++) {
      buffer1[i] = rp->BitMap->Planes[i];
      if (!(buffer2[i] = _AllocMem(PLANESIZE,CLEARED_CHIP)))
	cleanup(101L);
    }
  if (pointer = (short *)_AllocMem(16L,CLEARED_CHIP))
    /* set a white one-pixel pointer */
    pointer[2] = 0x8000;
    SetRGB4(vp,17L,15L,15L,15L);
    SetPointer(window,pointer,1L,1L,0L,0L);
    MainLoop();   /* terminates via cleanup() */
  }
  }
  cleanup(100L);  /* no direct indication of what went wrong above */
}


cleanup(code)
long code;
{
  long i;

  if (pointer) {
    ClearPointer(window);
    _FreeMem(pointer,16L);
  }
  if (window) {
    if (rp->BitMap->Planes[0] == buffer2[0]) {
      SetBuffer();
      DisplayBuffer();
    }
    CloseWindow(window);
  }
  for (i=0; i<PLANES; i++)
    if (buffer2[i])     _FreeMem(buffer2[i],PLANESIZE);
  if (screen)           CloseScreen(screen);
  if (MathTransBase)    _CloseLibrary(MathTransBase);
  if (MathBase)         _CloseLibrary(MathBase);
  if (GfxBase)          _CloseLibrary(GfxBase);
  if (IntuitionBase)    _CloseLibrary(IntuitionBase);
  exit(code);
}


/*** Info function ***/

struct TextAttr InfoFont =
  {(UBYTE *)"topaz.font",8,FS_NORMAL,FPF_ROMFONT|FPF_DESIGNED};
struct IntuiText Text = {1,0,JAM2,0,0,&InfoFont,NULL,NULL};

#define TEXT_MARGIN	14L
#define TEXT_START	22L

char *InfoTitle  =
	" Tesseract v1.00    \2511991, Jerry D. Hedden, All Rights Reserved";
char *InfoText[] = {
"You are viewing the three-dimensional projection of a four-dimensional object.",
"You can select amoung the objects supported by this program by pressing 't'",
"for the tesseract (a hyper-cube), 'o' for a hyper-octahedron or 'p' for a",
"hyper-tetrahedron (think [incorrectly] of a pyramid).  To quit, press 'esc'.",
"",
"With the mouse pointer in the center of the screen, the x-axis is horizontal",
"to the screen, y is vertical, and z extends outward.  (The fourth-dimensional",
"w-axis cannot be 'seen', of course.)  The mouse moves these axes; allowing",
"views of the object from any angle.",
"",
"You can rotate the object in four-dimensional space using the arrow and keypad",
"keys.  Rotations in the x-y, x-z and y-z planes are referred to as REAL",
"because they result in rotations of the object in 3D.  For the x-w, y-w and",
"z-w planes, rotations are IMAGINARY; occuring only in four-dimensional space.",
"The keypad 8, 2, 4 and 6 represent the up, down, left and right directions.",
"Pressing 'r' sets the keypad to perform REAL rotations on the object; 'i' sets",
"IMAGINARY rotations.  These correspond to rotations in the x-z plane (REAL",
"4/6), y-z plane (REAL 8/2), x-w plane (IMAGINARY 4/6) and y-w plane (IMAGINARY",
"8/2).  The left/right arrows perform REAL rotations counterclockwise/clockwise",
"in x-y plane.  The up/down arrows perform IMAGINARY rotations in the z-w plane.",
"",
"Pressing an arrow/keypad key alone causes its action to repeat continuously.",
"Press 'space' to stop.  The change between frames can be set from 0.5\260 to 5\260",
"using the keyboard 1 thru 0.  'Shift-' arrow/keypad keys cause a single 10\260",
"change; 'ctrl-' causes a 90\260 change.",
"",
"The object can be rotated in two planes at once.  Set up a rotation, either",
"REAL or IMAGINARY and at the desired rate, and press 'd' (for dual).  The",
"object will stop which indicates that the settings have been entered for the",
"dual rotation.  Subsequent continuous rotations of the object will have the",
"dual superimposed on it.  'Shift-d' will erase the dual rotation setting.",
"",
"Pressing 'x' will reset the object to its initial position in 4D, and cancel",
"all rotations.",
"",
"Press 'c' to toggle between two color sets (the 'white' set is intended for",
"printing).  'Shift-c' brings up a color requester.",
"",
"The Tesseract program's source code is a combination of 'C' and assembly",
"using Manx Aztec 'C' v3.6a.  The source contains examples of double-",
"buffering, fixed- and floating-point math, and in-line assembly code.  I also",
"used some of the code from Jonathan Potter's color requester.  Send $5.00 to",
"obtain a disk containing source code and updated program.",
"",
"        Comments,                        Jerry D. Hedden",
"        Questions,          \\\\           28 Windemere Dr.",
"        Bug reports,         >>>>>>      Sicklerville, NJ  08081",
"        Source requests,    //           Phone:  (609)629-3975",
"        etc.                             Email:  Hedden@ESDSDF.DNet.GE.com",
NULL
};


info()
{
  register long line;
  register struct Window *info_window;
  register struct IntuiMessage *msg;
  register ULONG class;
  register short all_done;

  MyWindow.IDCMPFlags = CLOSEWINDOW;
  MyWindow.Flags = SIMPLE_REFRESH|NOCAREREFRESH|ACTIVATE|WINDOWCLOSE;
  MyWindow.Title = (UBYTE *)InfoTitle;

  if (!(info_window = OpenWindow(&MyWindow)))   return;

  for (line = 0; Text.IText = (UBYTE *)InfoText[line]; line++)
    PrintIText(info_window->RPort,&Text,TEXT_MARGIN,TEXT_START+(line<<3));

  for (all_done=0; !all_done; ) {
    _WaitPort(info_window->UserPort);
    while (msg = _GetMsg(info_window->UserPort)) {
      class = msg->Class;
      _ReplyMsg(msg);
      if (class == CLOSEWINDOW)  all_done = 1;
    }
  }
  CloseWindow(info_window);
}


/*** common requester stuff ***/

#define RESET	41
#define CANCEL	42
#define OKAY	43

short border_xy[] = {0,0, 115,0, 115,10, 0,10, 0,0};
struct Border MyBorder = {-1,-1,1,0,JAM1,5,border_xy,NULL};

struct IntuiText
  OkayText   = {1,0,JAM1, 41,1,NULL,(UBYTE *)"OKAY",  NULL},
  ResetText  = {1,0,JAM1, 37,1,NULL,(UBYTE *)"RESET", NULL},
  CancelText = {1,0,JAM1, 33,1,NULL,(UBYTE *)"CANCEL",NULL};

struct Image PropImage1, PropImage2, PropImage3;

struct Window *ReqWindow;


/*** Color Requester ***/

#define RED	31
#define GREEN	32
#define BLUE	33

struct IntuiText
  RedText    = {1,0,JAM1,-14,2,NULL,(UBYTE *)"R",NULL},
  GreenText  = {1,0,JAM1,-14,2,NULL,(UBYTE *)"G",NULL},
  BlueText   = {1,0,JAM1,-14,2,NULL,(UBYTE *)"B",NULL},
  ValuesText = {0,1,JAM2,  0,0,NULL,(UBYTE *)"RGB",NULL};

struct PropInfo
  RedProp   = {AUTOKNOB|FREEHORIZ,0,0,0x1000},
  GreenProp = {AUTOKNOB|FREEHORIZ,0,0,0x1000},
  BlueProp  = {AUTOKNOB|FREEHORIZ,0,0,0x1000};

struct Image CurColorImage = {0,0,235,8,1,NULL,0,1,NULL};

struct Gadget
  RedCGadg = {NULL,24,54,100,11,
    GADGHNONE|GADGIMAGE,RELVERIFY|GADGIMMEDIATE,PROPGADGET,
    (APTR)&PropImage1,NULL,&RedText,NULL,(APTR)&RedProp,RED,NULL},
  GreenCGadg = {&RedCGadg,24,66,100,11,
    GADGHNONE|GADGIMAGE,RELVERIFY|GADGIMMEDIATE,PROPGADGET,
    (APTR)&PropImage2,NULL,&GreenText,NULL,(APTR)&GreenProp,GREEN,NULL},
  BlueCGadg = {&GreenCGadg,24,78,100,11,
    GADGHNONE|GADGIMAGE,RELVERIFY|GADGIMMEDIATE,PROPGADGET,
    (APTR)&PropImage3,NULL,&BlueText,NULL,(APTR)&BlueProp,BLUE,NULL},
  OkayCGadg = {&BlueCGadg,130,79,114,9,GADGHCOMP,RELVERIFY,BOOLGADGET,
    (APTR)&MyBorder,NULL,&OkayText,NULL,NULL,OKAY,NULL},
  ResetCGadg = {&OkayCGadg,130,67,114,9,GADGHCOMP,RELVERIFY,BOOLGADGET,
    (APTR)&MyBorder,NULL,&ResetText,NULL,NULL,RESET,NULL},
  CancelCGadg = {&ResetCGadg,130,55,114,9,GADGHCOMP,RELVERIFY,BOOLGADGET,
    (APTR)&MyBorder,NULL,&CancelText,NULL,NULL,CANCEL,NULL};

struct NewWindow ColorNewWindow={
  40,23,255,93,0,1,GADGETUP|GADGETDOWN|MOUSEBUTTONS|RAWKEY,
  ACTIVATE|SIMPLE_REFRESH|NOCAREREFRESH|RMBTRAP,
  &CancelCGadg,NULL,(UBYTE *)" Tesseract's Color Requester",
  NULL,NULL,0,0,0,0,CUSTOMSCREEN};

long CurColor;


ColorRequester(ColorSet)
short ColorSet;
{
  register long i;
  register ULONG Class;
  register USHORT Code, GadgetID;
  register struct IntuiMessage *msg;

  ColorNewWindow.Screen = screen;
  if (!(ReqWindow = OpenWindow(&ColorNewWindow)))  return;

  for (i=0; i < (1<<PLANES); i++)
    ColorTables[2][i] = ColorTables[ColorSet][i];

  DrawPalette(PLANES);
  CurColorImage.PlaneOnOff = CurColor = 1;
  ValuesText.FrontPen = CurColor^((1<<PLANES)-1);
  ValuesText.BackPen = 1;
  DrawImage(ReqWindow->RPort,&CurColorImage,10L,45L);
  SetRGBProps();

  FOREVER {
    _WaitPort(ReqWindow->UserPort);
    while (msg = _GetMsg(ReqWindow->UserPort)) {
      Class = msg->Class;  Code = msg->Code;
      if (Class == GADGETUP  ||  Class == GADGETDOWN)
	GadgetID = ((struct Gadget *)msg->IAddress)->GadgetID;
      _ReplyMsg(msg);
      switch (Class) {
	case MOUSEBUTTONS: {
	  long x,y;
	  long NewColor, ReadPixel();
	  if (Code != SELECTDOWN)  break;
	  x=ReqWindow->MouseX;	y=ReqWindow->MouseY;
	  if (x<12 || x>243 || y<15 || y>40)  break;
	  if (CurColor == (NewColor = ReadPixel(ReqWindow->RPort,x,y)))  break;
	  CurColorImage.PlaneOnOff = CurColor = NewColor;
	  DrawImage(ReqWindow->RPort,&CurColorImage,10L,45L);
	  SetRGBProps();
	  break;
	  }
	case GADGETDOWN:
	  if (GadgetID >= RED  &&  GadgetID <= BLUE) {
	    do {
	      ReadRGBProps();
	    } while (!(msg = _GetMsg(ReqWindow->UserPort)));
	    _ReplyMsg(msg);
	    ReadRGBProps();
	  }
	  break;
	case RAWKEY:
	  if (Code != ESCAPE)  break;
	  GadgetID = RESET;
	case GADGETUP:
	  switch (GadgetID) {
	    case RESET:
	      for (i=0; i < (1<<PLANES); i++)
		ColorTables[2][i] = ColorTables[ColorSet][i];
	      LoadRGB4(vp, &ColorTables[2][0], 1L<<PLANES);
	      SetRGBProps();
	      break;
	    case OKAY:
	      for (i=0; i < (1<<PLANES); i++)
		ColorTables[ColorSet][i] = ColorTables[2][i];
	    case CANCEL:
	      LoadRGB4(vp, &ColorTables[ColorSet][0], 1L<<PLANES);
	      while (msg = _GetMsg(ReqWindow->UserPort))  _ReplyMsg(msg);
	      CloseWindow(ReqWindow);
	      return;
	  }
      }
    }
  }
}


SetRGBProps()
{
  register UWORD cval = ColorTables[2][CurColor];
  NewModifyProp(&RedCGadg,  ReqWindow,0L,(long)(FREEHORIZ|AUTOKNOB),
			(long)(((cval>>8)&0xf)*0x1111),0L,0x1000L,0L,1L);
  NewModifyProp(&GreenCGadg,ReqWindow,0L,(long)(FREEHORIZ|AUTOKNOB),
			(long)(((cval>>4)&0xf)*0x1111),0L,0x1000L,0L,1L);
  NewModifyProp(&BlueCGadg, ReqWindow,0L,(long)(FREEHORIZ|AUTOKNOB),
			(long)((cval&0xf)*0x1111),0L,0x1000L,0L,1L);
  ShowRGBValues();
}


ReadRGBProps()
{
  register long re,gr,bl;

  re = RedProp.  HorizPot >> 12;
  gr = GreenProp.HorizPot >> 12;
  bl = BlueProp. HorizPot >> 12;
  ColorTables[2][CurColor] = (re<<8)|(gr<<4)|bl;
  SetRGB4(vp,CurColor,re,gr,bl);
  ShowRGBValues();
}


char *hex = "0123456789ABCDEF";

ShowRGBValues()
{
  register UWORD cval = ColorTables[2][CurColor];
  ValuesText.IText[0] = hex[(cval>>8)&0xf];
  ValuesText.IText[1] = hex[(cval>>4)&0xf];
  ValuesText.IText[2] = hex[ cval    &0xf];
  ValuesText.FrontPen = CurColor^((1<<PLANES)-1);
  ValuesText.BackPen  = CurColor;
  PrintIText(ReqWindow->RPort,&ValuesText,115L,45L);
}


DrawPalette(depth)
int depth;
{
  long w,h,ox,x,y,i;

#if PLANES>3
  if (depth==4)
    { w=29; h=12; ox=13; }
  else if (depth==5)
    { w=14; h=12; ox=17; }
  else
#endif
    { w=235/(1<<depth); h=25; ox=12; }
  i=0; x=ox; y=15;
  for (i=0; i<(1<<depth); i++) {
    SetAPen(ReqWindow->RPort,i);
    RectFill(ReqWindow->RPort,x,y,x+w,y+h);
#if PLANES>3
    if (depth>3 && i==(1<<(depth-1))-1) {
      x=ox; y+=h;
    } else
#endif
      x+=w;
  }
}


#asm
; Stubs for accessing the math libraries

	public	_MathBase

_LVOSPMul	equ	-78
_LVOSPSub	equ	-72
_LVOSPAdd	equ	-66
_LVOSPTst	equ	-48
_LVOSPFlt	equ	-36
_LVOSPFix	equ	-30

	public	_SPFix
_SPFix
	move.l	4(sp),d0
	move.l	_MathBase,a6
	jmp	_LVOSPFix(a6)

	public	_SPFlt
_SPFlt
	move.l	4(sp),d0
	move.l	_MathBase,a6
	jmp	_LVOSPFlt(a6)

	public	_SPAdd
_SPAdd
	move.l	4(sp),d1
	move.l	12(sp),d0
	move.l	_MathBase,a6
	jmp	_LVOSPAdd(a6)

	public	_SPSub
_SPSub
	move.l	4(sp),d1
	move.l	12(sp),d0
	move.l	_MathBase,a6
	jmp	_LVOSPSub(a6)

	public	_SPMul
_SPMul
	move.l	4(sp),d1
	move.l	12(sp),d0
	move.l	_MathBase,a6
	jmp	_LVOSPMul(a6)

	public	_SPTst
_SPTst
	move.l	4(sp),d1
	move.l	_MathBase,a6
	jmp	_LVOSPTst(a6)


	public	_MathTransBase
_LVOSPSincos	equ	-54
_LVOSPSqrt	equ	-96

	public	_SPSincos
_SPSincos
	move.l	4(sp),d1
	move.l	8(sp),d0
	move.l	_MathTransBase,a6
	jmp	_LVOSPSincos(a6)

	public	_SPSqrt
_SPSqrt
	move.l	4(sp),d0
	move.l	_MathTransBase,a6
	jmp	_LVOSPSqrt(a6)
#endasm
