/***********************************************************
************************************************************
**
**   When I say mackie, I thought, that I had to
**   do something alike (but without the keycontrol).
**
**   Here's hypno
**
**   HYPNO is based on Matt Dillons bezier.c and on
**   Steve Berry's lineart.c (which carries some nasty
**   bugs).
**
**   Like those it is pure PD, so use it or loose it.
**
**
**    Markus Schmidt             BIX: shimoda
**    Ludwigstr. 36
**    8510 Fuerth
**     W-GERMANY  (where the action is :-)
**
**
**    Complile as
**
**    cc +L -s +ff hypno.c
**	  ln hypno.o m32.lib c32.lib 
**
**
**    (this was edited with tabstop=4)
**
************************************************************
***********************************************************/



#include <graphics/gfx.h>
#include <intuition/intuition.h>



#define POINTS 4	/* You may increase this for bouncing polygons */

#define MAXLINES 50

short	points[MAXLINES+1][POINTS][2],
		directions[POINTS][2], 
		scales[POINTS];

int loopmode, input, scale, maxlines, ctemp, straight, Step;


#define WIDTH  640
#define HEIGHT 512
#define BORDER 10
#define XMARG  (WIDTH-BORDER)
#define YMARG  (HEIGHT-BORDER)

#define ONE   512

struct NewScreen scr = {
    0,0,   WIDTH,HEIGHT,  1,   2,1, 
    HIRES|LACE,  CUSTOMSCREEN, 
    NULL,  NULL,  NULL,  NULL 
    };

struct NewWindow mywin = {
    0,0,  WIDTH,HEIGHT,  2,1,         
    VANILLAKEY | REFRESHWINDOW | GADGETDOWN | GADGETUP | 
	    MOUSEBUTTONS | REQCLEAR | SELECTDOWN | SELECTUP,
    BORDERLESS|SMART_REFRESH|BACKDROP,
    NULL, NULL,   NULL,  NULL,  NULL,  0, 0, 8000, 8000, 
    CUSTOMSCREEN
    };

UWORD coltbl[2] = {
    0x000,              /* background color */
    0x260
};

char *parms= 
	"Hypno PD by M. Schmidt, 1989, no rights reserved\n"
	"  Possible parameters are:\n"
	"    -lnn : have a tail of n lines (must be below 50)\n"
	"    -L   : loopmode (curves in dropform, lines as triangles)\n"
	"    -c   : draw beziers instead of lines\n"
	"    -snn : nn steps for beziers (use with -c only)\n"
	"    -n   : be nice to the multitasking-environment\n"
	"    aa-bb: bounce with speed between aa and aa+bb\n"
    "  \n"
    "  Try: hypno -l30 -L 3-10  or hypno -c -s10"
	;


LONG GfxBase, IntuitionBase;

struct Window *win;
struct RastPort *rp;
struct ViewPort *vp;
struct Screen *scrptr;

long Enable_Abort = 0;
long _stack = 10000, _priority = -11, _BackGroundIO = 1;
char *_procname = "Line-Art";
double ran();

