home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NeXTSTEP 3.0
/
NeXTSTEP3.0.iso
/
NextDeveloper
/
Examples
/
AppKit
/
Draw
/
Scribble.m
< prev
next >
Wrap
Text File
|
1992-07-20
|
7KB
|
290 lines
#import "draRV0
/*
* This line is just a stub to get genstrings to generate
* a .strings file entry for the name of this type of Graphic.
* The name is used in the Undo New <Whatever> menu item.
*
* NXLocalString("Scribble", NULL, "Name of the tool that draws scribbles, i.e., the %s of the New %s operation.")
*/
@implementation Scribble : Graphic
static NXPoint lastPoint; /* used in creating only */
+ initialize
/*
* This bumps the class version so that we can compatibly read
* old Graphic objects out of an archive.
*/
{
[Scribble setVersion:1];
return self;
}
+ cursor
/*
* A Scribble uses a pencil as its cursor.
*/
{
NXPoint spot;
static NXCursor *cursor = nil;
if (!cursor) {
cursor = [NXCursor newFromImage:[NXImage newFromSection:"pencil.tiff"]];
spot.x = 0.0; spot.y = 15.0;
[cursor setHotSpot:&spot];
}
return cursor ? cursor : [super cursor];
}
- free
{
NX_FREE(points);
NX_FREE(userPathOps);
return [super free];
}
- allocateChunk
/*
* The Scribble's storage is allocated in chunks.
* This allocates another chunk.
*/
{
int i, newSize;
newSize = length + CHUNK_SIZE;
if (points) {
NX_ZONEREALLOC([self zone], points, float, newSize << 1);
NX_ZONEREALLOC([self zone], userPathOps, char, newSize);
} else {
NX_ZONEMALLOC([self zone], points, float, newSize << 1);
NX_ZONEMALLOC([self zone], userPathOps, char, newSize);
}
for (i = newSize - 1; i >= length; i--) {
userPathOps[i] = dps_rlineto;
}
return self;
}
- (float)naturalAspectRatio
/*
* The Scribble's natural aspect ratio is the one it was created with.
*/
{
return (gFlags.initialized ? ((bbox[2]-bbox[0])/(bbox[3]-bbox[1])) : 0.0);
}
- (int)moveCorner:(int)corner to:(const NXPoint *)point constrain:(BOOL)flag
/*
* After the Scribble is created (gFlags.initialized == YES), this method
* just returns super's implementation. During creation, every time the
* "corner" is moved, a new line segment is added to the Scribble and
* the bounding box is expanded if necessary.
*/
{
float *p;
if (gFlags.initialized) {
return [super moveCorner:corner to:point constrain:flag];
}
if (!(point->x - lastPoint.x || point->y - lastPoint.y)) return corner;
length++;
if (!(length % CHUNK_SIZE)) [self allocateChunk];
p = points + (length << 1);
RV1+ = point->x - lastPoint.x;
*p = point->y - lastPoint.y;
lastPoint = *point;
bbox[2] = MAX(point->x, bbox[2]);
bbox[0] = MIN(point->x, bbox[0]);
bbox[3] = MAX(point->y, bbox[3]);
bbox[1] = MIN(point->y, bbox[1]);
bounds.origin.x = bbox[0];
bounds.origin.y = bbox[1];
bounds.size.width = bbox[2] - bbox[0];
bounds.size.height = bbox[3] - bbox[1];
return corner;
}
- (BOOL)create:(NXEvent *)event in:view
/*
* Before creating, an initial chunk is initialized, and the userPathOps
* are initialized. The lastPoint is also remembered as the start point.
* After the Scribble is created, the initialized flag is set.
*/
{
NXPoint p;
[self allocateChunk];
userPathOps[0] = dps_moveto;
p = event->location;
[view convertPoint:&p fromView:nil];
[view grid:&p];
points[0] = p.x;
points[1] = p.y;
lastPoint = p;
bbox[0] = bbox[2] = p.x;
bbox[1] = bbox[3] = p.y;
bounds.origin = p;
bounds.size.width = bounds.size.height = 0.0;
if ([super create:event in:view]) {
gFlags.initialized = YES;
return YES;
}
return NO;
}
- draw
/*
* The Scribble is drawn simply by scaling appropriately from its
* initial bounding box and drawing the user path.
*/
{
NXCoord x, y;
NXPoint p1, p2;
int i, count, coords;
float angle, sx, sy, tx, ty;
if (bounds.size.width < 1.0 || bounds.size.height < 1.0) return self;
if (length && (bbox[2] - bbox[0]) && (bbox[3] - bbox[1])) {
sx = bounds.size.width / (bbox[2] - bbox[0]);
sy = bounds.size.height / (bbox[3] - bbox[1]);
tx = (bounds.origin.x +
((points[0]-bbox[0]) / (bbox[2]-bbox[0]) * bounds.size.width)) -
points[0] * sx;
ty = (bounds.origin.y +
((points[1]-bbox[1]) / (bbox[3]-bbox[1]) * bounds.size.height)) -
points[1] * sy;
if (gFlags.arrow && ![self fill] && (sx != 1.0 || sy != 1.0 || tx || ty)) {
PSgsave();
}
if ([self fill]) {
PSgsave();
PStranslate(tx, ty);
PSscale(sx, sy);
[self setFillColor];
DPSDoUserPath(points, (length + 1) << 1, dps_float,
userPathOps, length + 1, bbox,
gFlags.eofill ? dps_ueofill : dps_ufill);
PSgrestore();
}
if (!gFlags.nooutline) {
PStranslate(tx, ty);
PSscale(sx, sy);
[self setLineColor];
DPSDoUserPath(points, (length + 1) << 1, dpsRV2at,
userPathOps, length + 1, bbox, dps_ustroke);
}
if (gFlags.arrow && ![self fill]) {
if (sx != 1.0 || sy != 1.0 || tx || ty) {
PSgrestore();
[self setLineColor];
}
if (gFlags.arrow != ARROW_AT_END) {
i = 0;
p1.x = points[i++];
p1.y = points[i++];
p2 = p1;
p2.x += points[i++];
p2.y += points[i++];
count = length - 1;
while (hypot((p1.x-p2.x)*sx,(p1.y-p2.y)*sy) < 7.0 && count--) {
p2.x += points[i++];
p2.y += points[i++];
}
angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx);
angle = (angle / 3.1415) * 180.0;
x = bounds.origin.x + (p1.x - bbox[0]) * sx;
y = bounds.origin.y + (p1.y - bbox[1]) * sy;
PSArrow(x, y, angle);
}
if (gFlags.arrow != ARROW_AT_START) {
i = 0;
coords = (length + 1) << 1;
p1.x = points[i++];
p1.y = points[i++];
while (i < coords) {
p1.x += points[i++];
p1.y += points[i++];
}
p2 = p1;
i = coords;
p2.y -= points[--i];
p2.x -= points[--i];
count = length - 1;
while (hypot((p2.x-p1.x)*sx,(p2.y-p1.y)*sy) < 7.0 && count--) {
p2.y -= points[--i];
p2.x -= points[--i];
}
angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx);
angle = (angle / 3.1415) * 180.0;
x = bounds.origin.x + (p1.x - bbox[0]) * sx;
y = bounds.origin.y + (p1.y - bbox[1]) * sy;
PSArrow(x, y, angle);
}
}
}
return self;
}
- write:(NXTypedStream *)stream
/*
* The Scribble is written out by writing its length (in segments), its
* bounding box, and all the points.
*/
{
int i, numFloats;
[super write:stream];
NXWriteTypes(stream,"iffff",&length,&bbox[0],&bbox[1],&bbox[2],&bbox[3]);
numFloats = (length + 1) << 1;
for (i = 0; i < numFloats; i++) {
NXWriteTypes(stream, "f", &points[i]);
}
return self;
}
- read:(NXTypedStream *)stream
{
int i;
float *p;
[super read:stream];
NXReadTypes(stream,"iffff",&length,&bbox[0],&bbox[1],&bbox[2],&bbox[3]);
NX_ZONEMALLOC([self zone], points, float, (length + 1) << 1);
NX_ZONEMALLOC([self zone], userPathOps, char, length + 1);
p = points;
for (i = 0; i <= length; i++) {
NXReadTypes(stream, "f", p++);
NXReadTypes(stream, "f", p++);
userPathOps[i] = dps_rlineto;
}
userPathOps[0] = dps_moveto;
return self;
}
@end