/*
    ART_VektorView.hm
    The VectorView is a simple customView object for rough presantation
    of vectors produced by ART2 for animation.

    - simple line graph for real arrays
    - simple 2D plot of the inputarray with grayshading mag
    - simple bar graph to demonstrate the choice
*/

#import <appkit/appkit.h>
#import <appkit/View.h>
#import "ART_VektorView.h"

@implementation ART_VektorView:View
/*
    float *points,*merks;
    int numPoints,numMerks;
    float bbox[4];
    char *ops;
    float *gshades;
    float lineGray;
    float backgroundGray;
    int vec_mode;
    int bar_sel;
*/

- initFrame:(NXRect *)aRect
{
    [super initFrame:aRect];
    lineGray = NX_BLACK;
    backgroundGray = NX_LTGRAY;
    vec_mode =1; 
    return self;
}

- free
{
    NXZoneFree([self zone], points);
    NXZoneFree([self zone], ops);
    NXZoneFree([self zone], merks);
    NXZoneFree([self zone], gshades);
    return [super free];
}

// num is numbers of Vec max is the maximum magnitude
- setVector:(int)num Vec:(float *)Vec max:(float)max
{
    float *f;
    int i;
    char *op;
    NXZone *zone;
    int segPoints;

    vec_mode=1;
    zone = [self zone];

    NXZoneFree(zone, points);
    NXZoneFree(zone, ops);

    numPoints = num;
    points = NXZoneMalloc(zone, 2 * num * sizeof(float));
    ops = NXZoneMalloc(zone, num * sizeof(char));
    numPoints = 0;
    segPoints = 0;
    for (f = points, op = ops, i = 0; i<num; i++ ) {
	if ( *Vec > max ) {
	    if (segPoints == 1) {
		*f++ = f[-2];
		*f++ = f[-2];
		*op++ = dps_lineto;
		numPoints++;
	    }
	    segPoints = 0;
	    Vec++;
	} else {
	    *f++ = (float)i;
	    *f++ = *Vec++;
	    if (segPoints == 0)
		*op++ = dps_moveto;
	    else
		*op++ = dps_lineto;
	    segPoints++;
	    numPoints++;
	}
    }
    bbox[0] = 0;
    bbox[2] = num-1;
    bbox[1] = -0.0001;
    bbox[3] = max;
    return self;
}

// num is numbers of Vec max is the maximum magnitude
- setPattern:(float *)Vec row:(int)row col:(int)col
{
    NXRect *f;
    int r,c;
    float *gs,maxgray=0.0;
    NXZone *zone;
    NXRect rect;

    vec_mode=0;
    zone = [self zone];
    NXZoneFree(zone, merks);
    NXZoneFree(zone, gshades);

    numMerks = row*col;
    merks  = NXZoneMalloc(zone, numMerks * sizeof(NXRect));
    gshades = NXZoneMalloc(zone, numMerks * sizeof(float));

    f=merks;
    gs=gshades;

    for (r=numMerks;r--;)
      maxgray=( Vec[r]>maxgray) ? Vec[r]:maxgray;
    maxgray=(maxgray==0.0) ? 1.0:maxgray;
    rect.size.width=rect.size.height=0.975;
    for (r=row;r--;) {
      rect.origin.y=(float)(r);
      for (c=0;c<col;c++) {
        rect.origin.x=(float)c;
        *f++=rect;
        *gs++=1-*Vec++/maxgray;
        }
      }

    bbox[0] = 0;
    bbox[2] = col;
    bbox[1] = 0;
    bbox[3] = row;
    return self;
}
- setBar:(int)num Vec:(float *)Vec max:(float)max select:(int)s
{
    float *f;
    int i;
    char *op;
    NXZone *zone;
    int segPoints;

    vec_mode=3;
    zone = [self zone];
 //   bar_sel=s;

    NXZoneFree(zone, points);
    NXZoneFree(zone, ops);

    numPoints = 2*num;
    points = NXZoneMalloc(zone, 4 * num * sizeof(float));
    ops = NXZoneMalloc(zone, 2*num * sizeof(char));
    numPoints = 0;
    segPoints = 0;
    for (f = points, op = ops, i = 0; i<num; i++ ) {
      *f++ = (float)i;
      *f++ = 0.0;
      *op++ = dps_moveto;
      *f++ = (float)i;
      *f++ = *Vec++;
      *op++ = dps_lineto;
    }
    bbox[0] = 0;
    bbox[2] = num+1;
    bbox[1] = -0.0001;
    bbox[3] = max;
    return self;
}