main(argc,argv)
int argc;
char *argv[];
{
    int i, count, nice, boxes;
    register int offset, newoffset, tempx, tempy;
    struct IntuiMsg *msg;

    input = 2;          /* Some defaults for switches */
    scale = 5;          /* Some defaults for switches */
    loopmode=  0;  
    straight = -1;  
    Step = 25;  
    maxlines = 10;
    nice = FALSE;
    boxes = FALSE;

    while(--argc >= 1) {
        switch (argv[argc][0]) {
            case '?':
				puts(parms);
                exit(0);

            case '-':
                switch (argv[argc][1]) {
                    case 'n':
                        nice = TRUE;
                        break;
                    case 'L':
                        loopmode = TRUE;
                        break;
                    case 'l':
                        sscanf(&argv[argc][2],"%d",&maxlines);
                        maxlines = (maxlines > MAXLINES) ? 
									MAXLINES : (maxlines < 1) ? 1 : maxlines;
                        break;
                    case 'c':
						straight= 0;
                        break;
                    case 's':
                        sscanf(&argv[argc][2],"%d",&Step);
                        Step = ONE/Step;
                        break;
                    case 'b':
                        boxes = TRUE;
                        break;
                    default:
						puts(parms);
                        exit(0);
                }
                break;
            default:
                sscanf(argv[argc],"%d-%d",&input,&scale);
                break;
        }
    }

    input = (input > 9) ? 9 : (input < 0) ? 1 : input;

    openstuff();

    Enable_Abort = 1;
    for(i=0;i<VBeamPos();i++)
        get_new_point();


	for (i=0; i<POINTS; i++) {
	    points[0][i][0] = get_new_point();
	    points[0][i][1] = get_new_point();

        directions[i][0] = get_new_dir();
        directions[i][1] = get_new_dir();

		scales[i]= 2;
    }

    offset = 0;
    count = 0;

    rp = win->RPort;
    SetAPen(rp, 1L);

    while(1){

        Chk_Abort();

        if (nice)
            WaitTOF();

        msg = GetMsg(win->UserPort);
        if (msg == NULL) {

			/* Draw ito */
			DrawIt(offset);

            /* Erase the eldest line */
            if (++count > maxlines) {
				int last= (offset==maxlines) ? 0 : offset +1;

                SetAPen(rp, 0L);
		        DrawIt(last);
                SetAPen(rp, 1L);
            }

			/* Bounce'o-matic */
			for (i=0; i<POINTS; i++) {
				int doit= 0;

				if (points[offset][i][0] < BORDER) {
					directions[i][0]= doit= 1;
					directions[i][1]= get_new_dir();
				}
				else
				if (points[offset][i][0] > XMARG) {
					directions[i][0]= doit= -1;
					directions[i][1]= get_new_dir();
				}

				if (points[offset][i][1] < BORDER) {
					directions[i][1]= doit= 1;
					directions[i][0]= get_new_dir();
				}
				else
				if (points[offset][i][1] > YMARG) {
					directions[i][1]= doit= -1;
					directions[i][0]= get_new_dir();
				}

				if (doit)
	                scales[i]= get_new_scale();
			}

			/* Go to the next and ... */
            if (++offset > maxlines){
				offset= 0;

				/* Change colors now and then */
	            {
					int r,g,b;
					static rd= 1, gd= 1,bd= 1;
					static rs= 2, gs= 3,bs= 1;

					r= (coltbl[1]&0xf00) >> 8;
					g= (coltbl[1]&0x0f0) >> 4;
					b= (coltbl[1]&0x00f) >> 0;

					r+= rs*rd; g+= gs*gd; b+= bs*bd;

					if (r>15 || r<2)  { rd= -rd; r+= rs*rd; rs= ran_dom(2)+1; }
					if (g>15 || g<2)  { gd= -gd; g+= gs*gd; gs= ran_dom(2)+1; }
					if (b>15 || b<2)  { bd= -bd; b+= bs*bd; bs= ran_dom(2)+1; }

					coltbl[1]=  r<<8 | g<<4 | b;

	                LoadRGB4(vp,coltbl,2L);
	            }
			}


            /* Calculate the new points */
			for (i=0; i<POINTS; i++) {
            	int last= (offset > 0) ? offset-1 : maxlines;

	            points[offset][i][0]= 
						scales[i]*directions[i][0] + points[last][i][0];

	            points[offset][i][1]= 
						scales[i]*directions[i][1] + points[last][i][1];
			}

			if (loopmode) {
				points[offset][POINTS-1][0]=  points[offset][0][0];
				points[offset][POINTS-1][1]=  points[offset][0][1];
			}

        }
        else {
            ReplyMsg(msg);
            msg = GetMsg(win->UserPort);            
            while (msg != NULL) {
                ReplyMsg(msg);
                msg = GetMsg(win->UserPort);            
            }
            break;
        }
    }
_abort:
    CloseWindow(win);
    CloseScreen(scrptr);
    CloseLibrary(GfxBase);
    CloseLibrary(IntuitionBase);
}

