This is EnvelopeView.m in view mode; [Download] [Up]
/* EnvelopeView.m -- Implementation of EnvelopeView class * * For info, see EnvelopeView.h * * jwp@silvertone.Princeton.edu, 12/89 */ #import "EnvelopeView.h" #import "PWenv.h" #import <dpsclient/wraps.h> #import <appkit/Application.h> #import <stdlib.h> /* Shorthand for making new EnvPoints: */ #define MAKENODE() (EnvPoint *)malloc(sizeof(EnvPoint)) /* Macro to draw a 7 pixel-wide knob around a point (x,y) */ #define DRAWKNOB(x,y) PScompositerect(x-3,y-3,7,7,NX_HIGHLIGHT) /* Shorthand for view dimensions: */ #define WIDTH bounds.size.width #define HEIGHT bounds.size.height /* Grayscale values for PostScript */ #define WHITE 1.0 #define BLACK 0.0 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ /* C functions needed by EnvelopeView * * getpoint(n,ep) returns a pointer to node number n in list ep * getinsert(pt,ep) finds the point at which point pt should * be inserted into the list. Returns -1 on error. */ EnvPoint *getpoint(n,ep) int n; EnvPoint *ep; { while (n--) ep = ep->next; return ep; } int getinsert(pt,ep) NXPoint *pt; EnvPoint *ep; { int inacol; /* Number of pts with identical x coords */ int n; float x = pt->x; /* copy of x coord of pt */ float lastx = 2; for (n = inacol = 0; ep; ep = ep->next, n++) { if (ep->p.x > x) /* Break at first x > pt */ break; if (ep->p.x == lastx) inacol++; else inacol = 0; lastx = ep->p.x; } if (lastx == x && inacol) /* Can't have 3 in a column */ return -1; else return n-1; } /* testHit(pt,ep) checks to see whether pt lands on any knob. If so, * it returns the number of that point, else -1 */ int testHit(pt,ep,dx,dy) NXPoint *pt; EnvPoint *ep; float dx,dy; /* Size of knob (in envelope coords) */ { float x,y,kx,ky; int n; x = pt->x; /* Local copies of x and y */ y = pt->y; /* Test each point in the envelope. This loop exits when a hit * is found, or when we go past x in the envelope, or when there * are no more points to test. */ for (n = 0; ep; ep = ep->next, n++) { kx = ep->p.x; ky = ep->p.y; if (x < kx-dx) /* No hope of a match now */ break; if (x <= kx+dx && x >= kx-dx && y <= ky+dy && y >= ky-dy) return n; } return -1; } /* draw1(ep,grayval) draws one line segment from ep to ep->next * draw2(ep,grayval) draws two segments (ep->last to ep to ep->next) */ draw1(ep,grayval,width,height) EnvPoint *ep; float grayval; float width; float height; { float x1,y1,x2,y2; /* Coordinates */ x1 = (ep->p.x) * width; y1 = (ep->p.y) * height; ep = ep->next; x2 = (ep->p.x) * width; y2 = (ep->p.y) * height; PWdraw1(x1,y1,x2,y2,grayval); } draw2(ep,grayval,width,height) EnvPoint *ep; float grayval,width,height; { float x1,y1,x2,y2,x3,y3; ep = ep->last; x1 = ep->p.x * width; y1 = ep->p.y * height; ep = ep->next; x2 = ep->p.x * width; y2 = ep->p.y * height; ep = ep->next; x3 = ep->p.x * width; y3 = ep->p.y * height; PWdraw2(x1,y1,x2,y2,x3,y3,grayval); } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ @implementation EnvelopeView /* + newFrame: -- create a new object and initialize */ + newFrame:(const NXRect *)frameRect { EnvPoint *a, *b; /* Start/end points of envelope */ self = [super newFrame:frameRect]; envelope = a = MAKENODE(); b = MAKENODE(); /* Start-up points are at (0,0.5) and (1,0.5) */ a->p.x = 0.0; b->p.x = 1.0; a->p.y = b->p.y = 0.5; a->next = b; /* They point to each other */ b->last = a; a->last = NULL; /* And to nowhere */ b->next = NULL; npoints = 2; return self; } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ /* envelope: -- Get the envelope as an array of NXPoints. * Returns the number of points in the array. */ - (int) envelope:(NXPoint **)envptr { NXPoint *a; /* Local copy of array pointer */ EnvPoint *ep; /* Pointer into envelope list */ /* Make the array of npoints NXPoint structs */ a = *envptr = (NXPoint *)malloc(npoints * sizeof(NXPoint)); /* Go down the list, fill up the array, and return the number of * points. */ for (ep = envelope; ep; ep = ep->next, a++) *a = ep->p; return npoints; } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ /* setEnvelope:Points: -- Set the envelope from an array of NXPoints */ - setEnvelope:(NXPoint *)env Points:(int)n { EnvPoint *ep, *tmp; int i; float lastx; /* Test this envelope to see if it's valid */ if (n < 2) /* Must have at least 2 points */ return self; if (env[0].x != 0 || env[n-1].x != 1) return self; for (i = 1, lastx = 0; i < n; lastx = env[i].x, i++) if (env[i].x < lastx || env[i].y > 1 || env[i].y < 0) return self; npoints = n; /* Free up the old envelope space */ ep = envelope; while (ep) { tmp = ep->next; free(ep); ep = tmp; } /* Make the new envelope */ envelope = MAKENODE(); envelope->p = *env++; envelope->last = NULL; n--; for (ep = envelope; n; n--, env++) { ep->next = tmp = MAKENODE(); tmp->p = *env; tmp->last = ep; tmp->next = NULL; ep = tmp; } return self; } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ /* movePoint:To: -- Move a point to a new location. * Points are numbered from 0. * Returns -1 if disallowed; otherwise returns the number of the point. */ - (int) movePoint:(int)n To:(NXPoint *)pt { EnvPoint *ep; /* Determine the legality of this move: */ if (n < 0 || n >= npoints) /* Must be valid point */ return -1; ep = getpoint(n,envelope); /* Get it */ if (pt->y > 1) pt->y = 1; /* Put y in range */ if (pt->y < 0) pt->y = 0; /* Put x in proper range */ if (n == 0) pt->x = 0; else if (n == npoints-1) pt->x = 1; else { if (pt->x < ep->last->p.x) pt->x = ep->last->p.x; else if (pt->x > ep->next->p.x) pt->x = ep->next->p.x; } /* Avoid 3 in a column */ if (n > 1) if (pt->x == ep->last->p.x && pt->x == ep->last->last->p.x) pt->x = ep->last->p.x + 1/WIDTH; if (n < (npoints-2)) if (pt->x == ep->next->p.x && pt->x == ep->next->next->p.x) pt->x = ep->next->p.x - 1/WIDTH; /* Undraw the old lines, set the new point, and draw the new lines */ [self lockFocus]; if (n == 0) /* Only one segment to undraw for */ draw1(ep,WHITE,WIDTH,HEIGHT); /* endpoints */ else if (n == npoints-1) draw1(ep->last,WHITE,WIDTH,HEIGHT); else draw2(ep,WHITE,WIDTH,HEIGHT); ep->p = *pt; if (n == 0) draw1(ep,BLACK,WIDTH,HEIGHT); else if (n == (npoints-1)) draw1(ep->last,BLACK,WIDTH,HEIGHT); else draw2(ep,BLACK,WIDTH,HEIGHT); [self unlockFocus]; [[self window] flushWindow]; return n; } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ /* addPoint: -- Adds a point to the envelope * Returns number of new point, -1 on error */ - (int) addPoint:(NXPoint *)pt { EnvPoint *ep; int n; /* Determine validity of this point. */ if (pt->x < 0 || pt->x >= 1 || pt->y < 0 || pt->y > 1) return -1; if ((n = getinsert(pt,envelope)) < 0) return -1; ep = MAKENODE(); ep->p = *pt; ep->last = getpoint(n,envelope); ep->next = ep->last->next; /* Undraw the old segment, insert this point, then draw the new segments */ [self lockFocus]; draw1(ep->last,WHITE,WIDTH,HEIGHT); ep->last->next = ep->next->last = ep; npoints++; draw2(ep,BLACK,WIDTH,HEIGHT); [self unlockFocus]; [window flushWindow]; return n+1; } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ /* rmPoint: -- Remove a point from the envelope * Returns the new npoints or -1 on error */ - (int) rmPoint:(int)n { EnvPoint *ep; /* Determine the validity of this point */ if (n <= 0 || n >= npoints-1) return -1; ep = getpoint(n,envelope); /* Undraw the old segments, remove the point, then draw the new segment */ [self lockFocus]; draw2(ep,WHITE,WIDTH,HEIGHT); ep->last->next = ep->next; ep->next->last = ep->last; draw1(ep->last,BLACK,WIDTH,HEIGHT); [self unlockFocus]; [window flushWindow]; free(ep); npoints--; return npoints; } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ /* npoints -- Returns the number of points in the envelope */ - (int) npoints { return npoints; } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ - setDelegate:anObject { delegate = anObject; return self; } - delegate { return delegate; } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ - (BOOL) acceptsFirstResponder { return YES; } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ #define DRAG_MASK (NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK) /* mouseDown: -- Responds to a mousedown event */ - mouseDown:(NXEvent *)event { NXPoint eventp; /* Copy of event point */ NXPoint pt; /* To hold converted x/y values */ int hitpt; /* Point to move/remove */ BOOL changed = NO; /* Has envelope changed? */ int oldMask; /* Convert pixel coordinates to envelope coordinates. */ eventp = event->location; [self convertPoint:&eventp fromView:nil]; pt.x = eventp.x / WIDTH; pt.y = eventp.y / HEIGHT; /* Did we hit a knob? If so, which one? */ hitpt = testHit(&pt,envelope,4/WIDTH,4/HEIGHT); /* With shift key down, hit = remove point, nohit = add point. */ if (event->flags & NX_SHIFTMASK) { if (hitpt >= 0) { if ([self rmPoint:hitpt] > 0) changed = YES; hitpt = -1; } else hitpt = [self addPoint:&pt]; } /* Move hitpt as mouse drags */ if (hitpt >= 0) { changed = YES; oldMask = [[self window] addToEventMask:DRAG_MASK]; while (event->type != NX_MOUSEUP) { eventp = event->location; [self convertPoint:&eventp fromView:nil]; pt.x = eventp.x / WIDTH; pt.y = eventp.y / HEIGHT; [self movePoint:hitpt To:&pt]; if (delegate && [delegate respondsTo:@selector(point:MovedTo:)]) [delegate point:hitpt MovedTo:&pt]; event = [NXApp getNextEvent:DRAG_MASK]; } } /* Notify delegate if envelope changed */ if (changed && delegate && [delegate respondsTo:@selector(envelopeChanged:)]) [delegate envelopeChanged:self]; return self; } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ /* drawSelf:: -- Draw the envelope in the view */ - drawSelf:(NXRect *)rects :(int)rectCount { EnvPoint *ep; float x,y; /* Clear the view first */ NXEraseRect(&bounds); PSsetgray(0); /* Draw all the line segments */ if (!(ep = envelope)) return self; x = ep->p.x * WIDTH; y = ep->p.y * HEIGHT; PSnewpath(); PSmoveto(x,y); for (ep = envelope->next; ep; ep=ep->next) { x = ep->p.x * WIDTH; y = ep->p.y * HEIGHT; PSlineto(x,y); } PSstroke(); /* Draw all the knobs */ for (ep = envelope; ep; ep=ep->next) { x = ep->p.x * WIDTH; y = ep->p.y * HEIGHT; DRAWKNOB(x,y); } return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.