This is Bezier.m in view mode; [Download] [Up]
/* * (a) (C) 1990 by Adobe Systems Incorporated. All rights reserved. * * (b) If this Sample Code is distributed as part of the Display PostScript * System Software Development Kit from Adobe Systems Incorporated, * then this copy is designated as Development Software and its use is * subject to the terms of the License Agreement attached to such Kit. * * (c) If this Sample Code is distributed independently, then the following * terms apply: * * (d) This file may be freely copied and redistributed as long as: * 1) Parts (a), (d), (e) and (f) continue to be included in the file, * 2) If the file has been modified in any way, a notice of such * modification is conspicuously indicated. * * (e) PostScript, Display PostScript, and Adobe are registered trademarks of * Adobe Systems Incorporated. * * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO * CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED * AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED. * ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY * OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO * WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY) * WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY * DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. */ /* * Bezier.m * * The methods here handle the actions necessary to create and draw * a bezier curve. This is the only graphical object in the application. * Some methods found here would probably be moved to a generic * graphical object class in a more general application. Some methods * have special steps included because of the interface decisions * chosen for a bezier curve. When the first and last control points * are moved, the second and third control points, respectively, are * also moved. This has implications when scrolling and restraining the * drawing to the imagable area. * * Version: 2.0 * Author: Ken Fromm * History: * 03-07-91 Added this comment. */ #import "Bezier.h" #import "DetectApp.h" #import "DocView.h" #import "DrawingView.h" #import "DrawingViewWraps.h" #import <appkit/nextstd.h> #import <dpsclient/dpsclient.h> #import <dpsclient/wraps.h> @implementation Bezier - initFrame:(NXRect *)frm { self = [super init]; NX_MALLOC(path.pts, float, PTS_BEZIER*2 + 4); NX_MALLOC(path.ops, char, 3); [self create:frm]; return self; } /* Randomly select the points and calculate the bounds. */ - create:(const NXRect *)frm { int i; LLX(path.pts) = LLY(path.pts) = 9999; URX(path.pts) = URY(path.pts) = -9999; for (i = 0; i < PTS_BEZIER * 2; i += 2) { path.pts[i + 4] = rand () % ((int)frm->size.width -8) + frm->origin.x + 4; path.pts[i + 5] = rand () % ((int)frm->size.height -8) + frm ->origin.y + 4; LLX(path.pts) = MIN(LLX(path.pts), path.pts[i + 4]); LLY(path.pts) = MIN(LLY(path.pts), path.pts[i + 5]); URX(path.pts) = MAX(URX(path.pts), path.pts[i + 4]); URY(path.pts) = MAX(URY(path.pts), path.pts[i + 5]); } path.num_pts = PTS_BEZIER * 2 + 4; /* * The first entry is set to dps_setbbox for hit detection but then reset * to dps_ucache when drawing (except when the user is changing * the shape of the object frequently.) */ path.ops[0] = dps_setbbox; path.ops[1] = dps_moveto; path.ops[2] = dps_curveto; path.num_ops = 3; return self; } - (UPath *) getPath { return &path; } - copyPts:srcId { UPath *srcPath; srcPath = [srcId getPath]; NX_MALLOC(path.pts, float, PTS_BEZIER*2 + 4); NX_MALLOC(path.ops, char, 3); bcopy(srcPath->pts, path.pts, srcPath->num_pts* (sizeof(float)/sizeof(char))); bcopy(srcPath->ops, path.ops, srcPath->num_ops); return self; } /* * Returns the bounds. The flag variable determines whether the * knobs should be factored in. They may need to be for drawing but * might not if needed for constraining reasons. */ - getBounds:(NXRect *)bRect withKnobs:(BOOL) flag { float knobsize; bRect->origin.x = LLX(path.pts); bRect->origin.y = LLY(path.pts); bRect->size.width = URX(path.pts) - LLX(path.pts); bRect->size.height = URY(path.pts) -LLY(path.pts); if (flag) { knobsize = -[[[NXApp getDrawingView] superview] controlPointSize]/2; NXInsetRect(bRect, knobsize, knobsize); NXIntegralRect(bRect); } return self; } /* Given the point number, return the point. */ - getPoint:(int) pt_num :(NXPoint *) pt { pt->x = path.pts[pt_num*2 + 4]; pt->y = path.pts[pt_num*2 + 5]; return self; } /* * Depending on the pt_num passed in, return the rectangle * that should be used for scrolling purposes. When the rectangle * passes out of the visible rectangle then the screen should * scroll. If the first and last points are selected, then the second * and third points are included in the rectangle. If the second and * third points are selected, then they are used by themselves. */ - getScrollRect:(int) pt_num :(NXRect *) aRect { float knobsize; if (pt_num == -1) { [self getBounds:aRect withKnobs:NO]; } else if (pt_num == 0) { aRect->origin.x = MIN(path.pts[4], path.pts[6]); aRect->origin.y = MIN(path.pts[5], path.pts[7]); aRect->size.width = ABS(path.pts[4] - path.pts[6]); aRect->size.height = ABS(path.pts[5] - path.pts[7]); } else if (pt_num == 3) { aRect->origin.x = MIN(path.pts[10], path.pts[8]); aRect->origin.y = MIN(path.pts[11], path.pts[9]); aRect->size.width = ABS(path.pts[10] - path.pts[8]); aRect->size.height = ABS(path.pts[11] - path.pts[9]); } else { aRect->origin.x = path.pts[pt_num*2 + 4]; aRect->origin.y = path.pts[pt_num*2 + 5]; aRect->size.width = 0; aRect->size.height = 0; } knobsize = -[[[NXApp getDrawingView] superview] controlPointSize]/2; NXInsetRect(aRect, knobsize, knobsize); return self; } /* * This method constains the point to the bounds of the view passed * in. Like the method above, the constaining is dependent on the * control point that has been selected. */ - constrainPoint:(NXPoint *)aPt andNumber:(int) pt_num toView:aView { float knobsize; NXPoint viewMax; NXPoint *thisPt, *nextPt; NXRect viewRect; [aView getBounds:&viewRect]; viewMax.x = viewRect.origin.x + viewRect.size.width; viewMax.y = viewRect.origin.y + viewRect.size.height; if (pt_num == 0 || pt_num == 3) { thisPt = (NXPoint *) &path.pts[pt_num*2 + 4]; if (pt_num == 0) nextPt = (NXPoint *) &path.pts[6]; else nextPt = (NXPoint *) &path.pts[8]; if (thisPt->x > nextPt->x) viewRect.origin.x += thisPt->x - nextPt->x; else viewMax.x -= nextPt->x - thisPt->x; if (thisPt->y > nextPt->y) viewRect.origin.y += thisPt->y - nextPt->y; else viewMax.y -= nextPt->y - thisPt->y; } viewMax.x -= MARGIN; viewMax.y -= MARGIN; viewRect.origin.x += MARGIN; viewRect.origin.y += MARGIN; aPt->x = MAX(viewRect.origin.x, aPt->x); aPt->y = MAX(viewRect.origin.y, aPt->y); aPt->x = MIN(viewMax.x, aPt->x); aPt->y = MIN(viewMax.y, aPt->y); return self; } /* * Change the point number passed in by the amount passed in in pt. * Recalculate the bounds because one of the bounding points could * have been the changed point. */ - changePoint:(int) pt_num :(const NXPoint *) pt { int i; path.pts[pt_num *2 + 4] += pt->x; path.pts[pt_num *2 + 5] += pt->y; LLX(path.pts) = LLY(path.pts) = 9999; URX(path.pts) = URY(path.pts) = -9999; for (i = 0; i < PTS_BEZIER * 2; i += 2) { LLX(path.pts) = MIN(LLX(path.pts), path.pts[i + 4]); LLY(path.pts) = MIN(LLY(path.pts), path.pts[i + 5]); URX(path.pts) = MAX(URX(path.pts), path.pts[i + 4]); URY(path.pts) = MAX(URY(path.pts), path.pts[i + 5]); } return self; } /* * pt_num is the changing control point. pt holds the relative change in each coordinate. * The relative is needed and not the absolute because the closest inside control point * changes when one of the outside points change. */ - setPoint:(int) pt_num :(const NXPoint *) pt { [self changePoint:pt_num :pt]; if (pt_num == 0) [self changePoint:1 :pt]; else if (pt_num == 3) [self changePoint:2 :pt]; return self; } /* The pt argument holds the relative point change. */ - moveAll:(const NXPoint *) pt { int i; for (i = 0; i < PTS_BEZIER * 2; i += 2) { path.pts[i + 4] +=pt->x; path.pts[i + 5] += pt->y; } LLX(path.pts) += pt->x; LLY(path.pts) += pt->y; URX(path.pts) += pt->x; URY(path.pts) += pt->y; return self; } /* * Check for a control point hit. No need to perform the hit detection in * the server since its a simple rectangle intersection check. Return the * point number hit in the pt_num argument. */ - (BOOL) hitControl:(const NXRect *)hitRect :(int *) pt_num :(float) controlsize { int i; NXRect knobRect; knobRect.size.width = knobRect.size.height = controlsize; for (i=0; i < PTS_BEZIER*2; i += 2) { knobRect.origin.x = path.pts[i + 4] - controlsize/2; knobRect.origin.y = path.pts[i + 5] - controlsize/2; if (NXIntersectsRect(hitRect, &knobRect)) { *pt_num = i/2; return YES; } } return NO; } /* * Check for hit dectection on the object. This uses the * inustroke operator to check for an intersection of the * hit detection rectangle with the path of the Bezier. */ - (BOOL) hitObject:(UPath *) hitUpath { int hit = NO; NXRect aRect, bRect; NXSetRect(&aRect, hitUpath->pts[0], hitUpath->pts[1], hitUpath->pts[2] - hitUpath->pts[0], hitUpath->pts[3] - hitUpath->pts[1]); [self getBounds:&bRect withKnobs:NO]; if (NXIntersectsRect(&aRect, &bRect)) { path.ops[0] = dps_setbbox; PSWHitPath(hitUpath->pts, hitUpath->num_pts, hitUpath->ops, hitUpath->num_ops, path.pts, path.num_pts, path.ops, path.num_ops, &hit); } return (BOOL) hit; } /* * Place the point locations and the chararacters for the control * points into the user path description passed in. In this case, the * xyshow operator is used instead of a user path. But because the * xyshow operator takes the same data format as the user path, * the buffers for the user paths are used. The position of the point * is calculated relative to the position of the previous point. If * this is the first set of points in the description then put the * absolute values in place, otherwise use the lastPoint * argument to calculate the displacement. */ - putControlUPath:(UPath *)drawUpath forRect:(NXRect *)r :(NXPoint *) lastPoint { int i, j; NXRect bounds; [self getBounds:&bounds withKnobs:YES]; if (!r || NXIntersectsRect(r, &bounds)) { i = drawUpath->num_ops; drawUpath->ops[i++] = 'a'; drawUpath->ops[i++] = 'b'; drawUpath->ops[i++] = 'b'; drawUpath->ops[i++] = 'a'; drawUpath->num_ops += PTS_BEZIER; i = drawUpath->num_pts; if (i == 0) { drawUpath->pts[i++] = path.pts[4]; drawUpath->pts[i++] = path.pts[5]; } else { drawUpath->pts[i++] = path.pts[4] - lastPoint->x; drawUpath->pts[i++] = path.pts[5] - lastPoint->y; } for (j = 2; i < PTS_BEZIER * 2; j++, i++) drawUpath->pts[i] = path.pts[j + 4] - path.pts[j + 2]; drawUpath->num_pts += PTS_BEZIER*2; lastPoint->x = path.pts[10]; lastPoint->y = path.pts[11]; } return self; } /* * Place the description of the control lines into the the user path. * Update the bounding box of the user path if necessary. */ - putControlLinesUPath:(UPath *) drawUpath forRect:(NXRect *) r { int i; NXRect bounds; [self getBounds:&bounds withKnobs:YES]; if (!r || NXIntersectsRect(r, &bounds)) { i = drawUpath->num_ops; drawUpath->ops[ i++] = dps_moveto; drawUpath->ops[ i++] = dps_lineto; drawUpath->ops[ i++] = dps_moveto; drawUpath->ops[ i++] = dps_lineto; drawUpath->num_ops += 4; for (i=0; i < PTS_BEZIER * 2; i++) drawUpath->pts[drawUpath->num_pts + i] = path.pts[i + 4]; drawUpath->num_pts += PTS_BEZIER*2; drawUpath->pts[0] = MIN(LLX(path.pts), drawUpath->pts[0]); drawUpath->pts[1] = MIN(LLY(path.pts), drawUpath->pts[1]); drawUpath->pts[2] = MAX(URX(path.pts), drawUpath->pts[2]); drawUpath->pts[3] = MAX(URY(path.pts), drawUpath->pts[3]); } return self; } /* * Draws the graphic if it lies within the bounds of the rectangle passed in. * Draws with the ucache on if uFlag is YES. */ - drawObject:(NXRect *)r withUcache:(BOOL)uFlag { int start_pt = 1; NXRect bounds; [self getBounds:&bounds withKnobs:NO]; if (!r || NXIntersectsRect(r, &bounds)) { PSsetgray(COLOR); PSsetlinewidth(WIDTH); if (uFlag) { path.ops[0] = dps_ucache; start_pt = 0; } DPSDoUserPath(&path.pts[4], path.num_pts-4, dps_float, &path.ops[start_pt], path.num_ops - start_pt, path.pts, dps_ustroke); } return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.