openstuff()
{
    GfxBase = OpenLibrary("graphics.library",0L);
    if (GfxBase == NULL) {
        exit(100);
    }

    IntuitionBase = OpenLibrary("intuition.library",0L);
    if (IntuitionBase == NULL) {
        exit(50);
    }

    scrptr = OpenScreen(&scr);
    if (scrptr == NULL) {
        exit(30L);
    }

    mywin.Screen = scrptr;
    win = OpenWindow(&mywin);
    if (win == NULL) {
        CloseScreen(scrptr);
        exit(20);
    }
    
    ShowTitle(scrptr, FALSE);
    vp = &scrptr->ViewPort;

    LoadRGB4(vp,coltbl,2L);

}

/*
dobox(offset)
register int offset;
{
    register int tempx, tempy;

    tempx = WIDTH - point1[offset][0];
    tempy = HEIGHT - point1[offset][1];
    Draw(rp, tempx, tempy);

    tempx = WIDTH - point2[offset][0];
    tempy = HEIGHT - point2[offset][1];
    Draw(rp, tempx, tempy);

    Draw(rp, point1[offset][0], point1[offset][1]);
}
*/


int 
ran_dom(x)
{
	register unsigned short tmp;
	float f= ran();

	tmp= f*x;

	return(tmp);
}


int 
get_new_point()
{
    register short temp;

    temp = ran_dom(200)+20;

    return temp;
}

int 
get_new_dir()
{
    register short num;

    num = ran_dom(5)-2;

	if (num<0)
		num= -1;
	else
	if (num>0)
		num= 1;

    return (num);
}

int 
get_new_scale()
{
    register short temp;

    temp = ran_dom(scale)+input;

    return temp;
}

int 
DrawIt(offset)
{
	register i;

	if (straight) {
	    Move(rp, points[offset][0][0], points[offset][0][1]);
		for (i=1; i<POINTS; i++)
		     Draw(rp, points[offset][i][0], points[offset][i][1]);
	}
	else {
		drawcurve(points[offset]);
	}
}



/*  Matth. Dillons drawcurve-function */
/*
 *  So I can use integer arithmatic, I am defining 512 as 1 (as far
 *  as the mathematics go), which means that I must divide any power
 *  multiplication by 512^(n-1).  E.G. .5^2 = .25 ... to make 256^2
 *  equal 128, I must divide by 512^1
 */

#define     SHIFTS  9
#define     ONE     (1<<SHIFTS)
#define S10(x)	 ((x) >> SHIFTS)
#define S20(x)	 ((x) >> (2*SHIFTS))


drawcurve(a)
register short a[4][2];
{
    long  m1[4];	/* t matrix		    */
    long  mr[4];	/* partial result matrix    */
    long  corr[2];
    register short t, i;
    char  lastpt = 0;


    Move(rp, a[0][0], a[0][1]);
    for (t = 0; t <= ONE; t += Step) {	   /*  t = 0 to 1      */
oncemore:
		m1[3] = ONE;
		m1[2] = t;
		m1[1] = t * t;
		m1[0] = m1[1] * t;

		mr[0] = -S20(m1[0]  ) + S10(3*m1[1]) - 3*m1[2] + m1[3];
		mr[1] =  S20(3*m1[0]) - S10(6*m1[1]) + 3*m1[2];
		mr[2] = -S20(3*m1[0]) + S10(3*m1[1]);
		mr[3] =  S20(m1[0]  );
		for (i = 0; i < 2; ++i) {
		    corr[i] = (mr[0] * a[0][i] + mr[1] * a[1][i] +
			      mr[2] * a[2][i] + mr[3] * a[3][i]) >> SHIFTS;
		}
		Draw(rp, corr[0], corr[1]);
    }

    if (lastpt == 0 && t - Step < ONE) {
		lastpt = 1;
		t = ONE;
		goto oncemore;
    }

	if (!loopmode)
	    Draw(rp, a[0][0], a[0][1]);
}

