From 2ed61cfb3ad3d404b9a44bbb3f4c8083b469ae22 Mon Sep 17 00:00:00 2001 From: randomguy3 Date: Fri, 6 Jan 2012 13:44:40 +0000 Subject: Move everything into trunk. Also add branches and tags directories. git-svn-id: https://tikzit.svn.sourceforge.net/svnroot/tikzit/trunk@348 7c02a99a-9b00-45e3-bf44-6f3dd7fddb64 --- src/common/Edge.m | 526 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 526 insertions(+) create mode 100644 src/common/Edge.m (limited to 'src/common/Edge.m') diff --git a/src/common/Edge.m b/src/common/Edge.m new file mode 100644 index 0000000..9ecfebc --- /dev/null +++ b/src/common/Edge.m @@ -0,0 +1,526 @@ +// +// Edge.m +// TikZiT +// +// Copyright 2010 Aleks Kissinger. All rights reserved. +// +// +// This file is part of TikZiT. +// +// TikZiT is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// TikZiT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with TikZiT. If not, see . +// + +#import "Edge.h" +#import "util.h" + + +@implementation Edge + +- (id)init { + [super init]; + data = [[GraphElementData alloc] init]; + bend = 0; + inAngle = 135; + outAngle = 45; + bendMode = EdgeBendModeBasic; + weight = 0.4f; + dirty = YES; + source = nil; + target = nil; + edgeNode = nil; + + return self; +} + +- (id)initWithSource:(Node*)s andTarget:(Node*)t { + [self init]; + + [self setSource:s]; + [self setTarget:t]; + edgeNode = nil; + + dirty = YES; + + return self; +} + +- (BOOL)attachStyleFromTable:(NSArray*)styles { + NSString *style_name = [data propertyForKey:@"style"]; + + [self setStyle:nil]; + if (style_name == nil) return YES; + + for (EdgeStyle *s in styles) { + if ([[s name] compare:style_name]==NSOrderedSame) { + [self setStyle:s]; + return YES; + } + } + + // if we didn't find a style, fill in a default one + style = [[EdgeStyle defaultEdgeStyleWithName:style_name] retain]; + return NO; +} + +- (void)updateControls { + // check for external modification to the node locations + if (src.x != [source point].x || src.y != [source point].y || + targ.x != [target point].x || targ.y != [target point].y) + { + dirty = YES; + } + + if (dirty) { + src = [source point]; + targ = [target point]; + + float dx = (targ.x - src.x); + float dy = (targ.y - src.y); + + float angleSrc = 0.0f; + float angleTarg = 0.0f; + + bend = normaliseAngleDeg (bend); + inAngle = normaliseAngleDeg (inAngle); + outAngle = normaliseAngleDeg (outAngle); + if (bendMode == EdgeBendModeBasic) { + float angle = good_atan(dx, dy); + float bnd = (float)bend * (M_PI / 180.0f); + angleSrc = angle - bnd; + angleTarg = M_PI + angle + bnd; + } else if (bendMode == EdgeBendModeInOut) { + angleSrc = (float)outAngle * (M_PI / 180.0f); + angleTarg = (float)inAngle * (M_PI / 180.0f); + } + + // give a default distance for self-loops + float cdist = (dx==0.0f && dy==0.0f) ? weight : sqrt(dx*dx + dy*dy) * weight; + + cp1 = NSMakePoint(src.x + (cdist * cos(angleSrc)), + src.y + (cdist * sin(angleSrc))); + + cp2 = NSMakePoint(targ.x + (cdist * cos(angleTarg)), + targ.y + (cdist * sin(angleTarg))); + + mid.x = bezierInterpolate(0.5f, src.x, cp1.x, cp2.x, targ.x); + mid.y = bezierInterpolate(0.5f, src.y, cp1.y, cp2.y, targ.y); + + dx = bezierInterpolate(0.6f, src.x, cp1.x, cp2.x, targ.x) - + bezierInterpolate(0.4f, src.x, cp1.x, cp2.x, targ.x); + dy = bezierInterpolate(0.6f, src.y, cp1.y, cp2.y, targ.y) - + bezierInterpolate(0.4f, src.y, cp1.y, cp2.y, targ.y); + + // normalise + float len = sqrt(dx*dx+dy*dy); + if (len != 0) { + dx = (dx/len) * 0.1f; + dy = (dy/len) * 0.1f; + } + + midTan.x = mid.x + dx; + midTan.y = mid.y + dy; + } + dirty = NO; +} + +- (void)convertBendToAngles { + float dx = (targ.x - src.x); + float dy = (targ.y - src.y); + float angle = good_atan(dx, dy); + float bnd = (float)bend * (M_PI / 180.0f); + + outAngle = round((angle - bnd) * (180.0f/M_PI)); + inAngle = round((M_PI + angle + bnd) * (180.0f/M_PI)); + dirty = YES; +} + +- (void)convertAnglesToBend { + float dx = (targ.x - src.x); + float dy = (targ.y - src.y); + int angle = round((180.0f/M_PI) * good_atan(dx, dy)); + + // compute bend1 and bend2 to match inAngle and outAngle, resp. + int bend1, bend2; + + bend1 = outAngle - angle; + bend2 = angle - inAngle; + + bend = (bend1 + bend2) / 2; + + dirty = YES; +} + +- (BOOL)isSelfLoop { + return (source == target); +} + +- (BOOL)isStraight { + return (bendMode == EdgeBendModeBasic && bend == 0); +} + +- (NSPoint)mid { + [self updateControls]; + return mid; +} + +- (NSPoint)midTan { + [self updateControls]; + return midTan; +} + +- (NSPoint)leftNormal { + [self updateControls]; + return NSMakePoint(mid.x + (mid.y - midTan.y), mid.y - (mid.x - midTan.x)); +} + +- (NSPoint)rightNormal { + [self updateControls]; + return NSMakePoint(mid.x - (mid.y - midTan.y), mid.y + (mid.x - midTan.x)); +} + +- (NSPoint)cp1 { + [self updateControls]; + return cp1; +} + +- (NSPoint)cp2 { + [self updateControls]; + return cp2; +} + +- (int)inAngle {return inAngle;} +- (void)setInAngle:(int)a { + inAngle = a; + dirty = YES; +} + +- (int)outAngle {return outAngle;} +- (void)setOutAngle:(int)a { + outAngle = a; + dirty = YES; +} + +- (EdgeBendMode)bendMode {return bendMode;} +- (void)setBendMode:(EdgeBendMode)mode { + bendMode = mode; + dirty = YES; +} + +- (int)bend {return bend;} +- (void)setBend:(int)b { + bend = b; + dirty = YES; +} + +- (float)weight {return weight;} +- (void)setWeight:(float)w { +// if (source == target) weight = 1.0f; +// else weight = w; + weight = w; + dirty = YES; +} + +- (EdgeStyle*)style {return style;} +- (void)setStyle:(EdgeStyle*)s { + if (style != s) { + [style release]; + style = [s retain]; + } +} + +- (Node*)source {return source;} +- (void)setSource:(Node *)s { + if (source != s) { + [source release]; + source = [s retain]; + + if (source==target) { + bendMode = EdgeBendModeInOut; + weight = 1.0f; + } + + dirty = YES; + } +} + +- (Node*)target {return target;} +- (void)setTarget:(Node *)t { + if (target != t) { + [target release]; + target = [t retain]; + + if (source==target) { + bendMode = EdgeBendModeInOut; + weight = 1.0f; + } + + dirty = YES; + } +} + + +// edgeNode and hasEdgeNode use a bit of key-value observing to help the mac GUI keep up + +- (Node*)edgeNode {return edgeNode;} +- (void)setEdgeNode:(Node *)n { +#if defined (__APPLE__) + [self willChangeValueForKey:@"edgeNode"]; + [self willChangeValueForKey:@"hasEdgeNode"]; +#endif + if (edgeNode != n) { + hasEdgeNode = (n != nil); + [edgeNode release]; + edgeNode = [n retain]; + // don't set dirty bit, because control points don't need update + } +#if defined (__APPLE__) + [self didChangeValueForKey:@"edgeNode"]; + [self didChangeValueForKey:@"hasEdgeNode"]; +#endif +} + +- (BOOL)hasEdgeNode { return hasEdgeNode; } +- (void)setHasEdgeNode:(BOOL)b { +#if defined (__APPLE__) + [self willChangeValueForKey:@"edgeNode"]; + [self willChangeValueForKey:@"hasEdgeNode"]; +#endif + hasEdgeNode = b; + if (hasEdgeNode && edgeNode == nil) { + edgeNode = [[Node alloc] init]; + } +#if defined (__APPLE__) + [self didChangeValueForKey:@"edgeNode"]; + [self didChangeValueForKey:@"hasEdgeNode"]; +#endif +} + +- (GraphElementData*)data {return data;} +- (void)setData:(GraphElementData*)dt { + if (data != dt) { + [data release]; + data = [dt copy]; + } +} + +- (void)updateData { + // unset everything to avoid redundant defs + [data unsetAtom:@"loop"]; + [data unsetProperty:@"in"]; + [data unsetProperty:@"out"]; + [data unsetAtom:@"bend left"]; + [data unsetAtom:@"bend right"]; + [data unsetProperty:@"bend left"]; + [data unsetProperty:@"bend right"]; + [data unsetProperty:@"looseness"]; + + if (style == nil) { + [data unsetProperty:@"style"]; + } else { + [data setProperty:[style name] forKey:@"style"]; + } + + if (bendMode == EdgeBendModeBasic && bend != 0) { + NSString *bendkey = @"bend right"; + int b = [self bend]; + if (b < 0) { + bendkey = @"bend left"; + b = -b; + } + + if (b == 30) { + [data setAtom:bendkey]; + } else { + [data setProperty:[NSString stringWithFormat:@"%d",b] forKey:bendkey]; + } + + } else if (bendMode == EdgeBendModeInOut) { + [data setProperty:[NSString stringWithFormat:@"%d",inAngle] + forKey:@"in"]; + [data setProperty:[NSString stringWithFormat:@"%d",outAngle] + forKey:@"out"]; + } + + // loop needs to come after in/out + if (source == target) [data setAtom:@"loop"]; + + if (!( + ([self isSelfLoop]) || + (source!=target && weight == 0.4f) + )) + { + [data setProperty:[NSString stringWithFormat:@"%.2f",weight*2.5f] + forKey:@"looseness"]; + } +} + +- (void)setAttributesFromData { + bendMode = EdgeBendModeBasic; + + if ([data isAtomSet:@"bend left"]) { + bend = -30; + } else if ([data isAtomSet:@"bend right"]) { + bend = 30; + } else if ([data propertyForKey:@"bend left"] != nil) { + NSString *bnd = [data propertyForKey:@"bend left"]; + bend = -[bnd intValue]; + } else if ([data propertyForKey:@"bend right"] != nil) { + NSString *bnd = [data propertyForKey:@"bend right"]; + bend = [bnd intValue]; + } else { + bend = 0; + + if ([data propertyForKey:@"in"] != nil && [data propertyForKey:@"out"] != nil) { + bendMode = EdgeBendModeInOut; + inAngle = [[data propertyForKey:@"in"] intValue]; + outAngle = [[data propertyForKey:@"out"] intValue]; + } + } + + if ([data propertyForKey:@"looseness"] != nil) { + weight = [[data propertyForKey:@"looseness"] floatValue] / 2.5f; + } else { + weight = ([self isSelfLoop]) ? 1.0f : 0.4f; + } +} + +- (void)setPropertiesFromEdge:(Edge*)e { + Node *en = [[e edgeNode] copy]; + [self setEdgeNode:en]; + [en release]; + + GraphElementData *d = [[e data] copy]; + [self setData:d]; + [d release]; + + [self setStyle:[e style]]; + [self setBend:[e bend]]; + [self setInAngle:[e inAngle]]; + [self setOutAngle:[e outAngle]]; + [self setBendMode:[e bendMode]]; + [self setWeight:[e weight]]; + + dirty = YES; // cached data will be recomputed lazily, rather than copied +} + +- (NSRect)boundingRect { + [self updateControls]; + return NSRectAround4Points(src, targ, cp1, cp2); +} + +- (void) adjustWeight:(float)handle_dist withCourseness:(float)wcourseness { + float base_dist = NSDistanceBetweenPoints (src, targ); + if (base_dist == 0.0f) { + base_dist = 1.0f; + } + + [self setWeight:roundToNearest(wcourseness, handle_dist / base_dist)]; +} + +- (float) angleOf:(NSPoint)point relativeTo:(NSPoint)base { + float dx = point.x - base.x; + float dy = point.y - base.y; + return radiansToDegrees (good_atan(dx, dy)); +} + +- (void) moveCp1To:(NSPoint)point withWeightCourseness:(float)wc andBendCourseness:(int)bc forceLinkControlPoints:(BOOL)link { + [self updateControls]; + [self adjustWeight:NSDistanceBetweenPoints (point, src) withCourseness:wc]; + + float control_angle = [self angleOf:point relativeTo:src]; + if (bendMode == EdgeBendModeBasic) { + float base_angle = [self angleOf:targ relativeTo:src]; + int b = (int)roundToNearest (bc, base_angle - control_angle); + [self setBend:b]; + } else { + int angle = (int)roundToNearest (bc, control_angle); + if (link) { + [self setInAngle:(inAngle + angle - outAngle)]; + } + [self setOutAngle:angle]; + } +} + +- (void) moveCp1To:(NSPoint)point { + [self moveCp1To:point withWeightCourseness:0.0f andBendCourseness:0 forceLinkControlPoints:NO]; +} + +- (void) moveCp2To:(NSPoint)point withWeightCourseness:(float)wc andBendCourseness:(int)bc forceLinkControlPoints:(BOOL)link { + [self updateControls]; + + if (![self isSelfLoop]) { + [self adjustWeight:NSDistanceBetweenPoints (point, targ) withCourseness:wc]; + } + + float control_angle = [self angleOf:point relativeTo:targ]; + if (bendMode == EdgeBendModeBasic) { + float base_angle = [self angleOf:src relativeTo:targ]; + int b = (int)roundToNearest (bc, control_angle - base_angle); + [self setBend:b]; + } else { + int angle = (int)roundToNearest (bc, control_angle); + if (link) { + [self setOutAngle:(outAngle + angle - inAngle)]; + } + [self setInAngle: angle]; + } +} + +- (void) moveCp2To:(NSPoint)point { + [self moveCp2To:point withWeightCourseness:0.0f andBendCourseness:0 forceLinkControlPoints:NO]; +} + +- (void)reverse { + Node *n; + float f; + + n = source; + source = target; + target = n; + + f = inAngle; + inAngle = outAngle; + outAngle = f; + + bend = -1 * bend; + + dirty = YES; +} + +- (void)dealloc { + [self setSource:nil]; + [self setTarget:nil]; + [self setData:nil]; + [super dealloc]; +} + +- (id)copy { + Edge *cp = [[Edge alloc] init]; + [cp setSource:[self source]]; + [cp setTarget:[self target]]; + [cp setPropertiesFromEdge:self]; + return cp; +} + ++ (Edge*)edge { + return [[[Edge alloc] init] autorelease]; +} + ++ (Edge*)edgeWithSource:(Node*)s andTarget:(Node*)t { + return [[[Edge alloc] initWithSource:s andTarget:t] autorelease]; +} + +@end + +// vi:ft=objc:ts=4:noet:sts=4:sw=4 -- cgit v1.2.3