- sizeTo:(NXCoord)width :(NXCoord)height
{
    NXPoint center;
    center.x = NX_X(&bounds) + NX_WIDTH(&bounds) / 2;
    center.y = NX_Y(&bounds) + NX_HEIGHT(&bounds) / 2;
    [super sizeTo:width :height];
    [self setDrawOrigin:center.x - NX_WIDTH(&bounds) / 2
			:center.y - NX_HEIGHT(&bounds) / 2];
    [self scaleToFit]; 
    return self;
}

- scaleToFit
{
    float scaleX,scaleY;
    float bbWidth = bbox[2] - bbox[0];
    float bbHeight = bbox[3] - bbox[1];

    if (vec_mode) {
      scaleY = NX_HEIGHT(&frame) / bbHeight * 0.95;
      scaleX = NX_WIDTH(&frame) / bbWidth * 0.95;
      }
    else {
      scaleY=scaleX=MIN(NX_HEIGHT(&frame)/bbHeight,
                        NX_WIDTH(&frame)/bbWidth)*0.95;
      }

    [self setDrawSize:NX_WIDTH(&frame)/scaleX :NX_HEIGHT(&frame)/scaleY];
    [self setDrawOrigin:bbox[0]-(NX_WIDTH(&bounds)-bbWidth)/2
		       :bbox[1]-(NX_HEIGHT(&bounds)-bbHeight)/2];
    return self;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
    int i;
    NXRect *R;
    float *gs;
    switch (vec_mode) {
      case 1:

        PSsetgray(backgroundGray);
        NXRectFill(&bounds);
        if (points && numPoints > 0) {
	  PSnewpath();
	  PSsetlinewidth(NXDrawingStatus == NX_DRAWING ? 0.0 : 1.0);
	  PSsetgray(lineGray);
	  if (NXDrawingStatus == NX_DRAWING)
	    /* stroke the userpath */
	    DPSDoUserPath(points, numPoints * 2, dps_float, ops, numPoints,
							bbox, dps_ustroke);

	  else
	    /* append the userpath to the current path, but dont stroke yet */
	    DPSDoUserPath(points, numPoints * 2, dps_float, ops, numPoints,
							bbox, dps_uappend);
          }
        break;
      case 0:
        PSsetgray(backgroundGray);
        NXRectFill(&bounds);
        if (merks && numMerks > 0)
          for (i=numMerks,R=merks,gs=gshades;i--;) {
            PSsetgray(*gs++);
            NXRectFill(R++);
            }
        break;
      case 2:
        PSsetgray(backgroundGray);
        NXRectFill(&bounds);
        if (points && numPoints > 0) {
	  PSnewpath();
	  PSsetlinewidth(NXDrawingStatus == NX_DRAWING ? 0.0 : 1.0);
	  PSsetgray(lineGray);
	  if (NXDrawingStatus == NX_DRAWING)
	    /* stroke the userpath */
	    DPSDoUserPath(points, numPoints * 2, dps_float, ops, numPoints,
							bbox, dps_ustroke);

	  else
	    /* append the userpath to the current path, but dont stroke yet */
	    DPSDoUserPath(points, numPoints * 2, dps_float, ops, numPoints,
							bbox, dps_uappend);
          }
        break;
      }
    return self;
}

- setBackgroundGray:(float)gray
{
    backgroundGray = gray;
    return self;
}

- setLineGray:(float)gray
{
    lineGray = gray;
    return self;
}


- (float)lineGray
{
    return lineGray;
}

- (float)backgroundGray
{
    return backgroundGray;
}

@end
