home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-09-24 | 52.9 KB | 1,670 lines |
- //////////////////////////////////////////////////////////////
- //
- // EnvelopeView.m -- Implementation for the EnvelopeView class
- // Copyright 1991-94 Fernando Lopez Lezcano All Rights Reserved
- //
- //////////////////////////////////////////////////////////////
-
- // Revision History:
-
- // 8/25/92 Add parsing of exponential format numbers
- // 8/21/92 Memory allocation bug on allocateTemp and allocateDraw
- // 8/20/92 Added drawSegments option.
-
- #import "EnvelopeView.h"
- #import "Controller.h"
-
- #import <stdlib.h>
- #import <ctype.h>
- #import <objc/zone.h>
- #import <defaults/defaults.h>
- #import <appkit/Application.h>
- #import <musickit/musickit.h> // for envelope class stuff
- #import <dpsclient/wraps.h> // for PSxxx stuff
- #import <appkit/Form.h>
- #import <appkit/Button.h>
- #import <appkit/Panel.h>
- #import <appkit/NXCursor.h>
- #import <appkit/NXImage.h>
-
- #import <appkit/Pasteboard.h>
-
- #define STYLE_MK '0'
- #define STYLE_pX_Yp '1'
- #define STYLE_pXcYp '2'
- #define STYLE_X_Y '3'
- #define STYLE_XcY '4'
- #define STYLE_X_Y_Zc '5'
- #define STYLE_XcYcZ_ '6'
- #define STYLE_ppX_Yp '7'
-
- #define TEST 1
-
- #define KNOBSIZE 8
- #define MAXNUMSEGS 8
-
- #define WIDTH bounds.size.width
- #define HEIGHT bounds.size.height
-
- #define WHITE 1.0
- #define BLACK 0.0
- #define DKGRAY (1.0/3.0)
- #define LTGRAY (2.0/3.0)
- #define TRANSP 2.0
-
- #define DRAG_MASK (NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK)
-
- //===================================================================
- // Envelope auxiliar routines and macros
- //===================================================================
-
- //-------------------------------------------------------------------
- // Draw a knob in the display
-
- #define DRAWKNOB(x,y) PScompositerect(x-KNOBSIZE/2,y-KNOBSIZE/2,KNOBSIZE, KNOBSIZE,NX_SOVER)
-
- //-------------------------------------------------------------------
- // Translate from x/y envelope values to display pixel coordinates
-
- #define xToPix(x) (((x-xMin)/(xMax-xMin))*WIDTH)
- #define yToPix(y) (((y-yMin)/(yMax-yMin))*HEIGHT)
-
- //-------------------------------------------------------------------
- // Translate from display pixel coordinates to x/y envelope values
-
- #define pixToX(xPix) (xPix/WIDTH*(xMax-xMin)+xMin)
- #define pixToY(yPix) (yPix/HEIGHT*(yMax-yMin)+yMin)
-
- //-------------------------------------------------------------------
- // Shorthand to access envelope object instance variables
-
- #define pointCount [theEnvelope pointCount]
- #define stickyPoint [theEnvelope stickPoint]
- #define xValues [theEnvelope xArray]
- #define yValues [theEnvelope yArray]
- #define sValues [theEnvelope smoothingArray]
-
- //-------------------------------------------------------------------
- // Temporary copy arrays for envelope copy operations
-
- typedef struct _env {
- double *x; // x array for adding or removing points
- double *y; // y array
- double *s; // smoothing array
- int max; // currently allocated length of arrays
- } Env;
-
- static Env * temp=NULL; // only one copy for each envelope object
-
- // Allocate or reallocate arrays to "size" elements
-
- void allocateTemp(int size)
- {
- int newsize=size;
- if (newsize<64) newsize=64;
- if (temp==NULL) { // initial creation of arrays
- temp=malloc(sizeof(Env));
- temp->x=malloc(sizeof(double)*newsize);
- temp->y=malloc(sizeof(double)*newsize);
- temp->s=malloc(sizeof(double)*newsize);
- temp->max=newsize;
- }
- else if (size>temp->max) { // grow to double size each time
- if (size>temp->max*2)
- temp->max=size;
- else
- temp->max*=2;
- temp->x=realloc(temp->x,sizeof(double)*temp->max);
- temp->y=realloc(temp->y,sizeof(double)*temp->max);
- temp->s=realloc(temp->s,sizeof(double)*temp->max);
- }
- }
-
- // Free allocated memory
-
- void freeTemp()
- {
- if (temp!=NULL) {
- free(temp->x);
- free(temp->y);
- free(temp->s);
- free(temp);
- temp=NULL;
- }
- }
-
- //-------------------------------------------------------------------
- // Temporary arrays for drawing operations
-
- typedef struct _draw {
- int num; // number of points in draw arrays (x,y,p)
- int *p; // point number array
- double *x; // x array of drawing coordinates
- double *y; // y array
- double *yr; // real y coordinate of each point
- int max; // currently allocated length
- } Draw;
-
- // Each time the envelope is drawn in the screen the calculated positions
- // of all intermediate points are stored in the x and y arrays. The point
- // number is also stored in the p array. This arrays are then used to erase
- // the lines by drawing them in white.
-
- static Draw * draw=NULL;
-
- // Allocate or reallocate arrays to "size" elements
-
- void allocateDraw(int size)
- {
- int newsize=size;
- if (newsize<64) newsize=64;
- if (draw==NULL) {
- draw=malloc(sizeof(Draw));
- draw->x=malloc(sizeof(double)*newsize*MAXNUMSEGS);
- draw->y=malloc(sizeof(double)*newsize*MAXNUMSEGS);
- draw->p=malloc(sizeof(int)*newsize*MAXNUMSEGS);
- draw->yr=malloc(sizeof(double)*newsize);
- draw->max=newsize;
- draw->num=0;
- }
- else if (size>draw->max) {
- if (size>draw->max*2)
- draw->max=size;
- else
- draw->max*=2;
- draw->x=realloc(draw->x,sizeof(double)*draw->max*MAXNUMSEGS);
- draw->y=realloc(draw->y,sizeof(double)*draw->max*MAXNUMSEGS);
- draw->p=realloc(draw->p,sizeof(int)*draw->max*MAXNUMSEGS);
- draw->yr=realloc(draw->yr,sizeof(double)*draw->max);
- }
- }
-
- // Free allocated memory
-
- void freeDraw()
- {
- if (draw!=NULL) {
- free(draw->x);
- free(draw->y);
- free(draw->p);
- free(draw->yr);
- free(draw);
- draw=NULL;
- }
- }
-
- //===================================================================
- @implementation EnvelopeView
- //===================================================================
-
- //-------------------------------------------------------------------
- // - resetCursorRects sets a new cursor of the view. The message first
- // initializes the two cursor shapes if needed and the proceeds to
- // set the default cursor for the view to a cross.
-
- - resetCursorRects
- {
- NXRect visible;
- NXPoint spot;
-
- if (!theCross) {
- theCross=[NXCursor newFromImage:[NXImage newFromSection:"cross.tiff"]];
- spot.x = 7.0;
- spot.y = 7.0;
- [theCross setHotSpot:&spot];
- }
- if (!theFilledCross) {
- theFilledCross=[NXCursor newFromImage:[NXImage newFromSection:"crossfill.tiff"]];
- spot.x = 7.0;
- spot.y = 7.0;
- [theFilledCross setHotSpot:&spot];
- }
- if ([self getVisibleRect:&visible]) {
- [self addCursorRect:&visible cursor: theCross];
- }
- return self;
- }
-
- //-------------------------------------------------------------------
- // - selectPoint:n
- // Selects point number n in the envelope and broadcasts the change
- // to the controller object.
-
- - selectPoint:(int)n
- {
- selected=n;
- [self display];
- [theController update: self];
- return self;
- }
-
- //-------------------------------------------------------------------
- // - initFrame: -- initialize a new Envelope View object
-
- NXDefaultsVector envelopeDefaults={
- {"defaultSmoothing", "1.0" },
- {"showSmoothing", "YES" },
- {"drawSegments", "YES" },
- };
-
- - initFrame:(const NXRect *)frameRect
- {
- static double xs[]={0.0,1.0};
- static double ys[]={0.0,0.0};
- static double ss[]={1.0,1.0};
-
- malloc_debug(0xFFFF);
-
- defaultSmooth=atof(NXGetDefaultValue("EnvelopeEd","defaultSmoothing"));
- ss[0]=ss[1]=defaultSmooth;
- self = [super initFrame: frameRect];
-
- theEnvelope=[[[Envelope alloc] init]
- setPointCount: 2
- xArray: xs
- orSamplingPeriod: 1.0
- yArray: ys
- smoothingArray: ss
- orDefaultSmoothing: defaultSmooth];
- [theEnvelope setStickPoint: MAXINT]; // init new envelope to default values
-
- allocateDraw(64); // arrays for drawing and erasing
- allocateTemp(64); // temporary arrays for move operations
- userPath = newUserPath(); // allocate a user path for drawing
-
- xMin=yMin=0.0; // max and min limits of display
- xMax=yMax=1.0;
- xSnap=ySnap=0.001; // initial Snap off in both directions
-
- envColour=BLACK;
- if (strcasecmp(NXGetDefaultValue("EnvelopeEd","showSmoothing"),"YES")==0)
- showSmooth=-1;
- if (strcasecmp(NXGetDefaultValue("EnvelopeEd","drawSegments"),"YES")==0)
- drawSegments=-1;
- else showSmooth=0;
- [self selectPoint: 0]; // select first point and display
- return self;
- }
-
- //-------------------------------------------------------------------
- // - free -- Release the allocated memory when object is freed
-
- -free
- {
- [theEnvelope free];
- freeUserPath(userPath);
- freeTemp();
- freeDraw();
- return [super free];
- }
-
- //-------------------------------------------------------------------
- // - controllerIs: Receive the object id of the controller of the view
-
- - controllerIs:sender
- {
- theController=sender;
- return self;
- }
-
- //-------------------------------------------------------------------
- // - insertPointAt:p
- // Finds the place in the envelope where the point p should be inserted.
- // Returns the point number at the insertion or -1 if an error is
- // detected.
-
- - (int) insertPointAt:(NXPoint)p
- {
- int point;
-
- point=0;
- while (point<=pointCount-1) {
- if (xValues[point]>p.x)
- return point;
- point++;
- }
- return pointCount;
- }
-
- //-------------------------------------------------------------------
- // - (int) hitKnobAt: p border: delta
- // Test if the user hits a knob when clicking the mouse at point p.
- // The boundaries of the zone are delta pixels wide. Returns the
- // number of the hit point or -1 if no hit.
-
- - (int) hitKnobAt:(NXPoint)p border:(float)delta;
- {
- int point;
- float kx, ky, dx, dy;
-
- dx=delta/WIDTH*(xMax-xMin);
- dy=delta/HEIGHT*(yMax-yMin);
- for (point=0; point<pointCount; point++) {
- kx=xValues[point];
- ky=yValues[point];
- if (p.x<kx-dx)
- break;
- if (p.x<=kx+dx && p.x>=kx-dx &&
- p.y<=ky+dy && p.y>=ky-dy)
- return point;
- }
- return -1;
- }
-
- //-------------------------------------------------------------------
- // drawKnobs(from,to,lcolour,hcolour) draws a set of knobs starting at
- // point from and ending at point to. Also highlights the selected
- // knob if necessary. The second colour argument specifies the colour
- // of the highlighted knob.
-
- - drawKnobsFrom:(int)from to:(int)to in:(float)lcolour :(float)hcolour
- {
- int point;
-
- PSsetgray(lcolour);
- for (point=from; point<=to; point++) {
- if (point!=selected)
- DRAWKNOB(xToPix(xValues[point]),yToPix(yValues[point]));
- }
- if (selected>=from && selected<=to && hcolour<=WHITE) {
- PSsetgray(hcolour);
- DRAWKNOB(xToPix(xValues[selected]),yToPix(yValues[selected]));
- }
- return self;
- }
-
- //-------------------------------------------------------------------
- // drawSegments(from,to,colour) draws line segments starting at point
- // from and ending at point to. It also takes care of the sticky point
- // marker. Due to problems with rounding each time a path is drawn the
- // moves are recorded in a set of arrays. This path is used to "erase"
- // the segment when a point is moved.
-
- - recordMovex:(double)x y:(double)y p:(int)n draw:(int)state
- {
- double xp, yp;
-
- draw->p[draw->num]=n;
- draw->x[draw->num]=xp=xToPix(x);
- draw->y[draw->num++]=yp=yToPix(y);
- if (state==0)
- UPmoveto(userPath,xp,yp);
- else
- UPlineto(userPath,xp,yp);
- return self;
- }
-
- - drawSegmentsFrom:(int)from to:(int)to in:(float)colour
- {
- int point, seg;
- double
- incx, incy,
- deltax, deltay,
- xi, xf, x,
- yi, yf, y,
- smooth, numsegs;
-
- // If drawing colour is white then use erase arrays to remove
- // the previously drawn segments
-
- if (colour==WHITE) {
- PSsetgray(colour);
- beginUserPath(userPath, NO);
- for (point=0;
- draw->p[point]<=from && point<draw->num;
- point++);
- if (point-1>=0) point--;
- UPmoveto(userPath,draw->x[point],draw->y[point]);
- for (;
- draw->p[point]<=to && point<draw->num;
- point++)
- UPlineto(userPath,draw->x[point],draw->y[point]);
- endUserPath(userPath, dps_ustroke);
- sendUserPath(userPath);
- return self;
- }
-
- // Else construct path for next erase and draw segments
- // first draw x and y axis if necessary
-
- draw->num=0;
- if (xMin!=0 || xMax!=0) {
- PSsetgray(LTGRAY);
- beginUserPath(userPath, NO);
- if (xMin!=0) {
- [self recordMovex: 0 y: yMin p: from draw: 0];
- [self recordMovex: 0 y: yMax p: from draw: 1];
- }
- if (yMin!=0) {
- [self recordMovex: xMin y: 0 p: from draw: 0];
- [self recordMovex: xMax y: 0 p: from draw: 1];
- }
- endUserPath(userPath, dps_ustroke);
- sendUserPath(userPath);
- }
-
- // and then draw the segments that interconnect the breakpoints.
-
- if (drawSegments==0) return self;
-
- draw->num=0;
- PSsetgray(colour);
- beginUserPath(userPath, NO);
- [self recordMovex: xValues[from] y: yValues[from] p: from draw: 0];
- yi=draw->yr[from]=yValues[from];
- for (point=from+1; point<=to; point++) {
-
- xi=xValues[point-1];
- xf=x=xValues[point];
- yf=y=yValues[point];
- deltax=xf-xi;
- deltay=yf-yi;
- numsegs=rint((deltax*WIDTH/(xMax-xMin))/3); // at least 3 pixels per segment
- if (numsegs>MAXNUMSEGS) numsegs=MAXNUMSEGS; // but no more than MAXNUMSEGS...
- if (numsegs<2) numsegs=2; // or less than 2
- if (showSmooth!=0 && deltax!=0 && deltay!=0) {
- smooth=sValues[point];
- incx=deltax/numsegs; // effective deltax is always
- if (smooth<0.01) incx*=0.01; // determined by the time
- else if (smooth<1.0) incx*=smooth; // constant of the segment
- incy=deltay/numsegs;
- for (seg=0, x=xi+incx; seg<numsegs-1; seg++, x+=incx) {
- y=yi+(deltay*(1-exp(-5.5262*(x-xi)/(deltax*smooth))));
- [self recordMovex: x y: y p: point draw: 1];
- }
- y=yi+(deltay*(1-exp(-5.5262/smooth))); // last segment to xf directly
- [self recordMovex: xf y: y p: point draw: 1];
- }
- else [self recordMovex: x y: y p: point draw: 1];
- if (point==stickyPoint) {
- [self recordMovex: xValues[point] y: 0 p: point draw: 1];
- [self recordMovex: xValues[point] y: y p: point draw: 0];
- }
- yi=draw->yr[point]=y;
- }
- endUserPath(userPath, dps_ustroke);
- sendUserPath(userPath);
- return self;
- }
-
- //-------------------------------------------------------------------
- // - drawSelf:: - Draw the envelope object in the view
-
- - drawSelf:(NXRect *)rects :(int)rectCount
- {
- NXEraseRect(&bounds); // clear the view
- PSsetlinewidth(0.0);
- if (theEnvelope!=NULL) {
- [self drawKnobsFrom:0 to:pointCount-1 in:LTGRAY:envColour];
- [self drawSegmentsFrom: 0 to:pointCount-1 in: envColour];
- }
- return self;
- }
-
- //-------------------------------------------------------------------
- // - eraseSelectedKnob Draw the envelope again without drawing the
- // selected knob. This is used just before the modal loop for draging
- // a point is entered. In the loop the cursor itself acts as the knob.
-
- - eraseSelectedKnob
- {
- NXEraseRect(&bounds); // clear the view
- PSsetlinewidth(0.0);
- if (theEnvelope!=NULL) {
- [self drawKnobsFrom:0 to:pointCount-1 in:LTGRAY:TRANSP];
- [self drawSegmentsFrom: 0 to:pointCount-1 in: envColour];
- }
- return self;
- }
-
- //-------------------------------------------------------------------
- // movePoint:to: Move point n to a new location.
- // This is called from within the drag modal loop so it tries to be
- // quick. Each time it erases the old knobs and segments (up to two)
- // and then draws the new knobs and segments. This last step can draw
- // any number of segments depending on how close the other points are
- // to the selected point (the problem is that the knob can erase
- // small portions of the segments that are lying outside of the area
- // bounded by the selected point and its two enclosing points).
-
- - (int) movePoint:(int)n to: (NXPoint)p;
- {
- int left,right, // limits for x movement of point
- drawFrom,drawTo; // start and end points for drawing lines
-
- if (n==0) { // determine limits for erase and draw
- drawFrom=0;
- drawTo=1;
- }
- else
- if (n==pointCount-1) {
- drawFrom=n-1;
- drawTo=n;
- }
- else {
- drawFrom=n-1;
- drawTo=n+1;
- }
- left=drawFrom;
- right=drawTo;
-
- if (showSmooth!=0) { // if showing smoothing draw points...
- while(drawFrom-1>=0 && // ...that have smoothing greater that 1
- sValues[drawFrom]>1.0)
- drawFrom--;
- while(drawTo+1<pointCount &&
- sValues[drawTo]>1.0)
- drawTo++;
- }
- if (n!=0 && p.x<xValues[left]) // force selected point to be within
- p.x=xValues[left]; // neighbouring points
- if (n!=pointCount-1 && p.x>xValues[right])
- p.x=xValues[right];
- if (xSnap!=0 &&
- xValues[right]-xValues[left]>2*xSnap)
- p.x=rint(p.x/xSnap)*xSnap; // snap into x grid
- if (p.y>yMax) p.y=yMax; // clip y values to max and min
- if (p.y<yMin) p.y=yMin;
- if (ySnap!=0)
- p.y=rint(p.y/ySnap)*ySnap; // snap into y grid
-
- if (n>1) // avoid 3 points with the same x value
- if (p.x==xValues[n-1] &&
- p.x==xValues[n-2])
- p.x=xValues[n-1]+(1/WIDTH*(xMax-xMin));
- if (n<(pointCount-2))
- if (p.x==xValues[n+1] &&
- p.x==xValues[n+2])
- p.x=xValues[n+1]-(1/WIDTH*(xMax-xMin));
-
- [self drawKnobsFrom:drawFrom // erase old knobs and segments
- to:drawTo in:WHITE:TRANSP];
- [self drawSegmentsFrom:drawFrom
- to:drawTo in:WHITE];
-
- xValues[n]=p.x;
- yValues[n]=p.y; // update coordinates in arrays
-
- [self drawKnobsFrom:drawFrom // draw new knobs and segments
- to:drawTo in:LTGRAY:TRANSP];
- [self drawSegmentsFrom:drawFrom
- to:drawTo in:envColour];
-
- [[self window] flushWindow];
- return n;
- }
-
- //-------------------------------------------------------------------
- // addPoint: -- Adds a point to the envelope
- // Returns number of new point, -1 on error
-
- - (int) addPoint:(NXPoint)p
- {
- int point, newp, oldp, newpc, newSticky;
-
- if (p.x<xMin || p.x>=xMax || // check point is within bounds
- p.y<yMin || p.y>yMax)
- return -1;
- if (ySnap!=0) p.y=rint(p.y/ySnap)*ySnap; // snap into y grid
-
- if ((point=[self insertPointAt:p])<0) // and that we get a valid point number
- return -1;
-
- newpc=pointCount+1;
- allocateTemp(newpc);
- allocateDraw(newpc);
-
- draw->num=0; // clear erase path
- newSticky=[theEnvelope stickPoint];
- if (newSticky!=MAXINT && stickyPoint>point)
- newSticky++; // adjust value of stick point
-
- for (newp=oldp=0; newp<newpc; newp++) {
- if (newp==point) {
- temp->x[newp]=p.x; // set values of new point
- temp->y[newp]=p.y;
- draw->yr[newp]=p.y;
- temp->s[newp]=defaultSmooth;
- }
- else {
- temp->x[newp]=xValues[oldp]; // copy old values into new arrays
- temp->y[newp]=yValues[oldp];
- draw->yr[newp]=draw->yr[oldp];
- if (sValues!=NULL)
- temp->s[newp]=sValues[oldp];
- else
- temp->s[newp]=defaultSmooth;
- oldp++;
- }
- }
- [theEnvelope // redefine the old envelope object
- setPointCount: newpc
- xArray: temp->x
- orSamplingPeriod: 1.0
- yArray: temp->y
- smoothingArray: temp->s
- orDefaultSmoothing: defaultSmooth];
- [theEnvelope setStickPoint: newSticky];
-
- return point;
- }
-
- //-------------------------------------------------------------------
- // removePoint: -- Remove a point from the envelope
- // Returns the new number of points or -1 on error
-
- - (int) removePoint:(int)n
- {
- int oldp, newp, newpc, newSticky;
-
- if (pointCount<3) return -1; // leave always at least two breaks
-
- newpc=pointCount-1;
- allocateTemp(newpc);
- allocateDraw(newpc);
-
- draw->num=0;
- for (oldp=newp=0; newp<newpc; newp++,oldp++) {
- if (oldp==n) oldp++; // copy arrays skipping deleted element
- temp->x[newp]=xValues[oldp];
- temp->y[newp]=yValues[oldp];
- draw->yr[newp]=draw->yr[oldp];
- if (sValues!=NULL)
- temp->s[newp]=sValues[oldp];
- }
- newSticky=[theEnvelope stickPoint];
- if (newSticky!=MAXINT) {
- if (newSticky==n)
- newSticky=MAXINT; // remove stick point with point
- else if (newSticky>n)
- newSticky--; // or adjust value if higher
- }
- [theEnvelope
- setPointCount: newpc
- xArray: temp->x
- orSamplingPeriod: 1.0
- yArray: temp->y
- smoothingArray: temp->s
- orDefaultSmoothing: defaultSmooth];
- [theEnvelope setStickPoint: newSticky]; // update envelope object
-
- [self selectPoint: n-1]; // and select previous point
- return pointCount;
- }
-
- //-------------------------------------------------------------------
- // mouseDown: -- Responds to a mousedown event
- // The following is the behaviour of the mouse:
- // hit a knob: --> drag the point
- // hit with shift: --> delete envelope breakpoint
- // hit with alternate: --> toggle sticky point at breakpoint
- // no hit with shift: --> create a new envelope breakpoint
-
- - mouseDown:(NXEvent *)event
- {
- NXPoint ep, p;
- int hitpt; // Point to move/remove
- int oldMask;
-
- ep=event->location;
- [self convertPoint:&ep fromView:nil];
- p.x=pixToX(ep.x);
- p.y=pixToY(ep.y); // convert from pixels to x/y
- hitpt=[self hitKnobAt: p border: KNOBSIZE/2]; // see if it is a breakpoint
- if (event->flags & NX_SHIFTMASK) { // with shift key down...
- if (hitpt>=0) {
- if ([self removePoint: hitpt] > 0) // hit --> remove point
- hitpt=-1;
- }
- else hitpt=[self addPoint: p]; // no hit --> add point
- }
- if ((event->flags&NX_ALTERNATEMASK)&&(hitpt>=0)) { // hit plus alternate...
- if (stickyPoint==hitpt)
- [theEnvelope setStickPoint: MAXINT];
- else
- [theEnvelope setStickPoint: hitpt];
- [self selectPoint: hitpt];
- hitpt=-1;
- }
-
- if (hitpt >= 0) { // Move hitpt as mouse drags
- [self selectPoint: hitpt]; // select and redraw image
- oldMask = [[self window] addToEventMask:DRAG_MASK];
-
- [self lockFocus];
- [theFilledCross push]; // use cursor = cross+knob
- [self eraseSelectedKnob];
- while (event->type != NX_MOUSEUP) {
- ep=event->location;
- [self convertPoint:&ep fromView:nil];
- if ([NXApp peekNextEvent: DRAG_MASK into: event]==NULL) {
- p.x=pixToX(ep.x);
- p.y=pixToY(ep.y);
- [self movePoint:hitpt to:p];
- [theController updateCoords: self at: hitpt];
- NXPing();
- }
- event = [NXApp getNextEvent:DRAG_MASK];
- }
- [NXCursor pop]; // return to crosshair cursor
- [self unlockFocus];
- }
- [self display];
- return self;
- }
-
- //===================================================================
- // Messages received from window to change first responder status
- //===================================================================
-
- //-------------------------------------------------------------------
- // highlight
-
- - highlight
- {
- envColour=BLACK;
- [self display];
- return self;
- }
-
- //-------------------------------------------------------------------
- // dim
-
- - dim
- {
- envColour=DKGRAY;
- [self display];
- return self;
- }
-
- //-------------------------------------------------------------------
- // acceptsFirstResponder:
-
- - (BOOL) acceptsFirstResponder
- {
- return YES;
- }
-
- //-------------------------------------------------------------------
- // becomeFirstResponder:
-
- - becomeFirstResponder
- {
- if (theController!=NULL)
- [theController update: self];
- envColour=BLACK;
- [self display];
- return self;
- }
-
- //-------------------------------------------------------------------
- // resignFirstResponder:
-
- - resignFirstResponder
- {
- return self;
- }
-
- //===================================================================
- // Pasteboard interface methods
- //===================================================================
-
- //-------------------------------------------------------------------
- // copy: -- Copy the current envelope to the pasteboard
-
- - copy:sender
- {
- int point; // current point
- char select; // number representing the style
- const char *style; // string representing the style
- const char *types[1];
- int num=0;
- char *s; // output string
- int max; // max number of chars in the output string
- char tmp[128]; // temporary buffer for conversions
-
- max=1024;
- s=malloc(max*sizeof(char)); // allocate array for output string
-
- if (theController==NULL) // get current selected style
- style="MusicKit";
- else {
- style=[theController getStyle];
- if (style==NULL) style="MusicKit";
- }
- select=STYLE_MK;
- if (strcmp(style,"MusicKit")==0) select=STYLE_MK;
- if (strcmp(style,"(x y ...)")==0) select=STYLE_pX_Yp;
- if (strcmp(style,"(x,y ...)")==0) select=STYLE_pXcYp;
- if (strcmp(style,"x y ...")==0) select=STYLE_X_Y;
- if (strcmp(style,"x,y ...")==0) select=STYLE_XcY;
- if (strcmp(style,"x,y,z ...")==0) select=STYLE_XcYcZ_;
- if (strcmp(style,"x y z,...")==0) select=STYLE_X_Y_Zc;
- if (strcmp(style,"((x y)...)")==0) select=STYLE_ppX_Yp;
- switch(select)
- {
- case STYLE_MK:
- strcpy(s,"[");
- for (point=0; point<pointCount-1; point++) {
- sprintf(tmp,"(%5.3f,%5.3f",xValues[point],yValues[point]);
- strcat(s,tmp);
- if (sValues[point]!=defaultSmooth) {
- sprintf(tmp,",%5.3f",sValues[point]);
- strcat(s,tmp);
- }
- strcat(s,")");
- if (stickyPoint==point) strcat(s,"|");
- if (strlen(s)>max-128) {
- max+=1024;
- s=realloc(s,max*sizeof(char));
- }
- }
- sprintf(tmp,"(%5.3f,%5.3f",xValues[pointCount-1],yValues[pointCount-1]);
- strcat(s,tmp);
- if (sValues[point]!=defaultSmooth) {
- sprintf(tmp,",%5.3f",sValues[point]);
- strcat(s,tmp);
- }
- strcat(s,")");
- if (stickyPoint==point) strcat(s,"|");
- strcat(s,"]");
- break;
-
- case STYLE_pX_Yp:
- strcpy(s,"(");
- for (point=0; point<pointCount-1; point++) {
- sprintf(tmp,"%5.3f %5.3f ",xValues[point],yValues[point]);
- strcat(s,tmp);
- if (strlen(s)>max-128) {
- max+=1024;
- s=realloc(s,max*sizeof(char));
- }
- }
- sprintf(tmp,"%5.3f %5.3f)",xValues[pointCount-1],yValues[pointCount-1]);
- strcat(s,tmp);
- break;
-
- case STYLE_pXcYp:
- strcpy(s,"(");
- for (point=0; point<pointCount-1; point++) {
- sprintf(tmp,"%5.3f,%5.3f,",xValues[point],yValues[point]);
- strcat(s,tmp);
- if (strlen(s)>max-128) {
- max+=1024;
- s=realloc(s,max*sizeof(char));
- }
- }
- sprintf(tmp,"%5.3f,%5.3f)",xValues[pointCount-1],yValues[pointCount-1]);
- strcat(s,tmp);
- break;
-
- case STYLE_X_Y:
- strcpy(s,"");
- for (point=0; point<pointCount-1; point++) {
- sprintf(tmp,"%5.3f %5.3f ",xValues[point],yValues[point]);
- strcat(s,tmp);
- if (strlen(s)>max-128) {
- max+=1024;
- s=realloc(s,max*sizeof(char));
- }
- }
- sprintf(tmp,"%5.3f %5.3f",xValues[pointCount-1],yValues[pointCount-1]);
- strcat(s,tmp);
- break;
-
- case STYLE_XcY:
- strcpy(s,"");
- for (point=0; point<pointCount-1; point++) {
- sprintf(tmp,"%5.3f,%5.3f,",xValues[point],yValues[point]);
- strcat(s,tmp);
- if (strlen(s)>max-128) {
- max+=1024;
- s=realloc(s,max*sizeof(char));
- }
- }
- sprintf(tmp,"%5.3f,%5.3f",xValues[pointCount-1],yValues[pointCount-1]);
- strcat(s,tmp);
- break;
-
- case STYLE_XcYcZ_:
- strcpy(s,"");
- for (point=0; point<pointCount-1; point++) {
- sprintf(tmp,"%5.3f,%5.3f,%5.3f ",xValues[point],yValues[point],sValues[point]);
- strcat(s,tmp);
- if (strlen(s)>max-128) {
- max+=1024;
- s=realloc(s,max*sizeof(char));
- }
- }
- sprintf(tmp,"%5.3f,%5.3f,%5.3f",xValues[pointCount-1],yValues[pointCount-1],sValues[pointCount-1]);
- strcat(s,tmp);
- break;
-
- case STYLE_X_Y_Zc:
- strcpy(s,"");
- for (point=0; point<pointCount-1; point++) {
- sprintf(tmp,"%5.3f %5.3f %5.3f,",xValues[point],yValues[point],sValues[point]);
- strcat(s,tmp);
- if (strlen(s)>max-128) {
- max+=1024;
- s=realloc(s,max*sizeof(char));
- }
- }
- sprintf(tmp,"%5.3f %5.3f %5.3f",xValues[pointCount-1],yValues[pointCount-1],sValues[pointCount-1]);
- strcat(s,tmp);
- break;
-
- case STYLE_ppX_Yp:
- strcpy(s,"(");
- for (point=0; point<pointCount-1; point++) {
- sprintf(tmp,"(%5.3f %5.3f)",xValues[point],yValues[point]);
- strcat(s,tmp);
- if (strlen(s)>max-128) {
- max+=1024;
- s=realloc(s,max*sizeof(char));
- }
- }
- sprintf(tmp,"(%5.3f %5.3f))",xValues[pointCount-1],yValues[pointCount-1]);
- strcat(s,tmp);
- break;
-
- }
- types[num++] = NXAsciiPboardType;
- [[Pasteboard new] declareTypes: types num: num owner: [self class]];
- [[Pasteboard new] writeType: NXAsciiPboardType data:s length:strlen(s)];
- free(s);
- return self;
- }
-
- //-------------------------------------------------------------------
- // paste: -- Paste the current pasteboard contents into the view. The
- // method parses the text representation of the envelope automatically
- // deciding on the type of envelope received. The internal representation
- // is a standard MusicKit envelope object.
-
- // Get next token from a string (symbol or number)
-
- #define NUMBER 0
-
- char *data;
-
- // Parse the data stream in tokens representing symbols and numbers
-
- int token(char *t)
- {
- char c;
- int i;
-
- while (((t[0]=c=*data++) == ' ')||(c == '\t')||(c == '\n'));
- t[1]='\0';
- if (c=='\0') {
- data--;
- return EOF;
- }
- if (!isdigit(c) && c!= '.' && c!= '-')
- return c;
- i=0; // collect a number stream
- if (c=='-')
- t[++i]=c=*data++;
- if (isdigit(c))
- while (isdigit(t[++i]=c=*data++));
- if (c == '.')
- while (isdigit(t[++i]=c=*data++));
-
- if ((c == 'e')||(c == 'E')) {
- t[++i]=c=*data++;
- if (c=='-')
- t[++i]=c=*data++;
- if (isdigit(c))
- while (isdigit(t[++i]=c=*data++));
- }
-
- t[i]='\0';
- if (c!='\0')
- data--;
- return NUMBER;
- }
-
- - paste:sender
- {
- char *prs, *orig, tk[1024];
- int symb, length;
- const NXAtom *pastetypes;
- int sticky, point;
-
- pastetypes = [[Pasteboard new] types];
- if ([[Pasteboard new]
- readType: NXAsciiPboardType
- data:&prs
- length:&length]) { // if ASCII in pasteboard...
-
- data=orig=calloc(length+16,sizeof(char)); // copy data to local buffer
- strncpy(data,prs,length);
-
- point=0; // start by converting point 0
- sticky=MAXINT; // no sticky point by default
- allocateTemp(64); // 64 points long by default
-
- if ((symb=token(tk))=='(') {
- if (symb=token(tk)=='(') {
-
- // parse a list of lists type envelope
- // accepted syntax: "((x0 y0)...(xn yn))" or "((x0,y0)...(xn,yn))"
-
- while((symb!=')')&&(symb!=-1)) {
- symb=token(tk); // should be "x" component
- if (symb!=NUMBER) { // must be x component
- NXRunAlertPanel("Error",
- "Expected x component at:\n\"%s\"\nin:\n\"%s\"",
- NULL,NULL,"Continue",data-1,orig);
- break;
- }
- else {
- temp->x[point]=atof(tk); // convert "x" component!
- temp->y[point]=0.0; // and set defaults
- temp->s[point]=defaultSmooth;
- }
- symb=token(tk);
- if (symb!=NUMBER) {
- NXRunAlertPanel("Error",
- "Expected y component at:\n\"%s\"\nin:\n\"%s\"",
- NULL,NULL,"Continue",data-1,orig);
- break;
- }
- else {
- temp->y[point]=atof(tk); // convert "y" component
- point++; // count a complete envelope node
- allocateTemp(point+1);
- }
- if (symb=token(tk)!=')') {
- NXRunAlertPanel("Error",
- "Expected closing parenthesis at:\n\"%s\"\nin:\n\"%s\"",
- NULL,NULL,"Continue",data-1,orig);
- break;
- }
- else symb=token(tk);
- }
- showSmooth=0; // only MK shows smoothing by default
- }
- else {
-
- // parse a CLM type envelope
- // accepted syntax: "(x0 y0 ... xn yn)" or "(x0,y0, ... xn,yn)"
-
- while((symb!=')')&&(symb!=-1)) {
- if (symb==',') symb=token(tk); // ignore commas between xy pairs
- if (symb!=NUMBER) { // must be x component
- NXRunAlertPanel("Error",
- "Expected x component at:\n\"%s\"\nin:\n\"%s\"",
- NULL,NULL,"Continue",data-1,orig);
- break;
- }
- else {
- temp->x[point]=atof(tk); // convert "x" component!
- temp->y[point]=0.0; // and set defaults
- temp->s[point]=defaultSmooth;
- }
- if ((symb=token(tk))==',') // ignore commas between values
- symb=token(tk);
- if (symb!=NUMBER) {
- NXRunAlertPanel("Error",
- "Expected y component at:\n\"%s\"\nin:\n\"%s\"",
- NULL,NULL,"Continue",data-1,orig);
- break;
- }
- else {
- temp->y[point]=atof(tk); // convert "y" component
- point++; // count a complete envelope node
- allocateTemp(point+1);
- }
- symb=token(tk);
- }
- showSmooth=0; // only MK shows smoothing by default
- }
- }
- else if (symb=='[') {
-
- // parse a MusicKit envelope
- // uses normal MusicKit syntax
-
- symb=token(tk); // should be starting '(' or '|'
- while((symb=='(')||(symb=='|')) {
- if (symb=='|') {
- sticky=point-1; // last point was the sticky point
- symb=token(tk); // should be '(' or the end
- continue;
- }
- if ((symb=token(tk))!=NUMBER) { // break if not a number
- NXRunAlertPanel("Error",
- "Expected x component at:\n\"%s\"\nin:\n\"%s\"",
- NULL,NULL,"Continue",data-1,orig);
- break;
- }
- else {
- temp->x[point]=atof(tk); // convert "x" component!
- temp->y[point]=0.0;
- temp->s[point]=defaultSmooth;
- }
- if ((symb=token(tk))==',') // skip comma but also accept a space
- symb=token(tk);
- if (symb!=NUMBER) { // break if not a number
- NXRunAlertPanel("Error",
- "Expected y component at:\n\"%s\"\nin:\n\"%s\"",
- NULL,NULL,"Continue",data-1,orig);
- break;
- }
- else {
- temp->y[point]=atof(tk); // convert "y" component
- }
- if ((symb=token(tk))==',') // is there a smoothing component?
- if ((symb=token(tk))==NUMBER) { // if number...
- temp->s[point]=atof(tk); // convert smoothing component
- symb=token(tk);
- }
- point++; // count a complete envelope node
- allocateTemp(point+1);
-
- if (symb!=')') { // must be point's closing parenthesis
- NXRunAlertPanel("Error",
- "Expected a ')' at:\n\"%s\"\nin:\n\"%s\"",
- NULL,NULL,"Continue",data-1,orig);
- break;
- }
- symb=token(tk);
- }
- showSmooth=-1;
- }
- else if (symb==NUMBER) {
-
- // parse a x-y-<z> pair type envelope
- // accepted syntax: "x0 y0 ... xn yn" or "x0,y0, ... xn,yn" for xy pairs
- // accepted syntax: "x0,y0,z0 x1,y1,z1 ..." or " x0 y0 z0,x1 y1 z1, ..." for xyz sets
-
- int commas=0;
- double last_value=0.0;
- int pending=0;
-
- while(symb!=-1) {
- if (pending==0) {
- if (symb!=NUMBER) { // not a number!
- NXRunAlertPanel("Error",
- "Expected x component at:\n\"%s\"\nin:\n\"%s\"",
- NULL,NULL,"Continue",data-1,orig);
- break;
- }
- else {
- temp->x[point]=atof(tk); // convert "x" component!
- temp->y[point]=0;
- temp->s[point]=defaultSmooth;
- }
- if ((symb=token(tk))==',') {
- commas++;
- symb=token(tk);
- }
- }
- else {
- temp->x[point]=last_value;
- temp->y[point]=0.0;
- temp->s[point]=defaultSmooth;
- pending=0;
- }
- if (symb!=NUMBER) {
- NXRunAlertPanel("Error",
- "Expected y component at:\n\"%s\"\nin:\n\"%s\"",
- NULL,NULL,"Continue",data-1,orig);
- break;
- }
- else temp->y[point]=atof(tk); // convert "y" component
- if ((symb=token(tk))==',') {
- commas++;
- symb=token(tk);
- }
- if (symb==-1) {
- point++;
- allocateTemp(point+1);
- break;
- }
- else {
- if (symb!=NUMBER) {
- NXRunAlertPanel("Error",
- "Expected x or z component at:\n\"%s\"\nin:\n\"%s\"",
- NULL,NULL,"Continue",data-1,orig);
- break;
- }
- else last_value=atof(tk);
- if ((((symb=token(tk))!=',')&&(commas==2)) ||
- ((symb==',')&&(commas==0))) {
- temp->s[point]=last_value;
- pending=0;
- commas=0;
- }
- else {
- pending=1;
- commas=1;
- }
- point++;
- allocateTemp(point+1);
- }
- if (symb==',') symb=token(tk);
- }
- showSmooth=0; // only MK shows smoothing by default
- }
- else
- NXRunAlertPanel(
- "Error",
- "The envelope must start with '[','(' or a number:\n\"%s\"",
- NULL,NULL,"Continue",orig);
-
- vm_deallocate(task_self(),(vm_address_t)prs, length);
-
- if (point<2) { // if envelope has less than 2 nodes
- NXRunAlertPanel(
- "Error",
- "Less than 2 legal points in envelope:\n\"%s\"",
- NULL,NULL,"Continue",orig);
- }
- else {
- [theEnvelope
- setPointCount: point
- xArray: temp->x
- orSamplingPeriod: 1.0
- yArray: temp->y
- smoothingArray: temp->s
- orDefaultSmoothing: defaultSmooth];
- [theEnvelope setStickPoint: sticky]; // update envelope object
- allocateDraw(temp->max); //(pointCount); // resize drawing arrays
- [self scaleLimits]; // define drawing limits
- }
- if (selected<pointCount)
- [self selectPoint: selected];
- else
- [self selectPoint: 0]; // display and update controller
- free(orig); // free local copy of data
- }
- return self;
- }
-
- //===================================================================
- // Messages received from controller to change parameters
- //===================================================================
-
- //-------------------------------------------------------------------
- // setPointTo: set current point to n (as a side effect updates values
- // of x and y coordinates on the controller object
-
- - setPointTo: (int)point
- {
- if (point>=pointCount) point=pointCount-1;
- if (point<0) point=0;
- [self selectPoint: point];
- [theController update: self];
- return self;
- }
-
- //-------------------------------------------------------------------
- // nextPoint go to the next point in the envelope if possible
-
- - nextPoint
- {
- int next;
-
- next=selected+1;
- if (next>=pointCount) next=selected;
- if (next<0) next=0;
- if (next!=selected) {
- [self selectPoint:next];
- [theController update: self];
- }
- return self;
- }
-
- //-------------------------------------------------------------------
- // previousPoint go to the previous point in the envelope if possible
-
- - previousPoint
- {
- int previous;
-
- previous=selected-1;
- if (previous>=pointCount) previous=selected;
- if (previous<0) previous=0;
- if (previous!=selected) {
- [self selectPoint:previous];
- [theController update: self];
- }
- return self;
- }
-
- //-------------------------------------------------------------------
- // setXAt:to: changes value of x coordinate of point n
-
- - setXAt: (int)n to: (float)coord
- {
- if (n!=0 && coord<xValues[n-1]) // force selected point to be within
- coord=xValues[n-1]; // enclosing points
- if (n!=pointCount-1 && coord>xValues[n+1])
- coord=xValues[n+1];
- if (coord!=xValues[n]) { // if x changed update display panel
- xValues[n]=coord;
- [self display];
- [theController update: self];
- }
- return self;
- }
-
- //-------------------------------------------------------------------
- // setYAt:to: changes value of y coordinate of point n
-
- - setYAt: (int)n to: (float)coord
- {
- if (coord>yMax) coord=yMax; // clip y values to max and min
- if (coord<yMin) coord=yMin;
-
- if (coord!=yValues[n]) { // if y changed update display panel
- yValues[n]=coord;
- [self display];
- [theController update: self];
- }
- return self;
- }
-
- //-------------------------------------------------------------------
- // setYrAt:to: changes value of real y coordinate of point n
-
- - setYrAt: (int)n to: (float)coord
- {
- double y;
-
- if (coord>yMax) coord=yMax; // clip y values to max and min
- if (coord<yMin) coord=yMin;
-
- if (n==0) return self; // no sense to change this in first point
-
- draw->yr[n]=coord;
- y=(coord-yValues[n-1]*exp(-5.5262/sValues[n]))/(1-exp(-5.5262/sValues[n]));
- [self setYAt: n to: y];
- return self;
- }
-
- //-------------------------------------------------------------------
- // setSmoothAt:to: changes value of smoothing of point n
-
- - setSmoothAt: (int)n to: (float)value
- {
- if (value!=sValues[n]) { // if smoothing changed update panel
- sValues[n]=value;
- [self display];
- [theController update: self];
- }
- return self;
- }
-
- //-------------------------------------------------------------------
- // setXMinTo: changes minimum value of x component of envelope
-
- - setXMinTo: (float)coord
- {
- if (coord<xMax) {
- xMin=coord;
- [self display];
- }
- else if (theController!=NULL)
- [theController update: self];
- return self;
- }
-
- //-------------------------------------------------------------------
- // setXMaxTo: changes maximun value of x component of envelope
-
- - setXMaxTo: (float)coord
- {
- if (coord>xMin) {
- xMax=coord;
- [self display];
- }
- else if (theController!=NULL)
- [theController update: self];
- return self;
- }
-
- //-------------------------------------------------------------------
- // setXLimitsTo:: changes max and min values of x component
-
- - setXLimitsTo: (float)min : (float)max
- {
- xMin=min;
- xMax=max;
- [self display];
- return self;
- }
-
- //-------------------------------------------------------------------
- // setYMinTo: changes minimum value of y component of envelope
-
- - setYMinTo: (float)coord
- {
- if (coord<yMax) {
- yMin=coord;
- [self display];
- }
- else if (theController!=NULL)
- [theController update: self];
- return self;
- }
-
- //-------------------------------------------------------------------
- // setYMaxTo: changes maximun value of y component of envelope
-
- - setYMaxTo: (float)coord
- {
- if (coord>yMax) {
- yMax=coord;
- [self display];
- }
- else if (theController!=NULL)
- [theController update: self];
- return self;
- }
-
- //-------------------------------------------------------------------
- // setXSnapTo: changes value of x Snap
-
- - setXSnapTo: (float)coord
- {
- xSnap=coord;
- [self display];
- return self;
- }
-
- //-------------------------------------------------------------------
- // setYSnapTo: changes value of y Snap
-
- - setYSnapTo: (float)coord
- {
- ySnap=coord;
- [self display];
- return self;
- }
-
- //-------------------------------------------------------------------
- // setStickyAt:To: sets point to be the sticky point of envelope
-
- - setStickyAt:(int) point To: (int)state
- {
- if (state==0)
- [theEnvelope setStickPoint: MAXINT];
- else
- [theEnvelope setStickPoint: point];
- [theController update: self];
- [self display];
- return self;
- }
-
- //-------------------------------------------------------------------
- // setShowSmooth: sets type of graphics to be used
-
- - setShowSmooth: (int)state
- {
- showSmooth=state;
- [self display];
- [theController update: self];
- return self;
- }
-
- //-------------------------------------------------------------------
- // setDrawSegments: choose to draw segments or only points
-
- - setDrawSegments: (int)state
- {
- drawSegments=state;
- [self display];
- [theController update: self];
- return self;
- }
-
- //-------------------------------------------------------------------
- // scaleLimits computes the max and min bounds of the view
-
- - scaleLimits
- {
- double xmin, xmax, ymin, ymax;
- int point;
-
- xmin=xmax=xValues[0];
- ymin=ymax=yValues[0];
- for (point=1; point<pointCount; point++) {
- if (xValues[point]<xmin) xmin=xValues[point];
- if (xValues[point]>xmax) xmax=xValues[point];
- if (yValues[point]<ymin) ymin=yValues[point];
- if (yValues[point]>ymax) ymax=yValues[point];
- }
- xMax=xmax;
- xMin=xmin;
- if (ymax>=0.0 && ymax<=10.0)
- yMax=rint(ymax*10+1.0)/10;
- else
- yMax=rint(ymax+1.0);
- if (ymin<=0.0 && ymin>=-1.0)
- yMin=rint(ymin*10-1.0)/10;
- else
- yMin=rint(ymin-1.0);
- if (ymin==0) yMin=ymin;
-
- [self display];
- return self;
- }
-
- //===================================================================
- // Messages received from controller to query for envelope values
- //===================================================================
-
- //-------------------------------------------------------------------
- // (int)getPoint Return the selected point
-
- - (int)getPoint
- {
- return selected;
- }
-
- //-------------------------------------------------------------------
- // (float)getX:(int)i Return value of x component of point i
-
- - (float)getX:(int)i
- {
- if (i>=pointCount) i=pointCount-1;
- if (i<0) i=0;
- return xValues[i];
- }
-
- //-------------------------------------------------------------------
- // (float)getY:(int)i Return value of y component of point i
-
- - (float)getY:(int)i
- {
- if (i>=pointCount) i=pointCount-1;
- if (i<0) i=0;
- return yValues[i];
- }
-
- //-------------------------------------------------------------------
- // (float)getYr:(int)i Return value of real y component of point i
-
- - (float)getYr:(int)i
- {
- if (i>=pointCount) i=pointCount-1;
- if (i<0) i=0;
- return draw->yr[i];
- }
-
- //-------------------------------------------------------------------
- // (float)getSmoothing:(int)i Return value of smoothing of point i
-
- - (float)getSmoothing:(int)i
- {
- if (i>=pointCount) i=pointCount-1;
- if (i<0) i=0;
- return sValues[i];
- }
-
- //-------------------------------------------------------------------
- // (int)getSticky:(int)i Return value of stickyness of point i
-
- - (int)getSticky:(int)i
- {
- if (i>=pointCount) i=pointCount-1;
- if (i<0) i=0;
- if (stickyPoint==i)
- return 1;
- else
- return 0;
- }
-
- //-------------------------------------------------------------------
- // (float)getXMax
-
- - (float) getXMax { return xMax; }
-
- //-------------------------------------------------------------------
- // (float)getXMin
-
- - (float) getXMin { return xMin; }
-
- //-------------------------------------------------------------------
- // (float)getYMax
-
- - (float) getYMax { return yMax; }
-
- //-------------------------------------------------------------------
- // (float)getYMin
-
- - (float) getYMin { return yMin; }
-
- //-------------------------------------------------------------------
- // (float)getXSnap
-
- - (float) getXSnap { return xSnap; }
-
- //-------------------------------------------------------------------
- // (float)getYSnap
-
- - (float) getYSnap { return ySnap; }
-
- //-------------------------------------------------------------------
- // (int)getShowSmooth
-
- - (int)getShowSmooth
- {
- return showSmooth;
- }
-
- //-------------------------------------------------------------------
- // (int)getDrawSegments
-
- - (int)getDrawSegments
- {
- return drawSegments;
- }
-
- @end
-
-