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.