This is Tree.m in view mode; [Download] [Up]
#import <math.h>
#import <appkit/workspaceRequest.h>
#import <misckit/MiscString.h>
#import "PSWEnding.h"
#import "PSWUControl.h"
#import "RTFDescription.h"
#import "UndoManager.h"
#import "Tree.h"
//************************************************************************
// static c functions declaration
// static functions for layouting
static void setRectFromPolygon(NXRect *rect, Polygon *aPol, const NXPoint *ipu, const NXPoint *ipl,
NXCoord *low, NXCoord *loh);
static NXCoord merge(Polygon *c1,Polygon *c2, NXZone *zone);
static NXCoord offset(NXCoord p1,NXCoord p2,NXCoord a1,NXCoord a2,
NXCoord b1,NXCoord b2);
static Polyline *bridge(Polyline *line1,Polyline *line2,NXCoord x1,NXCoord y1,
NXCoord x2,NXCoord y2, NXZone *zone);
static Polyline *allocLine(NXCoord aX,NXCoord aY,Polyline *aLink, NXZone *zone);
static Polyline *copyPolyline(Polyline *aSource,Polyline **aTail, NXZone *zone);
static void freePolyline(Polyline *aLine);
static void partialFreePolyline(Polyline *aLine, Polyline *toEnd);
// static functions for calculating path intersection
static BOOL straddles(const NXPoint *p1, const NXPoint *p2, const NXPoint *p3, const NXPoint *p4);
// static variables for general use
static float controlPts[8],controlX,controlY,officialFontSize;
static char controlChars[5];
static char controlPrimChars[5];
static NXAtom fontname;
static ButtonCell *attachCell, *soundCell;
static Cell *zippedCell;
static NXRect attachCellFrame, soundCellFrame, zippedCellFrame;
static UPath *arrowEnding, *doubleEnding, *circleEnding;
static UPath *parentArrowEnding, *parentDoubleEnding, *parentCircleEnding;
// static variables for diagram2 streaming
#import "DGVariables.h"
@interface Tree(PrivateMethods)
- attachParent:(NXCoord)h;
- layoutLeaf;
- (NXCoord)join;
- makePath;
- changeText;
- changePos;
- fillGPathParams;
- calcWidthHeight;
- internalCopyFromZone:(NXZone *)zone parent:aParent;
- calcIntersection:(NXPoint *)ip angle:(float *)alpha toPoint:(const NXPoint *)aPoint;
@end
@implementation Tree
//************************************************************************
// creating and destroying
+ initialize
{
int i;
#ifndef FILTER
NXRect dummy;
id dummyId;
#endif
if(self == [Tree class]){
[Tree setVersion:1];
fontname = NXUniqueString("ControlPoints");
PSWDefineFont(fontname);
officialFontSize = 8.0;
controlPts[6] = 0;
controlPts[7] = 0;
controlChars[4] = 0;
controlPrimChars[4] = 0;
for(i = 0;i < 4;i++){
controlChars[i] = 'a';
controlPrimChars[i] = 'e';
}
#ifndef FILTER
dummyId = [NXImage findImageNamed:"NXLinkButton"];
if([dummyId lockFocus])
[dummyId unlockFocus];
dummyId = [NXImage findImageNamed:"NXLinkButtonH"];
if([dummyId lockFocus])
[dummyId unlockFocus];
dummyId = [NXImage findImageNamed:"zipped"];
if([dummyId lockFocus])
[dummyId unlockFocus];
dummyId = [NXImage findImageNamed:"soundMarker"];
if([dummyId lockFocus])
[dummyId unlockFocus];
dummyId = [NXImage findImageNamed:"soundMarkerH"];
if([dummyId lockFocus])
[dummyId unlockFocus];
NXSetRect(&dummy, 0, 0, 100, 100);
attachCell = [[ButtonCell alloc] initIconCell:"NXLinkButton"];
[attachCell setAltIcon:"NXLinkButtonH"];
[attachCell setBordered:NO];
[attachCell setHighlightsBy:NX_CONTENTS];
[attachCell setIconPosition:NX_ICONONLY];
[attachCell calcCellSize:&(attachCellFrame.size) inRect:&dummy];
zippedCell = [[Cell alloc] initIconCell:"zipped"];
[zippedCell setBordered:NO];
[zippedCell calcCellSize:&(zippedCellFrame.size) inRect:&dummy];
soundCell = [[ButtonCell alloc] initIconCell:"soundMarker"];
[soundCell setAltIcon:"soundMarkerH"];
[soundCell setBordered:NO];
[soundCell setHighlightsBy:NX_CONTENTS];
[soundCell setIconPosition:NX_ICONONLY];
[soundCell calcCellSize:&(soundCellFrame.size) inRect:&dummy];
#endif
arrowEnding = [[UPath alloc] init];
[arrowEnding moveto:0 :0];
[arrowEnding rlineto:-10 :5];
[arrowEnding rlineto:3 :-5];
[arrowEnding rlineto:-3 :-5];
[arrowEnding closepath];
doubleEnding = [[UPath alloc] init];
[doubleEnding moveto:0 :0];
[doubleEnding rlineto:-5 :5];
[doubleEnding rlineto:2 :-5];
[doubleEnding rlineto:-7 :5];
[doubleEnding rlineto:2 :-5];
[doubleEnding rlineto:-2 :-5];
[doubleEnding rlineto:7 :5];
[doubleEnding rlineto:-2 :-5];
[doubleEnding closepath];
circleEnding = [[UPath alloc] init];
[circleEnding moveto:0 :0];
[circleEnding arc:-3 :0 :3 :0 :360];
parentArrowEnding = [[UPath alloc] init];
[parentArrowEnding moveto:0 :0];
[parentArrowEnding rlineto:10 :5];
[parentArrowEnding rlineto:-3 :-5];
[parentArrowEnding rlineto:3 :-5];
[parentArrowEnding closepath];
parentDoubleEnding = [[UPath alloc] init];
[parentDoubleEnding moveto:0 :0];
[parentDoubleEnding rlineto:5 :5];
[parentDoubleEnding rlineto:-2 :-5];
[parentDoubleEnding rlineto:7 :5];
[parentDoubleEnding rlineto:-2 :-5];
[parentDoubleEnding rlineto:2 :-5];
[parentDoubleEnding rlineto:-7 :5];
[parentDoubleEnding rlineto:2 :-5];
[parentDoubleEnding closepath];
parentCircleEnding = [[UPath alloc] init];
[parentCircleEnding moveto:6 :0];
[parentCircleEnding arc:3 :0 :3 :0 :360];
}
return self;
}
- initLabel:(const char *)aLabel props:(Properties *)aProps
{
[super init];
contour.lower.head = contour.upper.head = NULL;
contour.lower.tail = contour.upper.tail = NULL;
parent = child = sibling = nil;
delegate = nil;
hasVerknuepfung = NO;
verknuepfung = nil;
hasSound = NO;
geraeusch = nil;
offset.x = offset.y = 0.0;
pos.x = pos.y = 0.0;
selected = NO;
updateLayout = YES;
if(aLabel)
textCell = [[TextFieldCell allocFromZone:[self zone]] initTextCell:aLabel];
else
textCell = [[TextFieldCell allocFromZone:[self zone]] initTextCell:aProps->defaultNodeName];
[textCell setBordered:NO];
[textCell setBackgroundTransparent:YES];
[textCell setTextColor:aProps->textColor];
[textCell setAlignment:NX_CENTERED];
nodeDescription = [[RTFDescription allocFromZone:[self zone]] init];
zipped = aProps->zipped;
border = aProps->border;
shadow = aProps->shadow;
fill = aProps->fill;
outline = aProps->outline;
linkKind = aProps->linkKind;
biegFactor = aProps->biegFactor;
parentDistance = aProps->parentDistance;
fillColor = aProps->fillColor;
outlineColor = aProps->outlineColor;
shadowColor = aProps->shadowColor;
ending = aProps->ending;
parentEnding = aProps->parentEnding;
gpath = [[UPath allocFromZone:[self zone]] init];
pathKind = aProps->pathKind;
deltaShadow.x = deltaShadow.y = 3;
placer.rect[0] = placer.rect[1] = 6;
placer.roundRect[0] = placer.roundRect[1] = 6;
placer.roundRect[2] = 3;
placer.ellipse[0] = 6;
placer.ellipse[1] = 4;
placer.rhomb[0] = 12;
placer.rhomb[1] = 3;
placer.hexagon[0] = 6;
placer.hexagon[1] = 6;
placer.hexagon[2] = 1;
[self makePath];
return self;
}
- free
{
[nodeDescription free];
if(contour.lower.head)
freePolyline(contour.lower.head);
if(contour.upper.head)
freePolyline(contour.upper.head);
if(child)
[child free];
if(sibling)
[sibling free];
[gpath free];
if(verknuepfung)
[verknuepfung free];
if(geraeusch)
[geraeusch free];
[textCell free];
return [super free];
}
- copyFromZone:(NXZone *)zone
{
Tree *theCopy;
theCopy = [super copyFromZone:zone];
theCopy->contour.upper.head = contour.upper.tail = NULL;
theCopy->contour.lower.head = contour.lower.tail = NULL;
theCopy->updateLayout = YES;
theCopy->parent = nil;
theCopy->sibling = nil;
if(child)
theCopy->child = [child internalCopyFromZone:zone parent:theCopy];
else
theCopy->child = nil;
theCopy->selected = NO;
theCopy->gpath = [gpath copyFromZone:zone];
theCopy->nodeDescription = [nodeDescription copyFromZone:zone];
theCopy->delegate = nil;
if(verknuepfung)
theCopy->verknuepfung = [verknuepfung copyFromZone:zone];
else
theCopy->verknuepfung = nil;
if(geraeusch)
theCopy->geraeusch = [geraeusch copyFromZone:zone];
else
theCopy->geraeusch = nil;
theCopy->textCell = [textCell copyFromZone:zone];
return theCopy;
}
//************************************************************************
// layout and drawing
- involved:(const NXRect *)aRect addTo:aList link:aPath
{
NXRect rect,dummyRect;
NXPoint point,mp,parentPoint,childPoint;
NXCoord dist;
if(parent && parent->zipped)
return self;
[self getBounds:&rect];
if(aRect == NULL || NXIntersectsRect(&rect,aRect))
[aList addObject:self];
if(parent){
[parent linkPoint:&point];
[self linkPoint:&mp];
if(linkKind){
[parent linkParentPoint:&parentPoint];
[self linkChildPoint:&childPoint];
}
dist = ABS(point.y - mp.y);
if(dist > 0)
NXSetRect(&dummyRect,MIN(mp.x,point.x) - 0.5,MIN(mp.y,point.y) - 0.5,
ABS(point.x - mp.x) + 1,dist + 1);
else
NXSetRect(&dummyRect,MIN(mp.x,point.x),mp.y - 0.5,
ABS(point.x - mp.x),1.0);
if(aRect == NULL || NXIntersectsRect(aRect,&dummyRect)){
if(linkKind == 0){
[aPath moveto:point.x :point.y];
[aPath lineto:mp.x :mp.y];
} else {
[aPath moveto:point.x :point.y];
[aPath lineto:parentPoint.x :parentPoint.y];
[aPath lineto:childPoint.x :childPoint.y];
[aPath lineto:mp.x :mp.y];
}
}
}
if(child)
[child involved:aRect addTo:aList link:aPath];
if(sibling)
[sibling involved:aRect addTo:aList link:aPath];
return self;
}
- getBounds:(NXRect *)rect
{
NXRect dummy;
NXCoord dxy, dwh;
NXSetRect(rect,pos.x,pos.y,width,height);
if(selected){
dxy = MAX(5, linewidth);
dwh = MAX(10, linewidth);
rect->origin.x -= dxy;
rect->origin.y -= dxy;
rect->size.width += dwh;
rect->size.height += dwh;
}
if(shadow){
rect->size.width += deltaShadow.x;
rect->size.height += deltaShadow.y;
}
if((ending && parent) || (parentEnding && child)){
NXInsetRect(rect, -15, -15);
}
if(hasVerknuepfung){
NXSetRect(&dummy, pos.x + 23,
pos.y - linewidth - attachCellFrame.size.height - 1,
attachCellFrame.size.width,
attachCellFrame.size.height);
NXUnionRect(&dummy, rect);
}
if(zipped){
NXSetRect(&dummy, pos.x + 7,
pos.y - linewidth - zippedCellFrame.size.height - 1,
zippedCellFrame.size.width,
zippedCellFrame.size.height);
NXUnionRect(&dummy, rect);
}
if(hasSound){
NXSetRect(&dummy, pos.x + 37,
pos.y - linewidth - soundCellFrame.size.height - 1,
soundCellFrame.size.width,
soundCellFrame.size.height);
NXUnionRect(&dummy, rect);
}
return self;
}
- getTreeBounds:(NXRect *)rect lowerWidth:(NXCoord *)lowW lowerHeight:(NXCoord *)lowH
{
NXPoint p1,p2;
p1.x = pos.x - border;
p1.y = pos.y - border;
p2.x = pos.x - border;
p2.y = pos.y + height + border;
setRectFromPolygon(rect, &contour, &p1, &p2, lowW, lowH);
NXInsetRect(rect,border - linewidth,border - linewidth);
if(shadow){
rect->size.width += deltaShadow.x;
rect->size.height += deltaShadow.y;
}
return self;
}
- hit:(HitPath *)hitUPath
{
BOOL didHit;
id dummy;
didHit = [hitUPath hitPathFill:gpath];
if(didHit)
return self;
else if(child){
dummy = [child hit:hitUPath];
if(dummy)
return dummy;
else if(sibling)
return [sibling hit:hitUPath];
} else if(sibling)
return [sibling hit:hitUPath];
return nil;
}
- hitAttachment:(const NXPoint *)aPoint :(BOOL *)theAttach
{
BOOL didHit;
id dummy;
NXRect dummyRect;
*theAttach = NO;
if(hasVerknuepfung){
NXSetRect(&dummyRect,
pos.x + 23,
pos.y - linewidth - attachCellFrame.size.height - 1,
attachCellFrame.size.width,
attachCellFrame.size.height);
didHit = NXPointInRect(aPoint, &dummyRect);
} else
didHit = NO;
if(didHit){
*theAttach = YES;
return self;
}
if(hasSound){
NXSetRect(&dummyRect,
pos.x + 37,
pos.y - linewidth - soundCellFrame.size.height - 1,
soundCellFrame.size.width,
soundCellFrame.size.height);
didHit = NXPointInRect(aPoint, &dummyRect);
} else
didHit = NO;
if(didHit)
return self;
if(child){
dummy = [child hitAttachment:aPoint :theAttach];
if(dummy)
return dummy;
else if(sibling)
return [sibling hitAttachment:aPoint :theAttach];
} else if(sibling)
return [sibling hitAttachment:aPoint :theAttach];
return nil;
}
- activateAttachment:(NXEvent *)event inView:aView
{
NXPoint p;
NXRect dummyRect;
id result;
if(!hasVerknuepfung)
return self;
attachCellFrame.origin.x = pos.x + 23;
attachCellFrame.origin.y = pos.y - linewidth - attachCellFrame.size.height - 1;
[attachCell highlight:&attachCellFrame inView:aView lit:YES];
[[aView window] flushWindow];
while(event->type != NX_MOUSEUP)
event = [NXApp getNextEvent:NX_MOUSEUPMASK];
p = event->location;
[aView convertPoint:&p fromView:nil];
dummyRect = attachCellFrame;
if(NXPointInRect(&p, &dummyRect)){
[[Application workspace] openFile:[verknuepfung sourceFilename]];
result = self;
} else
result = nil;
[attachCell highlight:&attachCellFrame inView:aView lit:NO];
[[aView window] flushWindow];
return result;
}
- activateSound:(NXEvent *)event inView:aView
{
NXPoint p;
NXRect dummyRect;
id result;
if(!hasSound)
return self;
soundCellFrame.origin.x = pos.x + 37;
soundCellFrame.origin.y = pos.y - linewidth - soundCellFrame.size.height - 1;
[soundCell highlight:&soundCellFrame inView:aView lit:YES];
[[aView window] flushWindow];
while(event->type != NX_MOUSEUP)
event = [NXApp getNextEvent:NX_MOUSEUPMASK];
p = event->location;
[aView convertPoint:&p fromView:nil];
dummyRect = soundCellFrame;
if(NXPointInRect(&p, &dummyRect)){
[geraeusch play];
result = self;
} else
result = nil;
[soundCell highlight:&soundCellFrame inView:aView lit:NO];
[[aView window] flushWindow];
return result;
}
- drawNodeShadow:(DrawState *)aState
{
NXPoint dummy;
if(shadow){
if(!NXEqualColor(aState->color,shadowColor)){
aState->color = shadowColor;
NXSetColor(shadowColor);
}
dummy = pos;
pos.x += deltaShadow.x;
pos.y += deltaShadow.y;
[self changePos];
[gpath send:dps_ufill cached:YES];
pos = dummy;
[self changePos];
}
return self;
}
- drawNodeFill:(DrawState *)aState
{
if(fill){
if(!NXEqualColor(aState->color,fillColor)){
aState->color = fillColor;
NXSetColor(fillColor);
}
[gpath send:dps_ufill cached:YES];
}
return self;
}
- drawNodeEnding:(DrawState *)aState
{
NXPoint outPoint,iPoint;
float rotAngle;
Tree *ptr;
if(ending != ENDING_NONE && parent){
if(linkKind)
[self linkChildPoint:&outPoint];
else
[parent linkPoint:&outPoint];
if(!NXEqualColor(aState->color,NX_COLORBLACK)){
aState->color = NX_COLORBLACK;
NXSetColor(NX_COLORBLACK);
}
if(aState->linewidth != 0.15){
aState->linewidth = 0.15;
PSsetlinewidth(0.15);
}
[self calcIntersection:&iPoint angle:&rotAngle toPoint:&outPoint];
switch(ending){
case ENDING_ARROW:
PSWEndingSolid(arrowEnding->bboxParams, arrowEnding->sizeParams + 4,
arrowEnding->bboxOps, arrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
case ENDING_HOLLOW:
PSWEndingHollow(arrowEnding->bboxParams, arrowEnding->sizeParams + 4,
arrowEnding->bboxOps, arrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
case ENDING_DOUBLE:
PSWEndingSolid(doubleEnding->bboxParams, doubleEnding->sizeParams + 4,
doubleEnding->bboxOps, doubleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
case ENDING_SOLID:
PSWEndingSolid(circleEnding->bboxParams, circleEnding->sizeParams + 4,
circleEnding->bboxOps, circleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
case ENDING_CIRCLE:
PSWEndingHollow(circleEnding->bboxParams, circleEnding->sizeParams + 4,
circleEnding->bboxOps, circleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
default:
break;
}
}
if(parentEnding != ENDING_NONE && child){
if(linkKind){
[self linkParentPoint:&outPoint];
[self calcIntersection:&iPoint angle:&rotAngle toPoint:&outPoint];
switch(parentEnding){
case ENDING_ARROW:
PSWEndingSolid(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4,
parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
case ENDING_HOLLOW:
PSWEndingHollow(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4,
parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
case ENDING_DOUBLE:
PSWEndingSolid(parentDoubleEnding->bboxParams, parentDoubleEnding->sizeParams + 4,
parentDoubleEnding->bboxOps, parentDoubleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
case ENDING_SOLID:
PSWEndingSolid(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4,
parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
case ENDING_CIRCLE:
PSWEndingHollow(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4,
parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
default:
break;
}
} else {
for(ptr = child;ptr;ptr = ptr->sibling){
[ptr linkPoint:&outPoint];
[self calcIntersection:&iPoint angle:&rotAngle toPoint:&outPoint];
switch(parentEnding){
case ENDING_ARROW:
PSWEndingSolid(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4,
parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
case ENDING_HOLLOW:
PSWEndingHollow(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4,
parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
case ENDING_DOUBLE:
PSWEndingSolid(parentDoubleEnding->bboxParams, parentDoubleEnding->sizeParams + 4,
parentDoubleEnding->bboxOps, parentDoubleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
case ENDING_SOLID:
PSWEndingSolid(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4,
parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
case ENDING_CIRCLE:
PSWEndingHollow(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4,
parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
break;
default:
break;
}
}
}
}
return self;
}
- drawNodeOutline:(DrawState *)aState knobs:(BOOL)aBool
{
if(outline){
if(!NXEqualColor(aState->color,outlineColor)){
aState->color = outlineColor;
NXSetColor(outlineColor);
}
if(aState->linewidth != linewidth){
aState->linewidth = linewidth;
PSsetlinewidth(linewidth);
}
[gpath send:dps_ustroke cached:YES];
}
if(selected && aBool){
if(delegate)
PSselectfont(fontname,officialFontSize / [delegate docScale]);
else
PSselectfont(fontname,officialFontSize);
aState->font = nil;
if(!NXEqualColor(aState->color,NX_COLORBLACK)){
aState->color = NX_COLORBLACK;
NXSetColor(NX_COLORBLACK);
}
controlX = gpath->bbox[0];
controlY = gpath->bbox[1];
controlPts[0] = gpath->bbox[2] - gpath->bbox[0];
controlPts[1] = 0;
controlPts[2] = 0;
controlPts[3] = gpath->bbox[3] - gpath->bbox[1];
controlPts[4] = -controlPts[0];
controlPts[5] = 0;
PSWDrawControlPoints(controlX,controlY,controlPts,8,controlChars);
if(!NXEqualColor(aState->color,NX_COLORDKGRAY)){
aState->color = NX_COLORDKGRAY;
NXSetColor(NX_COLORDKGRAY);
}
PSWDrawControlPoints(controlX,controlY,controlPts,8,controlPrimChars);
}
return self;
}
- drawNodeCellsInView:aView attachments:(BOOL)aBool
{
if(hasVerknuepfung && aBool){
attachCellFrame.origin.x = pos.x + 23;
attachCellFrame.origin.y = pos.y - linewidth - attachCellFrame.size.height - 1;
[attachCell drawSelf:&attachCellFrame inView:aView];
}
if(hasSound && aBool){
soundCellFrame.origin.x = pos.x + 37;
soundCellFrame.origin.y = pos.y - linewidth - soundCellFrame.size.height - 1;
[soundCell drawSelf:&soundCellFrame inView:aView];
}
textCellFrame.origin.x = pos.x + deltaText.x;
textCellFrame.origin.y = pos.y + deltaText.y;
[textCell drawSelf:&textCellFrame inView:aView];
if(aBool && zipped){
zippedCellFrame.origin.x = pos.x + 7;
zippedCellFrame.origin.y = pos.y - linewidth - zippedCellFrame.size.height - 1;
[zippedCell drawSelf:&zippedCellFrame inView:aView];
}
return self;
}
- setOffset:(NXCoord)aX :(NXCoord)aY
{
offset.x = aX;
offset.y = aY;
return self;
}
- (NXCoord)width
{
return width;
}
- (NXCoord)height
{
return height;
}
- (Polygon *)contour
{
return &contour;
}
- layout
{
Tree *c;
if(!updateLayout)
return nil;
if(zipped){
[self layoutLeaf];
updateLayout = NO;
return self;
}
for(c = child;c;c = [c sibling])
[c layout];
if(child)
[self attachParent:[self join]];
else
[self layoutLeaf];
updateLayout = NO;
return self;
}
- getTextFrame:(NXRect *)aRect
{
NXSetRect(aRect, pos.x, pos.y + deltaText.y, width, textCellFrame.size.height);
return self;
}
//************************************************************************
// tree data structure manipulation
- sibling
{
return sibling;
}
- child
{
return child;
}
- parent
{
return parent;
}
- addTree:(Tree *)aTree
{
Tree *dummy;
id undoManager;
aTree->parent = self;
aTree->linkKind = linkKind;
aTree->biegFactor = biegFactor;
aTree->parentDistance = parentDistance;
aTree->shadow = shadow;
aTree->shadowColor = shadowColor;
aTree->border = border;
if(!child){
child = aTree;
} else {
dummy = child;
while(dummy->sibling)
dummy = dummy->sibling;
dummy->sibling = aTree;
aTree->sibling = nil;
}
[self setUpdateLayout:YES];
if(delegate){
undoManager = [delegate undoManager];
[undoManager setUndoName:"Addition"];
[undoManager setRedoName:"Deletion"];
[[undoManager setUndoTarget:self] removeChild:aTree];
[delegate declareSelection:aTree];
[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_ALL | SCROLL_TREEVIEW)];
}
return self;
}
- removeChild:(Tree *)aChild
{
Tree *dummy;
id undoManager;
int i;
if(!child || !aChild)
return self;
[self setUpdateLayout:YES];
dummy = child;
i = 0;
if(dummy == aChild)
child = aChild->sibling;
else {
while(dummy->sibling && dummy->sibling != aChild){
dummy = dummy->sibling;
i++;
}
if(dummy->sibling == aChild)
dummy->sibling = aChild->sibling;
}
aChild->parent = nil;
aChild->sibling = nil;
if(delegate){
[delegate declareSelection:nil];
undoManager = [delegate undoManager];
[undoManager freeUndoArgsOnRecordDiscard];
[undoManager setUndoName:"Deletion"];
[undoManager setRedoName:"Addition"];
[[undoManager setUndoTarget:self] insertTree:aChild at:i];
[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_ALL];
} else
[aChild free];
return self;
}
- insertTree:(Tree *)aTree at:(int)aPos
{
Tree *dummy;
id undoManager;
int i;
aTree->parent = self;
aTree->linkKind = linkKind;
aTree->biegFactor = biegFactor;
aTree->parentDistance = parentDistance;
aTree->shadow = shadow;
aTree->shadowColor = shadowColor;
aTree->border = border;
if(!child){
child = aTree;
} else {
dummy = child;
i = 0;
while(dummy->sibling && i < aPos)
dummy = dummy->sibling;
aTree->sibling = dummy->sibling;
dummy->sibling = aTree;
}
[self setUpdateLayout:YES];
if(delegate){
undoManager = [delegate undoManager];
[undoManager setUndoName:"Addition"];
[undoManager setRedoName:"Deletion"];
[[undoManager setUndoTarget:self] removeChild:aTree];
[delegate declareSelection:aTree];
[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_ALL | SCROLL_TREEVIEW)];
}
return self;
}
//************************************************************************
// properties
- attachFile:(const char *)aFileName
{
NXRect r;
BOOL attachUpdate = YES;
if(verknuepfung){
[verknuepfung free];
attachUpdate = NO;
}
verknuepfung = [[NXDataLink allocFromZone:[self zone]] initLinkedToFile:aFileName];
if(attachUpdate){
hasVerknuepfung = YES;
[self getBounds:&r];
[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
}
return self;
}
- breakAttachment
{
NXRect r;
if(!hasVerknuepfung)
return self;
[self getBounds:&r];
hasVerknuepfung = NO;
[verknuepfung free];
verknuepfung = nil;
[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
return self;
}
- (BOOL)attached
{
return hasVerknuepfung;
}
- attachSound:(const char *)aSoundName
{
NXRect r;
BOOL soundUpdate = YES;
if(geraeusch){
[geraeusch free];
soundUpdate = NO;
}
geraeusch = [[Sound allocFromZone:[self zone]] initFromSoundfile:aSoundName];
if(soundUpdate){
hasSound = YES;
[self getBounds:&r];
[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
}
return self;
}
- breakSound
{
NXRect r;
if(!hasSound)
return self;
[self getBounds:&r];
hasSound = NO;
[geraeusch free];
geraeusch = nil;
[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
return self;
}
- (BOOL)hasSound
{
return hasSound;
}
- attachedSound
{
return geraeusch;
}
- setSound:aSound
{
NXRect r;
BOOL soundUpdate = YES;
if(geraeusch){
[geraeusch free];
soundUpdate = NO;
}
geraeusch = aSound;
if(soundUpdate){
hasSound = YES;
[self getBounds:&r];
[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
}
return self;
}
- applyStyleToSubtree
{
Properties props;
if(child){
[self getProps:&props];
[child promoteProps:&props];
[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
}
return self;
}
- promoteProps:(Properties *)aProps
{
outline = aProps->outline;
if([textCell font] != aProps->font){
[textCell setFont:aProps->font];
[self changeText];
[self setUpdateLayout:YES];
}
if(pathKind != aProps->pathKind){
[gpath resetFill];
pathKind = aProps->pathKind;
[self makePath];
[self setUpdateLayout:YES];
}
fillColor = aProps->fillColor;
outlineColor = aProps->outlineColor;
[textCell setTextColor:aProps->textColor];
linewidth = aProps->linewidth;
if(ending != aProps->ending){
ending = aProps->ending;
}
if(parentEnding != aProps->parentEnding){
parentEnding = aProps->parentEnding;
}
if(sibling)
[sibling promoteProps:aProps];
if(child)
[child promoteProps:aProps];
return self;
}
- getProps:(Properties *)aProps
{
aProps->outline = outline;
aProps->font = [textCell font];
aProps->pathKind = pathKind;
aProps->fillColor = fillColor;
aProps->outlineColor = outlineColor;
aProps->textColor = [textCell textColor];
aProps->linewidth = linewidth;
aProps->ending = ending;
aProps->parentEnding = parentEnding;
return self;
}
- changeTo:(Properties *)aProps
{
NXRect r, dummyRect, pr;
BOOL checkResize, makeUnion;
[self getBounds:&r];
checkResize = NO;
makeUnion = NO;
outline = aProps->outline;
if([textCell font] != aProps->font){
[textCell setFont:aProps->font];
[self changeText];
[self setUpdateLayout:YES];
checkResize = YES;
}
if(pathKind != aProps->pathKind){
[gpath resetFill];
pathKind = aProps->pathKind;
[self makePath];
[self setUpdateLayout:YES];
checkResize = YES;
}
fillColor = aProps->fillColor;
outlineColor = aProps->outlineColor;
[textCell setTextColor:aProps->textColor];
if(ending != aProps->ending){
ending = aProps->ending;
makeUnion = YES;
}
if(parentEnding != aProps->parentEnding){
parentEnding = aProps->parentEnding;
if(parent){
[parent getBounds:&pr];
[delegate updateViewsDirty:YES rect:&pr flag:UPDATE_TREEVIEW];
}
}
if(aProps->linewidth != linewidth){
linewidth = aProps->linewidth;
makeUnion = YES;
}
if(makeUnion){
[self getBounds:&dummyRect];
NXUnionRect(&dummyRect, &r);
}
if(checkResize)
[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
else
[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
return self;
}
//************************************************************************
// tree properties
- (NXCoord)border
{
return border;
}
- setBorder:(NXCoord)aBorder
{
border = aBorder;
[self setUpdateLayout:YES];
if(child)
[child setBorder:aBorder];
if(sibling)
[sibling setBorder:aBorder];
if(!parent)
[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
return self;
}
- (NXCoord)parentDistance
{
return parentDistance;
}
- setParentDistance:(NXCoord)aParentDistance
{
parentDistance = aParentDistance;
[self setUpdateLayout:YES];
if(child)
[child setParentDistance:aParentDistance];
if(sibling)
[sibling setParentDistance:aParentDistance];
if(!parent)
[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
return self;
}
- (BOOL)shadow
{
return shadow;
}
- setShadow:(BOOL)aShadow
{
shadow = aShadow;
if(child)
[child setShadow:aShadow];
if(sibling)
[sibling setShadow:aShadow];
if(!parent)
[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
return self;
}
- (NXColor)shadowColor
{
return shadowColor;
}
- setShadowColor:(NXColor)aColor
{
shadowColor = aColor;
if(child)
[child setShadowColor:aColor];
if(sibling)
[sibling setShadowColor:aColor];
if(!parent)
[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW];
return self;
}
- (int)linkKind
{
return linkKind;
}
- setLinkKind:(int)aLinkKind
{
linkKind = aLinkKind;
if(child)
[child setLinkKind:aLinkKind];
if(sibling)
[sibling setLinkKind:aLinkKind];
if(!parent)
[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW];
return self;
}
- changeLinkKind
{
linkKind = (linkKind + 1) % 2;
if(child)
[child changeLinkKind];
if(sibling)
[sibling changeLinkKind];
if(!parent)
[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW];
return self;
}
- (float)biegFactor
{
return biegFactor;
}
- setBiegFactor:(float)aBiegFactor
{
biegFactor = aBiegFactor;
if(child)
[child setBiegFactor:aBiegFactor];
if(sibling)
[sibling setBiegFactor:aBiegFactor];
if(!parent)
[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW];
return self;
}
//************************************************************************
// node properties
- (const char *)label
{
return [textCell stringValue];
}
- setLabel:(const char *)aLabel
{
[textCell setStringValue:aLabel];
[self changeText];
[self setUpdateLayout:YES];
[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
return self;
}
- setRTFDescription:(const char *)aDescription
{
[nodeDescription setRTF:aDescription];
[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TEXTVIEW];
return self;
}
- setDescription:(const char *)aDescription
{
[nodeDescription setAscii:aDescription];
[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TEXTVIEW];
return self;
}
- changeDescriptionFrom:aTextView
{
[nodeDescription setRTFFromTextView:aTextView];
[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_NONE];
return self;
}
- putDescriptionIn:aTextView
{
[aTextView replaceSelWithRichText:[nodeDescription stream]];
[nodeDescription freeStream];
return self;
}
- (int)pathKind
{
return pathKind;
}
- setPathKind:(int)aPathKind
{
[gpath resetFill];
pathKind = aPathKind;
[self makePath];
[self setUpdateLayout:YES];
[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
return self;
}
- (BOOL)outline
{
return outline;
}
- setOutline:(BOOL)aOutline
{
NXRect r;
outline = aOutline;
[self getBounds:&r];
[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
return self;
}
- font
{
return [textCell font];
}
- changeFont:sender
{
[textCell setFont:[sender convertFont:[textCell font]]];
[self changeText];
[self setUpdateLayout:YES];
[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
return self;
}
- (NXColor)fillColor
{
return fillColor;
}
- setFillColor:(NXColor)aColor
{
NXRect r;
fillColor = aColor;
[self getBounds:&r];
[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
return self;
}
- (NXColor)outlineColor
{
return outlineColor;
}
- setOutlineColor:(NXColor)aColor
{
NXRect r;
outlineColor = aColor;
[self getBounds:&r];
[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
return self;
}
- (NXColor)textColor
{
return [textCell textColor];
}
- setTextColor:(NXColor)aColor
{
NXRect r;
[textCell setTextColor:aColor];
[self getBounds:&r];
[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
return self;
}
- (float)linewidth
{
return linewidth;
}
- setLinewidth:(float)aLinewidth
{
NXRect r;
if(aLinewidth > linewidth){
linewidth = aLinewidth;
[self getBounds:&r];
} else {
[self getBounds:&r];
linewidth = aLinewidth;
}
[delegate updateViewsDirty:YES rect:&r flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
return self;
}
- (BOOL)zipped
{
return zipped;
}
- changeZipped
{
zipped = ! zipped;
[self setUpdateLayout:YES];
[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
return self;
}
- (int)ending
{
return ending;
}
- setEnding:(int)aEnding
{
NXRect r,br;
[self getBounds:&br];
ending = aEnding;
[self getBounds:&r];
NXUnionRect(&br,&r);
[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
return self;
}
- (int)parentEnding;
{
return parentEnding;
}
- setParentEnding:(int)aEnding
{
NXRect r,br;
[self getBounds:&br];
parentEnding = aEnding;
[self getBounds:&r];
NXUnionRect(&br,&r);
[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
return self;
}
//************************************************************************
// misc
- (BOOL)selected
{
return selected;
}
- setSelected:(BOOL)aBOOL
{
selected = aBOOL;
return self;
}
- setUpdateLayout:(BOOL)aBool
{
Tree *dummy;
updateLayout = aBool;
if(aBool)
if(parent){
[parent setUpdateLayout:YES];
dummy = [parent child];
dummy->updateLayout = YES;
while([dummy sibling]){
dummy = [dummy sibling];
dummy->updateLayout = YES;
}
}
return self;
}
- positionFrom:(const NXPoint *)aPoint
{
pos.x = aPoint->x + offset.x;
pos.y = aPoint->y + offset.y;
[self changePos];
if(child)
[child positionFrom:&pos];
if(sibling)
[sibling positionFrom:&pos];
return self;
}
- setPositionTo:(const NXPoint *)aPoint
{
pos.x = aPoint->x;
pos.y = aPoint->y;
[self changePos];
if(child)
[child positionFrom:&pos];
if(sibling)
[sibling positionFrom:&pos];
return self;
}
- linkPoint:(NXPoint *)aPoint
{
aPoint->x = pos.x + deltaLink.x;
aPoint->y = pos.y + deltaLink.y;
return self;
}
- linkParentPoint:(NXPoint *)aPoint
{
aPoint->x = pos.x + width + (parentDistance + border) * (1 - biegFactor) / 2;
aPoint->y = pos.y + deltaLink.y;
return self;
}
- linkChildPoint:(NXPoint *)aPoint
{
aPoint->x = pos.x - (parentDistance + border) * (1 - biegFactor) / 2;
aPoint->y = pos.y + deltaLink.y;
return self;
}
- delegate
{
return delegate;
}
- setDelegate:aDelegate
{
id oldDelegate;
oldDelegate = delegate;
delegate = aDelegate;
return oldDelegate;
}
- setDelegateRecursive:aDelegate
{
delegate = aDelegate;
if(child)
[child setDelegateRecursive:aDelegate];
if(sibling)
[sibling setDelegateRecursive:aDelegate];
return self;
}
- textDidEnd:textObject endChar:(unsigned short)endChar
{
int maxlen,length;
char *buffer;
NXStream *stream;
[textObject removeFromSuperview];
[textObject setDelegate:nil];
[textObject setSel:0 :0];
if([textObject textLength]){
stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
[textObject writeText:stream];
NXGetMemoryBuffer(stream, &buffer, &length, &maxlen);
[textCell setStringValue:buffer];
NXCloseMemory(stream, NX_FREEBUFFER);
}
[self changeText];
[self setUpdateLayout:YES];
[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
return self;
}
//*************************************************************************
// streaming to diagram2 format
- (unsigned)dgSymbolNr
{
return dgSymbolNr;
}
- dgWriteSymbol:(NXStream *)stream buffer:(char *)buffer filename:(const char *)aName
{
id font;
char rtfItalic[3], rtfBold[3];
unsigned i;
int err;
dgSymbolNr = [Tree dgNextNr];
sprintf(buffer,"symbol %d\n", dgSymbolNr);
NXWrite(stream,buffer,strlen(buffer));
sprintf(buffer,"\tshape \"%s\"\n", dgShapeSymbolName[pathKind]);
NXWrite(stream,buffer,strlen(buffer));
sprintf(buffer,"\tlocation %.2f %.2f\n",pos.x,pos.y);
NXWrite(stream,buffer,strlen(buffer));
sprintf(buffer,"\tsize %.2f %.2f\n",width,height);
NXWrite(stream,buffer,strlen(buffer));
if(outline)
NXWrite(stream,"\tframed\n", 8);
if(fill)
NXWrite(stream,"\tfilled\n", 8);
if(shadow)
NXWrite(stream,"\tshadowed\n", 10);
if(outline){
i = 0;
while(i < [dgColorList colorCount] &&
!NXEqualColor(outlineColor,[dgColorList colorNamed:[dgColorList nameOfColorAt:i]]))
i++;
if(i == [dgColorList colorCount]){
sprintf(buffer,"woodColorIndexed%d",i);
[dgColorList setColorNamed:buffer color:outlineColor];
}
sprintf(buffer,"\tframeColor colorIndex %d\n",i);
NXWrite(stream,buffer,strlen(buffer));
}
if(fill){
i = 0;
while(i < [dgColorList colorCount] &&
!NXEqualColor(fillColor,[dgColorList colorNamed:[dgColorList nameOfColorAt:i]]))
i++;
if(i == [dgColorList colorCount]){
sprintf(buffer,"woodColorIndexed%d",i);
[dgColorList setColorNamed:buffer color:fillColor];
}
sprintf(buffer,"\tfillColor colorIndex %d\n",i);
NXWrite(stream,buffer,strlen(buffer));
}
if(shadow){
i = 0;
while(i < [dgColorList colorCount] &&
!NXEqualColor(shadowColor,[dgColorList colorNamed:[dgColorList nameOfColorAt:i]]))
i++;
if(i == [dgColorList colorCount]){
sprintf(buffer,"woodColorIndexed%d",i);
[dgColorList setColorNamed:buffer color:shadowColor];
}
sprintf(buffer,"\tshadowColor colorIndex %d\n",i);
NXWrite(stream,buffer,strlen(buffer));
}
sprintf(buffer,"\tlineWidth %.2f\n",linewidth);
NXWrite(stream,buffer,strlen(buffer));
if(hasVerknuepfung){
sprintf(buffer,"\tattachment \"%s\"\n",[verknuepfung sourceFilename]);
NXWrite(stream,buffer,strlen(buffer));
}
if(hasSound){
sprintf(buffer,"%s/Sound%d.snd",aName,dgSymbolNr);
err = [geraeusch writeSoundfile:buffer];
if(err)
NXRunAlertPanel("Save Sound","Cannot write %s","OK",NULL,NULL,buffer);
else {
sprintf(buffer,"\tsoundFile \"Sound%d.snd\"\n",dgSymbolNr);
NXWrite(stream,buffer,strlen(buffer));
}
}
font = [textCell font];
if(strstr([font displayName], "Bold"))
strcpy(rtfBold,"b");
else
strcpy(rtfBold,"b0");
if(strstr([font displayName], "Italic") || strstr([font displayName], "Oblique"))
strcpy(rtfItalic,"i");
else
strcpy(rtfItalic,"i0");
sprintf(buffer, dgRtfControlName, [font familyName], rtfBold, rtfItalic, [font pointSize] * 2, [textCell stringValue]);
NXWrite(stream,buffer,strlen(buffer));
NXWrite(stream,"\n",1);
NXWrite(stream,"\ttextPlacement middle\n", 22);
NXWrite(stream,dgEndName, strlen(dgEndName));
if(child)
[child dgWriteSymbol:stream buffer:buffer filename:aName];
if(sibling)
[sibling dgWriteSymbol:stream buffer:buffer filename:aName];
return self;
}
- dgWriteVertex:(NXStream *)stream buffer:(char *)buffer
{
NXPoint p2,p3;
if(parent){
dgVertexNr[0] = [Tree dgNextNr];
sprintf(buffer,"vertex %d\n",dgVertexNr[0]);
NXWrite(stream,buffer,strlen(buffer));
NXWrite(stream,dgEndName, strlen(dgEndName));
dgVertexNr[1] = [Tree dgNextNr];
sprintf(buffer,"vertex %d\n",dgVertexNr[1]);
NXWrite(stream,buffer,strlen(buffer));
NXWrite(stream,dgEndName, strlen(dgEndName));
if(linkKind){
[parent linkParentPoint:&p3];
[self linkChildPoint:&p2];
dgVertexNr[2] = [Tree dgNextNr];
sprintf(buffer,"vertex %d\n",dgVertexNr[2]);
NXWrite(stream,buffer,strlen(buffer));
sprintf(buffer,"\tlocation %.2f %.2f\n", p2.x, p2.y);
NXWrite(stream,buffer,strlen(buffer));
NXWrite(stream,dgEndName, strlen(dgEndName));
dgVertexNr[3] = [Tree dgNextNr];
sprintf(buffer,"vertex %d\n",dgVertexNr[3]);
NXWrite(stream,buffer,strlen(buffer));
sprintf(buffer,"\tlocation %.2f %.2f\n", p3.x, p3.y);
NXWrite(stream,buffer,strlen(buffer));
NXWrite(stream,dgEndName, strlen(dgEndName));
}
}
if(child)
[child dgWriteVertex:stream buffer:buffer];
if(sibling)
[sibling dgWriteVertex:stream buffer:buffer];
return self;
}
- dgWriteLine:(NXStream *)stream buffer:(char *)buffer
{
if(parent){
sprintf(buffer,"line %d\n",[Tree dgNextNr]);
NXWrite(stream,buffer,strlen(buffer));
sprintf(buffer,"\tfrom %d\n",dgSymbolNr);
NXWrite(stream,buffer,strlen(buffer));
sprintf(buffer,"\tto %d\n",[parent dgSymbolNr]);
NXWrite(stream,buffer,strlen(buffer));
if(ending != ENDING_NONE){
switch(ending){
case ENDING_ARROW:
sprintf(buffer,"\ttailType arrow\n");
break;
case ENDING_HOLLOW:
sprintf(buffer,"\ttailType hollowArrow\n");
break;
case ENDING_DOUBLE:
sprintf(buffer,"\ttailType doubleArrow\n");
break;
case ENDING_SOLID:
sprintf(buffer,"\ttailType solidCircle\n");
break;
case ENDING_CIRCLE:
sprintf(buffer,"\ttailType circle\n");
break;
default:
break;
}
NXWrite(stream,buffer,strlen(buffer));
}
if([parent parentEnding] != ENDING_NONE){
switch([parent parentEnding]){
case ENDING_ARROW:
sprintf(buffer,"\theadType arrow\n");
break;
case ENDING_HOLLOW:
sprintf(buffer,"\theadType hollowArrow\n");
break;
case ENDING_DOUBLE:
sprintf(buffer,"\theadType doubleArrow\n");
break;
case ENDING_SOLID:
sprintf(buffer,"\theadType solidCircle\n");
break;
case ENDING_CIRCLE:
sprintf(buffer,"\theadType circle\n");
break;
default:
break;
}
NXWrite(stream,buffer,strlen(buffer));
}
sprintf(buffer,"\t%d\n",dgVertexNr[0]);
NXWrite(stream,buffer,strlen(buffer));
if(linkKind){
sprintf(buffer,"\t%d\n",dgVertexNr[2]);
NXWrite(stream,buffer,strlen(buffer));
sprintf(buffer,"\t%d\n",dgVertexNr[3]);
NXWrite(stream,buffer,strlen(buffer));
}
sprintf(buffer,"\t%d\n",dgVertexNr[1]);
NXWrite(stream,buffer,strlen(buffer));
NXWrite(stream,dgEndName, strlen(dgEndName));
}
if(child)
[child dgWriteLine:stream buffer:buffer];
if(sibling)
[sibling dgWriteLine:stream buffer:buffer];
return self;
}
+ saveToD2Tree:aTree fromView:aView toFile:(const char *)aFile printInfo:aPrintInfo
{
NXStream *stream;
NXTypedStream *typedStream;
char fileNameBuffer[MAXPATHLEN],commandBuffer[MAXPATHLEN + 20];
char buffer[1024];
NXRect dummyRect;
dgColorList = [[NXColorList alloc] init];
[aView getBounds:&dummyRect];
dgNextNr = 0;
stream = NXOpenMemory(NULL,0,NX_WRITEONLY);
NXWrite(stream, dgDocuBegin,strlen(dgDocuBegin));
strcpy(fileNameBuffer,aFile);
sprintf(commandBuffer,"mkdir %s",fileNameBuffer);
system(commandBuffer);
if(aTree){
NXWrite(stream, dgSymbolComment,strlen(dgSymbolComment));
[aTree dgWriteSymbol:stream buffer:buffer filename:aFile];
NXWrite(stream, dgVertexComment,strlen(dgVertexComment));
[aTree dgWriteVertex:stream buffer:buffer];
NXWrite(stream, dgLineComment,strlen(dgLineComment));
[aTree dgWriteLine:stream buffer:buffer];
}
strcpy(fileNameBuffer,aFile);
strcat(fileNameBuffer,"/DiagramText");
NXSaveToFile(stream, fileNameBuffer);
NXCloseMemory(stream,NX_FREEBUFFER);
strcpy(fileNameBuffer,aFile);
strcat(fileNameBuffer,"/PrintInfo");
stream = NXOpenMemory(NULL,0,NX_WRITEONLY);
typedStream = NXOpenTypedStream(stream,NX_WRITEONLY);
NXWriteRootObject(typedStream,aPrintInfo);
NXCloseTypedStream(typedStream);
NXSeek(stream, - 1, NX_FROMCURRENT);
NXWrite(stream,dgPrintInfoEnd,6);
NXSaveToFile(stream, fileNameBuffer);
NXCloseMemory(stream,NX_FREEBUFFER);
strcpy(fileNameBuffer,aFile);
strcat(fileNameBuffer,"/Colors.clr");
[dgColorList saveTo:fileNameBuffer];
[dgColorList free];
dgColorList = nil;
return self;
}
+ (unsigned)dgNextNr
{
dgNextNr++;
return dgNextNr;
}
//*************************************************************************
// streaming
- writeStyleToPBStream:(NXTypedStream *)stream
{
NXWriteTypes(stream,"iiiif",&outline,&pathKind,&ending,&parentEnding,&linewidth);
NXWriteColor(stream,fillColor);
NXWriteColor(stream,outlineColor);
NXWriteColor(stream,[textCell textColor]);
NXWriteObject(stream,[textCell font]);
return self;
}
- readStyleFromPBStream:(NXTypedStream *)stream
{
Properties props;
NXReadTypes(stream,"iiiif",&(props.outline),&(props.pathKind),&(props.ending),&(props.parentEnding),&(props.linewidth));
props.fillColor = NXReadColor(stream);
props.outlineColor = NXReadColor(stream);
props.textColor = NXReadColor(stream);
props.font = NXReadObject(stream);
[self changeTo:&props];
return self;
}
- writeToPBStream:(NXTypedStream *)stream
{
Tree *tmpParent, *tmpSibling;
tmpParent = parent;
tmpSibling = sibling;
parent = sibling = nil;
NXWriteRootObject(stream, self);
parent = tmpParent;
sibling = tmpSibling;
return self;
}
- writeToMMAPB:(NXStream *)stream
{
if(child){
NXWrite(stream,"{",1);
NXWrite(stream,[textCell stringValue],strlen([textCell stringValue]));
NXWrite(stream,", ",2);
[child writeToMMAPB:stream];
NXWrite(stream,"}",1);
} else {
NXWrite(stream,[textCell stringValue],strlen([textCell stringValue]));
}
if(sibling){
NXWrite(stream,", ",2);
[sibling writeToMMAPB:stream];
}
return self;
}
- write:(NXTypedStream *)stream
{
//BOOL hasChild,hasSibling;
int hasChild,hasSibling;
id retval;
NX_DURING
[super write:stream];
hasChild = (child ? YES : NO);
hasSibling = (sibling ? YES :NO);
NXWriteTypes(stream,"ii",&hasVerknuepfung,&hasSound);
NXWriteTypes(stream,"iiff",&hasChild,&hasSibling,&parentDistance,&border);
NXWriteTypes(stream,"iiiiiff",&zipped,&shadow,&outline,&pathKind,&linkKind,&linewidth,&biegFactor);
NXWriteTypes(stream,"ii",&ending,&parentEnding);
NXWriteColor(stream,fillColor);
NXWriteColor(stream,outlineColor);
NXWriteColor(stream,shadowColor);
NXWriteObject(stream,textCell);
NXWriteObject(stream,nodeDescription);
NXWriteObjectReference(stream, parent);
if(hasChild)
NXWriteObject(stream, child);
if(hasSibling)
NXWriteObject(stream, sibling);
if(hasVerknuepfung)
NXWriteObject(stream,verknuepfung);
if(hasSound)
NXWriteObject(stream,geraeusch);
retval = self;
NX_HANDLER
retval = nil;
NX_ENDHANDLER
return retval;
}
- read:(NXTypedStream *)stream
{
//BOOL hasChild,hasSibling;
int hasChild,hasSibling;
id retval;
NX_DURING
NXSetTypedStreamZone(stream, [self zone]);
[super read:stream];
NXReadTypes(stream,"ii",&hasVerknuepfung,&hasSound);
NXReadTypes(stream,"iiff", &hasChild,&hasSibling,&parentDistance,&border);
if(NXTypedStreamClassVersion(stream, "Tree") == [Tree version])
NXReadTypes(stream,"iiiiiff",&zipped,&shadow,&outline,&pathKind,&linkKind,&linewidth,&biegFactor);
else {
NXReadTypes(stream,"iiiiif",&zipped,&shadow,&outline,&pathKind,&linkKind,&linewidth);
biegFactor = 0.33;
}
NXReadTypes(stream,"ii",&ending,&parentEnding);
fillColor = NXReadColor(stream);
outlineColor = NXReadColor(stream);
shadowColor = NXReadColor(stream);
textCell = NXReadObject(stream);
nodeDescription = NXReadObject(stream);
parent = NXReadObject(stream);
if(hasChild)
child = NXReadObject(stream);
else
child = nil;
if(hasSibling)
sibling = NXReadObject(stream);
else
sibling = nil;
if(hasVerknuepfung)
verknuepfung = NXReadObject(stream);
if(hasSound)
geraeusch = NXReadObject(stream);
retval = self;
NX_HANDLER
retval = nil;
NX_ENDHANDLER
return retval;
}
- awake
{
[super awake];
contour.lower.head = contour.upper.head = NULL;
contour.lower.tail = contour.upper.tail = NULL;
offset.x = offset.y = 0.0;
pos.x = pos.y = 0.0;
gpath = [[UPath allocFromZone:[self zone]] init];
deltaShadow.x = deltaShadow.y = 3;
placer.rect[0] = placer.rect[1] = 6;
placer.roundRect[0] = placer.roundRect[1] = 6;
placer.roundRect[2] = 3;
placer.ellipse[0] = 6;
placer.ellipse[1] = 4;
placer.rhomb[0] = 12;
placer.rhomb[1] = 3;
placer.hexagon[0] = 6;
placer.hexagon[1] = 6;
placer.hexagon[2] = 1;
[self makePath];
selected = NO;
updateLayout = YES;
delegate = nil;
fill = YES;
return self;
}
//*************************************************************************
// SearchableText support
- searchTreeFor:(const char *)pattern untilNode:endNode
reverse:(BOOL)rev regexpr:(BOOL)regexpr cases:(BOOL)cases position:(int *)positionS size:(int *)sizeS
{
MiscString *labelS;
id result;
int resultPos;
result = nil;
resultPos = -1;
labelS = [[MiscString alloc] initString:[self label]];
if(regexpr)
resultPos = [labelS spotOfRegex:pattern caseSensitive:cases length:sizeS];
else
resultPos = [labelS spotOfStr:pattern caseSensitive:cases];
if(resultPos > -1){
*positionS = resultPos;
if(!regexpr)
*sizeS = strlen(pattern);
result = self;
} else if(self == endNode)
return nil;
else {
if(rev)
result = [[self previousInDepth] searchTreeFor:pattern untilNode:endNode
reverse:rev regexpr:regexpr cases:cases position:positionS size:sizeS];
else
result = [[self nextInDepth] searchTreeFor:pattern untilNode:endNode
reverse:rev regexpr:regexpr cases:cases position:positionS size:sizeS];
}
[labelS free];
return result;
}
- replaceTreeFor:(const char *)pattern with:(const char *)replacement untilNode:endNode
regexpr:(BOOL)regexpr cases:(BOOL)cases result:(int *)result
{
MiscString *labelS;
int nr;
nr = 0;
labelS = [[MiscString alloc] initString:[self label]];
if(regexpr)
nr = [labelS replaceEveryOccurrenceOfRegex:pattern with:replacement caseSensitive:cases];
else
nr = [labelS replaceEveryOccurrenceOf:pattern with:replacement caseSensitive:cases];
if(nr > 0){
[textCell setStringValue:[labelS stringValue]];
[self changeText];
[self setUpdateLayout:YES];
}
*result += nr;
if(self != endNode)
[[self nextInDepth] replaceTreeFor:pattern with:replacement untilNode:endNode
regexpr:regexpr cases:cases result:result];
[labelS free];
return self;
}
- nextInDepth
{
Tree *r, *t;
if(child)
r = child;
else if(sibling)
r = sibling;
else {
t = self;
while([t parent] && ![t sibling])
t = [t parent];
if([t sibling])
r = [t sibling];
else
r = t;
}
return r;
}
- previousInDepth
{
Tree *r, *t;
if(!parent)
return [self lastInDepth];
if([parent child] == self)
return parent;
else {
r = [parent child];
t = [r sibling];
while(t != self){
r = t;
t = [t sibling];
}
return [r lastInDepth];
}
}
- lastInDepth
{
Tree *r, *t;
r = self;
t = child;
while(t){
r = t;
while(t){
r = t;
t = [t sibling];
}
t = [r child];
}
return r;
}
- (BOOL)replaceLabelSelectionWith:(const char *)replacement pos:(int)aPos size:(int)aSize
{
MiscString *labelS, *pattern;
int nr;
BOOL result = NO;
labelS = [[MiscString alloc] initString:[self label]];
if(aPos + aSize > [labelS length])
return result;
pattern = [labelS midFrom:aPos to:(aPos + aSize - 1) fromZone:[self zone]];
nr = [labelS replaceEveryOccurrenceOfString:pattern with:replacement caseSensitive:YES];
if(nr > 0){
[textCell setStringValue:[labelS stringValue]];
[self changeText];
[self setUpdateLayout:YES];
result = YES;
}
[labelS free];
return result;
}
@end
@implementation Tree(PrivateMethods)
- attachParent:(NXCoord)h
{
NXCoord x,y1,y2;
x = border + parentDistance;
y2 = (h - height) / 2 - border;
y1 = y2 + height + 2 * border - h;
[child setOffset:x + width :y1];
contour.upper.head = allocLine(width,0,allocLine(x,y1,contour.upper.head,[self zone]),[self zone]);
contour.lower.head = allocLine(width,0,allocLine(x,y2,contour.lower.head,[self zone]),[self zone]);
return self;
}
- layoutLeaf
{
if(contour.lower.head)
freePolyline(contour.lower.head);
if(contour.upper.head)
freePolyline(contour.upper.head);
contour.upper.tail = allocLine(width + 2 * border,0,NULL,[self zone]);
contour.upper.head = contour.upper.tail;
contour.lower.tail = allocLine(0, - height - 2 * border,NULL,[self zone]);
contour.lower.head = allocLine(width + 2 * border,0,contour.lower.tail,[self zone]);
return self;
}
- (NXCoord)join
{
Tree *c;
NXCoord d,h,sum;
Polygon dummy;
c = child;
if(contour.lower.head)
freePolyline(contour.lower.head);
if(contour.upper.head)
freePolyline(contour.upper.head);
if(c->contour.upper.head)
contour.upper.head = copyPolyline(c->contour.upper.head,&(contour.upper.tail),[self zone]);
else
contour.upper.head = contour.upper.tail = NULL;
if(c->contour.lower.head)
contour.lower.head = copyPolyline(c->contour.lower.head,&(contour.lower.tail),[self zone]);
else
contour.lower.head = contour.lower.tail = NULL;
sum = h = [c height] + 2 * [c border];
c = [c sibling];
while(c) {
if(c->contour.upper.head)
dummy.upper.head = copyPolyline(c->contour.upper.head,&(dummy.upper.tail),[self zone]);
else
dummy.upper.head = dummy.upper.tail = NULL;
if(c->contour.lower.head)
dummy.lower.head = copyPolyline(c->contour.lower.head,&(dummy.lower.tail),[self zone]);
else
dummy.lower.head = dummy.lower.tail = NULL;
d = merge(&contour,&dummy,[self zone]);
[c setOffset:0 :d + h];
h = [c height] + 2 * [c border];
sum += d + h;
c = [c sibling];
}
return sum;
}
- makePath
{
float r;
[self calcWidthHeight];
switch(pathKind){
case RECT_NODETYPE:
[gpath moveto:pos.x :pos.y];
[gpath rlineto:width :0.0];
[gpath rlineto:0.0 :height];
[gpath rlineto:-width :0.0];
[gpath rlineto:0.0 :-height];
break;
case ROUNDRECT_NODETYPE:
r = MIN(width / 3, height / 3);
[gpath moveto:pos.x :pos.y + r];
[gpath lineto:pos.x :pos.y + height - r];
[gpath curveto:pos.x :pos.y + height - r + r * 0.5519
:pos.x + r - r * 0.5519 :pos.y + height
:pos.x + r :pos.y + height];
[gpath lineto:pos.x + width - r :pos.y + height];
[gpath curveto:pos.x + width - r + r * 0.5519 :pos.y + height
:pos.x + width :pos.y + height - r + r * 0.5519
:pos.x + width :pos.y + height - r];
[gpath lineto:pos.x + width :pos.y + r];
[gpath curveto:pos.x + width :pos.y + r - r * 0.5519
:pos.x + width - r + r * 0.5519 :pos.y
:pos.x + width - r :pos.y];
[gpath lineto:pos.x + r :pos.y];
[gpath curveto:pos.x + r - r * 0.5519 :pos.y
:pos.x :pos.y + r - r * 0.5519
:pos.x :pos.y + r];
/* [gpath moveto:(pos.x + height / 3) :pos.y];
[gpath arct:(pos.x + width) :pos.y :(pos.x + width) :(pos.y + height) :height / 3];
[gpath arct:(pos.x + width) :(pos.y + height) :pos.x :(pos.y + height) :height / 3];
[gpath arct:pos.x :(pos.y + height) :pos.x :pos.y : height / 3];
[gpath arct:pos.x :pos.y :(pos.x + width) :pos.y :height / 3]; */
break;
case ELLIPSE_NODETYPE:
[gpath moveto:(pos.x + width) :(pos.y + height / 2)];
[gpath curveto:(pos.x + width) :(pos.y + height / 2 * 0.445)
:(pos.x + width / 2 * 1.555) :pos.y
:(pos.x + width / 2) :pos.y];
[gpath curveto:(pos.x + width / 2 * 0.445) :pos.y
:pos.x :(pos.y + height / 2 * 0.445)
:pos.x :(pos.y + height / 2)];
[gpath curveto:pos.x :(pos.y + height /2 * 1.555)
:(pos.x + width / 2 * 0.445) :(pos.y + height)
:(pos.x + width / 2) :(pos.y + height)];
[gpath curveto:(pos.x + width / 2 * 1.555) :(pos.y + height)
:(pos.x + width) :(pos.y + height / 2 * 1.555)
:(pos.x + width) :(pos.y + height / 2)];
break;
case RHOMB_NODETYPE:
[gpath moveto:pos.x + width / 2 :pos.y];
[gpath rlineto:width / 2 :height / 2];
[gpath rlineto:-width / 2 :height / 2];
[gpath rlineto:-width / 2 :-height / 2];
[gpath rlineto:width / 2 :-height / 2];
break;
case HEXAGON_NODETYPE:
[gpath moveto:pos.x + placer.hexagon[0] :pos.y];
[gpath rlineto:(width - 2 * placer.hexagon[0]) :0];
[gpath rlineto:placer.hexagon[0] :height /2];
[gpath rlineto:-placer.hexagon[0] :height /2];
[gpath rlineto:(- width + 2 * placer.hexagon[0]) :0];
[gpath rlineto:-placer.hexagon[0] :-height / 2];
[gpath rlineto:placer.hexagon[0] :-height / 2];
break;
default:
break;
}
return self;
}
- changeText
{
[self calcWidthHeight];
[self fillGPathParams];
gpath->bbox[2] = gpath->bbox[0] + width;
gpath->bbox[3] = gpath->bbox[1] + height;
return self;
}
- changePos
{
NXPoint delta;
[self fillGPathParams];
switch(pathKind){
case RECT_NODETYPE:
delta.x = gpath->params[0] - gpath->bbox[0];
delta.y = gpath->params[1] - gpath->bbox[1];
break;
case ROUNDRECT_NODETYPE:
delta.x = gpath->params[0] - gpath->bbox[0];
delta.y = gpath->params[23] - gpath->bbox[1];
break;
case ELLIPSE_NODETYPE:
delta.x = gpath->params[10] - gpath->bbox[0];
delta.y = gpath->params[5] - gpath->bbox[1];
break;
case RHOMB_NODETYPE:
delta.x = gpath->params[0] - gpath->bbox[0] - width / 2;
delta.y = gpath->params[1] - gpath->bbox[1];
break;
case HEXAGON_NODETYPE:
delta.x = gpath->params[0] - gpath->bbox[0] - placer.hexagon[0];
delta.y = gpath->params[1] - gpath->bbox[1];
break;
default:
break;
}
gpath->bbox[0] += delta.x;
gpath->bbox[1] += delta.y;
gpath->bbox[2] += delta.x;
gpath->bbox[3] += delta.y;
return self;
}
- fillGPathParams
{
float r;
switch(pathKind){
case RECT_NODETYPE:
gpath->params[0] = pos.x;
gpath->params[1] = pos.y;
gpath->params[2] = width;
gpath->params[3] = 0.0;
gpath->params[4] = 0.0;
gpath->params[5] = height;
gpath->params[6] = -width;
gpath->params[7] = 0.0;
gpath->params[8] = 0.0;
gpath->params[9] = -height;
break;
case ROUNDRECT_NODETYPE:
r = MIN(width / 3, height / 3);
gpath->params[0] = pos.x;
gpath->params[1] = pos.y + r;
gpath->params[2] = pos.x;
gpath->params[3] = pos.y + height - r;
gpath->params[4] = pos.x;
gpath->params[5] = pos.y + height - r + r * 0.5519;
gpath->params[6] = pos.x + r - r * 0.5519;
gpath->params[7] = pos.y + height;
gpath->params[8] = pos.x + r;
gpath->params[9] = pos.y + height;
gpath->params[10] = pos.x + width - r;
gpath->params[11] = pos.y + height;
gpath->params[12] = pos.x + width - r + r * 0.5519;
gpath->params[13] = pos.y + height;
gpath->params[14] = pos.x + width;
gpath->params[15] = pos.y + height - r + r * 0.5519;
gpath->params[16] = pos.x + width;
gpath->params[17] = pos.y + height - r;
gpath->params[18] = pos.x + width;
gpath->params[19] = pos.y + r;
gpath->params[20] = pos.x + width;
gpath->params[21] = pos.y + r - r * 0.5519;
gpath->params[22] = pos.x + width - r + r * 0.5519;
gpath->params[23] = pos.y;
gpath->params[24] = pos.x + width - r;
gpath->params[25] = pos.y;
gpath->params[26] = pos.x + r;
gpath->params[27] = pos.y;
gpath->params[28] = pos.x + r - r * 0.5519;
gpath->params[29] = pos.y;
gpath->params[30] = pos.x;
gpath->params[31] = pos.y + r - r * 0.5519;
gpath->params[32] = pos.x;
gpath->params[33] = pos.y + r;
/* gpath->params[0] = pos.x + height / 3;
gpath->params[1] = pos.y;
gpath->params[2] = pos.x + width;
gpath->params[3] = pos.y;
gpath->params[4] = pos.x + width;
gpath->params[5] = pos.y + height;
gpath->params[6] = height / 3;
gpath->params[7] = pos.x + width;
gpath->params[8] = pos.y + height;
gpath->params[9] = pos.x;
gpath->params[10] = pos.y + height;
gpath->params[11] = height / 3;
gpath->params[12] = pos.x;
gpath->params[13] = pos.y + height;
gpath->params[14] = pos.x;
gpath->params[15] = pos.y;
gpath->params[16] = height / 3;
gpath->params[17] = pos.x;
gpath->params[18] = pos.y;
gpath->params[19] = pos.x + width;
gpath->params[20] = pos.y;
gpath->params[21] = height / 3; */
break;
case ELLIPSE_NODETYPE:
gpath->params[0] = pos.x + width;
gpath->params[1] = pos.y + height / 2;
gpath->params[2] = pos.x + width;
gpath->params[3] = pos.y + height / 2 * 0.445;
gpath->params[4] = pos.x + width / 2 * 1.555;
gpath->params[5] = pos.y;
gpath->params[6] = pos.x + width / 2;
gpath->params[7] = pos.y;
gpath->params[8] = pos.x + width / 2 * 0.445;
gpath->params[9] = pos.y;
gpath->params[10] = pos.x;
gpath->params[11] = pos.y + height / 2 * 0.445;
gpath->params[12] = pos.x;
gpath->params[13] = pos.y + height / 2;
gpath->params[14] = pos.x;
gpath->params[15] = pos.y + height /2 * 1.555;
gpath->params[16] = pos.x + width / 2 * 0.445;
gpath->params[17] = pos.y + height;
gpath->params[18] = pos.x + width / 2;
gpath->params[19] = pos.y + height;
gpath->params[20] = pos.x + width / 2 * 1.555;
gpath->params[21] = pos.y + height;
gpath->params[22] = pos.x + width;
gpath->params[23] = pos.y + height / 2 * 1.555;
gpath->params[24] = pos.x + width;
gpath->params[25] = pos.y + height / 2;
break;
case RHOMB_NODETYPE:
gpath->params[0] = pos.x + width / 2;
gpath->params[1] = pos.y;
gpath->params[2] = width / 2;
gpath->params[3] = height / 2;
gpath->params[4] = -width / 2;
gpath->params[5] = height / 2;
gpath->params[6] = -width / 2;
gpath->params[7] = -height / 2;
gpath->params[8] =width / 2;
gpath->params[9] = -height / 2;
break;
case HEXAGON_NODETYPE:
gpath->params[0] = pos.x + placer.hexagon[0];
gpath->params[1] = pos.y;
gpath->params[2] = width - 2 * placer.hexagon[0];
gpath->params[3] = 0;
gpath->params[4] = placer.hexagon[0];
gpath->params[5] = height / 2;
gpath->params[6] = -placer.hexagon[0];
gpath->params[7] = height / 2;
gpath->params[8] =-width + 2 * placer.hexagon[0];
gpath->params[9] = 0;
gpath->params[10] =-placer.hexagon[0];
gpath->params[11] = -height / 2;
gpath->params[12] = placer.hexagon[0];
gpath->params[13] = -height / 2;
break;
default:
break;
}
return self;
}
- calcWidthHeight
{
NXCoord v1,v2;
NXSize textSize;
[textCell calcCellSize:&textSize];
switch(pathKind){
case RECT_NODETYPE:
width = textSize.width + 2 * placer.rect[0];
height = textSize.height + 2 * placer.rect[1];
break;
case ROUNDRECT_NODETYPE:
width = textSize.width + 2 * placer.roundRect[0];
height = textSize.height + 2 * placer.roundRect[1];
break;
case ELLIPSE_NODETYPE:
height = textSize.height + 2 * placer.ellipse[0];
v1 = placer.ellipse[0] - placer.ellipse[1];
v2 = placer.ellipse[0] + height + placer.ellipse[1];
width = (textSize.width / 2 + placer.ellipse[1]) * height / sqrt(v1 * v2);
break;
case RHOMB_NODETYPE:
height = textSize.height + 2 * placer.rhomb[0];
v1 = placer.rhomb[0] - placer.rhomb[1];
width = (textSize.width / 2 + placer.rhomb[1]) * height / v1;
break;
case HEXAGON_NODETYPE:
width = textSize.width + 2 * placer.hexagon[0];
height = textSize.height + 2 * placer.hexagon[1];
break;
default:
break;
}
deltaText.x = (width - textSize.width) / 2;
deltaText.y = (height - textSize.height) / 2;
deltaLink.x = width / 2;
deltaLink.y = height / 2;
NXSetRect(&textCellFrame, 0, 0, textSize.width, textSize.height);
return self;
}
- internalCopyFromZone:(NXZone *)zone parent:aParent
{
Tree *theCopy;
theCopy = [super copyFromZone:zone];
theCopy->contour.upper.head = contour.upper.tail = NULL;
theCopy->contour.lower.head = contour.lower.tail = NULL;
theCopy->updateLayout = YES;
theCopy->parent = aParent;
if(child)
theCopy->child = [child internalCopyFromZone:zone parent:theCopy];
else
theCopy->child = nil;
if(sibling)
theCopy->sibling = [sibling internalCopyFromZone:zone parent:aParent];
else
theCopy->sibling = nil;
theCopy->selected = NO;
theCopy->gpath = [gpath copyFromZone:zone];
theCopy->nodeDescription = [nodeDescription copyFromZone:zone];;
theCopy->delegate = nil;
if(verknuepfung)
theCopy->verknuepfung = [verknuepfung copyFromZone:zone];
else
theCopy->verknuepfung = nil;
theCopy->textCell = [textCell copyFromZone:zone];
return theCopy;
}
- calcIntersection:(NXPoint *)ip angle:(float *)alpha toPoint:(const NXPoint *)aPoint
{
NXPoint middle,l1,l2;
float s,xm,ym,x0,y0,r,ausdruck,a,b;
middle.x = pos.x + width / 2;
middle.y = pos.y + height / 2;
if(aPoint->y == middle.y){
*alpha = 0;
ip->y = middle.y;
if(aPoint->x > middle.x)
ip->x = pos.x + width;
else
ip->x = pos.x;
return self;
}
if(aPoint->x == middle.x){
ip->x = middle.x;
if(aPoint->y > middle.y){
ip->y = pos.y + height;
*alpha = -90;
} else {
ip->y = pos.y;
*alpha = 90;
}
return self;
}
s = (aPoint->y - middle.y) / (aPoint->x - middle.x);
*alpha = 57.29577951 * atan(s);
if(aPoint->y > middle.y){
if(aPoint->x > middle.x){ // 4. quadrant
switch(pathKind){
case RECT_NODETYPE:
l1.x = middle.x;
l1.y = l2.y = pos.y + height;
l2.x = pos.x + width;
if(straddles(&l1, &l2, &middle, aPoint)){
ip->y = l1.y;
ip->x = middle.x + (ip->y - middle.y) / s;
} else {
ip->x = l2.x;
ip->y = middle.y + s * (ip->x - middle.x);
}
break;
case ROUNDRECT_NODETYPE:
l1.x = middle.x;
l1.y = pos.y + height;
l2.x = pos.x + width - height / 3;
l2.y = pos.y + height;
if(straddles(&l1, &l2, &middle, aPoint)){
ip->y = l1.y;
ip->x = middle.x + (ip->y - middle.y) / s;
} else {
l1.x = l2.x = pos.x + width;
l1.y = middle.y;
l2.y = pos.y + height - height / 3;
if(straddles(&l1, &l2, &middle, aPoint)){
ip->x = l2.x;
ip->y = middle.y + s * (ip->x - middle.x);
} else {
r = height / 3;
x0 = middle.x;
y0 = middle.y;
xm = pos.x + width - r;
ym = pos.y + height - r;
ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) +
2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym -
pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2));
ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2));
ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2));
if(ip->y < ym){
ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2));
ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2));
}
}
}
break;
case ELLIPSE_NODETYPE:
x0 = middle.x;
y0 = middle.y;
a = width / 2;
b = height / 2;
ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0;
ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
if(ip->y < y0){
ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0;
ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
}
break;
case RHOMB_NODETYPE:
l1.x = middle.x;
l1.y = pos.y + height;
l2.x = pos.x + width;
l2.y = middle.y;
ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
ip->y = middle.y + s * (ip->x - middle.x);
break;
case HEXAGON_NODETYPE:
l1.x = middle.x;
l1.y = l2.y = pos.y + height;
l2.x = pos.x + width - placer.hexagon[0];
if(straddles(&l1, &l2, &middle, aPoint)){
ip->y = l1.y;
ip->x = middle.x + (ip->y - middle.y) / s;
} else {
l1.x = pos.x + width;
l1.y = middle.y;
ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
ip->y = middle.y + s * (ip->x - middle.x);
}
break;
default:
break;
}
} else { // 3. quadrant
switch(pathKind){
case RECT_NODETYPE:
l1.x = middle.x;
l1.y = l2.y = pos.y + height;
l2.x = pos.x;
if(straddles(&l1, &l2, &middle, aPoint)){
ip->y = l1.y;
ip->x = middle.x + (ip->y - middle.y) / s;
} else {
ip->x = l2.x;
ip->y = middle.y + s * (ip->x - middle.x);
}
break;
case ROUNDRECT_NODETYPE:
l1.x = middle.x;
l1.y = pos.y + height;
l2.x = pos.x + height / 3;
l2.y = pos.y + height;
if(straddles(&l1, &l2, &middle, aPoint)){
ip->y = l1.y;
ip->x = middle.x + (ip->y - middle.y) / s;
} else {
l1.x = l2.x = pos.x;
l1.y = middle.y;
l2.y = pos.y + height - height / 3;
if(straddles(&l1, &l2, &middle, aPoint)){
ip->x = l2.x;
ip->y = middle.y + s * (ip->x - middle.x);
} else {
r = height / 3;
x0 = middle.x;
y0 = middle.y;
xm = pos.x + r;
ym = pos.y + height - r;
ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) +
2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym -
pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2));
ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2));
ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2));
if(ip->y < ym){
ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2));
ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2));
}
}
}
break;
case ELLIPSE_NODETYPE:
x0 = middle.x;
y0 = middle.y;
a = width / 2;
b = height / 2;
ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0;
ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
if(ip->y < y0){
ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0;
ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
}
break;
case RHOMB_NODETYPE:
l1.x = middle.x;
l1.y = pos.y + height;
l2.x = pos.x;
l2.y = middle.y;
ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
ip->y = middle.y + s * (ip->x - middle.x);
break;
case HEXAGON_NODETYPE:
l1.x = middle.x;
l1.y = l2.y = pos.y + height;
l2.x = pos.x + placer.hexagon[0];
if(straddles(&l1, &l2, &middle, aPoint)){
ip->y = l1.y;
ip->x = middle.x + (ip->y - middle.y) / s;
} else {
l1.x = pos.x;
l1.y = middle.y;
ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
ip->y = middle.y + s * (ip->x - middle.x);
}
break;
default:
break;
}
}
} else {
if(aPoint->x > middle.x){ // 1. quadrant
switch(pathKind){
case RECT_NODETYPE:
l1.x = middle.x;
l1.y = l2.y = pos.y;
l2.x = pos.x + width;
if(straddles(&l1, &l2, &middle, aPoint)){
ip->y = l1.y;
ip->x = middle.x + (ip->y - middle.y) / s;
} else {
ip->x = l2.x;
ip->y = middle.y + s * (ip->x - middle.x);
}
break;
case ROUNDRECT_NODETYPE:
l1.x = middle.x;
l1.y = pos.y;
l2.x = pos.x + width - height / 3;
l2.y = pos.y;
if(straddles(&l1, &l2, &middle, aPoint)){
ip->y = l1.y;
ip->x = middle.x + (ip->y - middle.y) / s;
} else {
l1.x = l2.x = pos.x + width;
l1.y = middle.y;
l2.y = pos.y + height / 3;
if(straddles(&l1, &l2, &middle, aPoint)){
ip->x = l2.x;
ip->y = middle.y + s * (ip->x - middle.x);
} else {
r = height / 3;
x0 = middle.x;
y0 = middle.y;
xm = pos.x + width - r;
ym = pos.y + r;
ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) +
2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym -
pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2));
ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2));
ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2));
if(ip->y > ym){
ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2));
ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2));
}
}
}
break;
case ELLIPSE_NODETYPE:
x0 = middle.x;
y0 = middle.y;
a = width / 2;
b = height / 2;
ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0;
ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
if(ip->y > y0){
ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0;
ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
}
break;
case RHOMB_NODETYPE:
l1.x = middle.x;
l1.y = pos.y;
l2.x = pos.x + width;
l2.y = middle.y;
ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
ip->y = middle.y + s * (ip->x - middle.x);
break;
case HEXAGON_NODETYPE:
l1.x = middle.x;
l1.y = l2.y = pos.y;
l2.x = pos.x + width - placer.hexagon[0];
if(straddles(&l1, &l2, &middle, aPoint)){
ip->y = l1.y;
ip->x = middle.x + (ip->y - middle.y) / s;
} else {
l1.x = pos.x + width;
l1.y = middle.y;
ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
ip->y = middle.y + s * (ip->x - middle.x);
}
break;
default:
break;
}
} else { // 2. quadrant
switch(pathKind){
case RECT_NODETYPE:
l1.x = middle.x;
l1.y = l2.y = pos.y;
l2.x = pos.x;
if(straddles(&l1, &l2, &middle, aPoint)){
ip->y = l1.y;
ip->x = middle.x + (ip->y - middle.y) / s;
} else {
ip->x = l2.x;
ip->y = middle.y + s * (ip->x - middle.x);
}
break;
case ROUNDRECT_NODETYPE:
l1.x = middle.x;
l1.y = pos.y;
l2.x = pos.x + height / 3;
l2.y = pos.y;
if(straddles(&l1, &l2, &middle, aPoint)){
ip->y = l1.y;
ip->x = middle.x + (ip->y - middle.y) / s;
} else {
l1.x = l2.x = pos.x;
l1.y = middle.y;
l2.y = pos.y + height / 3;
if(straddles(&l1, &l2, &middle, aPoint)){
ip->x = l2.x;
ip->y = middle.y + s * (ip->x - middle.x);
} else {
r = height / 3;
x0 = middle.x;
y0 = middle.y;
xm = pos.x + r;
ym = pos.y + r;
ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) +
2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym -
pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2));
ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2));
ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2));
if(ip->y > ym){
ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2));
ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2));
}
}
}
break;
case ELLIPSE_NODETYPE:
x0 = middle.x;
y0 = middle.y;
a = width / 2;
b = height / 2;
ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0;
ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
if(ip->y > y0){
ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0;
ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
}
break;
case RHOMB_NODETYPE:
l1.x = middle.x;
l1.y = pos.y;
l2.x = pos.x;
l2.y = middle.y;
ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
ip->y = middle.y + s * (ip->x - middle.x);
break;
case HEXAGON_NODETYPE:
l1.x = middle.x;
l1.y = l2.y = pos.y;
l2.x = pos.x + placer.hexagon[0];
if(straddles(&l1, &l2, &middle, aPoint)){
ip->y = l1.y;
ip->x = middle.x + (ip->y - middle.y) / s;
} else {
l1.x = pos.x;
l1.y = middle.y;
ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
ip->y = middle.y + s * (ip->x - middle.x);
}
break;
default:
break;
}
}
}
return self;
}
@end
//************************************************************************
// static c functions implementation
static void setRectFromPolygon(NXRect *rect, Polygon *aPol, const NXPoint *ipu, const NXPoint *ipl,
NXCoord *low, NXCoord *loh)
{
NXCoord minX, minY, maxX, maxY, x, y;
Polyline *dummy;
minX = minY = 9999;
maxX = maxY = -9999;
dummy = aPol->upper.head;
x = ipu->x;
y = ipu->y;
minX = MIN(minX, x);
minY = MIN(minY, y);
maxX = MAX(maxX, x);
maxY = MAX(maxY, y);
while(dummy){
x += dummy->dx;
y += dummy->dy;
minX = MIN(minX, x);
minY = MIN(minY, y);
maxX = MAX(maxX, x);
maxY = MAX(maxY, y);
dummy = dummy->link;
}
dummy = aPol->lower.head;
x = ipl->x;
y = ipl->y;
minX = MIN(minX, x);
minY = MIN(minY, y);
maxX = MAX(maxX, x);
maxY = MAX(maxY, y);
while(dummy){
x += dummy->dx;
y += dummy->dy;
minX = MIN(minX, x);
minY = MIN(minY, y);
maxX = MAX(maxX, x);
maxY = MAX(maxY, y);
dummy = dummy->link;
}
NXSetRect(rect, minX, minY, maxX - minX, maxY - minY);
*loh = ipu->y - minY;
*low = ipu->x - minX;
}
static NXCoord merge(Polygon *c1,Polygon *c2, NXZone *zone)
{
NXCoord x,y,total,d;
Polyline *lower,*upper,*b;
x = y = total = 0;
upper = c1->lower.head;
lower = c2->upper.head;
while(lower && upper){
d = offset(x,y,lower->dx,lower->dy,upper->dx,upper->dy);
y += d;
total += d;
if(x + lower->dx <= upper->dx){
y += lower->dy;
x += lower->dx;
lower = lower->link;
} else {
y -= upper->dy;
x -= upper->dx;
upper = upper->link;
}
}
if(lower){
b = bridge(c1->upper.tail,lower,0,0,x,y,zone);
c1->upper.tail = (b->link) ? c2->upper.tail : b;
c1->lower.tail = c2->lower.tail;
partialFreePolyline(c2->upper.head, lower->link);
freePolyline(c1->lower.head);
} else {
b = bridge(c2->lower.tail,upper,x,y,0,0,zone);
if(!b->link)
c1->lower.tail = b;
partialFreePolyline(c1->lower.head,upper->link);
freePolyline(c2->upper.head);
}
c1->lower.head = c2->lower.head;
return total;
}
static NXCoord offset(NXCoord p1,NXCoord p2,NXCoord a1,NXCoord a2,
NXCoord b1,NXCoord b2)
{
NXCoord d,s,t;
if(b1 <= p1 || p1 + a1 <= 0)
return 0;
t = b1 * a2 - a1 * b2;
if(t > 0){
if(p1 < 0)
s = p1 * a2,d = s / a1 - p2;
else if(p1 > 0)
s = p1 * b2,d = s / b1 - p2;
else
d = -p2;
} else if(b1 < p1 + a1)
s = (b1 - p1) * a2,d = b2 - (p2 + s / a1);
else if(b1 > p1 + a1)
s = (a1 + p1) * b2, d = s / b1 - (p2 + a2);
else
d = b2 - (p2 + a2);
return MAX(0,d);
}
static Polyline *bridge(Polyline *line1,Polyline *line2,NXCoord x1,NXCoord y1,
NXCoord x2,NXCoord y2, NXZone *zone)
{
NXCoord dx,dy,s;
Polyline *r;
dx = x2 + line2->dx - x1;
if(line2->dx == 0)
dy = line2->dy;
else
s = dx * line2->dy,dy = s / line2->dx;
r = allocLine(dx,dy,line2->link,zone);
line1->link = allocLine(0,y2 + line2->dy -dy - y1,r,zone);
return r;
}
static Polyline *allocLine(NXCoord aX,NXCoord aY,Polyline *aLink, NXZone *zone)
{
Polyline *theAlloced;
theAlloced = (Polyline *)NXZoneMalloc(zone,sizeof(Polyline));
theAlloced->dx = aX;
theAlloced->dy = aY;
theAlloced->link = aLink;
return theAlloced;
}
static Polyline *copyPolyline(Polyline *aSource,Polyline **aTail, NXZone *zone)
{
Polyline *aLine,*l,*result;
l = aSource;
aLine = (Polyline *)NXZoneMalloc(zone,sizeof(Polyline));
aLine->dx = l->dx;
aLine->dy = l->dy;
aLine->link = NULL;
result = aLine;
l = l->link;
while(l){
aLine->link = (Polyline *)NXZoneMalloc(zone,sizeof(Polyline));
aLine = aLine->link;
aLine->dx = l->dx;
aLine->dy = l->dy;
aLine->link = NULL;
l = l->link;
}
*aTail = aLine;
return result;
}
static void freePolyline(Polyline *aLine)
{
Polyline *index,*trailer;
trailer = aLine;
index = aLine;
while(index){
index = index->link;
NX_FREE(trailer);
trailer = index;
}
}
static void partialFreePolyline(Polyline *aLine, Polyline *toEnd)
{
Polyline *index,*trailer;
trailer = aLine;
index = aLine;
while(index && index != toEnd){
index = index->link;
NX_FREE(trailer);
trailer = index;
}
}
static BOOL straddles(const NXPoint *p1, const NXPoint *p2, const NXPoint *p3, const NXPoint *p4)
{
float crossP1, crossP2;
crossP1 = (p3->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (p3->y - p1->y);
crossP2 = (p4->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (p4->y - p1->y);
if((crossP1 > 0 && crossP2 < 0) || (crossP1 < 0 && crossP2 > 0)){
crossP1 = (p1->x - p3->x) * (p4->y - p3->y) - (p4->x - p3->x) * (p1->y - p3->y);
crossP2 = (p2->x - p3->x) * (p4->y - p3->y) - (p4->x - p3->x) * (p2->y - p3->y);
if((crossP1 > 0 && crossP2 < 0) || (crossP1 < 0 && crossP2 > 0))
return YES;
else
return NO;
} else
return NO;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.