summaryrefslogtreecommitdiff
path: root/tikzit-old/src/common
diff options
context:
space:
mode:
authorAleks Kissinger <aleks0@gmail.com>2018-01-04 15:58:21 +0100
committerAleks Kissinger <aleks0@gmail.com>2018-01-04 15:58:21 +0100
commit0421a96749743868554d44585050b1b3d04864d2 (patch)
tree02b0d4442bddee7c65bee0f875783b46610a4aef /tikzit-old/src/common
parentfecef0396026b80c5aec736171e4842df7518af9 (diff)
removed website
Diffstat (limited to 'tikzit-old/src/common')
-rw-r--r--tikzit-old/src/common/CircleShape.h33
-rw-r--r--tikzit-old/src/common/CircleShape.m57
-rw-r--r--tikzit-old/src/common/ColorRGB.h64
-rw-r--r--tikzit-old/src/common/ColorRGB.m353
-rw-r--r--tikzit-old/src/common/DiamondShape.h34
-rw-r--r--tikzit-old/src/common/DiamondShape.m63
-rw-r--r--tikzit-old/src/common/Edge.h401
-rw-r--r--tikzit-old/src/common/Edge.m757
-rw-r--r--tikzit-old/src/common/EdgeStyle.h71
-rw-r--r--tikzit-old/src/common/EdgeStyle.m222
-rw-r--r--tikzit-old/src/common/Graph.h401
-rw-r--r--tikzit-old/src/common/Graph.m922
-rw-r--r--tikzit-old/src/common/GraphChange.h344
-rw-r--r--tikzit-old/src/common/GraphChange.m369
-rw-r--r--tikzit-old/src/common/GraphElementData.h94
-rw-r--r--tikzit-old/src/common/GraphElementData.m188
-rw-r--r--tikzit-old/src/common/GraphElementProperty.h88
-rw-r--r--tikzit-old/src/common/GraphElementProperty.m164
-rw-r--r--tikzit-old/src/common/Grid.h110
-rw-r--r--tikzit-old/src/common/Grid.m186
-rw-r--r--tikzit-old/src/common/NSError+Tikzit.h44
-rw-r--r--tikzit-old/src/common/NSError+Tikzit.m64
-rw-r--r--tikzit-old/src/common/NSFileManager+Utils.h29
-rw-r--r--tikzit-old/src/common/NSFileManager+Utils.m46
-rw-r--r--tikzit-old/src/common/NSString+LatexConstants.h33
-rw-r--r--tikzit-old/src/common/NSString+LatexConstants.m212
-rw-r--r--tikzit-old/src/common/NSString+Tikz.h26
-rw-r--r--tikzit-old/src/common/NSString+Tikz.m72
-rw-r--r--tikzit-old/src/common/NSString+Util.h27
-rw-r--r--tikzit-old/src/common/NSString+Util.m66
-rw-r--r--tikzit-old/src/common/Node.h181
-rw-r--r--tikzit-old/src/common/Node.m214
-rw-r--r--tikzit-old/src/common/NodeStyle.h125
-rw-r--r--tikzit-old/src/common/NodeStyle.m246
-rw-r--r--tikzit-old/src/common/PickSupport.h164
-rw-r--r--tikzit-old/src/common/PickSupport.m232
-rw-r--r--tikzit-old/src/common/Preambles.h73
-rw-r--r--tikzit-old/src/common/Preambles.m320
-rw-r--r--tikzit-old/src/common/PropertyHolder.h36
-rw-r--r--tikzit-old/src/common/PropertyHolder.m74
-rw-r--r--tikzit-old/src/common/RColor.h50
-rw-r--r--tikzit-old/src/common/RColor.m33
-rw-r--r--tikzit-old/src/common/RectangleShape.h33
-rw-r--r--tikzit-old/src/common/RectangleShape.m57
-rw-r--r--tikzit-old/src/common/RegularPolyShape.h50
-rw-r--r--tikzit-old/src/common/RegularPolyShape.m76
-rw-r--r--tikzit-old/src/common/RenderContext.h156
-rw-r--r--tikzit-old/src/common/Shape.h49
-rw-r--r--tikzit-old/src/common/Shape.m171
-rw-r--r--tikzit-old/src/common/ShapeNames.h27
-rw-r--r--tikzit-old/src/common/StyleManager.h49
-rw-r--r--tikzit-old/src/common/StyleManager.m378
-rw-r--r--tikzit-old/src/common/SupportDir.h36
-rw-r--r--tikzit-old/src/common/SupportDir.m65
-rw-r--r--tikzit-old/src/common/TikzGraphAssembler+Parser.h36
-rw-r--r--tikzit-old/src/common/TikzGraphAssembler.h115
-rw-r--r--tikzit-old/src/common/TikzGraphAssembler.m310
-rw-r--r--tikzit-old/src/common/TikzShape.h37
-rw-r--r--tikzit-old/src/common/TikzShape.m70
-rw-r--r--tikzit-old/src/common/Transformer.h154
-rw-r--r--tikzit-old/src/common/Transformer.m231
-rw-r--r--tikzit-old/src/common/test/Makefile14
-rw-r--r--tikzit-old/src/common/test/color.m80
-rw-r--r--tikzit-old/src/common/test/common.m34
-rw-r--r--tikzit-old/src/common/test/maths.m562
-rw-r--r--tikzit-old/src/common/test/parser.m86
-rw-r--r--tikzit-old/src/common/test/test.h57
-rw-r--r--tikzit-old/src/common/test/test.m175
-rw-r--r--tikzit-old/src/common/tikzlexer.lm170
-rw-r--r--tikzit-old/src/common/tikzparser.ym224
-rw-r--r--tikzit-old/src/common/tikzparserdefs.h49
-rw-r--r--tikzit-old/src/common/util.h201
-rw-r--r--tikzit-old/src/common/util.m403
73 files changed, 11443 insertions, 0 deletions
diff --git a/tikzit-old/src/common/CircleShape.h b/tikzit-old/src/common/CircleShape.h
new file mode 100644
index 0000000..8215b92
--- /dev/null
+++ b/tikzit-old/src/common/CircleShape.h
@@ -0,0 +1,33 @@
+//
+// CircleShape.h
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+#import "Shape.h"
+
+@interface CircleShape : Shape {
+}
+
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/CircleShape.m b/tikzit-old/src/common/CircleShape.m
new file mode 100644
index 0000000..f2d1d52
--- /dev/null
+++ b/tikzit-old/src/common/CircleShape.m
@@ -0,0 +1,57 @@
+//
+// CircleShape.m
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import "CircleShape.h"
+#import "Node.h"
+#import "Edge.h"
+
+@implementation CircleShape
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ Node *n0,*n1,*n2,*n3;
+
+ n0 = [Node nodeWithPoint:NSMakePoint( 0.0f, 0.2f)];
+ n1 = [Node nodeWithPoint:NSMakePoint( 0.2f, 0.0f)];
+ n2 = [Node nodeWithPoint:NSMakePoint( 0.0f, -0.2f)];
+ n3 = [Node nodeWithPoint:NSMakePoint(-0.2f, 0.0f)];
+
+ Edge *e0,*e1,*e2,*e3;
+
+ e0 = [Edge edgeWithSource:n0 andTarget:n1]; [e0 setBend:-45];
+ e1 = [Edge edgeWithSource:n1 andTarget:n2]; [e1 setBend:-45];
+ e2 = [Edge edgeWithSource:n2 andTarget:n3]; [e2 setBend:-45];
+ e3 = [Edge edgeWithSource:n3 andTarget:n0]; [e3 setBend:-45];
+
+ paths = [[NSSet alloc] initWithObjects:[NSArray arrayWithObjects:e0,e1,e2,e3,nil],nil];
+
+ styleTikz = @"circle";
+ }
+ return self;
+}
+
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/ColorRGB.h b/tikzit-old/src/common/ColorRGB.h
new file mode 100644
index 0000000..607ba64
--- /dev/null
+++ b/tikzit-old/src/common/ColorRGB.h
@@ -0,0 +1,64 @@
+//
+// ColorRGB.h
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+#import "RColor.h"
+
+@interface ColorRGB : NSObject<NSCopying> {
+ unsigned short red, green, blue;
+}
+
+@property (assign) unsigned short red;
+@property (assign) unsigned short green;
+@property (assign) unsigned short blue;
+
+@property (assign) float redFloat;
+@property (assign) float greenFloat;
+@property (assign) float blueFloat;
+
+@property (readonly) NSString *name;
+
+- (RColor)rColor;
+- (RColor)rColorWithAlpha:(CGFloat)alpha;
+
+- (NSString*)hexName;
+- (BOOL)isEqual:(id)col;
+- (float)distanceFromColor:(ColorRGB*)col;
+- (int)hash;
+
+- (id)initWithRed:(unsigned short)r green:(unsigned short)g blue:(unsigned short)b;
+- (id)initWithFloatRed:(float)r green:(float)g blue:(float)b;
+- (id)initWithRColor:(RColor)color;
+
+- (void)setToClosestHashed;
+
++ (ColorRGB*)colorWithRed:(unsigned short)r green:(unsigned short)g blue:(unsigned short)b;
++ (ColorRGB*)colorWithFloatRed:(float)r green:(float)g blue:(float)b;
++ (ColorRGB*)colorWithRColor:(RColor)color;
+
++ (void)makeColorHash;
++ (void)releaseColorHash;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/ColorRGB.m b/tikzit-old/src/common/ColorRGB.m
new file mode 100644
index 0000000..840d716
--- /dev/null
+++ b/tikzit-old/src/common/ColorRGB.m
@@ -0,0 +1,353 @@
+//
+// ColorRGB.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 <http://www.gnu.org/licenses/>.
+//
+
+#import "ColorRGB.h"
+#import "util.h"
+
+typedef struct {
+#if __has_feature(objc_arc)
+ __unsafe_unretained NSString *name;
+#else
+ NSString *name;
+#endif
+ unsigned short r, g, b;
+} ColorRGBEntry;
+
+static const ColorRGBEntry kColors[147] = {
+ { @"AliceBlue", 240, 248, 255 },
+ { @"AntiqueWhite", 250, 235, 215 },
+ { @"Aqua", 0, 255, 255 },
+ { @"Aquamarine", 127, 255, 212 },
+ { @"Azure", 240, 255, 255 },
+ { @"Beige", 245, 245, 220 },
+ { @"Bisque", 255, 228, 196 },
+ { @"Black", 0, 0, 0 },
+ { @"BlanchedAlmond", 255, 235, 205 },
+ { @"Blue", 0, 0, 255 },
+ { @"BlueViolet", 138, 43, 226 },
+ { @"Brown", 165, 42, 42 },
+ { @"BurlyWood", 222, 184, 135 },
+ { @"CadetBlue", 95, 158, 160 },
+ { @"Chartreuse", 127, 255, 0 },
+ { @"Chocolate", 210, 105, 30 },
+ { @"Coral", 255, 127, 80 },
+ { @"CornflowerBlue", 100, 149, 237 },
+ { @"Cornsilk", 255, 248, 220 },
+ { @"Crimson", 220, 20, 60 },
+ { @"Cyan", 0, 255, 255 },
+ { @"DarkBlue", 0, 0, 139 },
+ { @"DarkCyan", 0, 139, 139 },
+ { @"DarkGoldenrod", 184, 134, 11 },
+ { @"DarkGray", 169, 169, 169 },
+ { @"DarkGreen", 0, 100, 0 },
+ { @"DarkGrey", 169, 169, 169 },
+ { @"DarkKhaki", 189, 183, 107 },
+ { @"DarkMagenta", 139, 0, 139 },
+ { @"DarkOliveGreen", 85, 107, 47 },
+ { @"DarkOrange", 255, 140, 0 },
+ { @"DarkOrchid", 153, 50, 204 },
+ { @"DarkRed", 139, 0, 0 },
+ { @"DarkSalmon", 233, 150, 122 },
+ { @"DarkSeaGreen", 143, 188, 143 },
+ { @"DarkSlateBlue", 72, 61, 139 },
+ { @"DarkSlateGray", 47, 79, 79 },
+ { @"DarkSlateGrey", 47, 79, 79 },
+ { @"DarkTurquoise", 0, 206, 209 },
+ { @"DarkViolet", 148, 0, 211 },
+ { @"DeepPink", 255, 20, 147 },
+ { @"DeepSkyBlue", 0, 191, 255 },
+ { @"DimGray", 105, 105, 105 },
+ { @"DimGrey", 105, 105, 105 },
+ { @"DodgerBlue", 30, 144, 255 },
+ { @"FireBrick", 178, 34, 34 },
+ { @"FloralWhite", 255, 250, 240 },
+ { @"ForestGreen", 34, 139, 34 },
+ { @"Fuchsia", 255, 0, 255 },
+ { @"Gainsboro", 220, 220, 220 },
+ { @"GhostWhite", 248, 248, 255 },
+ { @"Gold", 255, 215, 0 },
+ { @"Goldenrod", 218, 165, 32 },
+ { @"Gray", 128, 128, 128 },
+ { @"Grey", 128, 128, 128 },
+ { @"Green", 0, 128, 0 },
+ { @"GreenYellow", 173, 255, 47 },
+ { @"Honeydew", 240, 255, 240 },
+ { @"HotPink", 255, 105, 180 },
+ { @"IndianRed", 205, 92, 92 },
+ { @"Indigo", 75, 0, 130 },
+ { @"Ivory", 255, 255, 240 },
+ { @"Khaki", 240, 230, 140 },
+ { @"Lavender", 230, 230, 250 },
+ { @"LavenderBlush", 255, 240, 245 },
+ { @"LawnGreen", 124, 252, 0 },
+ { @"LemonChiffon", 255, 250, 205 },
+ { @"LightBlue", 173, 216, 230 },
+ { @"LightCoral", 240, 128, 128 },
+ { @"LightCyan", 224, 255, 255 },
+ { @"LightGoldenrodYellow", 250, 250, 210 },
+ { @"LightGray", 211, 211, 211 },
+ { @"LightGreen", 144, 238, 144 },
+ { @"LightGrey", 211, 211, 211 },
+ { @"LightPink", 255, 182, 193 },
+ { @"LightSalmon", 255, 160, 122 },
+ { @"LightSeaGreen", 32, 178, 170 },
+ { @"LightSkyBlue", 135, 206, 250 },
+ { @"LightSlateGray", 119, 136, 153 },
+ { @"LightSlateGrey", 119, 136, 153 },
+ { @"LightSteelBlue", 176, 196, 222 },
+ { @"LightYellow", 255, 255, 224 },
+ { @"Lime", 0, 255, 0 },
+ { @"LimeGreen", 50, 205, 50 },
+ { @"Linen", 250, 240, 230 },
+ { @"Magenta", 255, 0, 255 },
+ { @"Maroon", 128, 0, 0 },
+ { @"MediumAquamarine", 102, 205, 170 },
+ { @"MediumBlue", 0, 0, 205 },
+ { @"MediumOrchid", 186, 85, 211 },
+ { @"MediumPurple", 147, 112, 219 },
+ { @"MediumSeaGreen", 60, 179, 113 },
+ { @"MediumSlateBlue", 123, 104, 238 },
+ { @"MediumSpringGreen", 0, 250, 154 },
+ { @"MediumTurquoise", 72, 209, 204 },
+ { @"MediumVioletRed", 199, 21, 133 },
+ { @"MidnightBlue", 25, 25, 112 },
+ { @"MintCream", 245, 255, 250 },
+ { @"MistyRose", 255, 228, 225 },
+ { @"Moccasin", 255, 228, 181 },
+ { @"NavajoWhite", 255, 222, 173 },
+ { @"Navy", 0, 0, 128 },
+ { @"OldLace", 253, 245, 230 },
+ { @"Olive", 128, 128, 0 },
+ { @"OliveDrab", 107, 142, 35 },
+ { @"Orange", 255, 165, 0 },
+ { @"OrangeRed", 255, 69, 0 },
+ { @"Orchid", 218, 112, 214 },
+ { @"PaleGoldenrod", 238, 232, 170 },
+ { @"PaleGreen", 152, 251, 152 },
+ { @"PaleTurquoise", 175, 238, 238 },
+ { @"PaleVioletRed", 219, 112, 147 },
+ { @"PapayaWhip", 255, 239, 213 },
+ { @"PeachPuff", 255, 218, 185 },
+ { @"Peru", 205, 133, 63 },
+ { @"Pink", 255, 192, 203 },
+ { @"Plum", 221, 160, 221 },
+ { @"PowderBlue", 176, 224, 230 },
+ { @"Purple", 128, 0, 128 },
+ { @"Red", 255, 0, 0 },
+ { @"RosyBrown", 188, 143, 143 },
+ { @"RoyalBlue", 65, 105, 225 },
+ { @"SaddleBrown", 139, 69, 19 },
+ { @"Salmon", 250, 128, 114 },
+ { @"SandyBrown", 244, 164, 96 },
+ { @"SeaGreen", 46, 139, 87 },
+ { @"Seashell", 255, 245, 238 },
+ { @"Sienna", 160, 82, 45 },
+ { @"Silver", 192, 192, 192 },
+ { @"SkyBlue", 135, 206, 235 },
+ { @"SlateBlue", 106, 90, 205 },
+ { @"SlateGray", 112, 128, 144 },
+ { @"SlateGrey", 112, 128, 144 },
+ { @"Snow", 255, 250, 250 },
+ { @"SpringGreen", 0, 255, 127 },
+ { @"SteelBlue", 70, 130, 180 },
+ { @"Tan", 210, 180, 140 },
+ { @"Teal", 0, 128, 128 },
+ { @"Thistle", 216, 191, 216 },
+ { @"Tomato", 255, 99, 71 },
+ { @"Turquoise", 64, 224, 208 },
+ { @"Violet", 238, 130, 238 },
+ { @"Wheat", 245, 222, 179 },
+ { @"White", 255, 255, 255 },
+ { @"WhiteSmoke", 245, 245, 245 },
+ { @"Yellow", 255, 255, 0 },
+ { @"YellowGreen", 154, 205, 50 }
+};
+
+static NSMapTable *colorHash = nil;
+
+@implementation ColorRGB
+
++ (void)initialize {
+ [self setKeys:[NSArray arrayWithObject:@"red"] triggerChangeNotificationsForDependentKey:@"redFloat"];
+ [self setKeys:[NSArray arrayWithObject:@"green"] triggerChangeNotificationsForDependentKey:@"greenFloat"];
+ [self setKeys:[NSArray arrayWithObject:@"blue"] triggerChangeNotificationsForDependentKey:@"blueFloat"];
+ [self setKeys:[NSArray arrayWithObjects:@"red", @"green", @"blue", nil]
+ triggerChangeNotificationsForDependentKey:@"name"];
+}
+
+@synthesize red, green, blue;
+
+- (float)redFloat { return ((float)red)/255.0f; }
+- (void)setRedFloat:(float)r { [self setRed:round(r*255.0f)]; }
+- (float)greenFloat { return ((float)green)/255.0f; }
+- (void)setGreenFloat:(float)g { [self setGreen:round(g*255.0f)]; }
+- (float)blueFloat { return ((float)blue)/255.0f; }
+- (void)setBlueFloat:(float)b { [self setBlue:round(b*255.0f)]; }
+
+- (int)hash {
+ return (red<<4) + (green<<2) + blue;
+}
+
+- (id)initWithRed:(unsigned short)r green:(unsigned short)g blue:(unsigned short)b {
+ self = [super init];
+ if (self) {
+ red = r;
+ green = g;
+ blue = b;
+ }
+ return self;
+}
+
+- (id)initWithFloatRed:(float)r green:(float)g blue:(float)b {
+ self = [super init];
+ if (self) {
+ red = round(r*255.0f);
+ green = round(g*255.0f);
+ blue = round(b*255.0f);
+ }
+ return self;
+}
+
+- (id) initWithRColor:(RColor)color {
+ return [self initWithFloatRed:color.red green:color.green blue:color.blue];
+}
+
+- (RColor) rColor {
+ return MakeSolidRColor ([self redFloat], [self greenFloat], [self blueFloat]);
+}
+
+- (RColor) rColorWithAlpha:(CGFloat)alpha {
+ return MakeRColor ([self redFloat], [self greenFloat], [self blueFloat], alpha);
+}
+
+- (NSString*)name {
+ if (colorHash == nil)
+ [ColorRGB makeColorHash];
+ return [colorHash objectForKey:self];
+}
+
+- (NSString*)hexName {
+ return [NSString stringWithFormat:@"hexcolor0x%.2x%.2x%.2x", red, green, blue];
+}
+
+- (NSComparisonResult)compare:(ColorRGB*)col {
+ if (red > [col red]) return NSOrderedDescending;
+ else if (red < [col red]) return NSOrderedAscending;
+ else {
+ if (green > [col green]) return NSOrderedDescending;
+ else if (green < [col green]) return NSOrderedAscending;
+ else {
+ if (blue > [col blue]) return NSOrderedDescending;
+ else if (blue < [col blue]) return NSOrderedAscending;
+ else return NSOrderedSame;
+ }
+ }
+}
+
+- (BOOL)isEqual:(id)col {
+ return [self compare:col] == NSOrderedSame;
+}
+
+- (float)distanceFromColor:(ColorRGB *)col {
+ float dr = (float)(red - [col red]);
+ float dg = (float)(green - [col green]);
+ float db = (float)(blue - [col blue]);
+ return dr*dr + dg*dg + db*db;
+}
+
+- (id)copyWithZone:(NSZone*)zone {
+ ColorRGB *col = [[ColorRGB allocWithZone:zone] initWithRed:red green:green blue:blue];
+ return col;
+}
+
++ (ColorRGB*)colorWithRed:(unsigned short)r green:(unsigned short)g blue:(unsigned short)b {
+ ColorRGB *col = [[ColorRGB alloc] initWithRed:r green:g blue:b];
+#if __has_feature(objc_arc)
+ return col;
+#else
+ return [col autorelease];
+#endif
+}
+
+
++ (ColorRGB*)colorWithFloatRed:(float)r green:(float)g blue:(float)b {
+ ColorRGB *col = [[ColorRGB alloc] initWithFloatRed:r green:g blue:b];
+#if __has_feature(objc_arc)
+ return col;
+#else
+ return [col autorelease];
+#endif
+}
+
++ (ColorRGB*) colorWithRColor:(RColor)color {
+#if __has_feature(objc_arc)
+ return [[self alloc] initWithRColor:color];
+#else
+ return [[[self alloc] initWithRColor:color] autorelease];
+#endif
+}
+
++ (void)makeColorHash {
+ NSMapTable *h = [[NSMapTable alloc] init];
+ int i;
+ for (i = 0; i < 147; ++i) {
+ ColorRGB *col = [[ColorRGB alloc] initWithRed:kColors[i].r
+ green:kColors[i].g
+ blue:kColors[i].b];
+ [h setObject:kColors[i].name forKey:col];
+ //NSLog(@"adding color %@ (%d)", kColors[i].name, [col hash]);
+#if ! __has_feature(objc_arc)
+ [col release];
+#endif
+ }
+ colorHash = h;
+}
+
++ (void)releaseColorHash {
+#if ! __has_feature(objc_arc)
+ [colorHash release];
+#endif
+}
+
+- (void)setToClosestHashed {
+ if (colorHash == nil)
+ [ColorRGB makeColorHash];
+ float bestDist = -1;
+ ColorRGB *bestColor = nil;
+ NSEnumerator *enumerator = [colorHash keyEnumerator];
+ ColorRGB *tryColor;
+ while ((tryColor = [enumerator nextObject]) != nil) {
+ float dist = [self distanceFromColor:tryColor];
+ if (bestDist<0 || dist<bestDist) {
+ bestDist = dist;
+ bestColor = tryColor;
+ }
+ }
+ [self setRed:[bestColor red]];
+ [self setGreen:[bestColor green]];
+ [self setBlue:[bestColor blue]];
+}
+
+@end
+
+// vi:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/DiamondShape.h b/tikzit-old/src/common/DiamondShape.h
new file mode 100644
index 0000000..8f63386
--- /dev/null
+++ b/tikzit-old/src/common/DiamondShape.h
@@ -0,0 +1,34 @@
+//
+// DiamondShape.h
+// TikZiT
+//
+// Copyright 2012 Alex Merry
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+#import "Shape.h"
+
+@interface DiamondShape : Shape {
+}
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
+
diff --git a/tikzit-old/src/common/DiamondShape.m b/tikzit-old/src/common/DiamondShape.m
new file mode 100644
index 0000000..1a578b8
--- /dev/null
+++ b/tikzit-old/src/common/DiamondShape.m
@@ -0,0 +1,63 @@
+//
+// DiamondShape.m
+// TikZiT
+//
+// Copyright 2011 Aleks Kissinger
+// Copyright 2012 Alex Merry
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#import "DiamondShape.h"
+
+#import "Node.h"
+#import "Edge.h"
+
+@implementation DiamondShape
+
+- (id)init {
+ self = [super init];
+
+ if (!self)
+ return nil;
+
+ Node *n0,*n1,*n2,*n3;
+ float sz = 0.25f;
+
+ n0 = [Node nodeWithPoint:NSMakePoint(0, sz)];
+ n1 = [Node nodeWithPoint:NSMakePoint(sz, 0)];
+ n2 = [Node nodeWithPoint:NSMakePoint(0,-sz)];
+ n3 = [Node nodeWithPoint:NSMakePoint(-sz,0)];
+
+ Edge *e0,*e1,*e2,*e3;
+
+ e0 = [Edge edgeWithSource:n0 andTarget:n1];
+ e1 = [Edge edgeWithSource:n1 andTarget:n2];
+ e2 = [Edge edgeWithSource:n2 andTarget:n3];
+ e3 = [Edge edgeWithSource:n3 andTarget:n0];
+
+ paths = [[NSSet alloc] initWithObjects:[NSArray arrayWithObjects:e0,e1,e2,e3,nil],nil];
+
+ styleTikz = @"shape=diamond";
+
+ return self;
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/Edge.h b/tikzit-old/src/common/Edge.h
new file mode 100644
index 0000000..accf38c
--- /dev/null
+++ b/tikzit-old/src/common/Edge.h
@@ -0,0 +1,401 @@
+//
+// Edge.h
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+
+// Edge : store the data associated with an edge. Also, lazily compute
+// bezier curve control points based on bend and the coordinates of
+// the endpoints.
+
+#import "Node.h"
+#import "EdgeStyle.h"
+
+/*!
+ @typedef enum EdgeBendMode
+ @brief Indicates the type of edge bend.
+ @var EdgeBendModeBasic A basic, one-angle bend. Positive values will be interpreted
+ as bend left, negative as bend right.
+ @var EdgeBendModeInOut A two-angle bend mode, using inAngle and outAngle.
+ */
+typedef enum {
+ EdgeBendModeBasic,
+ EdgeBendModeInOut
+} EdgeBendMode;
+
+/*!
+ @class Edge
+ @brief A graph edge, with associated bend and style data.
+ @details A graph edge, with associated bend and style data. This class
+ also contains methods for computing the bezier control points
+ and the midpoint of the curve.
+ */
+@interface Edge : NSObject<NSCopying> {
+ Node *source;
+ Node *target;
+ Node *edgeNode;
+ int bend;
+ int inAngle, outAngle;
+ EdgeBendMode bendMode;
+ float weight;
+ EdgeStyle *style;
+ GraphElementData *data;
+ NSString *sourceAnchor;
+ NSString *targetAnchor;
+
+ // When set to YES, lazily create the edge node, and keep it around when set
+ // to NO (at least until saved/loaded).
+ BOOL hasEdgeNode;
+ BOOL dirty;
+
+ // these are all cached values computed from the above
+ NSPoint src;
+ NSPoint targ;
+ NSPoint head;
+ NSPoint tail;
+ NSPoint cp1;
+ NSPoint cp2;
+ NSPoint mid;
+ NSPoint midTan;
+ NSPoint headTan;
+ NSPoint tailTan;
+}
+
+/*!
+ @property data
+ @brief Associated edge data.
+ */
+@property (copy) GraphElementData *data;
+
+// KVC methods
+- (void) insertObject:(GraphElementProperty*)gep
+ inDataAtIndex:(NSUInteger)index;
+- (void) removeObjectFromDataAtIndex:(NSUInteger)index;
+- (void) replaceObjectInDataAtIndex:(NSUInteger)index
+ withObject:(GraphElementProperty*)gep;
+
+/*!
+ @property style
+ @brief Edge style.
+ */
+@property (retain) EdgeStyle *style;
+
+/*!
+ @property source
+ @brief Source node.
+ */
+@property (retain) Node *source;
+
+/*!
+ @property target
+ @brief Target node.
+ */
+@property (retain) Node *target;
+
+/*!
+ @property edgeNode
+ @brief A node attached to this edge, as in a label or tick.
+ */
+@property (retain) Node *edgeNode;
+
+/*!
+ @property sourceAnchor
+ @brief The source node anchor point, as in north or center.
+ */
+@property (copy) NSString *sourceAnchor;
+
+/*!
+ @property targetAnchor
+ @brief The target node anchor point, as in north or center.
+ */
+@property (copy) NSString *targetAnchor;
+
+/*!
+ @property hasEdgeNode
+ @brief A read/write property. When set to true, a new edge node is actually constructed.
+*/
+@property (assign) BOOL hasEdgeNode;
+
+/*!
+ @property bend
+ @brief The degrees by which the edge bends.
+ */
+@property (assign) int bend;
+
+/*!
+ @property weight
+ @brief How close the edge will pass to control points.
+ */
+@property (assign) float weight;
+
+/*!
+ @property inAngle
+ @brief The angle by which the edge enters its target.
+ */
+@property (assign) int inAngle;
+
+/*!
+ @property outAngle
+ @brief The angle by which the edge leaves its target.
+ */
+@property (assign) int outAngle;
+
+/*!
+ @property bendMode
+ @brief The mode of the edge bend. Either simple bend in in/out style.
+ */
+@property (assign) EdgeBendMode bendMode;
+
+/*!
+ @property head
+ @brief The starting point of the edge.
+ @detail This value is computed based on the source, target and
+ either bend or in/out angles. It is where the edge
+ makes contact with the source node.
+ */
+@property (readonly) NSPoint head;
+
+/*!
+ @property tail
+ @brief The ending point of the edge.
+ @detail This value is computed based on the source, target and
+ either bend or in/out angles. It is where the edge
+ makes contact with the target node.
+ */
+@property (readonly) NSPoint tail;
+
+/*!
+ @property cp1
+ @brief The first control point of the edge.
+ @detail This value is computed based on the source, target and
+ either bend or in/out angles.
+ */
+@property (readonly) NSPoint cp1;
+
+/*!
+ @property cp2
+ @brief The second control point of the edge.
+ @detail This value is computed based on the source, target and
+ either bend or in/out angles.
+ */
+@property (readonly) NSPoint cp2;
+
+/*!
+ @property mid
+ @brief The midpoint of the curve. Computed from the source, target, and control points.
+ */
+@property (readonly) NSPoint mid;
+
+/*!
+ @property mid_tan
+ @brief The second point of a line tangent to the midpoint. (The first is the midpoint itself.)
+ */
+@property (readonly) NSPoint midTan;
+
+/*!
+ @property left_normal
+ @brief The second point in a line perp. to the edge coming from mid-point. (left side)
+ */
+@property (readonly) NSPoint leftNormal;
+
+/*!
+ @property left_normal
+ @brief The second point in a line perp. to the edge coming from mid-point. (right side)
+ */
+@property (readonly) NSPoint rightNormal;
+
+@property (readonly) NSPoint headTan;
+
+/*!
+ @property leftHeadNormal
+ */
+@property (readonly) NSPoint leftHeadNormal;
+
+/*!
+ @property rightHeadNormal
+ */
+@property (readonly) NSPoint rightHeadNormal;
+
+@property (readonly) NSPoint tailTan;
+
+/*!
+ @property leftTailNormal
+ */
+@property (readonly) NSPoint leftTailNormal;
+
+/*!
+ @property rightTailNormal
+ */
+@property (readonly) NSPoint rightTailNormal;
+
+/*!
+ @property isSelfLoop
+ @brief Returns YES if this edge is a self loop.
+ */
+@property (readonly) BOOL isSelfLoop;
+
+/*!
+ @property isStraight
+ @brief Returns YES if this edge can be drawn as a straight line (as opposed to a bezier curve).
+ */
+@property (readonly) BOOL isStraight;
+
+
+/*!
+ @brief Construct a blank edge.
+ @result An edge.
+ */
+- (id)init;
+
+/*!
+ @brief Construct an edge with the given source and target.
+ @param s the source node.
+ @param t the target node.
+ @result An edge.
+ */
+- (id)initWithSource:(Node*)s andTarget:(Node*)t;
+
+/*!
+ @brief Force the recalculation of the derived properties.
+ */
+- (void)recalculateProperties;
+
+/*!
+ @brief Recompute the control points and midpoint.
+ */
+- (void)updateControls;
+
+/*!
+ @brief Push edge properties back into its <tt>GraphElementData</tt>.
+ */
+- (void)updateData;
+
+/*!
+ @brief Set edge properties from fields in <tt>GraphElementData</tt>.
+ */
+- (void)setAttributesFromData;
+
+/*!
+ @brief Use data.style to find and attach the <tt>EdgeStyle</tt> object from the given array.
+ */
+- (BOOL)attachStyleFromTable:(NSArray*)styles;
+
+/*!
+ @brief Convert the bend angle to an inAngle and outAngle.
+ */
+- (void)convertBendToAngles;
+
+/*!
+ @brief Set the bend angle to the average of the in and out angles.
+ */
+- (void)convertAnglesToBend;
+
+/*!
+ @brief Update this edge to look just like the given edge.
+ @param e an edge to mimic.
+ */
+- (void)setPropertiesFromEdge:(Edge *)e;
+
+/*!
+ @brief Get a bounding rect for this edge.
+ @detail Note that this may not be a tight bound.
+ */
+- (NSRect)boundingRect;
+
+/*!
+ @brief Moves the first control point, updating edge properties as necessary
+ @detail This will move a control point and adjust the weight and
+ bend (or outAngle) to fit.
+
+ A courseness can be specified for both the weight and the
+ bend, allowing them to be constrained to certain values. For
+ example, passing 10 as the bend courseness will force the bend
+ to be a multiple of 5. Passing 0 for either of these will
+ cause the relevant value to be unconstrained.
+ @param point the new position of the control point
+ @param wc force the weight to be a multiple of this value (unless 0)
+ @param bc force the bend (or outAngle) to be a multiple of this value (unless 0)
+ @param link when in EdgeBendModeInOut, change both the in and out angles at once
+ */
+- (void) moveCp1To:(NSPoint)point withWeightCourseness:(float)wc andBendCourseness:(int)bc forceLinkControlPoints:(BOOL)link;
+
+/*!
+ @brief Moves the first control point, updating edge properties as necessary
+ @detail This will move a control point and adjust the weight and
+ bend (or outAngle) to fit.
+
+ The same as moveCp1To:point withWeightCourseness:0.0f andBendCourseness:0 forceLinkControlPoints:No
+ @param point the new position of the control point
+ @param wc force the weight to be a multiple of this value (unless 0)
+ @param bc force the bend (or outAngle) to be a multiple of this value (unless 0)
+ @param link when in EdgeBendModeInOut, change both the in and out angles at once
+ */
+- (void) moveCp1To:(NSPoint)point;
+
+/*!
+ @brief Moves the first control point, updating edge properties as necessary
+ @detail This will move a control point and adjust the weight and
+ bend (or inAngle) to fit.
+
+ A courseness can be specified for both the weight and the
+ bend, allowing them to be constrained to certain values. For
+ example, passing 10 as the bend courseness will force the bend
+ to be a multiple of 5. Passing 0 for either of these will
+ cause the relevant value to be unconstrained.
+ @param point the new position of the control point
+ @param wc force the weight to be a multiple of this value (unless 0)
+ @param bc force the bend (or inAngle) to be a multiple of this value (unless 0)
+ @param link when in EdgeBendModeInOut, change both the in and out angles at once
+ */
+- (void) moveCp2To:(NSPoint)point withWeightCourseness:(float)wc andBendCourseness:(int)bc forceLinkControlPoints:(BOOL)link;
+
+/*!
+ @brief Moves the first control point, updating edge properties as necessary
+ @detail This will move a control point and adjust the weight and
+ bend (or inAngle) to fit.
+
+ The same as moveCp2To:point withWeightCourseness:0.0f andBendCourseness:0 forceLinkControlPoints:No
+ @param point the new position of the control point
+ */
+- (void) moveCp2To:(NSPoint)point;
+
+/*!
+ @brief Reverse edge direction, updating bend/inAngle/outAngle/etc
+ */
+- (void)reverse;
+
+/*!
+ @brief Factory method to make a blank edge.
+ @result An edge.
+ */
++ (Edge*)edge;
+
+/*!
+ @brief Factory method to make an edge with the given source and target.
+ @param s a source node.
+ @param t a target node.
+ @result An edge.
+ */
++ (Edge*)edgeWithSource:(Node*)s andTarget:(Node*)t;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/Edge.m b/tikzit-old/src/common/Edge.m
new file mode 100644
index 0000000..0c88e9d
--- /dev/null
+++ b/tikzit-old/src/common/Edge.m
@@ -0,0 +1,757 @@
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#import "Edge.h"
+#import "Shape.h"
+#import "util.h"
+
+@implementation Edge
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ data = [[GraphElementData alloc] init];
+ bend = 0;
+ inAngle = 135;
+ outAngle = 45;
+ bendMode = EdgeBendModeBasic;
+ weight = 0.4f;
+ dirty = YES;
+ source = nil;
+ target = nil;
+ edgeNode = nil;
+ sourceAnchor = @"";
+ targetAnchor = @"";
+ }
+ return self;
+}
+
+- (id)initWithSource:(Node*)s andTarget:(Node*)t {
+ self = [self init];
+ if (self) {
+ [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
+#if __has_feature(objc_arc)
+ style = [EdgeStyle defaultEdgeStyleWithName:style_name];
+#else
+ style = [[EdgeStyle defaultEdgeStyleWithName:style_name] retain];
+#endif
+ return NO;
+}
+
+- (NSPoint) _findContactPointOn:(Node*)node at:(float)angle {
+ NSPoint rayStart = [node point];
+ Shape *shape = [node shape];
+ if (shape == nil) {
+ return rayStart;
+ }
+
+ Transformer *shapeTrans = [node shapeTransformer];
+ // rounding errors are a pain
+ NSRect searchArea = NSInsetRect([node boundsUsingShapeTransform:shapeTrans],-0.01,-0.01);
+ if (!NSPointInRect(rayStart, searchArea)) {
+ return rayStart;
+ }
+
+ NSPoint rayEnd = findExitPointOfRay (rayStart, angle, searchArea);
+
+ for (NSArray *path in [shape paths]) {
+ for (Edge *curve in path) {
+ NSPoint intersect;
+ [curve updateControls];
+ if (lineSegmentIntersectsBezier (rayStart, rayEnd,
+ [shapeTrans toScreen:curve->tail],
+ [shapeTrans toScreen:curve->cp1],
+ [shapeTrans toScreen:curve->cp2],
+ [shapeTrans toScreen:curve->head],
+ &intersect)) {
+ // we just keep shortening the line
+ rayStart = intersect;
+ }
+ }
+ }
+
+ return rayStart;
+}
+
+- (NSPoint) _findTanFor:(NSPoint)pt usingSpanFrom:(float)t1 to:(float)t2 {
+ float dx = bezierInterpolate(t2, tail.x, cp1.x, cp2.x, head.x) -
+ bezierInterpolate(t1, tail.x, cp1.x, cp2.x, head.x);
+ float dy = bezierInterpolate(t2, tail.y, cp1.y, cp2.y, head.y) -
+ bezierInterpolate(t1, tail.y, cp1.y, cp2.y, head.y);
+
+ // normalise
+ float len = sqrt(dx*dx+dy*dy);
+ if (len != 0) {
+ dx = (dx/len) * 0.1f;
+ dy = (dy/len) * 0.1f;
+ }
+
+ return NSMakePoint (pt.x + dx, pt.y + dy);
+}
+
+- (void)recalculateProperties {
+ dirty = YES;
+}
+
+- (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;
+
+ 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);
+ }
+
+ tail = [self _findContactPointOn:source at:angleSrc];
+ head = [self _findContactPointOn:target at:angleTarg];
+
+ // 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 = bezierInterpolateFull (0.5f, tail, cp1, cp2, head);
+ midTan = [self _findTanFor:mid usingSpanFrom:0.4f to:0.6f];
+
+ tailTan = [self _findTanFor:tail usingSpanFrom:0.0f to:0.1f];
+ headTan = [self _findTanFor:head usingSpanFrom:1.0f to:0.9f];
+ }
+ 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);
+
+ [self setOutAngle:round((angle - bnd) * (180.0f/M_PI))];
+ [self setInAngle: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;
+
+ [self setBend:(bend1 + bend2) / 2];
+}
+
+- (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)headTan {
+ [self updateControls];
+ return headTan;
+}
+
+- (NSPoint)leftHeadNormal {
+ [self updateControls];
+ return NSMakePoint(headTan.x + (head.y - headTan.y), headTan.y - (head.x - headTan.x));
+}
+
+- (NSPoint)rightHeadNormal {
+ [self updateControls];
+ return NSMakePoint(headTan.x - (head.y - headTan.y), headTan.y + (head.x - headTan.x));
+}
+
+- (NSPoint)tailTan {
+ [self updateControls];
+ return tailTan;
+}
+
+- (NSPoint)leftTailNormal {
+ [self updateControls];
+ return NSMakePoint(tailTan.x + (tail.y - tailTan.y), tailTan.y - (tail.x - tailTan.x));
+}
+
+- (NSPoint)rightTailNormal {
+ [self updateControls];
+ return NSMakePoint(tailTan.x - (tail.y - tailTan.y), tailTan.y + (tail.x - tailTan.x));
+}
+
+- (NSPoint) head {
+ [self updateControls];
+ return head;
+}
+
+- (NSPoint) tail {
+ [self updateControls];
+ return tail;
+}
+
+- (NSPoint)cp1 {
+ [self updateControls];
+ return cp1;
+}
+
+- (NSPoint)cp2 {
+ [self updateControls];
+ return cp2;
+}
+
+- (int)inAngle {return inAngle;}
+- (void)setInAngle:(int)a {
+ inAngle = normaliseAngleDeg (a);
+ dirty = YES;
+}
+
+- (int)outAngle {return outAngle;}
+- (void)setOutAngle:(int)a {
+ outAngle = normaliseAngleDeg (a);
+ dirty = YES;
+}
+
+- (EdgeBendMode)bendMode {return bendMode;}
+- (void)setBendMode:(EdgeBendMode)mode {
+ bendMode = mode;
+ dirty = YES;
+}
+
+- (int)bend {return bend;}
+- (void)setBend:(int)b {
+ bend = normaliseAngleDeg (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) {
+#if __has_feature(objc_arc)
+ style = s;
+#else
+ [style release];
+ style = [s retain];
+#endif
+ }
+}
+
+- (Node*)source {return source;}
+- (void)setSource:(Node *)s {
+ if (source != s) {
+ [source removeObserver:self
+ forKeyPath:@"style"];
+
+ if ([s style] == nil) {
+ [self setSourceAnchor:@"center"];
+ } else if ([sourceAnchor isEqual:@"center"]) {
+ [self setSourceAnchor:@""];
+ }
+
+#if __has_feature(objc_arc)
+ source = s;
+#else
+ [source release];
+ source = [s retain];
+#endif
+
+ if (source==target) {
+ bendMode = EdgeBendModeInOut;
+ weight = 1.0f;
+ }
+
+ [source addObserver:self
+ forKeyPath:@"style"
+ options:NSKeyValueObservingOptionNew
+ | NSKeyValueObservingOptionOld
+ context:NULL];
+
+ dirty = YES;
+ }
+}
+
+- (Node*)target {return target;}
+- (void)setTarget:(Node *)t {
+ if (target != t) {
+ [target removeObserver:self
+ forKeyPath:@"style"];
+
+ if ([t style] == nil) {
+ [self setTargetAnchor:@"center"];
+ } else if ([targetAnchor isEqual:@"center"]) {
+ [self setTargetAnchor:@""];
+ }
+
+#if __has_feature(objc_arc)
+ target = t;
+#else
+ [target release];
+ target = [t retain];
+#endif
+
+ if (source==target) {
+ bendMode = EdgeBendModeInOut;
+ weight = 1.0f;
+ }
+
+ [target addObserver:self
+ forKeyPath:@"style"
+ options:NSKeyValueObservingOptionNew
+ | NSKeyValueObservingOptionOld
+ context:NULL];
+
+ dirty = YES;
+ }
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath
+ ofObject:(id)object
+ change:(NSDictionary *)change
+ context:(void *)context
+
+{
+ if ([keyPath isEqual:@"style"]) {
+ id oldStyle = [change objectForKey:NSKeyValueChangeOldKey];
+ id newStyle = [change objectForKey:NSKeyValueChangeNewKey];
+ id none = [NSNull null];
+ if (object == source) {
+ if (oldStyle != none && newStyle == none) {
+ [self setSourceAnchor:@"center"];
+ } else if (oldStyle == none && newStyle != none &&
+ [sourceAnchor isEqual:@"center"]) {
+ [self setSourceAnchor:@""];
+ }
+ }
+ if (object == target) {
+ if (oldStyle != none && newStyle == none) {
+ [self setTargetAnchor:@"center"];
+ } else if (oldStyle == none && newStyle != none &&
+ [targetAnchor isEqual:@"center"]) {
+ [self setTargetAnchor:@""];
+ }
+ }
+ }
+ 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 {
+ [self willChangeValueForKey:@"edgeNode"];
+ [self willChangeValueForKey:@"hasEdgeNode"];
+ if (edgeNode != n) {
+ hasEdgeNode = (n != nil);
+#if __has_feature(objc_arc)
+ edgeNode = n;
+#else
+ [edgeNode release];
+ edgeNode = [n retain];
+#endif
+ // don't set dirty bit, because control points don't need update
+ }
+ [self didChangeValueForKey:@"edgeNode"];
+ [self didChangeValueForKey:@"hasEdgeNode"];
+}
+
+- (BOOL)hasEdgeNode { return hasEdgeNode; }
+- (void)setHasEdgeNode:(BOOL)b {
+ [self willChangeValueForKey:@"edgeNode"];
+ [self willChangeValueForKey:@"hasEdgeNode"];
+ hasEdgeNode = b;
+ if (hasEdgeNode && edgeNode == nil) {
+ edgeNode = [[Node alloc] init];
+ }
+ [self didChangeValueForKey:@"edgeNode"];
+ [self didChangeValueForKey:@"hasEdgeNode"];
+}
+
+- (NSString*) sourceAnchor { return sourceAnchor; }
+- (void)setSourceAnchor:(NSString *)_sourceAnchor{
+ NSString *oldSourceAnchor = sourceAnchor;
+ if(_sourceAnchor != nil){
+ sourceAnchor = [_sourceAnchor copy];
+ }else{
+ sourceAnchor = @"";
+ }
+#if ! __has_feature(objc_arc)
+ [oldSourceAnchor release];
+#endif
+}
+
+- (NSString*) targetAnchor { return targetAnchor; }
+- (void)setTargetAnchor:(NSString *)_targetAnchor{
+ NSString *oldTargetAnchor = targetAnchor;
+ if(_targetAnchor != nil){
+ targetAnchor = [_targetAnchor copy];
+ }else{
+ targetAnchor = @"";
+ }
+#if ! __has_feature(objc_arc)
+ [oldTargetAnchor release];
+#endif
+}
+
+@synthesize data;
+- (void) insertObject:(GraphElementProperty*)gep
+ inDataAtIndex:(NSUInteger)index {
+ [data insertObject:gep atIndex:index];
+}
+- (void) removeObjectFromDataAtIndex:(NSUInteger)index {
+ [data removeObjectAtIndex:index];
+}
+- (void) replaceObjectInDataAtIndex:(NSUInteger)index
+ withObject:(GraphElementProperty*)gep {
+ [data replaceObjectAtIndex:index withObject:gep];
+}
+
+- (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] && ![self isStraight])
+ {
+ [data setProperty:[NSString stringWithFormat:@"%.2f",weight*2.5f]
+ forKey:@"looseness"];
+ }
+}
+
+- (void)setAttributesFromData {
+ bendMode = EdgeBendModeBasic;
+
+ if ([data isAtomSet:@"bend left"]) {
+ [self setBend:-30];
+ } else if ([data isAtomSet:@"bend right"]) {
+ [self setBend:30];
+ } else if ([data propertyForKey:@"bend left"] != nil) {
+ NSString *bnd = [data propertyForKey:@"bend left"];
+ [self setBend:-[bnd intValue]];
+ } else if ([data propertyForKey:@"bend right"] != nil) {
+ NSString *bnd = [data propertyForKey:@"bend right"];
+ [self setBend:[bnd intValue]];
+ } else {
+ [self setBend:0];
+
+ if ([data propertyForKey:@"in"] != nil && [data propertyForKey:@"out"] != nil) {
+ bendMode = EdgeBendModeInOut;
+ [self setInAngle:[[data propertyForKey:@"in"] intValue]];
+ [self setOutAngle:[[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];
+#if ! __has_feature(objc_arc)
+ [en release];
+#endif
+
+ GraphElementData *d = [[e data] copy];
+ [self setData:d];
+#if ! __has_feature(objc_arc)
+ [d release];
+#endif
+
+ [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];
+ NSRect bound = NSRectAround4Points(head, tail, cp1, cp2);
+ if ([self style] != nil) {
+ switch ([[self style] decorationStyle]) {
+ case ED_Arrow:
+ bound = NSRectWithPoint(bound, [self midTan]);
+ case ED_Tick:
+ bound = NSRectWithPoint(bound, [self leftNormal]);
+ bound = NSRectWithPoint(bound, [self rightNormal]);
+ case ED_None:
+ break;
+ }
+ if ([[self style] headStyle] != AH_None) {
+ bound = NSRectWithPoint(bound, [self leftHeadNormal]);
+ bound = NSRectWithPoint(bound, [self rightHeadNormal]);
+ }
+ if ([[self style] tailStyle] != AH_None) {
+ bound = NSRectWithPoint(bound, [self leftTailNormal]);
+ bound = NSRectWithPoint(bound, [self rightTailNormal]);
+ }
+ }
+ return bound;
+}
+
+- (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;
+ NSString *a;
+
+ n = source;
+ source = target;
+ target = n;
+
+ f = inAngle;
+ inAngle = outAngle;
+ outAngle = f;
+
+ a = sourceAnchor;
+ sourceAnchor = targetAnchor;
+ targetAnchor = a;
+
+ [self setBend:-bend];
+
+ dirty = YES;
+}
+
+- (void)dealloc {
+ [source removeObserver:self
+ forKeyPath:@"style"];
+ [target removeObserver:self
+ forKeyPath:@"style"];
+#if ! __has_feature(objc_arc)
+ [source release];
+ [target release];
+ [data release];
+ [sourceAnchor release];
+ [targetAnchor release];
+ [super dealloc];
+#endif
+}
+
+- (id)copyWithZone:(NSZone*)zone {
+ Edge *cp = [[Edge allocWithZone:zone] init];
+ [cp setSource:[self source]];
+ [cp setTarget:[self target]];
+ [cp setSourceAnchor:[self sourceAnchor]];
+ [cp setTargetAnchor:[self targetAnchor]];
+ [cp setPropertiesFromEdge:self];
+ return cp;
+}
+
++ (Edge*)edge {
+#if __has_feature(objc_arc)
+ return [[Edge alloc] init];
+#else
+ return [[[Edge alloc] init] autorelease];
+#endif
+}
+
++ (Edge*)edgeWithSource:(Node*)s andTarget:(Node*)t {
+#if __has_feature(objc_arc)
+ return [[Edge alloc] initWithSource:s andTarget:t];
+#else
+ return [[[Edge alloc] initWithSource:s andTarget:t] autorelease];
+#endif
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/EdgeStyle.h b/tikzit-old/src/common/EdgeStyle.h
new file mode 100644
index 0000000..a51f129
--- /dev/null
+++ b/tikzit-old/src/common/EdgeStyle.h
@@ -0,0 +1,71 @@
+//
+// EdgeStyle.h
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+#import "PropertyHolder.h"
+#import "ColorRGB.h"
+
+typedef enum {
+ AH_None = 0,
+ AH_Plain = 1,
+ AH_Latex = 2
+} ArrowHeadStyle;
+
+typedef enum {
+ ED_None = 0,
+ ED_Arrow = 1,
+ ED_Tick = 2
+} EdgeDectorationStyle;
+
+@interface EdgeStyle : PropertyHolder <NSCopying> {
+ ArrowHeadStyle headStyle, tailStyle;
+ EdgeDectorationStyle decorationStyle;
+ float thickness;
+ ColorRGB *colorRGB;
+ NSString *name;
+ NSString *category;
+}
+
+/*!
+ @property colorRGB
+ @brief The color to render the line in
+ */
+@property (copy) ColorRGB *colorRGB;
+
+@property (copy) NSString *name;
+@property (copy) NSString *category;
+@property (assign) ArrowHeadStyle headStyle;
+@property (assign) ArrowHeadStyle tailStyle;
+@property (assign) EdgeDectorationStyle decorationStyle;
+@property (assign) float thickness;
+
+@property (readonly) NSString *tikz;
+
+- (id)init;
+- (id)initWithName:(NSString*)nm;
++ (EdgeStyle*)defaultEdgeStyleWithName:(NSString*)nm;
+- (void) updateFromStyle:(EdgeStyle*)style;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/EdgeStyle.m b/tikzit-old/src/common/EdgeStyle.m
new file mode 100644
index 0000000..c61e94a
--- /dev/null
+++ b/tikzit-old/src/common/EdgeStyle.m
@@ -0,0 +1,222 @@
+//
+// EdgeStyle.m
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import "EdgeStyle.h"
+
+@implementation EdgeStyle
+
++ (void)initialize {
+ [self setKeys:[NSArray arrayWithObjects:
+ @"tailStyle",
+ @"headStyle",
+ @"decorationStyle",
+ @"thickness",
+ @"colorRGB.red",
+ @"colorRGB.blue",
+ @"colorRGB.green",
+ @"name",
+ nil]
+ triggerChangeNotificationsForDependentKey:@"tikz"];
+ [self setKeys:[NSArray arrayWithObjects:
+ @"colorRGB.name",
+ nil]
+ triggerChangeNotificationsForDependentKey:@"colorIsKnown"];
+}
+
+- (id)initWithName:(NSString*)nm {
+ self = [super initWithNotificationName:@"EdgeStylePropertyChanged"];
+
+ if (self != nil) {
+ headStyle = AH_None;
+ tailStyle = AH_None;
+ decorationStyle = ED_None;
+ colorRGB = [[ColorRGB alloc] initWithRed:0 green:0 blue:0];
+ name = nm;
+ category = nil;
+ thickness = 1.0f;
+ }
+
+ return self;
+}
+
+- (id)init {
+ self = [self initWithName:@"new"];
+ return self;
+}
+
+- (id)copyWithZone:(NSZone*)zone {
+ EdgeStyle *style = [[EdgeStyle allocWithZone:zone] init];
+ [style setName:[self name]];
+ [style setCategory:[self category]];
+ [style setHeadStyle:[self headStyle]];
+ [style setTailStyle:[self tailStyle]];
+ [style setDecorationStyle:[self decorationStyle]];
+ [style setThickness:[self thickness]];
+ [style setColorRGB:[self colorRGB]];
+ return style;
+}
+
+- (void)dealloc {
+#if ! __has_feature(objc_arc)
+ [name release];
+ [category release];
+ [colorRGB release];
+ [super dealloc];
+#endif
+}
+
+- (NSString*) description {
+ return [NSString stringWithFormat:@"Edge style \"%@\"", name];
+}
+
+- (void) updateFromStyle:(EdgeStyle*)style {
+ [self setName:[style name]];
+ [self setCategory:[style category]];
+ [self setHeadStyle:[style headStyle]];
+ [self setTailStyle:[style tailStyle]];
+ [self setDecorationStyle:[style decorationStyle]];
+ [self setThickness:[style thickness]];
+ [self setColorRGB:[style colorRGB]];
+}
+
++ (EdgeStyle*)defaultEdgeStyleWithName:(NSString*)nm {
+#if __has_feature(objc_arc)
+ return [[EdgeStyle alloc] initWithName:nm];
+#else
+ return [[[EdgeStyle alloc] initWithName:nm] autorelease];
+#endif
+}
+
+- (NSString*)name { return name; }
+- (void)setName:(NSString *)s {
+ if (name != s) {
+ NSString *oldValue = name;
+ name = [s copy];
+ [self postPropertyChanged:@"name" oldValue:oldValue];
+#if ! __has_feature(objc_arc)
+ [oldValue release];
+#endif
+ }
+}
+
+- (ArrowHeadStyle)headStyle { return headStyle; }
+- (void)setHeadStyle:(ArrowHeadStyle)s {
+ ArrowHeadStyle oldValue = headStyle;
+ headStyle = s;
+ [self postPropertyChanged:@"headStyle" oldValue:[NSNumber numberWithInt:oldValue]];
+}
+
+- (ArrowHeadStyle)tailStyle { return tailStyle; }
+- (void)setTailStyle:(ArrowHeadStyle)s {
+ ArrowHeadStyle oldValue = tailStyle;
+ tailStyle = s;
+ [self postPropertyChanged:@"tailStyle" oldValue:[NSNumber numberWithInt:oldValue]];
+}
+
+- (EdgeDectorationStyle)decorationStyle { return decorationStyle; }
+- (void)setDecorationStyle:(EdgeDectorationStyle)s {
+ EdgeDectorationStyle oldValue = decorationStyle;
+ decorationStyle = s;
+ [self postPropertyChanged:@"decorationStyle" oldValue:[NSNumber numberWithInt:oldValue]];
+}
+- (float)thickness { return thickness; }
+- (void)setThickness:(float)s {
+ float oldValue = thickness;
+ thickness = s;
+ [self postPropertyChanged:@"thickness" oldValue:[NSNumber numberWithFloat:oldValue]];
+}
+
+- (NSString*)category {
+ return category;
+}
+
+- (void)setCategory:(NSString *)s {
+ if (category != s) {
+ NSString *oldValue = category;
+ category = [s copy];
+ [self postPropertyChanged:@"category" oldValue:oldValue];
+#if ! __has_feature(objc_arc)
+ [oldValue release];
+#endif
+ }
+}
+
+- (ColorRGB*)colorRGB {
+ return colorRGB;
+}
+
+- (void)setColorRGB:(ColorRGB*)c {
+ if (colorRGB != c) {
+ ColorRGB *oldValue = colorRGB;
+ colorRGB = [c copy];
+ [self postPropertyChanged:@"colorRGB" oldValue:oldValue];
+#if ! __has_feature(objc_arc)
+ [oldValue release];
+#endif
+ }
+}
+
+- (NSString*)tikz {
+ NSMutableString *buf = [NSMutableString stringWithFormat:@"\\tikzstyle{%@}=[", name];
+
+ NSString *colorName = [colorRGB name];
+ if (colorName == nil)
+ colorName = [colorRGB hexName];
+
+ if (tailStyle == AH_Plain)
+ [buf appendString:@"<"];
+ else if (tailStyle == AH_Latex)
+ [buf appendString:@"latex"];
+
+ [buf appendString:@"-"];
+
+ if (headStyle == AH_Plain)
+ [buf appendString:@">"];
+ else if (headStyle == AH_Latex)
+ [buf appendString:@"latex"];
+
+ if(colorName != nil){
+ [buf appendString:@",draw="];
+ [buf appendString:colorName];
+ }
+
+ if (decorationStyle != ED_None) {
+ [buf appendString:@",postaction={decorate},decoration={markings,mark="];
+ if (decorationStyle == ED_Arrow)
+ [buf appendString:@"at position .5 with {\\arrow{>}}"];
+ else if (decorationStyle == ED_Tick)
+ [buf appendString:@"at position .5 with {\\draw (0,-0.1) -- (0,0.1);}"];
+ [buf appendString:@"}"];
+ }
+
+ if (thickness != 1.0f) {
+ [buf appendFormat:@",line width=%.3f", thickness];
+ }
+
+ [buf appendString:@"]"];
+ return buf;
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/Graph.h b/tikzit-old/src/common/Graph.h
new file mode 100644
index 0000000..1f98858
--- /dev/null
+++ b/tikzit-old/src/common/Graph.h
@@ -0,0 +1,401 @@
+//
+// Graph.h
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+
+/*!
+
+ @mainpage TikZiT
+ TikZiT is a GUI application for drawing, editing, and parsing TikZ
+ diagrams. Common code is in src/common, and plaform-specific code is
+ in src/{osx,linux}.
+
+ */
+
+
+#import "Node.h"
+#import "Edge.h"
+#import "GraphChange.h"
+#import "Transformer.h"
+
+/*!
+ @class Graph
+ @brief Store a graph, output to TikZ.
+ @details All of the methods that change a graph return an object of type GraphChange.
+ Graph changes can be re-done by calling applyGraphChange. They can be undone
+ by calling applyGraphChange on [change inverse].
+ */
+@interface Graph : NSObject <NSCopying> {
+ NSRecursiveLock *graphLock;
+ BOOL dirty; // keep track of when inEdges and outEdges need an update
+ NSMutableArray *nodes;
+ NSMutableArray *edges;
+
+ NSMapTable *inEdges;
+ NSMapTable *outEdges;
+
+ GraphElementData *data;
+ NSRect boundingBox;
+}
+
+/*!
+ @property data
+ @brief Data associated with the graph.
+ */
+@property (copy) GraphElementData *data;
+
+// KVC methods
+- (void) insertObject:(GraphElementProperty*)gep
+ inDataAtIndex:(NSUInteger)index;
+- (void) removeObjectFromDataAtIndex:(NSUInteger)index;
+- (void) replaceObjectInDataAtIndex:(NSUInteger)index
+ withObject:(GraphElementProperty*)gep;
+
+/*!
+ @property nodes
+ @brief The set of nodes.
+ @details The node set is cached internally, so no need to lock
+ the graph when enumerating.
+ */
+@property (readonly) NSArray *nodes;
+
+/*!
+ @property edges
+ @brief The set of edges.
+ @details The edge set is cached internally, so no need to lock
+ the graph when enumerating.
+ */
+@property (readonly) NSArray *edges;
+
+/*!
+ @property boundingBox
+ @brief The bounding box of a graph
+ @details Optional data containing the bounding box, set with
+ \path [use as bounding box] ....
+ */
+@property (assign) NSRect boundingBox;
+
+/*!
+ @property hasBoundingBox
+ @brief Returns true if this graph has a bounding box.
+ */
+@property (readonly) BOOL hasBoundingBox;
+
+
+/*!
+ @brief Computes graph bounds.
+ @result Graph bounds.
+ */
+- (NSRect)bounds;
+
+/*!
+ @brief Returns the set of edges incident to the given node set.
+ @param nds a set of nodes.
+ @result A set of incident edges.
+ */
+- (NSSet*)incidentEdgesForNodes:(NSSet*)nds;
+
+/*!
+ @brief Returns the set of in-edges for this node.
+ @param nd a node.
+ @result A set of edges.
+*/
+- (NSSet*)inEdgesForNode:(Node*)nd;
+
+/*!
+ @brief Returns the set of out-edges for this node.
+ @param nd a node.
+ @result A set of edges.
+*/
+- (NSSet*)outEdgesForNode:(Node*)nd;
+
+/*!
+ @brief Gives a copy of the full subgraph with the given nodes.
+ @param nds a set of nodes.
+ @result A subgraph.
+ */
+- (Graph*)copyOfSubgraphWithNodes:(NSSet*)nds;
+
+/*!
+ @brief Gives a copy of the full subgraph with the given nodes.
+ @param nds a set of nodes.
+ @param zone an allocation zone
+ @result A subgraph.
+ */
+- (Graph*)copyOfSubgraphWithNodes:(NSSet*)nds zone:(NSZone*)zone;
+
+/*!
+ @brief Gives a set of edge-arrays that partition all of the edges in the graph.
+ @result An NSet of NSArrays of edges.
+ */
+- (NSSet*)pathCover;
+
+/*!
+ @brief Adds a node.
+ @param node
+ @result A <tt>GraphChange</tt> recording this action.
+ */
+- (GraphChange*)addNode:(Node*)node;
+
+/*!
+ @brief Removes a node.
+ @param node
+ @result A <tt>GraphChange</tt> recording this action.
+ */
+- (GraphChange*)removeNode:(Node*)node;
+
+/*!
+ @brief Removes a set of nodes.
+ @param nds a set of nodes
+ @result A <tt>GraphChange</tt> recording this action.
+ */
+- (GraphChange*)removeNodes:(NSSet *)nds;
+
+/*!
+ @brief Adds an edge.
+ @param edge
+ @result A <tt>GraphChange</tt> recording this action.
+ */
+- (GraphChange*)addEdge:(Edge*)edge;
+
+/*!
+ @brief Removed an edge.
+ @param edge
+ @result A <tt>GraphChange</tt> recording this action.
+ */
+- (GraphChange*)removeEdge:(Edge*)edge;
+
+/*!
+ @brief Removes a set of edges.
+ @param es a set of edges.
+ @result A <tt>GraphChange</tt> recording this action.
+ */
+- (GraphChange*)removeEdges:(NSSet *)es;
+
+/*!
+ @brief Convenience function, intializes an edge with the given
+ source and target and adds it.
+ @param source the source of the edge.
+ @param target the target of the edge.
+ @result A <tt>GraphChange</tt> recording this action.
+ */
+- (GraphChange*)addEdgeFrom:(Node*)source to:(Node*)target;
+
+/*!
+ @brief Return the z-index for a given node (lower is farther back).
+ @param node a node in the graph
+ @result An <tt>int</tt>
+ */
+- (int)indexOfNode:(Node*)node;
+
+/*!
+ @brief Set the z-index for a given node (lower is farther back).
+ @param idx a new z-index
+ @param node a node in the graph
+ */
+- (void)setIndex:(int)idx ofNode:(Node*)node;
+
+/*!
+ @brief Bring set of nodes forward by one.
+ @param nodeSet a set of nodes
+ */
+- (GraphChange*)bringNodesForward:(NSSet*)nodeSet;
+
+/*!
+ @brief Bring set of nodes to the front.
+ @param nodeSet a set of nodes
+ */
+- (GraphChange*)bringNodesToFront:(NSSet*)nodeSet;
+
+/*!
+ @brief Bring set of edges to the front.
+ @param edgeSet a set of edges
+ */
+- (GraphChange*)bringEdgesToFront:(NSSet*)edgeSet;
+
+/*!
+ @brief Bring set of edges forward by one.
+ @param edgeSet a set of edges
+ */
+- (GraphChange*)bringEdgesForward:(NSSet*)edgeSet;
+
+/*!
+ @brief Send set of nodes backward by one.
+ @param nodeSet a set of nodes
+ */
+- (GraphChange*)sendNodesBackward:(NSSet*)nodeSet;
+
+/*!
+ @brief Send set of edges backward by one.
+ @param edgeSet a set of edges
+ */
+- (GraphChange*)sendEdgesBackward:(NSSet*)edgeSet;
+
+/*!
+ @brief Send set of nodes to back.
+ @param nodeSet a set of nodes
+ */
+- (GraphChange*)sendNodesToBack:(NSSet*)nodeSet;
+
+/*!
+ @brief Send set of edges to back.
+ @param edgeSet a set of edges
+ */
+- (GraphChange*)sendEdgesToBack:(NSSet*)edgeSet;
+
+
+/*!
+ @brief Transform every node in the graph to screen space.
+ @param t a transformer
+ */
+- (void)applyTransformer:(Transformer*)t;
+
+/*!
+ @brief Shift nodes by a given distance.
+ @param ns a set of nodes.
+ @param p an x and y distance, given as an NSPoint.
+ @result A <tt>GraphChange</tt> recording this action.
+ */
+- (GraphChange*)shiftNodes:(id<NSFastEnumeration>)ns byPoint:(NSPoint)p;
+
+/*!
+ @brief Reverse the given edges
+ @param es the edges to reverse
+ @result A <tt>GraphChange</tt> recording this action.
+ */
+- (GraphChange*)reverseEdges:(NSSet *)es;
+
+/*!
+ @brief Insert the given graph into this one. Used for copy
+ and paste.
+ @param g a graph.
+ @result A <tt>GraphChange</tt> recording this action.
+ */
+- (GraphChange*)insertGraph:(Graph*)g;
+
+/*!
+ @brief Flip the subgraph defined by the given node set
+ horizontally.
+ @param nds a set of nodes.
+ @result A <tt>GraphChange</tt> recording this action.
+ */
+- (GraphChange*)flipHorizontalNodes:(NSSet*)nds;
+
+/*!
+ @brief Flip the subgraph defined by the given node set
+ vertically.
+ @param nds a set of nodes.
+ @result A <tt>GraphChange</tt> recording this action.
+ */
+- (GraphChange*)flipVerticalNodes:(NSSet*)nds;
+
+/*!
+ @brief Apply a graph change.
+ @details An undo manager should maintain a stack of GraphChange
+ objects returned. To undo a GraphChange, call this method
+ with <tt>[change inverse]</tt> as is argument.
+ @param ch a graph change.
+ */
+- (void)applyGraphChange:(GraphChange*)ch;
+
+/*!
+ @brief The TikZ representation of this graph.
+ @details The TikZ representation of this graph. The TikZ code should
+ contain enough data to totally reconstruct the graph.
+ @result A string containing TikZ code.
+ */
+- (NSString*)tikz;
+
+
+/*!
+ @brief Copy the node set and return a table of copies, whose
+ keys are the original nodes. This is used to save the state
+ of a set of nodes in a GraphChange.
+ @param nds a set of nodes.
+ @result A <tt>NSMapTable</tt> of node copies.
+ */
++ (NSMapTable*)nodeTableForNodes:(NSSet*)nds;
+
++ (NSMapTable*)nodeTableForNodes:(NSSet*)nds withZone:(NSZone*)zone;
+
+/*!
+ @brief Copy the edge set and return a table of copies, whose
+ keys are the original edges. This is used to save the state
+ of a set of edges in a GraphChange.
+ @param es a set of edges.
+ @result A <tt>NSMapTable</tt> of edge copies.
+ */
++ (NSMapTable*)edgeTableForEdges:(NSSet*)es;
+
++ (NSMapTable*)edgeTableForEdges:(NSSet*)es withZone:(NSZone*)zone;
+
+/*!
+ @brief Compute the bounds for a set of nodes.
+ @param nds an enumerable collection of nodes.
+ @result The bounds.
+ */
++ (NSRect)boundsForNodes:(id<NSFastEnumeration>)nds;
+
+/*!
+ @brief Factory method for constructing graphs.
+ @result An empty graph.
+ */
++ (Graph*)graph;
+
+/**
+ * Initialize an empty graph
+ */
+- (id)init;
+
+/**
+ * Constructs a graph from the given tikz code
+ *
+ * See TikzGraphAssembler for more information about the error argument.
+ */
++ (Graph*)graphFromTikz:(NSString*)tikz error:(NSError**)e;
+
+/**
+ * Constructs a graph from the given tikz code
+ */
++ (Graph*)graphFromTikz:(NSString*)tikz;
+
+/**
+ * Initialize an empty graph from the given tikz code
+ *
+ * Note that this may not return the same object it was called on,
+ * and will return nil if parsing failed.
+ *
+ * See TikzGraphAssembler for more information about the error argument.
+ */
+- (id)initFromTikz:(NSString*)tikz error:(NSError**)e;
+
+/**
+ * Initialize an empty graph from the given tikz code
+ *
+ * Note that this may not return the same object it was called on,
+ * and will return nil if parsing failed.
+ */
+- (id)initFromTikz:(NSString*)tikz;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/Graph.m b/tikzit-old/src/common/Graph.m
new file mode 100644
index 0000000..cf09a69
--- /dev/null
+++ b/tikzit-old/src/common/Graph.m
@@ -0,0 +1,922 @@
+//
+// Graph.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 <http://www.gnu.org/licenses/>.
+//
+
+#import "Graph.h"
+#import "TikzGraphAssembler.h"
+#import "Shape.h"
+
+@interface Graph (Private)
+- (void) shapeDictionaryReplaced:(NSNotification*)notification;
+@end
+
+@implementation Graph
+
+- (id)init {
+ self = [super init];
+ if (self != nil) {
+ data = [[GraphElementData alloc] init];
+ boundingBox = NSMakeRect(0, 0, 0, 0);
+ graphLock = [[NSRecursiveLock alloc] init];
+ nodes = [[NSMutableArray alloc] initWithCapacity:10];
+ edges = [[NSMutableArray alloc] initWithCapacity:10];
+ inEdges = nil;
+ outEdges = nil;
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(shapeDictionaryReplaced:)
+ name:@"ShapeDictionaryReplaced"
+ object:[Shape class]];
+ }
+ return self;
+}
+
+- (id)initFromTikz:(NSString*)tikz error:(NSError**)e {
+#if __has_feature(objc_arc)
+ return [TikzGraphAssembler parseTikz:tikz error:e];
+#else
+ [self release];
+ return [[TikzGraphAssembler parseTikz:tikz error:e] retain];
+#endif
+}
+
+- (id)initFromTikz:(NSString*)tikz {
+ return [self initFromTikz:tikz error:NULL];
+}
+
+- (id) copyWithZone:(NSZone*)zone {
+ Graph *newGraph = [self copyOfSubgraphWithNodes:[NSSet setWithArray:nodes] zone:zone];
+ [newGraph setData:[self data]];
+ return newGraph;
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [graphLock lock];
+#if ! __has_feature(objc_arc)
+ [inEdges release];
+ [outEdges release];
+ [edges release];
+ [nodes release];
+ [data release];
+ [graphLock unlock];
+ [graphLock release];
+
+ [super dealloc];
+#endif
+}
+
+- (void)sync {
+ [graphLock lock];
+ if (dirty) {
+#if ! __has_feature(objc_arc)
+ [inEdges release];
+ [outEdges release];
+#endif
+ inEdges = [[NSMapTable alloc] initWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory capacity:10];
+ outEdges = [[NSMapTable alloc] initWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory capacity:10];
+
+#if ! __has_feature(objc_arc)
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+ for (Edge *e in edges) {
+ NSMutableSet *ie = [inEdges objectForKey:[e target]];
+ NSMutableSet *oe = [outEdges objectForKey:[e source]];
+
+ if (ie == nil) {
+ ie = [NSMutableSet setWithCapacity:4];
+ [inEdges setObject:ie forKey:[e target]];
+ }
+
+ if (oe == nil) {
+ oe = [NSMutableSet setWithCapacity:4];
+ [outEdges setObject:oe forKey:[e source]];
+ }
+
+ [ie addObject:e];
+ [oe addObject:e];
+ }
+
+#if ! __has_feature(objc_arc)
+ [pool drain];
+#endif
+
+ dirty = NO;
+ }
+ [graphLock unlock];
+}
+
+@synthesize nodes;
+@synthesize edges;
+
+@synthesize data;
+- (void) insertObject:(GraphElementProperty*)gep
+ inDataAtIndex:(NSUInteger)index {
+ [data insertObject:gep atIndex:index];
+}
+- (void) removeObjectFromDataAtIndex:(NSUInteger)index {
+ [data removeObjectAtIndex:index];
+}
+- (void) replaceObjectInDataAtIndex:(NSUInteger)index
+ withObject:(GraphElementProperty*)gep {
+ [data replaceObjectAtIndex:index withObject:gep];
+}
+
+@synthesize boundingBox;
+
+- (NSRect)bounds {
+ [graphLock lock];
+ NSRect b = [Graph boundsForNodes:nodes];
+ [graphLock unlock];
+ return b;
+}
+
+- (BOOL)hasBoundingBox {
+ return !(
+ boundingBox.size.width == 0 &&
+ boundingBox.size.height == 0
+ );
+}
+
+- (NSSet*)inEdgesForNode:(Node*)nd {
+ [self sync];
+#if __has_feature(objc_arc)
+ return [inEdges objectForKey:nd];
+#else
+ return [[[inEdges objectForKey:nd] retain] autorelease];
+#endif
+}
+
+- (NSSet*)outEdgesForNode:(Node*)nd {
+ [self sync];
+#if __has_feature(objc_arc)
+ return [outEdges objectForKey:nd];
+#else
+ return [[[outEdges objectForKey:nd] retain] autorelease];
+#endif
+}
+
+- (NSSet*)incidentEdgesForNodes:(NSSet*)nds {
+ [self sync];
+
+ NSMutableSet *mset = [NSMutableSet setWithCapacity:10];
+ for (Node *n in nds) {
+ [mset unionSet:[self inEdgesForNode:n]];
+ [mset unionSet:[self outEdgesForNode:n]];
+ }
+
+ return mset;
+}
+
+- (void)applyTransformer:(Transformer *)t {
+ [graphLock lock];
+ for (Node *n in nodes) {
+ [n setPoint:[t toScreen:[n point]]];
+ }
+ [graphLock unlock];
+}
+
+- (GraphChange*)addNode:(Node *)node{
+ [graphLock lock];
+ NSSet *addedNode;
+
+ // addNode is a no-op if graph already contains node
+ if (![nodes containsObject:node]) {
+ [nodes addObject:node];
+ dirty = YES;
+ addedNode = [NSSet setWithObject:node];
+ } else {
+ addedNode = [NSSet set];
+ }
+ [graphLock unlock];
+
+ return [GraphChange graphAdditionWithNodes:addedNode
+ edges:[NSSet set]];
+}
+
+- (GraphChange*)removeNode:(Node*)node {
+ [graphLock lock];
+ NSMutableSet *affectedEdges = [NSMutableSet set];
+ for (Edge *e in edges) {
+ if ([e source] == node || [e target] == node) {
+ [affectedEdges addObject:e];
+ }
+ }
+ for (Edge *e in affectedEdges) {
+ [edges removeObject:e];
+ }
+ [nodes removeObject:node];
+ dirty = YES;
+ [graphLock unlock];
+
+ return [GraphChange graphDeletionWithNodes:[NSSet setWithObject:node]
+ edges:affectedEdges];
+}
+
+- (GraphChange*)removeNodes:(NSSet *)nds {
+ [graphLock lock];
+
+ Node *n;
+ Edge *e;
+
+ NSMutableSet *affectedEdges = [NSMutableSet set];
+ NSEnumerator *en = [edges objectEnumerator];
+ while ((e = [en nextObject])) {
+ if ([nds containsObject:[e source]] || [nds containsObject:[e target]]) {
+ [affectedEdges addObject:e];
+ }
+ }
+
+ en = [affectedEdges objectEnumerator];
+ while ((e = [en nextObject])) [edges removeObject:e];
+
+ en = [nds objectEnumerator];
+ while ((n = [en nextObject])) [nodes removeObject:n];
+
+ dirty = YES;
+ [graphLock unlock];
+
+ return [GraphChange graphDeletionWithNodes:nds edges:affectedEdges];
+}
+
+- (GraphChange*)addEdge:(Edge*)edge {
+ [graphLock lock];
+ NSSet *addedEdge;
+
+ // addEdge is a no-op if graph already contains edge
+ if (![edges containsObject:edge]) {
+ [edges addObject:edge];
+ dirty = YES;
+ addedEdge = [NSSet setWithObject:edge];
+ } else {
+ addedEdge = [NSSet set];
+ }
+ [graphLock unlock];
+
+ return [GraphChange graphAdditionWithNodes:[NSSet set]
+ edges:addedEdge];
+}
+
+- (GraphChange*)removeEdge:(Edge *)edge {
+ [graphLock lock];
+ [edges removeObject:edge];
+ dirty = YES;
+ [graphLock unlock];
+ return [GraphChange graphDeletionWithNodes:[NSSet set]
+ edges:[NSSet setWithObject:edge]];
+}
+
+- (GraphChange*)removeEdges:(NSSet *)es {
+ [graphLock lock];
+
+ for (Edge *e in es) {
+ [edges removeObject:e];
+ }
+ dirty = YES;
+ [graphLock unlock];
+ return [GraphChange graphDeletionWithNodes:[NSSet set] edges:es];
+}
+
+- (GraphChange*)addEdgeFrom:(Node *)source to:(Node *)target {
+ return [self addEdge:[Edge edgeWithSource:source andTarget:target]];
+}
+
+- (GraphChange*)shiftNodes:(id<NSFastEnumeration>)ns byPoint:(NSPoint)p {
+ NSPoint newLoc;
+ NSMutableSet *nodeSet = [NSMutableSet setWithCapacity:5];
+ for (Node *n in ns) {
+ newLoc = NSMakePoint([n point].x + p.x, [n point].y + p.y);
+ [n setPoint:newLoc];
+ [nodeSet addObject:n];
+ }
+ return [GraphChange shiftNodes:nodeSet byPoint:p];
+}
+
+- (GraphChange*)reverseEdges:(NSSet *)es {
+ [graphLock lock];
+ for (Edge *e in es) {
+ [e reverse];
+ }
+ dirty = YES;
+ [graphLock unlock];
+ return [GraphChange reverseEdges:es];
+}
+
+- (int)indexOfNode:(Node *)node {
+ return [nodes indexOfObject:node];
+}
+
+- (void)setIndex:(int)idx ofNode:(Node *)node {
+ [graphLock lock];
+
+ if ([nodes containsObject:node]) {
+ [nodes removeObject:node];
+ [nodes insertObject:node atIndex:idx];
+ }
+
+ [graphLock unlock];
+}
+
+- (int)indexOfEdge:(Edge *)edge {
+ return [edges indexOfObject:edge];
+}
+
+- (void)setIndex:(int)idx ofEdge:(Edge *)edge {
+ [graphLock lock];
+
+ if ([edges containsObject:edge]) {
+ [edges removeObject:edge];
+ [edges insertObject:edge atIndex:idx];
+ }
+
+ [graphLock unlock];
+}
+
+- (GraphChange*)bringNodesForward:(NSSet*)nodeSet {
+ NSArray *oldOrder = [nodes copy];
+ [graphLock lock];
+ // start at the top of the array and work backwards
+ for (int i = [nodes count]-2; i >= 0; --i) {
+ if ( [nodeSet containsObject:[nodes objectAtIndex:i]] &&
+ ![nodeSet containsObject:[nodes objectAtIndex:i+1]])
+ {
+ [self setIndex:(i+1) ofNode:[nodes objectAtIndex:i]];
+ }
+ }
+ GraphChange *change = [GraphChange nodeOrderChangeFrom:oldOrder to:nodes moved:nodeSet];
+ [graphLock unlock];
+
+#if ! __has_feature(objc_arc)
+ [oldOrder release];
+#endif
+
+ return change;
+}
+
+- (GraphChange*)bringNodesToFront:(NSSet*)nodeSet {
+ NSArray *oldOrder = [nodes copy];
+ int i = 0, top = [nodes count]-1;
+
+ while (i <= top) {
+ if ([nodeSet containsObject:[nodes objectAtIndex:i]]) {
+ [self setIndex:([nodes count]-1) ofNode:[nodes objectAtIndex:i]];
+ --top;
+ } else {
+ ++i;
+ }
+ }
+ GraphChange *change = [GraphChange nodeOrderChangeFrom:oldOrder to:nodes moved:nodeSet];
+
+#if ! __has_feature(objc_arc)
+ [oldOrder release];
+#endif
+
+ return change;
+}
+
+- (GraphChange*)bringEdgesForward:(NSSet*)edgeSet {
+ [graphLock lock];
+ NSArray *oldOrder = [edges copy];
+ // start at the top of the array and work backwards
+ for (int i = [edges count]-2; i >= 0; --i) {
+ if ( [edgeSet containsObject:[edges objectAtIndex:i]] &&
+ ![edgeSet containsObject:[edges objectAtIndex:i+1]])
+ {
+ [self setIndex:(i+1) ofEdge:[edges objectAtIndex:i]];
+ }
+ }
+ GraphChange *change = [GraphChange edgeOrderChangeFrom:oldOrder to:edges moved:edgeSet];
+ [graphLock unlock];
+
+#if ! __has_feature(objc_arc)
+ [oldOrder release];
+#endif
+
+ return change;
+}
+
+- (GraphChange*)bringEdgesToFront:(NSSet*)edgeSet {
+ NSArray *oldOrder = [edges copy];
+ int i = 0, top = [edges count]-1;
+
+ while (i <= top) {
+ if ([edgeSet containsObject:[edges objectAtIndex:i]]) {
+ [self setIndex:([edges count]-1) ofEdge:[edges objectAtIndex:i]];
+ --top;
+ } else {
+ ++i;
+ }
+ }
+ GraphChange *change = [GraphChange edgeOrderChangeFrom:oldOrder to:edges moved:edgeSet];
+
+#if ! __has_feature(objc_arc)
+ [oldOrder release];
+#endif
+
+ return change;
+}
+
+- (GraphChange*)sendNodesBackward:(NSSet*)nodeSet {
+ [graphLock lock];
+ NSArray *oldOrder = [nodes copy];
+ // start at the top of the array and work backwards
+ for (int i = 1; i < [nodes count]; ++i) {
+ if ( [nodeSet containsObject:[nodes objectAtIndex:i]] &&
+ ![nodeSet containsObject:[nodes objectAtIndex:i-1]])
+ {
+ [self setIndex:(i-1) ofNode:[nodes objectAtIndex:i]];
+ }
+ }
+ GraphChange *change = [GraphChange nodeOrderChangeFrom:oldOrder to:nodes moved:nodeSet];
+ [graphLock unlock];
+
+#if ! __has_feature(objc_arc)
+ [oldOrder release];
+#endif
+
+ return change;
+}
+
+- (GraphChange*)sendEdgesBackward:(NSSet*)edgeSet {
+ [graphLock lock];
+ NSArray *oldOrder = [edges copy];
+ // start at the top of the array and work backwards
+ for (int i = 1; i < [edges count]; ++i) {
+ if ( [edgeSet containsObject:[edges objectAtIndex:i]] &&
+ ![edgeSet containsObject:[edges objectAtIndex:i-1]])
+ {
+ [self setIndex:(i-1) ofEdge:[edges objectAtIndex:i]];
+ }
+ }
+ GraphChange *change = [GraphChange edgeOrderChangeFrom:oldOrder to:edges moved:edgeSet];
+ [graphLock unlock];
+
+#if ! __has_feature(objc_arc)
+ [oldOrder release];
+#endif
+
+ return change;
+}
+
+- (GraphChange*)sendNodesToBack:(NSSet*)nodeSet {
+ NSArray *oldOrder = [nodes copy];
+ int i = [nodes count]-1, bot = 0;
+
+ while (i >= bot) {
+ if ([nodeSet containsObject:[nodes objectAtIndex:i]]) {
+ [self setIndex:0 ofNode:[nodes objectAtIndex:i]];
+ ++bot;
+ } else {
+ --i;
+ }
+ }
+ GraphChange *change = [GraphChange nodeOrderChangeFrom:oldOrder to:nodes moved:nodeSet];
+
+#if ! __has_feature(objc_arc)
+ [oldOrder release];
+#endif
+
+ return change;
+}
+
+- (GraphChange*)sendEdgesToBack:(NSSet*)edgeSet {
+ NSArray *oldOrder = [edges copy];
+ int i = [edges count]-1, bot = 0;
+
+ while (i >= bot) {
+ if ([edgeSet containsObject:[edges objectAtIndex:i]]) {
+ [self setIndex:0 ofEdge:[edges objectAtIndex:i]];
+ ++bot;
+ } else {
+ --i;
+ }
+ }
+ GraphChange *change = [GraphChange edgeOrderChangeFrom:oldOrder to:edges moved:edgeSet];
+
+#if ! __has_feature(objc_arc)
+ [oldOrder release];
+#endif
+
+ return change;
+}
+
+- (GraphChange*)insertGraph:(Graph*)g {
+ [graphLock lock];
+
+ for (Node *n in [g nodes]) {
+ [self addNode:n];
+ }
+
+ for (Edge *e in [g edges]) {
+ [self addEdge:e];
+ }
+
+ dirty = YES;
+
+ [graphLock unlock];
+
+
+ return [GraphChange graphAdditionWithNodes:[NSSet setWithArray:[g nodes]] edges:[NSSet setWithArray:[g edges]]];
+}
+
+- (void)flipNodes:(NSSet*)nds horizontal:(BOOL)horiz {
+ [graphLock lock];
+
+ NSRect bds = [Graph boundsForNodes:nds];
+ float ctr;
+ if (horiz) ctr = bds.origin.x + (bds.size.width/2);
+ else ctr = bds.origin.y + (bds.size.height/2);
+
+ Node *n;
+ NSPoint p;
+ NSEnumerator *en = [nds objectEnumerator];
+ while ((n = [en nextObject])) {
+ p = [n point];
+ if (horiz) p.x = 2 * ctr - p.x;
+ else p.y = 2 * ctr - p.y;
+ [n setPoint:p];
+ }
+
+ Edge *e;
+ en = [edges objectEnumerator];
+ while ((e = [en nextObject])) {
+ if ([nds containsObject:[e source]] &&
+ [nds containsObject:[e target]])
+ {
+ if ([e bendMode] == EdgeBendModeInOut) {
+ if (horiz) {
+ if ([e inAngle] < 0) [e setInAngle:(-180 - [e inAngle])];
+ else [e setInAngle:180 - [e inAngle]];
+
+ if ([e outAngle] < 0) [e setOutAngle:(-180 - [e outAngle])];
+ else [e setOutAngle:180 - [e outAngle]];
+ } else {
+ [e setInAngle:-[e inAngle]];
+ [e setOutAngle:-[e outAngle]];
+ }
+ } else {
+ [e setBend:-[e bend]];
+ }
+ }
+ }
+
+ [graphLock unlock];
+}
+
+- (GraphChange*)flipHorizontalNodes:(NSSet*)nds {
+ [self flipNodes:nds horizontal:YES];
+ return [GraphChange flipNodes:nds horizontal:YES];
+}
+
+- (GraphChange*)flipVerticalNodes:(NSSet*)nds {
+ [self flipNodes:nds horizontal:NO];
+ return [GraphChange flipNodes:nds horizontal:NO];
+}
+
+- (Graph*)copyOfSubgraphWithNodes:(NSSet*)nds {
+ return [self copyOfSubgraphWithNodes:nds zone:NSDefaultMallocZone()];
+}
+
+- (Graph*)copyOfSubgraphWithNodes:(NSSet*)nds zone:(NSZone*)zone {
+ [graphLock lock];
+
+ NSMapTable *newNds = [Graph nodeTableForNodes:nds withZone:zone];
+ Graph* newGraph = [[Graph allocWithZone:zone] init];
+
+ for (Node *nd in [newNds objectEnumerator]) {
+ [newGraph addNode:nd];
+ }
+
+ for (Edge *e in edges) {
+ if ([nds containsObject:[e source]] && [nds containsObject:[e target]]) {
+ Edge *e1 = [e copyWithZone:zone];
+ [e1 setSource:[newNds objectForKey:[e source]]];
+ [e1 setTarget:[newNds objectForKey:[e target]]];
+ [newGraph addEdge:e1];
+#if ! __has_feature(objc_arc)
+ [e1 release]; // e1 belongs to newGraph
+#endif
+ }
+ }
+
+ [graphLock unlock];
+
+ return newGraph;
+}
+
+- (NSSet*)pathCover {
+ [self sync];
+
+ NSMutableSet *cover = [NSMutableSet set];
+#if ! __has_feature(objc_arc)
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+#endif
+ NSMutableSet *remainingEdges = [NSMutableSet setWithArray:edges];
+
+ while ([remainingEdges count] != 0) {
+ NSMutableArray *path = [[NSMutableArray alloc] init];
+ NSSet *succs;
+ Edge *succ;
+ NSEnumerator *en;
+
+ Edge *e = [remainingEdges anyObject];
+
+ while (e!=nil) {
+ [path addObject:e];
+ [remainingEdges removeObject:e];
+
+ succs = [self outEdgesForNode:[e target]];
+ en = [succs objectEnumerator];
+ e = nil;
+
+ while ((succ = [en nextObject])) {
+ if ([remainingEdges containsObject:succ]) e = succ;
+ }
+ }
+
+ [cover addObject:path];
+#if ! __has_feature(objc_arc)
+ [path release];
+#endif
+ }
+
+#if ! __has_feature(objc_arc)
+ [pool drain];
+#endif
+ return cover;
+}
+
+- (void)applyGraphChange:(GraphChange*)ch {
+ [graphLock lock];
+ switch ([ch changeType]) {
+ case GraphAddition:
+ for (Node *n in [[ch affectedNodes] objectEnumerator]) {
+ [nodes addObject:n];
+ }
+
+ for (Edge *e in [[ch affectedEdges] objectEnumerator]) {
+ [edges addObject:e];
+ }
+
+ break;
+ case GraphDeletion:
+ for (Edge *e in [[ch affectedEdges] objectEnumerator]) {
+ [edges removeObject:e];
+ }
+
+ for (Node *n in [[ch affectedNodes] objectEnumerator]) {
+ [nodes removeObject:n];
+ }
+
+ break;
+ case NodePropertyChange:
+ [[ch nodeRef] setPropertiesFromNode:[ch nwNode]];
+ break;
+ case NodesPropertyChange:
+ for (Node *key in [[ch nwNodeTable] keyEnumerator]) {
+ [key setPropertiesFromNode:[[ch nwNodeTable] objectForKey:key]];
+ }
+ break;
+ case EdgePropertyChange:
+ [[ch edgeRef] setPropertiesFromEdge:[ch nwEdge]];
+ break;
+ case EdgesPropertyChange:
+ for (Edge *key in [[ch nwEdgeTable] keyEnumerator]) {
+ [key setPropertiesFromEdge:[[ch nwEdgeTable] objectForKey:key]];
+ }
+ break;
+ case NodesShift:
+ for (Node *n in [[ch affectedNodes] objectEnumerator]) {
+ [n setPoint:NSMakePoint([n point].x + [ch shiftPoint].x,
+ [n point].y + [ch shiftPoint].y)];
+ }
+ break;
+ case NodesFlip:
+ [self flipNodes:[ch affectedNodes] horizontal:[ch horizontal]];
+ break;
+ case EdgesReverse:
+ for (Edge *e in [[ch affectedEdges] objectEnumerator]) {
+ [e reverse];
+ }
+ break;
+ case BoundingBoxChange:
+ [self setBoundingBox:[ch nwBoundingBox]];
+ break;
+ case GraphPropertyChange:
+ [data setArray:[ch nwGraphData]];
+ break;
+ case NodeOrderChange:
+ [nodes setArray:[ch nwNodeOrder]];
+ break;
+ case EdgeOrderChange:
+ [edges setArray:[ch nwEdgeOrder]];
+ break;
+ }
+
+ dirty = YES;
+ [graphLock unlock];
+}
+
+//- (void)undoGraphChange:(GraphChange*)ch {
+// [self applyGraphChange:[GraphChange inverseGraphChange:ch]];
+//}
+
+- (NSString*)tikz {
+ [graphLock lock];
+
+ NSMutableString *code = [NSMutableString
+ stringWithFormat:@"\\begin{tikzpicture}%@\n",
+ [[self data] tikzList]];
+
+ if ([self hasBoundingBox]) {
+ [code appendFormat:@"\t\\path [use as bounding box] (%@,%@) rectangle (%@,%@);\n",
+ [NSNumber numberWithFloat:boundingBox.origin.x],
+ [NSNumber numberWithFloat:boundingBox.origin.y],
+ [NSNumber numberWithFloat:boundingBox.origin.x + boundingBox.size.width],
+ [NSNumber numberWithFloat:boundingBox.origin.y + boundingBox.size.height]];
+ }
+
+// NSArray *sortedNodeList = [[nodes allObjects]
+// sortedArrayUsingSelector:@selector(compareTo:)];
+ //NSMutableDictionary *nodeNames = [NSMutableDictionary dictionary];
+
+ if ([nodes count] > 0) [code appendFormat:@"\t\\begin{pgfonlayer}{nodelayer}\n"];
+
+ int i = 0;
+ for (Node *n in nodes) {
+ [n updateData];
+ [n setName:[NSString stringWithFormat:@"%d", i]];
+ [code appendFormat:@"\t\t\\node %@ (%d) at (%@, %@) {%@};\n",
+ [[n data] tikzList],
+ i,
+ formatFloat([n point].x, 4),
+ formatFloat([n point].y, 4),
+ [n label]
+ ];
+ i++;
+ }
+
+ if ([nodes count] > 0) [code appendFormat:@"\t\\end{pgfonlayer}\n"];
+ if ([edges count] > 0) [code appendFormat:@"\t\\begin{pgfonlayer}{edgelayer}\n"];
+
+ NSString *nodeStr;
+ for (Edge *e in edges) {
+ [e updateData];
+
+ if ([e hasEdgeNode]) {
+ nodeStr = [NSString stringWithFormat:@"node%@{%@} ",
+ [[[e edgeNode] data] tikzList],
+ [[e edgeNode] label]
+ ];
+ } else {
+ nodeStr = @"";
+ }
+
+ NSString *edata = [[e data] tikzList];
+
+ NSString *srcAnchor;
+ NSString *tgtAnchor;
+
+ if ([[e sourceAnchor] isEqual:@""]) {
+ srcAnchor = @"";
+ } else {
+ srcAnchor = [NSString stringWithFormat:@".%@", [e sourceAnchor]];
+ }
+
+ if ([[e targetAnchor] isEqual:@""]) {
+ tgtAnchor = @"";
+ } else {
+ tgtAnchor = [NSString stringWithFormat:@".%@", [e targetAnchor]];
+ }
+
+ [code appendFormat:@"\t\t\\draw%@ (%@%@) to %@(%@%@);\n",
+ ([edata isEqual:@""]) ? @"" : [NSString stringWithFormat:@" %@", edata],
+ [[e source] name],
+ srcAnchor,
+ nodeStr,
+ ([e source] == [e target]) ? @"" : [[e target] name],
+ tgtAnchor
+ ];
+ }
+
+ if ([edges count] > 0) [code appendFormat:@"\t\\end{pgfonlayer}\n"];
+
+ [code appendString:@"\\end{tikzpicture}"];
+
+ [graphLock unlock];
+
+ return code;
+}
+
++ (Graph*)graph {
+#if __has_feature(objc_arc)
+ return [[self alloc] init];
+#else
+ return [[[self alloc] init] autorelease];
+#endif
+}
+
++ (Graph*)graphFromTikz:(NSString*)tikz error:(NSError**)e {
+ return [TikzGraphAssembler parseTikz:tikz error:e];
+}
+
++ (Graph*)graphFromTikz:(NSString*)tikz {
+ return [self graphFromTikz:tikz error:NULL];
+}
+
++ (NSMapTable*)nodeTableForNodes:(NSSet*)nds {
+ return [self nodeTableForNodes:nds withZone:NSDefaultMallocZone()];
+}
+
++ (NSMapTable*)nodeTableForNodes:(NSSet*)nds withZone:(NSZone*)zone {
+ NSMapTable *tab = [[NSMapTable allocWithZone:zone]
+ initWithKeyOptions:NSMapTableStrongMemory
+ valueOptions:NSMapTableStrongMemory
+ capacity:[nds count]];
+ for (Node *n in nds) {
+ Node *ncopy = [n copyWithZone:zone];
+ [tab setObject:ncopy forKey:n];
+#if ! __has_feature(objc_arc)
+ [ncopy release]; // tab should still retain ncopy.
+#endif
+ }
+#if __has_feature(objc_arc)
+ return tab;
+#else
+ return [tab autorelease];
+#endif
+}
+
++ (NSMapTable*)edgeTableForEdges:(NSSet*)es {
+ return [self edgeTableForEdges:es withZone:NSDefaultMallocZone()];
+}
+
++ (NSMapTable*)edgeTableForEdges:(NSSet*)es withZone:(NSZone*)zone {
+ NSMapTable *tab = [[NSMapTable allocWithZone:zone]
+ initWithKeyOptions:NSMapTableStrongMemory
+ valueOptions:NSMapTableStrongMemory
+ capacity:[es count]];
+ for (Edge *e in es) {
+ Edge *ecopy = [e copyWithZone:zone];
+ [tab setObject:ecopy forKey:e];
+#if ! __has_feature(objc_arc)
+ [ecopy release]; // tab should still retain ecopy.
+#endif
+ }
+ return tab;
+}
+
+
++ (NSRect)boundsForNodes:(id<NSFastEnumeration>)nds {
+ NSPoint tl, br;
+ NSPoint p;
+ BOOL hasPoints = NO;
+ for (Node *n in nds) {
+ p = [n point];
+ if (!hasPoints) {
+ tl = p;
+ br = p;
+ hasPoints = YES;
+ } else {
+ if (p.x < tl.x) tl.x = p.x;
+ if (p.y > tl.y) tl.y = p.y;
+ if (p.x > br.x) br.x = p.x;
+ if (p.y < br.y) br.y = p.y;
+ }
+ }
+
+ return (hasPoints) ? NSRectAroundPoints(tl, br) : NSMakeRect(0, 0, 0, 0);
+}
+
+@end
+
+@implementation Graph (Private)
+- (void) shapeDictionaryReplaced:(NSNotification*)notification {
+ for (Edge *e in edges) {
+ [e recalculateProperties];
+ }
+}
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/GraphChange.h b/tikzit-old/src/common/GraphChange.h
new file mode 100644
index 0000000..0e71a90
--- /dev/null
+++ b/tikzit-old/src/common/GraphChange.h
@@ -0,0 +1,344 @@
+//
+// GraphChange.h
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#import "Node.h"
+#import "Edge.h"
+
+typedef enum {
+ GraphAddition,
+ GraphDeletion,
+ NodePropertyChange,
+ EdgePropertyChange,
+ NodesPropertyChange,
+ EdgesPropertyChange,
+ NodesShift,
+ NodesFlip,
+ EdgesReverse,
+ BoundingBoxChange,
+ GraphPropertyChange,
+ NodeOrderChange,
+ EdgeOrderChange
+} ChangeType;
+
+/*!
+ @class GraphChange
+ @brief Store the data associated with a graph change.
+ @details All of the methods that change a graph return an object of type GraphChange.
+ Graph changes can be re-done by calling [graph applyGraphChange:]. They can be undone
+ by calling applyGraphChange on [change inverse]. This class has no public initializer,
+ so everything should be constructed by factory methods.
+ */
+@interface GraphChange : NSObject {
+ ChangeType changeType;
+
+ // for addition, deletion, and shifts
+ NSSet *affectedNodes;
+ NSSet *affectedEdges;
+ NSPoint shiftPoint;
+
+ // for flip
+ BOOL horizontal;
+
+ // for property changes
+ Node *nodeRef;
+ Edge *edgeRef;
+
+ Node *oldNode, *nwNode;
+ Edge *oldEdge, *nwEdge;
+ NSMapTable *oldNodeTable, *nwNodeTable;
+ NSMapTable *oldEdgeTable, *nwEdgeTable;
+ NSRect oldBoundingBox, nwBoundingBox;
+ GraphElementData *oldGraphData, *nwGraphData;
+
+ NSArray *oldNodeOrder, *nwNodeOrder;
+ NSArray *oldEdgeOrder, *nwEdgeOrder;
+}
+
+/*!
+ @property changeType
+ @brief Type of GraphChange.
+ */
+@property (assign) ChangeType changeType;
+
+/*!
+ @property shiftPoint
+ @brief A point storing a shifted distance.
+ */
+@property (assign) NSPoint shiftPoint;
+
+/*!
+ @property horizontal
+ @brief Flags whether nodes were flipped horizontally
+ */
+@property (assign) BOOL horizontal;
+
+/*!
+ @property affectedNodes
+ @brief A set of nodes affected by this change, may be undefined.
+ */
+@property (copy) NSSet *affectedNodes;
+
+/*!
+ @property affectedEdges
+ @brief A set of edges affected by this change, may be undefined.
+ */
+@property (copy) NSSet *affectedEdges;
+
+/*!
+ @property nodeRef
+ @brief A reference to a single node affected by this change, may be undefined.
+ */
+@property (retain) Node *nodeRef;
+
+/*!
+ @property oldNode
+ @brief A copy of the node pre-change.
+ */
+@property (copy) Node *oldNode;
+
+/*!
+ @property nwNode
+ @brief A copy of the node post-change.
+ */
+@property (copy) Node *nwNode;
+
+/*!
+ @property edgeRef
+ @brief A reference to a single edge affected by this change, may be undefined.
+ */
+@property (retain) Edge *edgeRef;
+
+/*!
+ @property oldEdge
+ @brief A copy of the edge pre-change.
+ */
+@property (copy) Edge *oldEdge;
+
+/*!
+ @property nwEdge
+ @brief A copy of the edge post-change.
+ */
+@property (copy) Edge *nwEdge;
+
+/*!
+ @property oldNodeTable
+ @brief A a table containing copies of a set of nodes pre-change.
+ */
+@property (retain) NSMapTable *oldNodeTable;
+
+/*!
+ @property nwNodeTable
+ @brief A a table containing copies of a set of nodes post-change.
+ */
+@property (retain) NSMapTable *nwNodeTable;
+
+/*!
+ @property oldEdgeTable
+ @brief A a table containing copies of a set of edges pre-change.
+ */
+@property (retain) NSMapTable *oldEdgeTable;
+
+/*!
+ @property nwEdgeTable
+ @brief A a table containing copies of a set of edges post-change.
+ */
+@property (retain) NSMapTable *nwEdgeTable;
+
+/*!
+ @property oldBoundingBox
+ @brief The old bounding box.
+ */
+@property (assign) NSRect oldBoundingBox;
+
+/*!
+ @property nwBoundingBox
+ @brief The new bounding box.
+ */
+@property (assign) NSRect nwBoundingBox;
+
+/*!
+ @property oldGraphData
+ @brief The old graph data.
+ */
+@property (copy) GraphElementData *oldGraphData;
+
+/*!
+ @property nwGraphData
+ @brief The new graph data.
+ */
+@property (copy) GraphElementData *nwGraphData;
+
+/*!
+ @property oldNodeOrder
+ @brief The old node list.
+ */
+@property (copy) NSArray *oldNodeOrder;
+
+/*!
+ @property nwNodeOrder
+ @brief The new node list.
+ */
+@property (copy) NSArray *nwNodeOrder;
+
+/*!
+ @property oldEdgeOrder
+ @brief The old edge list.
+ */
+@property (copy) NSArray *oldEdgeOrder;
+
+/*!
+ @property nwEdgeOrder
+ @brief The new edge list.
+ */
+@property (copy) NSArray *nwEdgeOrder;
+
+/*!
+ @brief Invert a GraphChange.
+ @details Invert a GraphChange. Calling [graph applyGraphChange:[[graph msg:...] invert]]
+ should leave the graph unchanged for any method of Graph that returns a
+ GraphChange.
+ @result The inverse of the current Graph Change.
+ */
+- (GraphChange*)invert;
+
+/*!
+ @brief Construct a graph addition. affectedNodes are the added nodes,
+ affectedEdges are the added edges.
+ @param ns a set of nodes.
+ @param es a set of edges.
+ @result A graph addition.
+ */
++ (GraphChange*)graphAdditionWithNodes:(NSSet*)ns edges:(NSSet*)es;
+
+/*!
+ @brief Construct a graph deletion. affectedNodes are the deleted nodes,
+ affectedEdges are the deleted edges.
+ @param ns a set of nodes.
+ @param es a set of edges.
+ @result A graph deletion.
+ */
++ (GraphChange*)graphDeletionWithNodes:(NSSet*)ns edges:(NSSet*)es;
+
+/*!
+ @brief Construct a property change of a single node.
+ @param nd the affected node.
+ @param old a copy of the node pre-change
+ @param nw a copy of the node post-change
+ @result A property change of a single node.
+ */
++ (GraphChange*)propertyChangeOfNode:(Node*)nd fromOld:(Node*)old toNew:(Node*)nw;
+
+/*!
+ @brief Construct a property change of a single edge.
+ @param e the affected edge.
+ @param old a copy of the edge pre-change
+ @param nw a copy of the edge post-change
+ @result A property change of a single node.
+ */
++ (GraphChange*)propertyChangeOfEdge:(Edge*)e fromOld:(Edge *)old toNew:(Edge *)nw;
+
+/*!
+ @brief Construct a property change of set of nodes.
+ @details Construct a property change of set of nodes. oldC and newC should be
+ constructed using the class method [Graph nodeTableForNodes:] before
+ and after the property change, respectively. The affected nodes are
+ keys(oldC) = keys(newC).
+ @param oldC a table of copies of nodes pre-change
+ @param newC a table of copies of nodes post-change
+ @result A property change of a set of nodes.
+ */
++ (GraphChange*)propertyChangeOfNodesFromOldCopies:(NSMapTable*)oldC
+ toNewCopies:(NSMapTable*)newC;
+
+/*!
+ @brief Construct a property change of set of edges.
+ @details Construct a property change of set of edges. oldC and newC should be
+ constructed using the class method [Graph edgeTableForEdges:] before
+ and after the property change, respectively. The affected edges are
+ keys(oldC) = keys(newC).
+ @param oldC a table of copies of edges pre-change
+ @param newC a table of copies of edges post-change
+ @result A property change of a set of edges.
+ */
++ (GraphChange*)propertyChangeOfEdgesFromOldCopies:(NSMapTable*)oldC
+ toNewCopies:(NSMapTable*)newC;
+
+
+/*!
+ @brief Construct a shift of a set of nodes by a given point.
+ @param ns the affected nodes.
+ @param p a point storing (dx,dy)
+ @result A shift of a set of nodes.
+ */
++ (GraphChange*)shiftNodes:(NSSet*)ns byPoint:(NSPoint)p;
+
+/*!
+ @brief Construct a horizontal or vertical flip of a set of nodes.
+ @param ns the affected nodes.
+ @param b flag for whether to flip horizontally
+ @result A flip of a set of nodes.
+ */
++ (GraphChange*)flipNodes:(NSSet*)ns horizontal:(BOOL)b;
+
+/*!
+ @brief Construct a reversal of a set of edges.
+ @param es the affected edges.
+ @result A reverse of a set of edges.
+ */
++ (GraphChange*)reverseEdges:(NSSet*)es;
+
+/*!
+ @brief Construct a bounding box change
+ @param oldBB the old bounding box
+ @param newBB the new bounding box
+ @result A bounding box change.
+ */
++ (GraphChange*)changeBoundingBoxFrom:(NSRect)oldBB to:(NSRect)newBB;
+
+/*!
+ @brief Construct a graph property change
+ @param oldData the old graph data
+ @param newData the new graph data
+ @result A graph property change.
+ */
++ (GraphChange*)propertyChangeOfGraphFrom:(GraphElementData*)oldData to:(GraphElementData*)newData;
+
+/*!
+ @brief Construct a node order change
+ @param old The old ordering
+ @param new The new ordering
+ @result A node order change
+ */
++ (GraphChange*)nodeOrderChangeFrom:(NSArray*)old to:(NSArray*)new moved:(NSSet*)affected;
+
+/*!
+ @brief Construct an edge order change
+ @param old The old ordering
+ @param new The new ordering
+ @result A edge order change
+ */
++ (GraphChange*)edgeOrderChangeFrom:(NSArray*)old to:(NSArray*)new moved:(NSSet*)affected;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/GraphChange.m b/tikzit-old/src/common/GraphChange.m
new file mode 100644
index 0000000..29a3939
--- /dev/null
+++ b/tikzit-old/src/common/GraphChange.m
@@ -0,0 +1,369 @@
+//
+// GraphChange.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 <http://www.gnu.org/licenses/>.
+//
+
+
+// GraphChange : store the data associated to a single, undo-able change
+// to a graph. An undo manager should maintain a stack of such changes
+// and undo/redo them on request using [graph applyGraphChange:...].
+
+#import "GraphChange.h"
+
+
+@implementation GraphChange
+
+- (id)init {
+ return [super init];
+}
+
+- (void)dealloc {
+#if ! __has_feature(objc_arc)
+ [affectedNodes release];
+ [affectedEdges release];
+ [nodeRef release];
+ [edgeRef release];
+ [oldNode release];
+ [nwNode release];
+ [oldEdge release];
+ [nwEdge release];
+ [oldNodeTable release];
+ [nwNodeTable release];
+ [oldEdgeTable release];
+ [nwEdgeTable release];
+ [oldGraphData release];
+ [nwGraphData release];
+ [oldNodeOrder release];
+ [nwNodeOrder release];
+ [oldEdgeOrder release];
+ [nwEdgeOrder release];
+
+ [super dealloc];
+#endif
+}
+
+@synthesize changeType;
+@synthesize shiftPoint, horizontal;
+@synthesize affectedEdges, affectedNodes;
+@synthesize edgeRef, nodeRef;
+@synthesize nwNode, oldNode;
+@synthesize nwEdge, oldEdge;
+@synthesize oldNodeTable, nwNodeTable;
+@synthesize oldEdgeTable, nwEdgeTable;
+@synthesize oldBoundingBox, nwBoundingBox;
+@synthesize oldGraphData, nwGraphData;
+@synthesize oldNodeOrder, nwNodeOrder;
+@synthesize oldEdgeOrder, nwEdgeOrder;
+
+- (GraphChange*)invert {
+ GraphChange *inverse = [[GraphChange alloc] init];
+ [inverse setChangeType:[self changeType]];
+ switch ([self changeType]) {
+ case GraphAddition:
+ [inverse setChangeType:GraphDeletion];
+#if __has_feature(objc_arc)
+ inverse->affectedNodes = affectedNodes;
+ inverse->affectedEdges = affectedEdges;
+#else
+ inverse->affectedNodes = [affectedNodes retain];
+ inverse->affectedEdges = [affectedEdges retain];
+#endif
+ break;
+ case GraphDeletion:
+ [inverse setChangeType:GraphAddition];
+#if __has_feature(objc_arc)
+ inverse->affectedNodes = affectedNodes;
+ inverse->affectedEdges = affectedEdges;
+#else
+ inverse->affectedNodes = [affectedNodes retain];
+ inverse->affectedEdges = [affectedEdges retain];
+#endif
+ break;
+ case NodePropertyChange:
+#if __has_feature(objc_arc)
+ inverse->nodeRef = nodeRef;
+ inverse->oldNode = nwNode;
+ inverse->nwNode = oldNode;
+#else
+ inverse->nodeRef = [nodeRef retain];
+ inverse->oldNode = [nwNode retain];
+ inverse->nwNode = [oldNode retain];
+#endif
+ break;
+ case NodesPropertyChange:
+#if __has_feature(objc_arc)
+
+#else
+ inverse->oldNodeTable = [nwNodeTable retain];
+ inverse->nwNodeTable = [oldNodeTable retain];
+#endif
+ break;
+ case EdgePropertyChange:
+#if __has_feature(objc_arc)
+ inverse->edgeRef = edgeRef;
+ inverse->oldEdge = nwEdge;
+ inverse->nwEdge = oldEdge;
+#else
+ inverse->edgeRef = [edgeRef retain];
+ inverse->oldEdge = [nwEdge retain];
+ inverse->nwEdge = [oldEdge retain];
+#endif
+ break;
+ case EdgesPropertyChange:
+#if __has_feature(objc_arc)
+ inverse->oldEdgeTable = nwEdgeTable;
+ inverse->nwEdgeTable = oldEdgeTable;
+#else
+ inverse->oldEdgeTable = [nwEdgeTable retain];
+ inverse->nwEdgeTable = [oldEdgeTable retain];
+#endif
+ break;
+ case NodesShift:
+#if __has_feature(objc_arc)
+ inverse->affectedNodes = affectedNodes;
+#else
+ inverse->affectedNodes = [affectedNodes retain];
+#endif
+ [inverse setShiftPoint:NSMakePoint(-[self shiftPoint].x,
+ -[self shiftPoint].y)];
+ break;
+ case NodesFlip:
+#if __has_feature(objc_arc)
+ inverse->affectedNodes = affectedNodes;
+#else
+ inverse->affectedNodes = [affectedNodes retain];
+#endif
+ [inverse setHorizontal:[self horizontal]];
+ break;
+ case EdgesReverse:
+#if __has_feature(objc_arc)
+ inverse->affectedEdges = affectedEdges;
+#else
+ inverse->affectedEdges = [affectedEdges retain];
+#endif
+ break;
+ case BoundingBoxChange:
+ inverse->oldBoundingBox = nwBoundingBox;
+ inverse->nwBoundingBox = oldBoundingBox;
+ break;
+ case GraphPropertyChange:
+#if __has_feature(objc_arc)
+ inverse->oldGraphData = nwGraphData;
+ inverse->nwGraphData = oldGraphData;
+#else
+ inverse->oldGraphData = [nwGraphData retain];
+ inverse->nwGraphData = [oldGraphData retain];
+#endif
+ break;
+ case NodeOrderChange:
+#if __has_feature(objc_arc)
+ inverse->affectedNodes = affectedNodes;
+ inverse->oldNodeOrder = nwNodeOrder;
+ inverse->nwNodeOrder = oldNodeOrder;
+#else
+ inverse->affectedNodes = [affectedNodes retain];
+ inverse->oldNodeOrder = [nwNodeOrder retain];
+ inverse->nwNodeOrder = [oldNodeOrder retain];
+#endif
+ break;
+ case EdgeOrderChange:
+#if __has_feature(objc_arc)
+ inverse->affectedEdges = affectedEdges;
+ inverse->oldEdgeOrder = nwEdgeOrder;
+ inverse->nwEdgeOrder = oldEdgeOrder;
+#else
+ inverse->affectedEdges = [affectedEdges retain];
+ inverse->oldEdgeOrder = [nwEdgeOrder retain];
+ inverse->nwEdgeOrder = [oldEdgeOrder retain];
+#endif
+ break;
+ }
+#if __has_feature(objc_arc)
+ return inverse;
+#else
+ return [inverse autorelease];
+#endif
+}
+
++ (GraphChange*)graphAdditionWithNodes:(NSSet *)ns edges:(NSSet *)es {
+ GraphChange *gc = [[GraphChange alloc] init];
+ [gc setChangeType:GraphAddition];
+ [gc setAffectedNodes:ns];
+ [gc setAffectedEdges:es];
+#if __has_feature(objc_arc)
+ return gc;
+#else
+ return [gc autorelease];
+#endif
+}
+
++ (GraphChange*)graphDeletionWithNodes:(NSSet *)ns edges:(NSSet *)es {
+ GraphChange *gc = [[GraphChange alloc] init];
+ [gc setChangeType:GraphDeletion];
+ [gc setAffectedNodes:ns];
+ [gc setAffectedEdges:es];
+#if __has_feature(objc_arc)
+ return gc;
+#else
+ return [gc autorelease];
+#endif
+}
+
++ (GraphChange*)propertyChangeOfNode:(Node*)nd fromOld:(Node*)old toNew:(Node*)nw {
+ GraphChange *gc = [[GraphChange alloc] init];
+ [gc setChangeType:NodePropertyChange];
+ [gc setNodeRef:nd];
+ [gc setOldNode:old];
+ [gc setNwNode:nw];
+#if __has_feature(objc_arc)
+ return gc;
+#else
+ return [gc autorelease];
+#endif
+}
+
++ (GraphChange*)propertyChangeOfNodesFromOldCopies:(NSMapTable*)oldC
+ toNewCopies:(NSMapTable*)newC {
+ GraphChange *gc = [[GraphChange alloc] init];
+ [gc setChangeType:NodesPropertyChange];
+ [gc setOldNodeTable:oldC];
+ [gc setNwNodeTable:newC];
+#if __has_feature(objc_arc)
+ return gc;
+#else
+ return [gc autorelease];
+#endif
+}
+
++ (GraphChange*)propertyChangeOfEdge:(Edge*)e fromOld:(Edge *)old toNew:(Edge *)nw {
+ GraphChange *gc = [[GraphChange alloc] init];
+ [gc setChangeType:EdgePropertyChange];
+ [gc setEdgeRef:e];
+ [gc setOldEdge:old];
+ [gc setNwEdge:nw];
+#if __has_feature(objc_arc)
+ return gc;
+#else
+ return [gc autorelease];
+#endif
+}
+
++ (GraphChange*)propertyChangeOfEdgesFromOldCopies:(NSMapTable*)oldC
+ toNewCopies:(NSMapTable*)newC {
+ GraphChange *gc = [[GraphChange alloc] init];
+ [gc setChangeType:EdgesPropertyChange];
+ [gc setOldEdgeTable:oldC];
+ [gc setNwEdgeTable:newC];
+#if __has_feature(objc_arc)
+ return gc;
+#else
+ return [gc autorelease];
+#endif
+}
+
++ (GraphChange*)shiftNodes:(NSSet*)ns byPoint:(NSPoint)p {
+ GraphChange *gc = [[GraphChange alloc] init];
+ [gc setChangeType:NodesShift];
+ [gc setAffectedNodes:ns];
+ [gc setShiftPoint:p];
+#if __has_feature(objc_arc)
+ return gc;
+#else
+ return [gc autorelease];
+#endif
+}
+
++ (GraphChange*)flipNodes:(NSSet*)ns horizontal:(BOOL)b {
+ GraphChange *gc = [[GraphChange alloc] init];
+ [gc setChangeType:NodesFlip];
+ [gc setAffectedNodes:ns];
+ [gc setHorizontal:b];
+#if __has_feature(objc_arc)
+ return gc;
+#else
+ return [gc autorelease];
+#endif
+}
+
++ (GraphChange*)reverseEdges:(NSSet*)es {
+ GraphChange *gc = [[GraphChange alloc] init];
+ [gc setChangeType:EdgesReverse];
+ [gc setAffectedEdges:es];
+#if __has_feature(objc_arc)
+ return gc;
+#else
+ return [gc autorelease];
+#endif
+}
+
++ (GraphChange*)changeBoundingBoxFrom:(NSRect)oldBB to:(NSRect)newBB {
+ GraphChange *gc = [[GraphChange alloc] init];
+ [gc setChangeType:BoundingBoxChange];
+ [gc setOldBoundingBox:oldBB];
+ [gc setNwBoundingBox:newBB];
+#if __has_feature(objc_arc)
+ return gc;
+#else
+ return [gc autorelease];
+#endif
+}
+
++ (GraphChange*)propertyChangeOfGraphFrom:(GraphElementData*)oldData to:(GraphElementData*)newData {
+ GraphChange *gc = [[GraphChange alloc] init];
+ [gc setChangeType:GraphPropertyChange];
+ [gc setOldGraphData:oldData];
+ [gc setNwGraphData:newData];
+#if __has_feature(objc_arc)
+ return gc;
+#else
+ return [gc autorelease];
+#endif
+}
+
++ (GraphChange*)nodeOrderChangeFrom:(NSArray*)old to:(NSArray*)new moved:(NSSet*)affected {
+ GraphChange *gc = [[GraphChange alloc] init];
+ [gc setChangeType:NodeOrderChange];
+ [gc setAffectedNodes:affected];
+ [gc setOldNodeOrder:old];
+ [gc setNwNodeOrder:new];
+#if __has_feature(objc_arc)
+ return gc;
+#else
+ return [gc autorelease];
+#endif
+}
+
++ (GraphChange*)edgeOrderChangeFrom:(NSArray*)old to:(NSArray*)new moved:(NSSet*)affected {
+ GraphChange *gc = [[GraphChange alloc] init];
+ [gc setChangeType:EdgeOrderChange];
+ [gc setAffectedEdges:affected];
+ [gc setOldEdgeOrder:old];
+ [gc setNwEdgeOrder:new];
+#if __has_feature(objc_arc)
+ return gc;
+#else
+ return [gc autorelease];
+#endif
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/GraphElementData.h b/tikzit-old/src/common/GraphElementData.h
new file mode 100644
index 0000000..a65e6df
--- /dev/null
+++ b/tikzit-old/src/common/GraphElementData.h
@@ -0,0 +1,94 @@
+//
+// GraphElementData.h
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+
+#import <Foundation/Foundation.h>
+
+/*!
+ @class GraphElementData
+ @brief Store extra data associated with a graph, node, or edge.
+ @details Store the extra (style, ...) data associated with
+ a graph, node, or edge. This data is stored as a mutable
+ array of properties. It also implements hash-like accessors,
+ but care should be taken using these, as the list can contain
+ multiple occurrences of the same key.
+
+ Convention: Getters and setters act on the *first* occurrence
+ of the key. 'Unsetters' remove *all* occurrences.
+ */
+@interface GraphElementData : NSMutableArray {
+ NSMutableArray *properties;
+}
+
+- (id)init;
++ (id)data;
+
+/*!
+ @brief Set the given value for the *first* property matching this key. Add a
+ new property if it doesn't already exist.
+ @param val the value to set
+ @param key the key for this property
+ */
+- (void)setProperty:(NSString*)val forKey:(NSString*)key;
+
+/*!
+ @brief Remove *all* occurences of the property with the given key.
+ @param key
+ */
+- (void)unsetProperty:(NSString*)key;
+
+/*!
+ @brief Return the value of the *first* occurrence of the given key.
+ @param key
+ */
+- (NSString*)propertyForKey:(NSString*)key;
+
+/*!
+ @brief Add the given atom to the list, if it's not already present.
+ @param atom
+ */
+- (void)setAtom:(NSString*)atom;
+
+/*!
+ @brief Remove *all* occurrences of the given atom.
+ @param atom
+ */
+- (void)unsetAtom:(NSString*)atom;
+
+/*!
+ @brief Returns YES if the list contains at least one occurrence of
+ the given atom.
+ @param atom
+ @result A boolean value.
+ */
+- (BOOL)isAtomSet:(NSString*)atom;
+
+/*!
+ @brief Returns a TikZ-friendly string containing all of the properties.
+ @result A string.
+ */
+- (NSString*)tikzList;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/GraphElementData.m b/tikzit-old/src/common/GraphElementData.m
new file mode 100644
index 0000000..41dc9aa
--- /dev/null
+++ b/tikzit-old/src/common/GraphElementData.m
@@ -0,0 +1,188 @@
+//
+// GraphElementData.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 <http://www.gnu.org/licenses/>.
+//
+
+#import "GraphElementData.h"
+#import "GraphElementProperty.h"
+
+
+@implementation GraphElementData
+
++ (id)data {
+#if __has_feature(objc_arc)
+ return [[self alloc] init];
+#else
+ return [[[self alloc] init] autorelease];
+#endif
+}
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ properties = [[NSMutableArray alloc] init];
+ }
+ return self;
+}
+
+// all of the array messages delegate to 'properties'
+
+- (NSUInteger)count { return [properties count]; }
+- (id)objectAtIndex:(NSUInteger)index {
+ return [properties objectAtIndex:index];
+}
+- (NSArray*)objectsAtIndexes:(NSIndexSet*)indexes {
+ return [properties objectsAtIndexes:indexes];
+}
+
+#if __has_feature(objc_arc)
+- (void) getObjects:(__unsafe_unretained id*)buffer range:(NSRange)range {
+#else
+- (void) getObjects:(id*)buffer range:(NSRange)range {
+#endif
+ [properties getObjects:buffer range:range];
+}
+
+- (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
+ [properties insertObject:anObject atIndex:index];
+}
+- (void)removeObjectAtIndex:(NSUInteger)index {
+ [properties removeObjectAtIndex:index];
+}
+- (void)addObject:(id)anObject {
+ [properties addObject:anObject];
+}
+- (void)removeLastObject {
+ [properties removeLastObject];
+}
+- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
+ [properties replaceObjectAtIndex:index withObject:anObject];
+}
+
+#if __has_feature(objc_arc)
+- (NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *)state
+ objects:(__unsafe_unretained id [])stackbuf
+ count:(NSUInteger)len {
+#else
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
+ objects:(id *)stackbuf
+ count:(NSUInteger)len {
+#endif
+ return [properties countByEnumeratingWithState:state objects:stackbuf count:len];
+}
+
+- (void)setProperty:(NSString*)val forKey:(NSString*)key {
+ GraphElementProperty *m = [[GraphElementProperty alloc] initWithKeyMatching:key];
+ NSInteger idx = [properties indexOfObject:m];
+#if !__has_feature(objc_arc)
+ [m release];
+#endif
+
+ GraphElementProperty *p;
+ if (idx == NSNotFound) {
+ p = [[GraphElementProperty alloc] initWithPropertyValue:val forKey:key];
+ [properties addObject:p];
+#if !__has_feature(objc_arc)
+ [p release];
+#endif
+ } else {
+ p = [properties objectAtIndex:idx];
+ [p setValue:val];
+ }
+}
+
+- (void)unsetProperty:(NSString*)key {
+ GraphElementProperty *m = [[GraphElementProperty alloc] initWithKeyMatching:key];
+ [properties removeObject:m];
+#if !__has_feature(objc_arc)
+ [m release];
+#endif
+
+}
+
+- (NSString*)propertyForKey:(NSString*)key {
+ GraphElementProperty *m = [[GraphElementProperty alloc] initWithKeyMatching:key];
+ NSInteger idx = [properties indexOfObject:m];
+#if !__has_feature(objc_arc)
+ [m release];
+#endif
+
+
+ if (idx == NSNotFound) {
+ return nil;
+ }else {
+ GraphElementProperty *p = [properties objectAtIndex:idx];
+ return [p value];
+ }
+}
+
+- (void)setAtom:(NSString*)atom {
+ GraphElementProperty *a = [[GraphElementProperty alloc] initWithAtomName:atom];
+ if (![properties containsObject:a]) [properties addObject:a];
+#if !__has_feature(objc_arc)
+ [a release];
+#endif
+}
+
+- (void)unsetAtom:(NSString*)atom {
+ GraphElementProperty *a = [[GraphElementProperty alloc] initWithAtomName:atom];
+ [properties removeObject:a];
+#if !__has_feature(objc_arc)
+ [a release];
+#endif
+}
+
+- (BOOL)isAtomSet:(NSString*)atom {
+ GraphElementProperty *a = [[GraphElementProperty alloc] initWithAtomName:atom];
+ BOOL set = [properties containsObject:a];
+#if !__has_feature(objc_arc)
+ [a release];
+#endif
+ return set;
+}
+
+- (NSString*)tikzList {
+ NSString *s = [properties componentsJoinedByString:@", "];
+ return ([s isEqualToString:@""]) ? @"" : [NSString stringWithFormat:@"[%@]", s];
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+ GraphElementData *cp = [[GraphElementData allocWithZone:zone] init];
+ for (GraphElementProperty *p in properties) {
+ GraphElementProperty *p2 = [p copy];
+ [cp addObject:p2];
+#if !__has_feature(objc_arc)
+ [p2 release];
+#endif
+ }
+ return cp;
+}
+
+- (void)dealloc {
+#if !__has_feature(objc_arc)
+ [properties release];
+ [super dealloc];
+#endif
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/GraphElementProperty.h b/tikzit-old/src/common/GraphElementProperty.h
new file mode 100644
index 0000000..057cdbb
--- /dev/null
+++ b/tikzit-old/src/common/GraphElementProperty.h
@@ -0,0 +1,88 @@
+//
+// GraphElementProperty.h
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+
+/*!
+ @class GraphElementProperty
+ @brief A property. I.e. a single entry in a node's/edge's/graph's
+ GraphElementData table.
+ */
+@interface GraphElementProperty : NSObject<NSCopying> {
+ NSString *key;
+ NSString *value;
+ BOOL isAtom;
+ BOOL isKeyMatch;
+}
+
+@property (copy) NSString *key;
+@property (copy) NSString *value;
+@property (readonly) BOOL isAtom;
+@property (readonly) BOOL isKeyMatch;
+
+/*!
+ @brief Initialize a new key-matching object.
+ @param k a key to match
+ @result A key-matching object.
+ */
+- (id)initWithKeyMatching:(NSString*)k;
++ (id)keyMatching:(NSString*)k;
+
+/*!
+ @brief Initialize a new atomic property.
+ @param n the atom's name
+ @result An atom.
+ */
+- (id)initWithAtomName:(NSString*)n;
++ (id)atom:(NSString*)n;
+
+/*!
+ @brief Initialize a new property.
+ @param v the property's value
+ @param k the associated key
+ @result A property.
+ */
+- (id)initWithPropertyValue:(NSString*)v forKey:(NSString*)k;
++ (id)property:(NSString*)k withValue:(NSString*)v;
+
+/*!
+ @brief A matching function for properties.
+ @details Two properties match iff their keys match and one of the following:
+ (a) they are both atomic, (b) one is a key-matching and one is a non-atomic
+ property, or (c) they are both non-atomic and their values match.
+ @param object another GraphElementProperty
+ @result A boolean.
+ */
+- (BOOL)matches:(GraphElementProperty*)object;
+
+/*!
+ @brief An alias for <tt>matches:</tt>. This allows one to use built-in methods that
+ filter on <tt>isEqual:</tt> for <tt>NSObject</tt>s.
+ @param object another GraphElementProperty
+ @result A boolean.
+ */
+- (BOOL)isEqual:(id)object;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/GraphElementProperty.m b/tikzit-old/src/common/GraphElementProperty.m
new file mode 100644
index 0000000..25e1b15
--- /dev/null
+++ b/tikzit-old/src/common/GraphElementProperty.m
@@ -0,0 +1,164 @@
+//
+//
+// GraphElementProperty.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 <http://www.gnu.org/licenses/>.
+//
+
+#import "GraphElementProperty.h"
+#import "NSString+Tikz.h"
+
+@implementation GraphElementProperty
+
++ (id)atom:(NSString*)n {
+#if __has_feature(objc_arc)
+ return [[self alloc] initWithAtomName:n];
+#else
+ return [[[self alloc] initWithAtomName:n] autorelease];
+#endif
+}
++ (id)property:(NSString*)k withValue:(NSString*)v {
+#if __has_feature(objc_arc)
+ return [[self alloc] initWithPropertyValue:v forKey:k];
+#else
+ return [[[self alloc] initWithPropertyValue:v forKey:k] autorelease];
+#endif
+}
++ (id)keyMatching:(NSString*)k {
+#if __has_feature(objc_arc)
+ return [[self alloc] initWithKeyMatching:k];
+#else
+ return [[[self alloc] initWithKeyMatching:k] autorelease];
+#endif
+}
+
+- (id)initWithAtomName:(NSString*)n {
+ self = [super init];
+ if (self) {
+ [self setKey:n];
+ isAtom = YES;
+ }
+ return self;
+}
+
+- (id)initWithPropertyValue:(NSString*)v forKey:(NSString*)k {
+ self = [super init];
+ if (self) {
+ [self setKey:k];
+ [self setValue:v];
+ }
+ return self;
+}
+
+- (id)initWithKeyMatching:(NSString*)k {
+ self = [super init];
+ if (self) {
+ [self setKey:k];
+ isKeyMatch = YES;
+ }
+ return self;
+}
+
+- (void) dealloc {
+#if ! __has_feature(objc_arc)
+ [key release];
+ [value release];
+ [super dealloc];
+#endif
+}
+
+- (void)setValue:(NSString *)v {
+ if (value != v) {
+#if ! __has_feature(objc_arc)
+ [value release];
+#endif
+ value = [v copy];
+ }
+}
+
+- (NSString*)value {
+ if (isAtom) {
+ return @"(atom)";
+ } else {
+ return value;
+ }
+}
+
+
+- (void)setKey:(NSString *)k {
+ if (key != k) {
+#if ! __has_feature(objc_arc)
+ [key release];
+#endif
+ key = [k copy];
+ }
+ if (key == nil)
+ key = @""; // don't allow nil keys
+}
+
+- (NSString*)key {
+ return key;
+}
+
+- (BOOL)isAtom { return isAtom; }
+- (BOOL)isKeyMatch { return isKeyMatch; }
+
+- (BOOL)matches:(GraphElementProperty*)object {
+ // properties and atoms are taken to be incomparable
+ if ([self isAtom] != [object isAtom]) return NO;
+
+ // only compare keys if (a) we are both atoms, (b) i am a key match, or (c) object is a key match
+ if (([self isAtom] && [object isAtom]) || [self isKeyMatch] || [object isKeyMatch]) {
+ return [[self key] isEqual:[object key]];
+ }
+
+ // otherwise compare key and value
+ return [[self key] isEqual:[object key]] && [[self value] isEqual:[object value]];
+}
+
+- (BOOL)isEqual:(id)object {
+ return [self matches:object];
+}
+
+- (id)copyWithZone:(NSZone*)zone {
+ if (isAtom) {
+ return [[GraphElementProperty allocWithZone:zone] initWithAtomName:[self key]];
+ } else if (isKeyMatch) {
+ return [[GraphElementProperty allocWithZone:zone] initWithKeyMatching:[self key]];
+ } else {
+ return [[GraphElementProperty allocWithZone:zone] initWithPropertyValue:[self value] forKey:[self key]];
+ }
+}
+
+- (NSString*)description {
+ if ([self isAtom]) {
+ return [[self key] tikzEscapedString];
+ } else if ([self isKeyMatch]) {
+ return [NSString stringWithFormat:@"%@=*", [[self key] tikzEscapedString]];
+ } else {
+ return [NSString stringWithFormat:@"%@=%@",
+ [[self key] tikzEscapedString],
+ [[self value] tikzEscapedString]];
+ }
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/Grid.h b/tikzit-old/src/common/Grid.h
new file mode 100644
index 0000000..b267536
--- /dev/null
+++ b/tikzit-old/src/common/Grid.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2011 Alex Merry <dev@randomguy3.me.uk>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+#import "RenderContext.h"
+#import "Transformer.h"
+
+/*!
+ * Provides a grid, which can be use for snapping points
+ *
+ * The grid is divided into cells, and each cell is further subdivided.
+ * These subdivisions are the snap points for the grid.
+ */
+@interface Grid: NSObject <NSCopying> {
+ Transformer *transformer;
+ float spacing;
+ int cellSubdivisions;
+}
+
+/*!
+ * The number of times to subdivide the edge of each cell
+ *
+ * Each cell will be divided into cellSubdivisions^2 squares.
+ */
+@property (assign) int cellSubdivisions;
+
+/*!
+ * The cell spacing
+ *
+ * Each cell will be @p cellSpacing wide and @p cellSpacing high.
+ */
+@property (assign) float cellSpacing;
+
+
+/*!
+ * Create a new grid object.
+ *
+ * @param sp the cell spacing - this will be the width and height of each cell
+ * @param subs the number of cell subdivisions; the cell will end up being
+ * divided into subs*subs squares that are each sp/subs wide and high
+ * @param t the transformer to be used when snapping screen points
+ */
++ (Grid*) gridWithSpacing:(float)sp subdivisions:(int)subs transformer:(Transformer*)t;
+/*!
+ * Initialize a grid object.
+ *
+ * @param sp the cell spacing - this will be the width and height of each cell
+ * @param subs the number of cell subdivisions; each cell will end up being
+ * divided into subs*subs squares that are each sp/subs wide and high
+ * @param t the transformer to be used when snapping screen points
+ */
+- (id) initWithSpacing:(float)sp subdivisions:(int)subs transformer:(Transformer*)t;
+
+/*!
+ * Snap a point in screen co-ordinates
+ *
+ * @param p the point to snap, in screen co-ordinates
+ * @result @p p aligned to the nearest corner of a cell subdivision
+ */
+- (NSPoint) snapScreenPoint:(NSPoint)p;
+/*!
+ * Snap a point in base co-ordinates
+ *
+ * @param p the point to snap
+ * @result @p p aligned to the nearest corner of a cell subdivision
+ */
+- (NSPoint) snapPoint:(NSPoint)p;
+
+/**
+ * Renders the grid
+ *
+ * The grid is rendered across the entire surface (subject to the context's
+ * clip).
+ *
+ * The internal transformer is used to convert between graph co-ordinates
+ * and graphics co-ordinates.
+ *
+ * @param cr the context to render in
+ */
+- (void) renderGridInContext:(id<RenderContext>)cr;
+
+/**
+ * Renders the grid
+ *
+ * The grid is rendered across the entire surface (subject to the context's
+ * clip).
+ *
+ * @param cr the context to render in
+ * @param t a transformer that will be used to map graph co-ordinates
+ * to graphics co-ordinates
+ */
+- (void) renderGridInContext:(id<RenderContext>)cr transformer:(Transformer*)t;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/Grid.m b/tikzit-old/src/common/Grid.m
new file mode 100644
index 0000000..f597a4a
--- /dev/null
+++ b/tikzit-old/src/common/Grid.m
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2011 Alex Merry <dev@randomguy3.me.uk>
+ * Copyright 2010 Chris Heunen
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "Grid.h"
+#import "util.h"
+
+@implementation Grid
+
++ (Grid*) gridWithSpacing:(float)sp subdivisions:(int)subs transformer:(Transformer*)t {
+ return [[[self alloc] initWithSpacing:sp subdivisions:subs transformer:t] autorelease];
+}
+
+- (id) initWithSpacing:(float)sp subdivisions:(int)subs transformer:(Transformer*)t {
+ self = [super init];
+
+ if (self) {
+ transformer = [t retain];
+ spacing = sp;
+ cellSubdivisions = subs;
+ }
+
+ return self;
+}
+
+- (id) copyWithZone:(NSZone*)zone {
+ return [[Grid allocWithZone:zone]
+ initWithSpacing:spacing
+ subdivisions:cellSubdivisions
+ transformer:transformer];
+}
+
+- (void) dealloc {
+ [transformer release];
+ [super dealloc];
+}
+
+- (int) cellSubdivisions {
+ return cellSubdivisions;
+}
+
+- (void) setCellSubdivisions:(int)count {
+ cellSubdivisions = count;
+}
+
+- (float) cellSpacing {
+ return spacing;
+}
+
+- (void) setCellSpacing:(float)sp {
+ spacing = sp;
+}
+
+- (NSPoint) snapScreenPoint:(NSPoint)point {
+ NSPoint gridPoint = [transformer fromScreen:point];
+ return [transformer toScreen:[self snapPoint:gridPoint]];
+}
+
+- (NSPoint) snapPoint:(NSPoint)p {
+ const float snapDistance = spacing/(float)cellSubdivisions;
+ p.x = roundToNearest (snapDistance, p.x);
+ p.y = roundToNearest (snapDistance, p.y);
+ return p;
+}
+
+- (void) _setupLinesForContext:(id<RenderContext>)context withSpacing:(float)offset omittingEvery:(int)omitEvery origin:(NSPoint)origin {
+ NSRect clip = [context clipBoundingBox];
+ float clipx1 = clip.origin.x;
+ float clipx2 = clipx1 + clip.size.width;
+ float clipy1 = clip.origin.y;
+ float clipy2 = clipy1 + clip.size.height;
+
+ // left of the Y axis, moving outwards
+ unsigned int count = 1;
+ float x = origin.x - offset;
+ while (x >= clipx1) {
+ if (omitEvery == 0 || (count % omitEvery != 0)) {
+ [context moveTo:NSMakePoint(x, clipy1)];
+ [context lineTo:NSMakePoint(x, clipy2)];
+ }
+
+ x -= offset;
+ ++count;
+ }
+ // right of the Y axis, moving outwards
+ count = 1;
+ x = origin.x + offset;
+ while (x <= clipx2) {
+ if (omitEvery == 0 || (count % omitEvery != 0)) {
+ [context moveTo:NSMakePoint(x, clipy1)];
+ [context lineTo:NSMakePoint(x, clipy2)];
+ }
+
+ x += offset;
+ ++count;
+ }
+
+ // above the Y axis, moving outwards
+ count = 1;
+ float y = origin.y - offset;
+ while (y >= clipy1) {
+ if (omitEvery == 0 || (count % omitEvery != 0)) {
+ [context moveTo:NSMakePoint(clipx1, y)];
+ [context lineTo:NSMakePoint(clipx2, y)];
+ }
+
+ y -= offset;
+ ++count;
+ }
+ // below the Y axis, moving outwards
+ count = 1;
+ y = origin.y + offset;
+ while (y <= clipy2) {
+ if (omitEvery == 0 || (count % omitEvery != 0)) {
+ [context moveTo:NSMakePoint(clipx1, y)];
+ [context lineTo:NSMakePoint(clipx2, y)];
+ }
+
+ y += offset;
+ ++count;
+ }
+}
+
+- (void) _renderSubdivisionsWithContext:(id<RenderContext>)context origin:(NSPoint)origin cellSize:(float)cellSize {
+ const float offset = cellSize / cellSubdivisions;
+
+ [self _setupLinesForContext:context withSpacing:offset omittingEvery:cellSubdivisions origin:origin];
+
+ [context strokePathWithColor:MakeSolidRColor (0.9, 0.9, 1.0)];
+}
+
+- (void) _renderCellsWithContext:(id<RenderContext>)context origin:(NSPoint)origin cellSize:(float)cellSize {
+ [self _setupLinesForContext:context withSpacing:cellSize omittingEvery:0 origin:origin];
+
+ [context strokePathWithColor:MakeSolidRColor (0.8, 0.8, 0.9)];
+}
+
+- (void) _renderAxesWithContext:(id<RenderContext>)context origin:(NSPoint)origin {
+ NSRect clip = [context clipBoundingBox];
+
+ [context moveTo:NSMakePoint(origin.x, clip.origin.y)];
+ [context lineTo:NSMakePoint(origin.x, clip.origin.y + clip.size.height)];
+ [context moveTo:NSMakePoint(clip.origin.x, origin.y)];
+ [context lineTo:NSMakePoint(clip.origin.x + clip.size.width, origin.y)];
+
+ [context strokePathWithColor:MakeSolidRColor (0.6, 0.6, 0.7)];
+}
+
+- (void) renderGridInContext:(id<RenderContext>)cr {
+ [self renderGridInContext:cr transformer:transformer];
+}
+
+- (void) renderGridInContext:(id<RenderContext>)context transformer:(Transformer*)t {
+ const NSPoint origin = [t toScreen:NSZeroPoint];
+ const float cellSize = [t scaleToScreen:spacing];
+
+ [context saveState];
+
+ // common line settings
+ [context setLineWidth:1.0];
+ [context setAntialiasMode:AntialiasDisabled];
+
+ [self _renderSubdivisionsWithContext:context origin:origin cellSize:cellSize];
+ [self _renderCellsWithContext:context origin:origin cellSize:cellSize];
+ [self _renderAxesWithContext:context origin:origin];
+
+ [context restoreState];
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/NSError+Tikzit.h b/tikzit-old/src/common/NSError+Tikzit.h
new file mode 100644
index 0000000..0f45fba
--- /dev/null
+++ b/tikzit-old/src/common/NSError+Tikzit.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2011 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+
+NSString* const TZErrorDomain;
+
+enum {
+ TZ_ERR_OTHER = 1,
+ TZ_ERR_BADSTATE,
+ TZ_ERR_BADFORMAT,
+ TZ_ERR_IO,
+ TZ_ERR_TOOL_FAILED,
+ TZ_ERR_NOTDIRECTORY,
+ TZ_ERR_PARSE
+};
+
+NSString* const TZToolOutputErrorKey;
+
+@interface NSError(Tikzit)
++ (NSString*)tikzitErrorDomain;
++ (id) errorWithMessage:(NSString*)message code:(NSInteger)code cause:(NSError*)cause;
++ (id) errorWithMessage:(NSString*)message code:(NSInteger)code;
++ (id) errorWithLibcError:(NSInteger)errnum;
+- (NSString*)toolOutput;
+@end
+
+void logError (NSError *error, NSString *message);
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/NSError+Tikzit.m b/tikzit-old/src/common/NSError+Tikzit.m
new file mode 100644
index 0000000..6b9404b
--- /dev/null
+++ b/tikzit-old/src/common/NSError+Tikzit.m
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "NSError+Tikzit.h"
+
+NSString* const TZErrorDomain = @"tikzit";
+
+NSString* const TZToolOutputErrorKey = @"tool-output";
+
+@implementation NSError(Tikzit)
++ (NSString*)tikzitErrorDomain {
+ return TZErrorDomain;
+}
+
++ (id) errorWithMessage:(NSString*)message code:(NSInteger)code cause:(NSError*)cause {
+ NSMutableDictionary *errorDetail = [NSMutableDictionary dictionaryWithCapacity:2];
+ [errorDetail setValue:message forKey:NSLocalizedDescriptionKey];
+ if (cause)
+ [errorDetail setValue:cause forKey:NSUnderlyingErrorKey];
+ return [self errorWithDomain:TZErrorDomain code:code userInfo:errorDetail];
+}
+
++ (id) errorWithMessage:(NSString*)message code:(NSInteger)code {
+ NSMutableDictionary *errorDetail = [NSMutableDictionary dictionaryWithObject:message
+ forKey:NSLocalizedDescriptionKey];
+ return [self errorWithDomain:TZErrorDomain code:code userInfo:errorDetail];
+}
+
++ (id) errorWithLibcError:(NSInteger)errnum {
+ NSString *message = [NSString stringWithUTF8String:strerror(errnum)];
+ NSMutableDictionary *errorDetail = [NSMutableDictionary dictionaryWithObject:message
+ forKey:NSLocalizedDescriptionKey];
+ return [self errorWithDomain:NSPOSIXErrorDomain code:errnum userInfo:errorDetail];
+}
+
+- (NSString*)toolOutput {
+ return [[self userInfo] objectForKey:TZToolOutputErrorKey];
+}
+
+@end
+
+void logError (NSError *error, NSString *message) {
+ if (message == nil) {
+ NSLog (@"%@", [error localizedDescription]);
+ } else {
+ NSLog (@"%@: %@", message, [error localizedDescription]);
+ }
+}
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/NSFileManager+Utils.h b/tikzit-old/src/common/NSFileManager+Utils.h
new file mode 100644
index 0000000..1349919
--- /dev/null
+++ b/tikzit-old/src/common/NSFileManager+Utils.h
@@ -0,0 +1,29 @@
+//
+// NSFileManager+Utils.h
+// TikZiT
+//
+// Copyright 2010 Alex Merry
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NSFileManager(Utils)
+- (BOOL) ensureDirectoryExists:(NSString*)path error:(NSError**)error;
+@end
+
+// vi:ft=objc:sts=4:sw=4:ts=4:noet
diff --git a/tikzit-old/src/common/NSFileManager+Utils.m b/tikzit-old/src/common/NSFileManager+Utils.m
new file mode 100644
index 0000000..87ede95
--- /dev/null
+++ b/tikzit-old/src/common/NSFileManager+Utils.m
@@ -0,0 +1,46 @@
+//
+// NSFileManager+Utils.h
+// TikZiT
+//
+// Copyright 2010 Alex Merry
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+#import "NSError+Tikzit.h"
+
+@implementation NSFileManager(Utils)
+- (BOOL) ensureDirectoryExists:(NSString*)directory error:(NSError**)error {
+ BOOL isDirectory = NO;
+ if (![self fileExistsAtPath:directory isDirectory:&isDirectory]) {
+ if (![self createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:error]) {
+ return NO;
+ }
+ } else if (!isDirectory) {
+ if (error) {
+ NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
+ [errorDetail setValue:@"Directory is a file" forKey:NSLocalizedDescriptionKey];
+ [errorDetail setValue:directory forKey:NSFilePathErrorKey];
+ *error = [NSError errorWithDomain:TZErrorDomain code:TZ_ERR_NOTDIRECTORY userInfo:errorDetail];
+ }
+ return NO;
+ }
+ return YES;
+}
+@end
+
+// vi:ft=objc:sts=4:sw=4:ts=4:noet
diff --git a/tikzit-old/src/common/NSString+LatexConstants.h b/tikzit-old/src/common/NSString+LatexConstants.h
new file mode 100644
index 0000000..f4b5236
--- /dev/null
+++ b/tikzit-old/src/common/NSString+LatexConstants.h
@@ -0,0 +1,33 @@
+//
+// NSString+LatexConstants.h
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+
+
+@interface NSString(LatexConstants)
+
+- (NSString*)stringByExpandingLatexConstants;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/NSString+LatexConstants.m b/tikzit-old/src/common/NSString+LatexConstants.m
new file mode 100644
index 0000000..634c189
--- /dev/null
+++ b/tikzit-old/src/common/NSString+LatexConstants.m
@@ -0,0 +1,212 @@
+//
+// NSString+LatexConstants.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 <http://www.gnu.org/licenses/>.
+//
+
+#import "NSString+LatexConstants.h"
+
+// can't use sizeof() in non-fragile ABI (eg: clang)
+#define texConstantCount 63
+static NSString *texConstantNames[texConstantCount] = {
+ @"alpha",
+ @"beta",
+ @"gamma",
+ @"delta",
+ @"epsilon",
+ @"zeta",
+ @"eta",
+ @"theta",
+ @"iota",
+ @"kappa",
+ @"lambda",
+ @"mu",
+ @"nu",
+ @"xi",
+ @"pi",
+ @"rho",
+ @"sigma",
+ @"tau",
+ @"upsilon",
+ @"phi",
+ @"chi",
+ @"psi",
+ @"omega",
+ @"Gamma",
+ @"Delta",
+ @"Theta",
+ @"Lambda",
+ @"Xi",
+ @"Pi",
+ @"Sigma",
+ @"Upsilon",
+ @"Phi",
+ @"Psi",
+ @"Omega",
+
+ @"pm",
+ @"to",
+ @"Rightarrow",
+ @"Leftrightarrow",
+ @"forall",
+ @"partial",
+ @"exists",
+ @"emptyset",
+ @"nabla",
+ @"in",
+ @"notin",
+ @"prod",
+ @"sum",
+ @"surd",
+ @"infty",
+ @"wedge",
+ @"vee",
+ @"cap",
+ @"cup",
+ @"int",
+ @"approx",
+ @"neq",
+ @"equiv",
+ @"leq",
+ @"geq",
+ @"subset",
+ @"supset",
+ @"cdot",
+ @"ldots"
+};
+
+static char * texConstantCodes[texConstantCount] = {
+ "\u03b1","\u03b2","\u03b3","\u03b4","\u03b5","\u03b6","\u03b7",
+ "\u03b8","\u03b9","\u03ba","\u03bb","\u03bc","\u03bd","\u03be",
+ "\u03c0","\u03c1","\u03c3","\u03c4","\u03c5","\u03c6","\u03c7",
+ "\u03c8","\u03c9","\u0393","\u0394","\u0398","\u039b","\u039e",
+ "\u03a0","\u03a3","\u03a5","\u03a6","\u03a8","\u03a9",
+
+ "\u00b1","\u2192","\u21d2","\u21d4","\u2200","\u2202","\u2203",
+ "\u2205","\u2207","\u2208","\u2209","\u220f","\u2211","\u221a",
+ "\u221e","\u2227","\u2228","\u2229","\u222a","\u222b","\u2248",
+ "\u2260","\u2261","\u2264","\u2265","\u2282","\u2283","\u22c5",
+ "\u2026"
+};
+
+#define texModifierCount 10
+static NSString *texModifierNames[texModifierCount] = {
+ @"tiny",
+ @"scriptsize",
+ @"footnotesize",
+ @"small",
+ @"normalsize",
+ @"large",
+ @"Large",
+ @"LARGE",
+ @"huge",
+ @"Huge"
+};
+
+static NSDictionary *texConstants = nil;
+static NSSet *texModifiers = nil;
+
+@implementation NSString(LatexConstants)
+
+- (NSString*)stringByExpandingLatexConstants {
+
+ if (texConstants == nil) {
+ NSMutableDictionary *constants = [[NSMutableDictionary alloc] initWithCapacity:texConstantCount];
+ for (int i = 0; i < texConstantCount; ++i) {
+ [constants setObject:[NSString stringWithUTF8String:texConstantCodes[i]] forKey:texConstantNames[i]];
+ }
+ texConstants = constants;
+ }
+ if (texModifiers == nil) {
+ texModifiers = [[NSSet alloc] initWithObjects:texModifierNames count:texModifierCount];
+ }
+
+ NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:[self length]];
+ NSMutableString *wordBuf = [[NSMutableString alloc] initWithCapacity:10];
+
+ unichar c_a = [@"a" characterAtIndex:0];
+ unichar c_z = [@"z" characterAtIndex:0];
+ unichar c_A = [@"A" characterAtIndex:0];
+ unichar c_Z = [@"Z" characterAtIndex:0];
+
+ int state = 0;
+ // a tiny little DFA to replace \\([\w*]) with unicode of $1
+ unichar c;
+ NSString *code;
+ int i;
+ for (i = 0; i<[self length]; ++i) {
+ c = [self characterAtIndex:i];
+ switch (state) {
+ case 0:
+ if (c=='\\') {
+ state = 1;
+ } else if (c!='$') {
+ [buf appendFormat:@"%C", c];
+ }
+ break;
+ case 1:
+ if ((c>=c_a && c<=c_z) || (c>=c_A && c<=c_Z)) {
+ [wordBuf appendFormat:@"%C", c];
+ } else {
+ code = [texConstants objectForKey:wordBuf];
+ if (code != nil) {
+ [buf appendString:code];
+ } else if (![texModifiers containsObject:wordBuf]) {
+ [buf appendFormat:@"\\%@", wordBuf];
+ }
+
+ [wordBuf setString:@""];
+ if (c=='\\') {
+ state = 1;
+ } else {
+ if (c!='$') {
+ [buf appendFormat:@"%C", c];
+ }
+ state = 0;
+ }
+
+ }
+ break;
+ }
+ }
+
+ if (state == 1) {
+ code = [texConstants objectForKey:wordBuf];
+ if (code != nil) {
+ [buf appendString:code];
+ } else if (![texModifiers containsObject:wordBuf]) {
+ [buf appendFormat:@"\\%@", wordBuf];
+ }
+ }
+
+ NSString *ret = [buf copy];
+#if __has_feature(objc_arc)
+ return ret;
+#else
+ [buf release];
+ [wordBuf release];
+
+ return [ret autorelease];
+#endif
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/NSString+Tikz.h b/tikzit-old/src/common/NSString+Tikz.h
new file mode 100644
index 0000000..ea6ea40
--- /dev/null
+++ b/tikzit-old/src/common/NSString+Tikz.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2013 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+
+@interface NSString (Tikz)
+ - (NSString*) tikzEscapedString;
+ - (BOOL) isValidTikzPropertyNameOrValue;
+ - (BOOL) isValidAnchor;
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/NSString+Tikz.m b/tikzit-old/src/common/NSString+Tikz.m
new file mode 100644
index 0000000..1e3073b
--- /dev/null
+++ b/tikzit-old/src/common/NSString+Tikz.m
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2013 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "NSString+Tikz.h"
+#import "TikzGraphAssembler.h"
+
+@implementation NSString (Tikz)
+
+- (NSString*) tikzEscapedString {
+ static NSCharacterSet *avoid = nil;
+ if (avoid == nil)
+#if __has_feature(objc_arc)
+ avoid = [[NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<>-'0123456789. "] invertedSet];
+#else
+ avoid = [[[NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<>-'0123456789. "] invertedSet] retain];
+#endif
+
+
+ if ([self rangeOfCharacterFromSet:avoid].length > 0) {
+ return [NSString stringWithFormat:@"{%@}", self];
+ } else {
+#if __has_feature(objc_arc)
+ return self;
+#else
+ return [[self retain] autorelease];
+#endif
+ }
+}
+
+- (BOOL) isValidTikzPropertyNameOrValue {
+ NSUInteger length = [self length];
+ unsigned int brace_depth = 0;
+ unsigned int escape = 0;
+ for (NSUInteger i = 0; i < length; ++i) {
+ unichar c = [self characterAtIndex:i];
+
+ if (escape) {
+ escape = 0;
+ } else if (c == '\\') {
+ escape = 1;
+ } else if (c == '{') {
+ brace_depth++;
+ } else if (c == '}') {
+ if (brace_depth == 0)
+ return NO;
+ brace_depth--;
+ }
+ }
+ return !escape && brace_depth == 0;
+}
+
+- (BOOL) isValidAnchor {
+ return [TikzGraphAssembler validateTikzEdgeAnchor:self];
+}
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/NSString+Util.h b/tikzit-old/src/common/NSString+Util.h
new file mode 100644
index 0000000..548edb3
--- /dev/null
+++ b/tikzit-old/src/common/NSString+Util.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+
+@interface NSString (Util)
+ + (NSString*) stringWithContentsOfFile:(NSString*)path
+ error:(NSError**)error;
+ - (id) initWithContentsOfFile:(NSString*)path
+ error:(NSError**)error;
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/NSString+Util.m b/tikzit-old/src/common/NSString+Util.m
new file mode 100644
index 0000000..b18f397
--- /dev/null
+++ b/tikzit-old/src/common/NSString+Util.m
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+#import "NSString+Util.h"
+#import "NSError+Tikzit.h"
+
+@implementation NSString (Util)
++ (NSString*) stringWithContentsOfFile:(NSString*)path
+ error:(NSError**)error
+{
+ return [[[self alloc] initWithContentsOfFile:path error:error] autorelease];
+}
+- (id) initWithContentsOfFile:(NSString*)path
+ error:(NSError**)error
+{
+ // Fun fact: on GNUstep, at least,
+ // [stringWithContentsOfFile:usedEncoding:error:] only
+ // sets error objects if the decoding fails, not if file
+ // access fails.
+ // Fun fact 2: on GNUstep, trying to read a directory using
+ // [stringWithContentsOfFile:] causes an out-of-memory error;
+ // hence we do these checks *before* trying to read the file.
+ NSFileManager *fm = [NSFileManager defaultManager];
+ BOOL isDir = NO;
+ NSString *msg = nil;
+ if (![fm fileExistsAtPath:path isDirectory:&isDir]) {
+ msg = [NSString stringWithFormat:@"\"%@\" does not exist", path];
+ } else if (isDir) {
+ msg = [NSString stringWithFormat:@"\"%@\" is a directory", path];
+ } else if (![fm isReadableFileAtPath:path]) {
+ msg = [NSString stringWithFormat:@"\"%@\" is not readable", path];
+ }
+ if (msg != nil) {
+ if (error) {
+ *error = [NSError errorWithMessage:msg
+ code:TZ_ERR_IO];
+ }
+ return nil;
+ }
+ self = [self initWithContentsOfFile:path];
+ if (self == nil) {
+ if (error) {
+ *error = [NSError errorWithMessage:@"unknown error"
+ code:TZ_ERR_IO];
+ }
+ }
+ return self;
+}
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/Node.h b/tikzit-old/src/common/Node.h
new file mode 100644
index 0000000..1e580ce
--- /dev/null
+++ b/tikzit-old/src/common/Node.h
@@ -0,0 +1,181 @@
+//
+// Node.h
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+
+// Node : store the data associated with a node.
+
+#import <Foundation/Foundation.h>
+
+#import "NodeStyle.h"
+#import "GraphElementData.h"
+
+@class GraphElementProperty;
+@class Shape;
+@class Transformer;
+
+/*!
+ @class Node
+ @brief A graph node, with associated location and style data.
+ */
+@interface Node : NSObject<NSCopying> {
+ NSPoint point;
+ NodeStyle *style;
+ NSString *name;
+ NSString *label;
+ GraphElementData *data;
+}
+
+/*!
+ @property shape
+ @brief The shape to use
+ @detail This is a convenience property that resolves the shape name
+ from the style, and uses a circle if there is no style.
+
+ This property is NOT KVO-compliant
+ */
+@property (readonly) Shape *shape;
+
+/*!
+ @property point
+ @brief The point where this node is located.
+ */
+@property (assign) NSPoint point;
+
+/*!
+ @property style
+ @brief The style of this node.
+ */
+@property (retain) NodeStyle *style;
+
+/*!
+ @property name
+ @brief The name of this node. This is a temporary name and may change between
+ successive TikZ outputs.
+ */
+@property (copy) NSString *name;
+
+/*!
+ @property label
+ @brief The latex label that appears on this node.
+ */
+@property (copy) NSString *label;
+
+/*!
+ @property data
+ @brief Associated extra data.
+ */
+@property (copy) GraphElementData *data;
+
+// KVC methods
+- (void) insertObject:(GraphElementProperty*)gep
+ inDataAtIndex:(NSUInteger)index;
+- (void) removeObjectFromDataAtIndex:(NSUInteger)index;
+- (void) replaceObjectInDataAtIndex:(NSUInteger)index
+ withObject:(GraphElementProperty*)gep;
+
+/*!
+ @brief Initialize a new node with the given point.
+ @param p a point.
+ @result A node.
+ */
+- (id)initWithPoint:(NSPoint)p;
+
+/*!
+ @brief Initialize a new node at (0,0).
+ @result A node.
+ */
+- (id)init;
+
+/*!
+ @brief Composes the shape transformer with another transformer
+ @param t The transform to apply before the shape transform
+ @result A transformer that first maps according to t, then according
+ to -shapeTransformer.
+ */
+- (Transformer*) shapeTransformerFromTransformer:(Transformer*)t;
+
+/*!
+ @brief A transformer that may be used to convert the shape to the
+ right position and scale
+ */
+- (Transformer*) shapeTransformer;
+
+/*!
+ @brief The bounding rect in the given co-ordinate system
+ @detail This is the bounding rect of the shape (after being
+ suitably translated and scaled). The label is not
+ considered.
+ @param shapeTrans The mapping from graph co-ordinates to the required
+ co-ordinates
+ @result The bounding rectangle
+ */
+- (NSRect) boundsUsingShapeTransform:(Transformer*)shapeTrans;
+
+/*!
+ @brief The bounding rect in graph co-ordinates
+ @detail This is the bounding rect of the shape (after being suitably
+ translated and scaled). The label is not considered.
+ */
+- (NSRect) boundingRect;
+
+/*!
+ @brief Try to attach a style of the correct name from the given style list.
+ @param styles an array of styles.
+ @result YES if successfully attached, NO otherwise.
+ */
+- (BOOL)attachStyleFromTable:(NSArray*)styles;
+
+/*!
+ @brief Set node properties from <tt>GraphElementData</tt>.
+ */
+- (void)updateData;
+
+/*!
+ @brief Set properties of this node to match the given node.
+ @param nd a node to mimic.
+ */
+- (void)setPropertiesFromNode:(Node *)nd;
+
+/*!
+ @brief Compare a node to another node using a lex ordering on coordinates.
+ @param nd another node.
+ @result A comparison result.
+ */
+- (NSComparisonResult)compareTo:(id)nd;
+
+/*!
+ @brief Factory method to construct a node with the given point.
+ @param p a point.
+ @result A node.
+ */
++ (Node*)nodeWithPoint:(NSPoint)p;
+
+/*!
+ @brief Factory method to construct a node at (0,0).
+ @result A node.
+ */
++ (Node*)node;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/Node.m b/tikzit-old/src/common/Node.m
new file mode 100644
index 0000000..c5b11d1
--- /dev/null
+++ b/tikzit-old/src/common/Node.m
@@ -0,0 +1,214 @@
+//
+// Node.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 <http://www.gnu.org/licenses/>.
+//
+
+#import "Node.h"
+
+#import "Shape.h"
+
+
+@implementation Node
+
+- (id)initWithPoint:(NSPoint)p {
+ self = [super init];
+ if (self) {
+ data = [[GraphElementData alloc] init];
+ style = nil;
+ label = @"";
+ point = p;
+ }
+ return self;
+}
+
+- (id)init {
+ return [self initWithPoint:NSMakePoint(0.0f, 0.0f)];
+}
+
+- (id)copyWithZone:(NSZone*)z {
+ Node *cp = [[Node allocWithZone:z] init];
+ [cp setPropertiesFromNode:self];
+ return cp;
+}
+
+- (void)dealloc {
+#if ! __has_feature(objc_arc)
+ [name release];
+ [style release];
+ [data release];
+ [label release];
+ [super dealloc];
+#endif
+}
+
+- (Shape*) shape {
+ if (style) {
+ return [Shape shapeForName:[style shapeName]];
+ } else {
+ return nil;
+ }
+}
+
+- (Transformer*) shapeTransformerFromTransformer:(Transformer*)t {
+ // we take a copy to keep the reflection attributes
+#if ! __has_feature(objc_arc)
+ Transformer *transformer = [[t copy] autorelease];
+#else
+ Transformer *transformer = [t copy];
+#endif
+ NSPoint screenPos = [t toScreen:point];
+ [transformer setOrigin:screenPos];
+ float scale = [t scale];
+ if (style) {
+ scale *= [style scale];
+ }
+ [transformer setScale:scale];
+ return transformer;
+}
+
+- (Transformer*) shapeTransformer {
+ float scale = 1.0f;
+ if (style) {
+ scale = [style scale];
+ }
+ return [Transformer transformerWithOrigin:point andScale:scale];
+}
+
+- (NSRect) boundsUsingShapeTransform:(Transformer*)shapeTrans {
+ //if (style) {
+ return [shapeTrans rectToScreen:[[self shape] boundingRect]];
+ /*} else {
+ NSRect r = NSZeroRect;
+ r.origin = [shapeTrans toScreen:[self point]];
+ return r;
+ }*/
+}
+
+- (NSRect) boundingRect {
+ return [self boundsUsingShapeTransform:[self shapeTransformer]];
+}
+
+- (BOOL)attachStyleFromTable:(NSArray*)styles {
+#if __has_feature(objc_arc)
+ NSString *style_name = [data propertyForKey:@"style"];
+#else
+ NSString *style_name = [[[data propertyForKey:@"style"] retain] autorelease];
+#endif
+
+ [self setStyle:nil];
+
+ // 'none' is a reserved style
+ if (style_name == nil || [style_name isEqualToString:@"none"]) return YES;
+
+ for (NodeStyle *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
+ [self setStyle:[NodeStyle defaultNodeStyleWithName:style_name]];
+ return NO;
+}
+
+- (void)updateData {
+ if (style == nil) {
+ [data setProperty:@"none" forKey:@"style"];
+ } else {
+ [data setProperty:[style name] forKey:@"style"];
+ }
+}
+
+- (void)setPropertiesFromNode:(Node*)nd {
+ [self setPoint:[nd point]];
+ [self setStyle:[nd style]];
+ [self setName:[nd name]];
+ [self setData:[nd data]];
+ [self setLabel:[nd label]];
+}
+
++ (Node*)nodeWithPoint:(NSPoint)p {
+#if __has_feature(objc_arc)
+ return [[Node alloc] initWithPoint:p];
+#else
+ return [[[Node alloc] initWithPoint:p] autorelease];
+#endif
+}
+
++ (Node*)node {
+#if __has_feature(objc_arc)
+ return [[Node alloc] init];
+#else
+ return [[[Node alloc] init] autorelease];
+#endif
+}
+
+
+// perform a lexicographic ordering (-y, x) on coordinates.
+- (NSComparisonResult)compareTo:(id)nd {
+ Node *node = (Node*)nd;
+ if (point.y > [node point].y) return NSOrderedAscending;
+ else if (point.y < [node point].y) return NSOrderedDescending;
+ else {
+ if (point.x < [node point].x) return NSOrderedAscending;
+ else if (point.x > [node point].x) return NSOrderedDescending;
+ else return NSOrderedSame;
+ }
+}
+
+@synthesize name;
+@synthesize label;
+@synthesize point;
+
+@synthesize data;
+- (void) insertObject:(GraphElementProperty*)gep
+ inDataAtIndex:(NSUInteger)index {
+ [data insertObject:gep atIndex:index];
+}
+- (void) removeObjectFromDataAtIndex:(NSUInteger)index {
+ [data removeObjectAtIndex:index];
+}
+- (void) replaceObjectInDataAtIndex:(NSUInteger)index
+ withObject:(GraphElementProperty*)gep {
+ [data replaceObjectAtIndex:index withObject:gep];
+}
+
+- (NodeStyle*)style {
+ return style;
+}
+
+- (void)setStyle:(NodeStyle *)st {
+ if (style != st) {
+#if __has_feature(objc_arc)
+ style = st;
+#else
+ NodeStyle *oldStyle = style;
+ style = [st retain];
+ [oldStyle release];
+#endif
+ }
+ [self updateData];
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/NodeStyle.h b/tikzit-old/src/common/NodeStyle.h
new file mode 100644
index 0000000..034f95d
--- /dev/null
+++ b/tikzit-old/src/common/NodeStyle.h
@@ -0,0 +1,125 @@
+//
+// NodeStyle.h
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#import "util.h"
+#import "ColorRGB.h"
+#import "PropertyHolder.h"
+
+/*!
+ @class NodeStyle
+ @brief Store node style information.
+ @details Store node style information. These properties affect how a node
+ is displayed in TikZiT. Colors are stored in the ColorRGB struct
+ to avoid any Cocoa dependency. These styles should be persistant,
+ which should be implemented in a platform-specific category. For
+ OS X, this is NodeStyle+Coder.
+ */
+@interface NodeStyle : PropertyHolder <NSCopying> {
+ int strokeThickness;
+ float scale;
+ ColorRGB *strokeColorRGB;
+ ColorRGB *fillColorRGB;
+ NSString *name;
+ NSString *shapeName;
+ NSString *category;
+}
+
+/*!
+ @property strokeThickness
+ @brief Thickness of the stroke.
+ */
+@property (assign) int strokeThickness;
+
+/*!
+ @property scale
+ @brief Overall scale of the shape. Defaults to 1.0.
+ */
+@property (assign) float scale;
+
+
+/*!
+ @property strokeColorRGB
+ @brief The stroke color used to render the node
+ */
+@property (copy) ColorRGB *strokeColorRGB;
+
+/*!
+ @property fillColorRGB
+ @brief The fill color used to render the node
+ */
+@property (copy) ColorRGB *fillColorRGB;
+
+/*!
+ @property name
+ @brief Style name.
+ @details Style name. This is the only thing that affects how the node
+ will look when the latex code is rendered.
+ */
+@property (copy) NSString *name;
+
+/*!
+ @property shapeName
+ @brief The name of the shape that will be drawn in TikZiT.
+ */
+@property (copy) NSString *shapeName;
+
+/*!
+ @property category
+ @brief ???
+ */
+@property (copy) NSString *category;
+
+@property (readonly) NSString *tikz;
+@property (readonly) BOOL strokeColorIsKnown;
+@property (readonly) BOOL fillColorIsKnown;
+
++ (int) defaultStrokeThickness;
+
+/*!
+ @brief Designated initializer. Construct a blank style with name 'new'.
+ @result A default style.
+ */
+- (id)init;
+
+/*!
+ @brief Create a named style.
+ @param nm the style name.
+ @result A <tt>NodeStyle</tt> with the given name.
+ */
+- (id)initWithName:(NSString *)nm;
+
+/*!
+ @brief Factory method for initWithName:
+ @param nm the style name.
+ @result A <tt>NodeStyle</tt> with the given name.
+ */
++ (NodeStyle*)defaultNodeStyleWithName:(NSString *)nm;
+
+/*!
+ * Make this style the same as the given one
+ */
+- (void) updateFromStyle:(NodeStyle*)style;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/NodeStyle.m b/tikzit-old/src/common/NodeStyle.m
new file mode 100644
index 0000000..193d44d
--- /dev/null
+++ b/tikzit-old/src/common/NodeStyle.m
@@ -0,0 +1,246 @@
+//
+// NodeStyle.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 <http://www.gnu.org/licenses/>.
+//
+
+#import "NodeStyle.h"
+#import "Shape.h"
+#import "ShapeNames.h"
+
+@implementation NodeStyle
+
++ (void)initialize {
+ [self setKeys:[NSArray arrayWithObjects:
+ @"fillColorRGB.red",
+ @"fillColorRGB.blue",
+ @"fillColorRGB.green",
+ @"strokeColorRGB.red",
+ @"strokeColorRGB.blue",
+ @"strokeColorRGB.green",
+ @"strokeThickness",
+ @"shapeName",
+ @"name",
+ nil]
+ triggerChangeNotificationsForDependentKey:@"tikz"];
+ [self setKeys:[NSArray arrayWithObjects:
+ @"fillColorRGB.name",
+ nil]
+ triggerChangeNotificationsForDependentKey:@"fillColorIsKnown"];
+ [self setKeys:[NSArray arrayWithObjects:
+ @"strokeColorRGB.name",
+ nil]
+ triggerChangeNotificationsForDependentKey:@"strokeColorIsKnown"];
+}
+
++ (int) defaultStrokeThickness { return 1; }
+
+- (id)initWithName:(NSString*)nm {
+ self = [super initWithNotificationName:@"NodeStylePropertyChanged"];
+ if (self != nil) {
+ strokeThickness = [NodeStyle defaultStrokeThickness];
+ scale = 1.0f;
+ strokeColorRGB = [[ColorRGB alloc] initWithRed:0 green:0 blue:0];
+ fillColorRGB = [[ColorRGB alloc] initWithRed:255 green:255 blue:255];
+
+ name = nm;
+ category = nil;
+ shapeName = SHAPE_CIRCLE;
+ }
+ return self;
+}
+
+- (id)init {
+ self = [self initWithName:@"new"];
+ return self;
+}
+
+- (id)copyWithZone:(NSZone*)zone {
+ NodeStyle *style = [[NodeStyle allocWithZone:zone] init];
+
+ [style setStrokeThickness:[self strokeThickness]];
+ [style setScale:[self scale]];
+ [style setStrokeColorRGB:[self strokeColorRGB]];
+ [style setFillColorRGB:[self fillColorRGB]];
+ [style setName:[self name]];
+ [style setShapeName:[self shapeName]];
+ [style setCategory:[self category]];
+
+ return style;
+}
+
+- (void)dealloc {
+#if ! __has_feature(objc_arc)
+ [name release];
+ [category release];
+ [shapeName release];
+ [strokeColorRGB release];
+ [fillColorRGB release];
+ [super dealloc];
+#endif
+}
+
+- (NSString*) description {
+ return [NSString stringWithFormat:@"Node style \"%@\"", name];
+}
+
+- (void) updateFromStyle:(NodeStyle*)style {
+ [self setStrokeThickness:[style strokeThickness]];
+ [self setScale:[style scale]];
+ [self setStrokeColorRGB:[style strokeColorRGB]];
+ [self setFillColorRGB:[style fillColorRGB]];
+ [self setName:[style name]];
+ [self setShapeName:[style shapeName]];
+ [self setCategory:[style category]];
+}
+
++ (NodeStyle*)defaultNodeStyleWithName:(NSString*)nm {
+#if __has_feature(objc_arc)
+ return [[NodeStyle alloc] initWithName:nm];
+#else
+ return [[[NodeStyle alloc] initWithName:nm] autorelease];
+#endif
+}
+
+- (NSString*)name {
+ return name;
+}
+
+- (void)setName:(NSString *)s {
+ if (name != s) {
+ NSString *oldValue = name;
+ name = [s copy];
+ [self postPropertyChanged:@"name" oldValue:oldValue];
+#if ! __has_feature(objc_arc)
+ [oldValue release];
+#endif
+ }
+}
+
+- (NSString*)shapeName {
+ return shapeName;
+}
+
+- (void)setShapeName:(NSString *)s {
+ if (shapeName != s) {
+ NSString *oldValue = shapeName;
+ shapeName = [s copy];
+ [self postPropertyChanged:@"shapeName" oldValue:oldValue];
+#if ! __has_feature(objc_arc)
+ [oldValue release];
+#endif
+ }
+}
+
+- (NSString*)category {
+ return category;
+}
+
+- (void)setCategory:(NSString *)s {
+ if (category != s) {
+ NSString *oldValue = category;
+ category = [s copy];
+ [self postPropertyChanged:@"category" oldValue:oldValue];
+#if ! __has_feature(objc_arc)
+ [oldValue release];
+#endif
+ }
+}
+
+- (int)strokeThickness { return strokeThickness; }
+- (void)setStrokeThickness:(int)i {
+ int oldValue = strokeThickness;
+ strokeThickness = i;
+ [self postPropertyChanged:@"strokeThickness" oldValue:[NSNumber numberWithInt:oldValue]];
+}
+
+- (float)scale { return scale; }
+- (void)setScale:(float)s {
+ float oldValue = scale;
+ scale = s;
+ [self postPropertyChanged:@"scale" oldValue:[NSNumber numberWithFloat:oldValue]];
+}
+
+- (ColorRGB*)strokeColorRGB {
+ return strokeColorRGB;
+}
+
+- (void)setStrokeColorRGB:(ColorRGB*)c {
+ if (strokeColorRGB != c) {
+ ColorRGB *oldValue = strokeColorRGB;
+ strokeColorRGB = [c copy];
+ [self postPropertyChanged:@"strokeColorRGB" oldValue:oldValue];
+#if ! __has_feature(objc_arc)
+ [oldValue release];
+#endif
+ }
+}
+
+- (ColorRGB*)fillColorRGB {
+ return fillColorRGB;
+}
+
+- (void)setFillColorRGB:(ColorRGB*)c {
+ if (fillColorRGB != c) {
+ ColorRGB *oldValue = fillColorRGB;
+ fillColorRGB = [c copy];
+ [self postPropertyChanged:@"fillColorRGB" oldValue:oldValue];
+#if ! __has_feature(objc_arc)
+ [oldValue release];
+#endif
+ }
+}
+
+- (NSString*)tikz {
+ NSString *fillName = [fillColorRGB name];
+ NSString *strokeName = [strokeColorRGB name];
+ NSString *stroke = @"";
+ if (strokeThickness != 1) {
+ stroke = [NSString stringWithFormat:@",line width=%@ pt",
+ [NSNumber numberWithFloat:(float)strokeThickness * 0.4f]];
+ }
+
+ // If the colors are unknown, fall back on hexnames. These should be defined as colors
+ // in the Preambles class.
+ if (fillName == nil) fillName = [fillColorRGB hexName];
+ if (strokeName == nil) strokeName = [strokeColorRGB hexName];
+
+ NSString *shapeDesc = [[Shape shapeForName:shapeName] styleTikz];
+ if (shapeDesc == nil) shapeDesc = shapeName;
+
+ return [NSString stringWithFormat:@"\\tikzstyle{%@}=[%@,fill=%@,draw=%@%@]",
+ name,
+ shapeDesc,
+ fillName,
+ strokeName,
+ stroke];
+}
+
+- (BOOL)strokeColorIsKnown {
+ return ([strokeColorRGB name] != nil);
+}
+
+- (BOOL)fillColorIsKnown {
+ return ([fillColorRGB name] != nil);
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/PickSupport.h b/tikzit-old/src/common/PickSupport.h
new file mode 100644
index 0000000..0749649
--- /dev/null
+++ b/tikzit-old/src/common/PickSupport.h
@@ -0,0 +1,164 @@
+//
+// PickSupport.h
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+
+#import <Foundation/Foundation.h>
+#import "Node.h"
+#import "Edge.h"
+
+/*!
+ @class PickSupport
+ @brief Maintain the selection state of nodes and edges.
+ @detail In addition to the notifications listed for specific methods,
+ whenever the node selection changes, a "NodeSelectionChanged"
+ signal is emitted, and whenever the edge selection changes,
+ an "EdgeSelectionChanged" signal is emitted.
+ */
+@interface PickSupport : NSObject {
+ NSMutableSet *selectedNodes;
+ NSMutableSet *selectedEdges;
+}
+
+/*!
+ @property selectedNodes
+ @brief A set of selected nodes.
+ */
+@property (readonly) NSSet *selectedNodes;
+
+// KVC methods
+- (void)addSelectedNodesObject:(Node*)node;
+- (void)addSelectedNodes:(NSSet*)nodes;
+- (void)removeSelectedNodesObject:(Node*)node;
+- (void)removeSelectedNodes:(NSSet*)nodes;
+
+/*!
+ @property selectedEdges
+ @brief A set of selected edges.
+ */
+@property (readonly) NSSet *selectedEdges;
+
+// KVC methods
+- (void)addSelectedEdgesObject:(Edge*)edge;
+- (void)addSelectedEdges:(NSSet*)edges;
+- (void)removeSelectedEdgesObject:(Edge*)edge;
+- (void)removeSelectedEdges:(NSSet*)edges;
+
+/*!
+ @brief Check if a node is selected.
+ @param nd a node.
+ @result YES if nd is selected.
+ */
+- (BOOL)isNodeSelected:(Node*)nd;
+
+/*!
+ @brief Check if an edge is selected.
+ @param e an edge.
+ @result YES if e is selected.
+ */
+- (BOOL)isEdgeSelected:(Edge*)e;
+
+/*!
+ @brief Select a node.
+ @details Sends the "NodeSelected" notification if the node was not
+ already selected, with @p nd as "node" in the userInfo
+ @param nd a node.
+ */
+- (void)selectNode:(Node*)nd;
+
+/*!
+ @brief Deselect a node.
+ @details Sends the "NodeDeselected" notification if the node was
+ selected, with @p nd as "node" in the userInfo
+ @param nd a node.
+ */
+- (void)deselectNode:(Node*)nd;
+
+/*!
+ @brief Select an edge.
+ @details Sends the "EdgeSelected" notification if the node was not
+ already selected, with @p e as "edge" in the userInfo
+ @param e an edge.
+ */
+- (void)selectEdge:(Edge*)e;
+
+/*!
+ @brief Deselect an edge.
+ @details Sends the "EdgeDeselected" notification if the node was
+ selected, with @p e as "edge" in the userInfo
+ @param e an edge.
+ */
+- (void)deselectEdge:(Edge*)e;
+
+/*!
+ @brief Toggle the selected state of the given node.
+ @details Sends the "NodeSelected" or "NodeDeselected" notification as
+ appropriate, with @p nd as "node" in the userInfo
+ @param nd a node.
+ */
+- (void)toggleNodeSelected:(Node*)nd;
+
+/*!
+ @brief Select all nodes in the given set.
+ @details Sends the "NodeSelectionReplaced" notification if this
+ caused the selection to change.
+
+ Equivalent to selectAllNodes:nodes replacingSelection:YES
+ @param nodes a set of nodes.
+ */
+- (void)selectAllNodes:(NSSet*)nodes;
+
+/*!
+ @brief Select all nodes in the given set.
+ @details Sends the "NodeSelectionReplaced" notification if this
+ caused the selection to change.
+
+ If replace is NO, @p nodes will be added to the existing
+ selection, otherwise it will replace the existing selection.
+ @param nodes a set of nodes.
+ @param replace whether to replace the existing selection
+ */
+- (void)selectAllNodes:(NSSet*)nodes replacingSelection:(BOOL)replace;
+
+/*!
+ @brief Deselect all nodes.
+ @details Sends the "NodeSelectionReplaced" notification if there
+ were any nodes previously selected
+ */
+- (void)deselectAllNodes;
+
+/*!
+ @brief Deselect all edges.
+ @details Sends the "EdgeSelectionReplaced" notification if there
+ were any edges previously selected
+ */
+- (void)deselectAllEdges;
+
+/*!
+ @brief Factory method for getting a new <tt>PickSupport</tt> object.
+ @result An empty <tt>PickSupport</tt>.
+ */
++ (PickSupport*)pickSupport;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/PickSupport.m b/tikzit-old/src/common/PickSupport.m
new file mode 100644
index 0000000..560fc2c
--- /dev/null
+++ b/tikzit-old/src/common/PickSupport.m
@@ -0,0 +1,232 @@
+//
+// PickSupport.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 <http://www.gnu.org/licenses/>.
+//
+
+#import "PickSupport.h"
+
+
+@implementation PickSupport
+
+- (void) postNodeSelectionChanged {
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:@"NodeSelectionChanged"
+ object:self];
+}
+
+- (void) postEdgeSelectionChanged {
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:@"EdgeSelectionChanged"
+ object:self];
+}
+
+- (id) init {
+ self = [super init];
+
+ if (self) {
+#if __has_feature(objc_arc)
+ selectedNodes = [NSMutableSet set];
+ selectedEdges = [NSMutableSet set];
+#else
+ selectedNodes = [[NSMutableSet set] retain];
+ selectedEdges = [[NSMutableSet set] retain];
+#endif
+ }
+
+ return self;
+}
+
++ (PickSupport*)pickSupport {
+#if __has_feature(objc_arc)
+ return [[PickSupport alloc] init];
+#else
+ return [[[PickSupport alloc] init] autorelease];
+#endif
+}
+
+@synthesize selectedNodes;
+- (void)addSelectedNodesObject:(Node*)node {
+ return [self selectNode:node];
+}
+- (void)addSelectedNodes:(NSSet*)nodes {
+ return [self selectAllNodes:nodes replacingSelection:NO];
+}
+- (void)removeSelectedNodesObject:(Node*)node {
+ return [self deselectNode:node];
+}
+- (void)removeSelectedNodes:(NSSet*)nodes {
+ if ([selectedNodes count] > 0) {
+ [selectedNodes minusSet:nodes];
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:@"NodeSelectionReplaced"
+ object:self];
+ [self postNodeSelectionChanged];
+ }
+}
+
+@synthesize selectedEdges;
+- (void)addSelectedEdgesObject:(Edge*)edge {
+ return [self selectEdge:edge];
+}
+- (void)addSelectedEdges:(NSSet*)edges {
+ if (selectedEdges == edges) {
+ return;
+ }
+ if ([edges count] == 0) {
+ return;
+ }
+
+ [selectedEdges unionSet:edges];
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:@"EdgeSelectionReplaced"
+ object:self];
+ [self postEdgeSelectionChanged];
+}
+- (void)removeSelectedEdgesObject:(Edge*)edge {
+ return [self deselectEdge:edge];
+}
+- (void)removeSelectedEdges:(NSSet*)edges {
+ if ([selectedEdges count] > 0 && [edges count] > 0) {
+ [selectedEdges minusSet:edges];
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:@"EdgeSelectionReplaced"
+ object:self];
+ [self postEdgeSelectionChanged];
+ }
+}
+
+- (BOOL)isNodeSelected:(Node*)nd {
+ return [selectedNodes containsObject:nd];
+}
+
+- (BOOL)isEdgeSelected:(Edge*)e {
+ return [selectedEdges containsObject:e];
+}
+
+- (void)selectNode:(Node*)nd {
+ if (nd != nil && ![selectedNodes member:nd]) {
+ [selectedNodes addObject:nd];
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:@"NodeSelected"
+ object:self
+ userInfo:[NSDictionary dictionaryWithObject:nd forKey:@"node"]];
+ [self postNodeSelectionChanged];
+ }
+}
+
+- (void)deselectNode:(Node*)nd {
+ if (nd != nil && [selectedNodes member:nd]) {
+ [selectedNodes removeObject:nd];
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:@"NodeDeselected"
+ object:self
+ userInfo:[NSDictionary dictionaryWithObject:nd forKey:@"node"]];
+ [self postNodeSelectionChanged];
+ }
+}
+
+- (void)selectEdge:(Edge*)e {
+ if (e != nil && ![selectedEdges member:e]) {
+ [selectedEdges addObject:e];
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:@"EdgeSelected"
+ object:self
+ userInfo:[NSDictionary dictionaryWithObject:e forKey:@"edge"]];
+ [self postEdgeSelectionChanged];
+ }
+}
+
+- (void)deselectEdge:(Edge*)e {
+ if (e != nil && [selectedEdges member:e]) {
+ [selectedEdges removeObject:e];
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:@"EdgeDeselected"
+ object:self
+ userInfo:[NSDictionary dictionaryWithObject:e forKey:@"edge"]];
+ [self postEdgeSelectionChanged];
+ }
+}
+
+- (void)toggleNodeSelected:(Node*)nd {
+ if ([self isNodeSelected:nd])
+ [self deselectNode:nd];
+ else
+ [self selectNode:nd];
+}
+
+- (void)selectAllNodes:(NSSet*)nodes {
+ [self selectAllNodes:nodes replacingSelection:YES];
+}
+
+- (void)selectAllNodes:(NSSet*)nodes replacingSelection:(BOOL)replace {
+ if (selectedNodes == nodes) {
+ return;
+ }
+ if (!replace && [nodes count] == 0) {
+ return;
+ }
+
+ if (replace) {
+#if ! __has_feature(objc_arc)
+ [selectedNodes release];
+#endif
+ selectedNodes = [nodes mutableCopy];
+ } else {
+ [selectedNodes unionSet:nodes];
+ }
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:@"NodeSelectionReplaced"
+ object:self];
+ [self postNodeSelectionChanged];
+}
+
+- (void)deselectAllNodes {
+ if ([selectedNodes count] > 0) {
+ [selectedNodes removeAllObjects];
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:@"NodeSelectionReplaced"
+ object:self];
+ [self postNodeSelectionChanged];
+ }
+}
+
+- (void)deselectAllEdges {
+ if ([selectedEdges count] > 0) {
+ [selectedEdges removeAllObjects];
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:@"EdgeSelectionReplaced"
+ object:self];
+ [self postEdgeSelectionChanged];
+ }
+}
+
+- (void)dealloc {
+#if ! __has_feature(objc_arc)
+ [selectedNodes release];
+ [selectedEdges release];
+
+ [super dealloc];
+#endif
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/Preambles.h b/tikzit-old/src/common/Preambles.h
new file mode 100644
index 0000000..2fb084a
--- /dev/null
+++ b/tikzit-old/src/common/Preambles.h
@@ -0,0 +1,73 @@
+//
+// Preambles.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+// Copyright 2011 Alex Merry. 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+#import "StyleManager.h"
+
+@class Graph;
+
+@interface Preambles : NSObject {
+ NSMutableDictionary *preambleDict;
+ NSString *selectedPreambleName;
+ NSArray *styles;
+ NSArray *edges;
+ StyleManager *styleManager;
+}
+
+@property (copy) NSString *selectedPreambleName;
+@property (retain) NSString *currentPreamble;
+@property (retain) StyleManager *styleManager;
+@property (readonly) NSMutableDictionary *preambleDict;
+
++ (Preambles*)preambles;
+- (id)init;
+- (void)setStyles:(NSArray*)sty;
+- (void)setEdges:(NSArray*)edg;
+
+- (NSString*)preambleForName:(NSString*)name;
+- (BOOL)setPreamble:(NSString*)content forName:(NSString*)name;
+
+- (NSString*)addPreamble;
+- (NSString*)addPreambleWithNameBase:(NSString*)name;
+
+- (BOOL)renamePreambleFrom:(NSString*)old to:(NSString*)new;
+- (BOOL)removePreamble:(NSString*)name;
+
+- (NSEnumerator*)customPreambleNameEnumerator;
+
+- (void)removeAllPreambles;
+
+- (BOOL)selectedPreambleIsDefault;
+
+- (NSString*)styleDefinitions;
+- (NSString*)defaultPreamble;
+- (NSString*)defaultPreambleName;
+- (NSString*)currentPostamble;
+
+- (NSString*)buildDocumentForTikz:(NSString*)tikz;
+- (NSString*)buildDocumentForGraph:(Graph*)g;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/Preambles.m b/tikzit-old/src/common/Preambles.m
new file mode 100644
index 0000000..922fc30
--- /dev/null
+++ b/tikzit-old/src/common/Preambles.m
@@ -0,0 +1,320 @@
+//
+// Preambles.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 <http://www.gnu.org/licenses/>.
+//
+
+#import "Preambles.h"
+#import "NodeStyle.h"
+#import "EdgeStyle.h"
+#import "Graph.h"
+
+static NSString *DEF_PREAMBLE_START =
+@"\\usepackage[svgnames]{xcolor}\n"
+@"\\usepackage{tikz}\n"
+@"\\usetikzlibrary{decorations.markings}\n"
+@"\\usetikzlibrary{shapes.geometric}\n"
+@"\n"
+@"\\pgfdeclarelayer{edgelayer}\n"
+@"\\pgfdeclarelayer{nodelayer}\n"
+@"\\pgfsetlayers{edgelayer,nodelayer,main}\n"
+@"\n"
+@"\\tikzstyle{none}=[inner sep=0pt]\n";
+
+static NSString *PREAMBLE_TAIL =
+@"\n"
+@"\\pagestyle{empty}\n"
+@"\\usepackage[graphics,tightpage,active]{preview}\n"
+@"\\PreviewEnvironment{tikzpicture}\n"
+@"\\newlength{\\imagewidth}\n"
+@"\\newlength{\\imagescale}\n"
+@"\n"
+@"\\begin{document}\n";
+
+static NSString *POSTAMBLE =
+@"\n"
+@"\\end{document}\n";
+
+@implementation Preambles
+
++ (Preambles*)preambles {
+#if __has_feature(objc_arc)
+ return [[self alloc] init];
+#else
+ return [[[self alloc] init] autorelease];
+#endif
+}
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ selectedPreambleName = @"default";
+ preambleDict = [[NSMutableDictionary alloc] initWithCapacity:1];
+ [preambleDict setObject:[self defaultPreamble] forKey:@"custom"];
+ styles = nil;
+ edges = nil;
+ styleManager = nil;
+ }
+ return self;
+}
+
+- (void)dealloc {
+#if ! __has_feature(objc_arc)
+ [selectedPreambleName release];
+ [styles release];
+ [styleManager release];
+ [super dealloc];
+#endif
+}
+
+- (NSString*)preambleForName:(NSString*)name {
+ if ([name isEqualToString:@"default"])
+ return [self defaultPreamble];
+ else
+ return [preambleDict objectForKey:name];
+}
+
+- (BOOL)setPreamble:(NSString*)content forName:(NSString*)name {
+ if ([name isEqualToString:@"default"])
+ return NO;
+ [preambleDict setObject:content forKey:name];
+ return YES;
+}
+
+- (void)removeAllPreambles {
+ [preambleDict removeAllObjects];
+}
+
+- (NSEnumerator*)customPreambleNameEnumerator {
+ return [preambleDict keyEnumerator];
+}
+
+- (void)setStyles:(NSArray*)sty {
+#if ! __has_feature(objc_arc)
+ [sty retain];
+ [styles release];
+#endif
+ styles = sty;
+}
+
+- (void)setEdges:(NSArray*)edg {
+#if ! __has_feature(objc_arc)
+ [edg retain];
+ [edges release];
+#endif
+ edges = edg;
+}
+
+- (NSString*)styleDefinitions {
+ if (styleManager != nil) {
+ [self setStyles:[styleManager nodeStyles]];
+ }
+#if ! __has_feature(objc_arc)
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+#endif
+ NSMutableString *buf = [NSMutableString string];
+ NSMutableString *colbuf = [NSMutableString string];
+ NSMutableSet *colors = [NSMutableSet setWithCapacity:2*[styles count]];
+ for (NodeStyle *st in styles) {
+ [buf appendFormat:@"%@\n", [st tikz]];
+ ColorRGB *fill = [st fillColorRGB];
+ ColorRGB *stroke = [st strokeColorRGB];
+ if ([fill name] == nil && ![colors containsObject:fill]) {
+ [colors addObject:fill];
+ [colbuf appendFormat:@"\\definecolor{%@}{rgb}{%.3f,%.3f,%.3f}\n",
+ [fill hexName], [fill redFloat], [fill greenFloat], [fill blueFloat]];
+ }
+
+ if ([stroke name] == nil && ![colors containsObject:stroke]) {
+ [colors addObject:stroke];
+ [colbuf appendFormat:@"\\definecolor{%@}{rgb}{%.3f,%.3f,%.3f}\n",
+ [stroke hexName], [stroke redFloat], [stroke greenFloat], [stroke blueFloat]];
+ }
+ }
+
+ if (styleManager != nil) {
+ [self setEdges:[styleManager edgeStyles]];
+ }
+
+ [buf appendString:@"\n"];
+ for (EdgeStyle *st in edges) {
+ [buf appendFormat:@"%@\n", [st tikz]];
+ ColorRGB *color = [st colorRGB];
+ if (color != nil && [color name] == nil && ![colors containsObject:color]) {
+ [colors addObject:color];
+ [colbuf appendFormat:@"\\definecolor{%@}{rgb}{%.3f,%.3f,%.3f}\n",
+ [color hexName], [color redFloat], [color greenFloat], [color blueFloat]];
+ }
+ }
+
+ NSString *defs = [[NSString alloc] initWithFormat:@"%@\n%@", colbuf, buf];
+
+#if __has_feature(objc_arc)
+ return defs;
+#else
+ [pool drain];
+ return [defs autorelease];
+#endif
+}
+
+- (NSString*)defaultPreamble {
+ return [NSString stringWithFormat:@"%@%@",
+ DEF_PREAMBLE_START, [self styleDefinitions]];
+}
+
+- (BOOL)selectedPreambleIsDefault {
+ return [selectedPreambleName isEqualToString:@"default"];
+}
+
+- (NSString*)selectedPreambleName { return selectedPreambleName; }
+- (void)setSelectedPreambleName:(NSString *)sel {
+ if (sel != selectedPreambleName) {
+#if ! __has_feature(objc_arc)
+ [selectedPreambleName release];
+#endif
+ selectedPreambleName = [sel copy];
+ }
+}
+
+- (NSString*)currentPreamble {
+ NSString *pre = [self preambleForName:selectedPreambleName];
+ return (pre == nil) ? [self defaultPreamble] : pre;
+}
+
+- (void)setCurrentPreamble:(NSString*)str {
+ if (![selectedPreambleName isEqualToString:@"default"])
+ [preambleDict setObject:str forKey:selectedPreambleName];
+}
+
+- (StyleManager*)styleManager {
+ return styleManager;
+}
+
+- (void)setStyleManager:(StyleManager *)manager {
+#if ! __has_feature(objc_arc)
+ [manager retain];
+ [styleManager release];
+#endif
+ styleManager = manager;
+}
+
+- (NSString*)currentPostamble {
+ return POSTAMBLE;
+}
+
+- (NSMutableDictionary*)preambleDict {
+ return preambleDict;
+}
+
+- (NSString*)defaultPreambleName {
+ return @"default";
+}
+
+- (NSString*)addPreamble {
+ return [self addPreambleWithNameBase:@"new preamble"];
+}
+
+- (NSString*)addPreambleWithNameBase:(NSString*)base {
+ if ([preambleDict objectForKey:base] == nil) {
+ [self setPreamble:[self defaultPreamble] forName:base];
+ return base;
+ }
+ int i = 0;
+ NSString *tryName = nil;
+ do {
+ ++i;
+ tryName = [NSString stringWithFormat:@"%@ %d", base, i];
+ } while ([preambleDict objectForKey:tryName] != nil);
+
+ [self setPreamble:[self defaultPreamble] forName:tryName];
+ return tryName;
+}
+
+- (BOOL)renamePreambleFrom:(NSString*)old to:(NSString*)new {
+ if ([old isEqualToString:@"default"])
+ return NO;
+ if ([new isEqualToString:@"default"])
+ return NO;
+ if ([old isEqualToString:new])
+ return YES;
+ BOOL isSelected = NO;
+ if ([old isEqualToString:selectedPreambleName]) {
+ [self setSelectedPreambleName:nil];
+ isSelected = YES;
+ }
+ NSString *preamble = [preambleDict objectForKey:old];
+#if ! __has_feature(objc_arc)
+ [preamble retain];
+#endif
+ [preambleDict removeObjectForKey:old];
+ [preambleDict setObject:preamble forKey:new];
+#if ! __has_feature(objc_arc)
+ [preamble release];
+#endif
+ if (isSelected) {
+ [self setSelectedPreambleName:new];
+ }
+ return YES;
+}
+
+- (BOOL)removePreamble:(NSString*)name {
+ if ([name isEqualToString:@"default"])
+ return NO;
+ // "name" may be held only by being the selected preamble...
+#if ! __has_feature(objc_arc)
+ [name retain];
+#endif
+ if ([name isEqualToString:selectedPreambleName])
+ [self setSelectedPreambleName:nil];
+ [preambleDict removeObjectForKey:name];
+#if ! __has_feature(objc_arc)
+ [name release];
+#endif
+ return YES;
+}
+
+- (NSString*)buildDocumentForTikz:(NSString*)tikz
+{
+ NSString *preamble = [self currentPreamble];
+ NSString *doc_head = @"";
+ if (![preamble hasPrefix:@"\\documentclass"]) {
+ doc_head = @"\\documentclass{article}\n";
+ }
+ NSString *preamble_suffix = @"";
+ if ([preamble rangeOfString:@"\\begin{document}"
+ options:NSBackwardsSearch].length == 0) {
+ preamble_suffix = PREAMBLE_TAIL;
+ }
+ return [NSString stringWithFormat:@"%@%@%@%@%@",
+ doc_head,
+ [self currentPreamble],
+ preamble_suffix,
+ tikz,
+ POSTAMBLE];
+}
+
+- (NSString*)buildDocumentForGraph:(Graph*)g
+{
+ return [self buildDocumentForTikz:[g tikz]];
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/PropertyHolder.h b/tikzit-old/src/common/PropertyHolder.h
new file mode 100644
index 0000000..ba1d825
--- /dev/null
+++ b/tikzit-old/src/common/PropertyHolder.h
@@ -0,0 +1,36 @@
+//
+// PropertyHolder.h
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface PropertyHolder : NSObject {
+ NSString *notificationName;
+}
+
+- (id)initWithNotificationName:(NSString*)name;
+- (void) postPropertyChanged:(NSString*)property oldValue:(id)value;
+- (void) postPropertyChanged:(NSString*)property;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/PropertyHolder.m b/tikzit-old/src/common/PropertyHolder.m
new file mode 100644
index 0000000..6aaf125
--- /dev/null
+++ b/tikzit-old/src/common/PropertyHolder.m
@@ -0,0 +1,74 @@
+//
+// PropertyHolder.m
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import "PropertyHolder.h"
+
+@implementation PropertyHolder
+
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ notificationName = @"UnknownPropertyChanged";
+ }
+ return self;
+}
+
+- (id)initWithNotificationName:(NSString*)n {
+ self = [super init];
+ if (self) {
+ notificationName = [n copy];
+ }
+ return self;
+}
+
+- (void)postPropertyChanged:(NSString*)property oldValue:(id)value {
+ NSDictionary *userInfo;
+ if (value != nil) {
+ userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ property, @"propertyName",
+ value, @"oldValue",
+ nil];
+ } else {
+ userInfo = [NSDictionary dictionaryWithObject:property
+ forKey:@"propertyName"];
+ }
+ [[NSNotificationCenter defaultCenter] postNotificationName:notificationName
+ object:self
+ userInfo:userInfo];
+}
+
+- (void)postPropertyChanged:(NSString*)property {
+ [self postPropertyChanged:property oldValue:nil];
+}
+
+- (void)dealloc {
+#if ! __has_feature(objc_arc)
+ [notificationName release];
+ [super dealloc];
+#endif
+}
+
+@end
+
+// vi:ft=objc:ts=4:et:sts=4:sw=4
diff --git a/tikzit-old/src/common/RColor.h b/tikzit-old/src/common/RColor.h
new file mode 100644
index 0000000..7f22547
--- /dev/null
+++ b/tikzit-old/src/common/RColor.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+
+#ifndef CGFloat
+#define CGFloat float
+#endif
+
+/**
+ * A lightweight color structure used by RenderContext
+ *
+ * This is mainly to avoid the overhead of ColorRGB when
+ * rendering things not based on a NodeStyle
+ *
+ * All values range from 0.0f to 1.0f.
+ */
+typedef struct {
+ CGFloat red;
+ CGFloat green;
+ CGFloat blue;
+ CGFloat alpha;
+}
+RColor;
+
+/** Solid white */
+static const RColor WhiteRColor __attribute__((unused)) = {1.0, 1.0, 1.0, 1.0};
+/** Solid black */
+static const RColor BlackRColor __attribute__((unused)) = {0.0, 0.0, 0.0, 1.0};
+
+/** Create a color with alpha set to 1.0 */
+RColor MakeSolidRColor (CGFloat red, CGFloat green, CGFloat blue);
+/** Create a color */
+RColor MakeRColor (CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha);
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/RColor.m b/tikzit-old/src/common/RColor.m
new file mode 100644
index 0000000..49914fe
--- /dev/null
+++ b/tikzit-old/src/common/RColor.m
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2011 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "RColor.h"
+
+RColor MakeSolidRColor (CGFloat red, CGFloat green, CGFloat blue) {
+ return MakeRColor (red, green, blue, 1.0);
+}
+
+RColor MakeRColor (CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) {
+ RColor color;
+ color.red = red;
+ color.green = green;
+ color.blue = blue;
+ color.alpha = alpha;
+ return color;
+}
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/RectangleShape.h b/tikzit-old/src/common/RectangleShape.h
new file mode 100644
index 0000000..3fa0f31
--- /dev/null
+++ b/tikzit-old/src/common/RectangleShape.h
@@ -0,0 +1,33 @@
+//
+// RectangleShape.h
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+#import "Shape.h"
+
+
+@interface RectangleShape : Shape {
+}
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/RectangleShape.m b/tikzit-old/src/common/RectangleShape.m
new file mode 100644
index 0000000..db9c803
--- /dev/null
+++ b/tikzit-old/src/common/RectangleShape.m
@@ -0,0 +1,57 @@
+//
+// RectangleShape.m
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import "RectangleShape.h"
+#import "Node.h"
+#import "Edge.h"
+
+@implementation RectangleShape
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ Node *n0,*n1,*n2,*n3;
+ float sz = 0.2f;
+
+ n0 = [Node nodeWithPoint:NSMakePoint(-sz, sz)];
+ n1 = [Node nodeWithPoint:NSMakePoint( sz, sz)];
+ n2 = [Node nodeWithPoint:NSMakePoint( sz,-sz)];
+ n3 = [Node nodeWithPoint:NSMakePoint(-sz,-sz)];
+
+ Edge *e0,*e1,*e2,*e3;
+
+ e0 = [Edge edgeWithSource:n0 andTarget:n1];
+ e1 = [Edge edgeWithSource:n1 andTarget:n2];
+ e2 = [Edge edgeWithSource:n2 andTarget:n3];
+ e3 = [Edge edgeWithSource:n3 andTarget:n0];
+
+ paths = [[NSSet alloc] initWithObjects:[NSArray arrayWithObjects:e0,e1,e2,e3,nil],nil];
+
+ styleTikz = @"rectangle";
+ }
+ return self;
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/RegularPolyShape.h b/tikzit-old/src/common/RegularPolyShape.h
new file mode 100644
index 0000000..1fd8f1e
--- /dev/null
+++ b/tikzit-old/src/common/RegularPolyShape.h
@@ -0,0 +1,50 @@
+//
+// RegularPolyShape.h
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+#import "Shape.h"
+
+/**
+ * A regular polygon
+ *
+ * Matches the "regular polygon" shape in the shapes.geometric
+ * PGF/TikZ library.
+ */
+@interface RegularPolyShape : Shape {
+}
+
+/**
+ * Initialise a regular polygon
+ *
+ * A rotation of 0 will produce a polygon with one
+ * edge flat along the bottom (just like PGF/TikZ
+ * does it).
+ *
+ * @param sides the number of sides the polygon should have
+ * @param rotation the rotation of the polygon, in degrees
+ */
+- (id)initWithSides:(int)sides rotation:(int)rotation;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/RegularPolyShape.m b/tikzit-old/src/common/RegularPolyShape.m
new file mode 100644
index 0000000..3555115
--- /dev/null
+++ b/tikzit-old/src/common/RegularPolyShape.m
@@ -0,0 +1,76 @@
+//
+// RegularPolyShape.m
+// TikZiT
+//
+// Copyright 2011 Aleks Kissinger
+// Copyright 2012 Alex Merry
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#import "RegularPolyShape.h"
+#import "Node.h"
+#import "Edge.h"
+#import "util.h"
+
+@implementation RegularPolyShape
+
+- (id)initWithSides:(int)sides rotation:(int)rotation {
+ self = [super init];
+ if (self == nil)
+ return nil;
+
+ // TikZ draws regular polygons using a radius inscribed
+ // _inside_ the shape (touching middles of edges), not
+ // outside (touching points)
+ const float innerRadius = 0.2f;
+
+ NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:sides];
+ NSMutableArray *edges = [NSMutableArray arrayWithCapacity:sides];
+
+ float dtheta = (M_PI * 2.0f) / ((float)sides);
+ float theta = (dtheta/2.0f) - (M_PI / 2.0f);
+ theta += degreesToRadians(rotation);
+ // radius of the outer circle
+ float radius = ABS(innerRadius / cos(dtheta));
+
+ for (int i = 0; i < sides; ++i) {
+ NSPoint p;
+ p.x = radius * cos(theta);
+ p.y = radius * sin(theta);
+
+ [nodes addObject:[Node nodeWithPoint:p]];
+ theta += dtheta;
+ }
+
+ for (int i = 0; i < sides; ++i) {
+ [edges addObject:[Edge edgeWithSource:[nodes objectAtIndex:i]
+ andTarget:[nodes objectAtIndex:(i+1)%sides]]];
+ }
+
+ paths = [[NSSet alloc] initWithObjects:edges,nil];
+
+ styleTikz = [[NSString alloc] initWithFormat:
+ @"regular polygon,regular polygon sides=%d,shape border rotate=%d",
+ sides, rotation];
+
+ return self;
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/RenderContext.h b/tikzit-old/src/common/RenderContext.h
new file mode 100644
index 0000000..8633944
--- /dev/null
+++ b/tikzit-old/src/common/RenderContext.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2011 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+#import "RColor.h"
+
+typedef enum {
+ AntialiasDisabled,
+ AntialiasDefault
+} AntialiasMode;
+
+// encapsulates a CTLine on OSX and
+// a PangoLayout in GTK+
+@protocol TextLayout
+@property (readonly) NSSize size;
+@property (readonly) NSString *text;
+- (void) showTextAt:(NSPoint)topLeft withColor:(RColor)color;
+@end
+
+@protocol RenderContext
+- (void) saveState;
+- (void) restoreState;
+
+- (NSRect) clipBoundingBox;
+- (BOOL) strokeIncludesPoint:(NSPoint)p;
+- (BOOL) fillIncludesPoint:(NSPoint)p;
+- (id<TextLayout>) layoutText:(NSString*)text withSize:(CGFloat)fontSize;
+
+// this may not affect text rendering
+- (void) setAntialiasMode:(AntialiasMode)mode;
+- (void) setLineWidth:(CGFloat)width;
+// setting to 0 will unset the dash
+- (void) setLineDash:(CGFloat)dashLength;
+
+/**
+ * Clear the current path, including all subpaths
+ */
+- (void) startPath;
+/**
+ * Close the current subpath
+ */
+- (void) closeSubPath;
+/**
+ * Start a new subpath, and set the current point.
+ *
+ * The point will be the current point and the starting point
+ * for the subpath.
+ */
+- (void) moveTo:(NSPoint)p;
+/**
+ * Add a cubic bezier curve to the current subpath.
+ *
+ * The curve will start at the current point, terminate at end and
+ * be defined by cp1 and cp2.
+ */
+- (void) curveTo:(NSPoint)end withCp1:(NSPoint)cp1 andCp2:(NSPoint)cp2;
+/**
+ * Add a straight line to the current subpath.
+ *
+ * The line will start at the current point, and terminate at end.
+ */
+- (void) lineTo:(NSPoint)end;
+/**
+ * Add a new rectangular subpath.
+ *
+ * The current point is undefined after this call.
+ */
+- (void) rect:(NSRect)rect;
+/**
+ * Add a new circular subpath.
+ *
+ * The current point is undefined after this call.
+ */
+- (void) circleAt:(NSPoint)c withRadius:(CGFloat)r;
+
+/**
+ * Paint along the current path.
+ *
+ * The current line width and dash style will be used,
+ * and the colour is given by color.
+ *
+ * The path will be cleared by this call, as though
+ * startPath had been called.
+ */
+- (void) strokePathWithColor:(RColor)color;
+/**
+ * Paint inside the current path.
+ *
+ * The fill colour is given by color.
+ *
+ * The path will be cleared by this call, as though
+ * startPath had been called.
+ */
+- (void) fillPathWithColor:(RColor)color;
+/**
+ * Paint along and inside the current path.
+ *
+ * The current line width and dash style will be used,
+ * and the colour is given by color.
+ *
+ * The path will be cleared by this call, as though
+ * startPath had been called.
+ *
+ * Note that the fill and stroke may overlap, although
+ * the stroke is always painted on top, so this is only
+ * relevant when the stroke colour has an alpha channel
+ * other than 1.0f.
+ */
+- (void) strokePathWithColor:(RColor)scolor
+ andFillWithColor:(RColor)fcolor;
+/**
+ * Paint along and inside the current path using an alpha channel.
+ *
+ * The current line width and dash style will be used,
+ * and the colour is given by color.
+ *
+ * The path will be cleared by this call, as though
+ * startPath had been called.
+ *
+ * Note that the fill and stroke may overlap, although
+ * the stroke is always painted on top, so this is only
+ * relevant when the stroke colour has an alpha channel
+ * other than 1.0f.
+ */
+- (void) strokePathWithColor:(RColor)scolor
+ andFillWithColor:(RColor)fcolor
+ usingAlpha:(CGFloat)alpha;
+/**
+ * Set the clip to the current path.
+ *
+ * The path will be cleared by this call, as though
+ * startPath had been called.
+ */
+- (void) clipToPath;
+
+/**
+ * Paint everywhere within the clip.
+ */
+- (void) paintWithColor:(RColor)color;
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/Shape.h b/tikzit-old/src/common/Shape.h
new file mode 100644
index 0000000..b401a87
--- /dev/null
+++ b/tikzit-old/src/common/Shape.h
@@ -0,0 +1,49 @@
+//
+// Shape.h
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+#import "Transformer.h"
+
+@interface Shape : NSObject <NSCopying> {
+ NSSet *paths;
+ NSRect boundingRect; // cache
+ NSString *styleTikz;
+}
+
+@property (retain) NSSet *paths;
+@property (readonly) NSRect boundingRect;
+/**
+ * The tikz code to use in style properties for this shape
+ *
+ * This can return nil, in which case the shape name should be used
+ */
+@property (retain) NSString *styleTikz;
+
+- (id)init;
++ (void)refreshShapeDictionary;
++ (NSDictionary*)shapeDictionary;
++ (Shape*)shapeForName:(NSString*)shapeName;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/Shape.m b/tikzit-old/src/common/Shape.m
new file mode 100644
index 0000000..e887688
--- /dev/null
+++ b/tikzit-old/src/common/Shape.m
@@ -0,0 +1,171 @@
+//
+// Shape.m
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import "Shape.h"
+
+#import "Edge.h"
+#import "SupportDir.h"
+#import "ShapeNames.h"
+
+#import "CircleShape.h"
+#import "DiamondShape.h"
+#import "RectangleShape.h"
+#import "RegularPolyShape.h"
+#import "TikzShape.h"
+
+#import "util.h"
+
+@implementation Shape
+
+- (void)calcBoundingRect {
+ boundingRect = NSZeroRect;
+
+ if (paths == nil)
+ return;
+
+ for (NSArray *arr in paths) {
+ for (Edge *e in arr) {
+ boundingRect = NSUnionRect(boundingRect, [e boundingRect]);
+ }
+ }
+}
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ paths = nil;
+ }
+ return self;
+}
+
+- (NSSet*)paths {return paths;}
+- (void)setPaths:(NSSet *)p {
+ if (paths != p) {
+#if __has_feature(objc_arc)
+ paths = p;
+#else
+ [paths release];
+ paths = [p retain];
+#endif
+ [self calcBoundingRect];
+ }
+}
+
+- (NSRect)boundingRect { return boundingRect; }
+
+@synthesize styleTikz;
+
+- (id)copyWithZone:(NSZone*)zone {
+ Shape *cp = [[[self class] allocWithZone:zone] init];
+ [cp setPaths:paths];
+ [cp setStyleTikz:styleTikz];
+ return cp;
+}
+
+- (void)dealloc {
+#if ! __has_feature(objc_arc)
+ [paths release];
+ [styleTikz release];
+ [super dealloc];
+#endif
+}
+
+NSDictionary *shapeDictionary = nil;
+
++ (void)addShapesInDir:(NSString*)shapeDir to:(NSMutableDictionary*)shapeDict {
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ NSError *err = nil;
+ NSArray *files = [fileManager contentsOfDirectoryAtPath:shapeDir error:&err];
+
+ if (files != nil) {
+ NSString *nm;
+ for (NSString *f in files) {
+ if ([f hasSuffix:@".tikz"]) {
+ nm = [f substringToIndex:[f length]-5];
+ TikzShape *sh =
+ [[TikzShape alloc] initWithTikzFile:
+ [shapeDir stringByAppendingPathComponent:f]];
+ if (sh != nil) {
+ [shapeDict setObject:sh forKey:nm];
+#if ! __has_feature(objc_arc)
+ [sh release];
+#endif
+ }
+ }
+ }
+ }
+}
+
++ (void)refreshShapeDictionary {
+ Shape *shapes[5] = {
+ [[CircleShape alloc] init],
+ [[RectangleShape alloc] init],
+ [[DiamondShape alloc] init],
+ [[RegularPolyShape alloc] initWithSides:3 rotation:0],
+ [[RegularPolyShape alloc] initWithSides:3 rotation:180]};
+ NSMutableDictionary *shapeDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
+ shapes[0], SHAPE_CIRCLE,
+ shapes[1], SHAPE_RECTANGLE,
+ shapes[2], SHAPE_DIAMOND,
+ shapes[3], SHAPE_UP_TRIANGLE,
+ shapes[4], SHAPE_DOWN_TRIANGLE,
+ nil];
+#if ! __has_feature(objc_arc)
+ for (int i = 0; i<5; ++i) [shapes[i] release];
+#endif
+
+ NSString *systemShapeDir = [[SupportDir systemSupportDir] stringByAppendingPathComponent:@"shapes"];
+ NSString *userShapeDir = [[SupportDir userSupportDir] stringByAppendingPathComponent:@"shapes"];
+
+ [Shape addShapesInDir:systemShapeDir to:shapeDict];
+ [Shape addShapesInDir:userShapeDir to:shapeDict];
+
+ NSDictionary *oldShapeDictionary = shapeDictionary;
+ shapeDictionary = shapeDict;
+
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:@"ShapeDictionaryReplaced"
+ object:self];
+
+#if ! __has_feature(objc_arc)
+ [oldShapeDictionary release];
+#endif
+}
+
++ (NSDictionary*)shapeDictionary {
+ if (shapeDictionary == nil) [Shape refreshShapeDictionary];
+ return shapeDictionary;
+}
+
++ (Shape*)shapeForName:(NSString*)shapeName {
+ Shape *s = [[[self shapeDictionary] objectForKey:shapeName] copy];
+#if __has_feature(objc_arc)
+ return s;
+#else
+ return [s autorelease];
+#endif
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/ShapeNames.h b/tikzit-old/src/common/ShapeNames.h
new file mode 100644
index 0000000..66ecfb1
--- /dev/null
+++ b/tikzit-old/src/common/ShapeNames.h
@@ -0,0 +1,27 @@
+//
+// ShapeNames.h
+// TikZiT
+//
+// Copyright 2011 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.
+//
+
+#define SHAPE_CIRCLE @"circle"
+#define SHAPE_RECTANGLE @"rectangle"
+#define SHAPE_UP_TRIANGLE @"up triangle"
+#define SHAPE_DOWN_TRIANGLE @"down triangle"
+#define SHAPE_DIAMOND @"diamond"
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/StyleManager.h b/tikzit-old/src/common/StyleManager.h
new file mode 100644
index 0000000..bc920e7
--- /dev/null
+++ b/tikzit-old/src/common/StyleManager.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011 Alex Merry <dev@randomguy3.me.uk>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+#import "NodeStyle.h"
+#import "EdgeStyle.h"
+
+@interface StyleManager: NSObject <NSCopying> {
+ NSMutableArray *nodeStyles;
+ NSMutableArray *edgeStyles;
+}
+
++ (StyleManager*) manager;
+- (id) init;
+
+@property (readonly) NSArray *nodeStyles;
+@property (readonly) NSArray *edgeStyles;
+
+// only for use by loading code
+- (void) _setNodeStyles:(NSMutableArray*)styles;
+- (void) _setEdgeStyles:(NSMutableArray*)styles;
+
+- (NodeStyle*) nodeStyleForName:(NSString*)name;
+- (EdgeStyle*) edgeStyleForName:(NSString*)name;
+
+- (void) addNodeStyle:(NodeStyle*)style;
+- (void) removeNodeStyle:(NodeStyle*)style;
+- (void) addEdgeStyle:(EdgeStyle*)style;
+- (void) removeEdgeStyle:(EdgeStyle*)style;
+
+- (void) updateFromManager:(StyleManager*)manager;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/StyleManager.m b/tikzit-old/src/common/StyleManager.m
new file mode 100644
index 0000000..05c6c86
--- /dev/null
+++ b/tikzit-old/src/common/StyleManager.m
@@ -0,0 +1,378 @@
+/*
+ * Copyright 2011 Alex Merry <dev@randomguy3.me.uk>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "StyleManager.h"
+
+@implementation StyleManager
+
+- (void) nodeStylePropertyChanged:(NSNotification*)n {
+ if ([[[n userInfo] objectForKey:@"propertyName"] isEqual:@"name"]) {
+ NSDictionary *userInfo;
+ userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ [n object], @"style",
+ [[n userInfo] objectForKey:@"oldValue"], @"oldName",
+ nil];
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"NodeStyleRenamed"
+ object:self
+ userInfo:userInfo];
+ }
+}
+
+- (void) ignoreAllNodeStyles {
+ [[NSNotificationCenter defaultCenter]
+ removeObserver:self
+ name:@"NodeStylePropertyChanged"
+ object:nil];
+}
+
+- (void) ignoreNodeStyle:(NodeStyle*)style {
+ [[NSNotificationCenter defaultCenter]
+ removeObserver:self
+ name:@"NodeStylePropertyChanged"
+ object:style];
+}
+
+- (void) listenToNodeStyle:(NodeStyle*)style {
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(nodeStylePropertyChanged:)
+ name:@"NodeStylePropertyChanged"
+ object:style];
+}
+
+- (void) edgeStylePropertyChanged:(NSNotification*)n {
+ if ([[[n userInfo] objectForKey:@"propertyName"] isEqual:@"name"]) {
+ NSDictionary *userInfo;
+ userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ [n object], @"style",
+ [[n userInfo] objectForKey:@"oldValue"], @"oldName",
+ nil];
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"EdgeStyleRenamed"
+ object:self
+ userInfo:userInfo];
+ }
+}
+
+- (void) ignoreAllEdgeStyles {
+ [[NSNotificationCenter defaultCenter]
+ removeObserver:self
+ name:@"EdgeStylePropertyChanged"
+ object:nil];
+}
+
+- (void) ignoreEdgeStyle:(EdgeStyle*)style {
+ [[NSNotificationCenter defaultCenter]
+ removeObserver:self
+ name:@"EdgeStylePropertyChanged"
+ object:style];
+}
+
+- (void) listenToEdgeStyle:(EdgeStyle*)style {
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(edgeStylePropertyChanged:)
+ name:@"EdgeStylePropertyChanged"
+ object:style];
+}
+
++ (StyleManager*) manager {
+#if __has_feature(objc_arc)
+ return [[self alloc] init];
+#else
+ return [[[self alloc] init] autorelease];
+#endif
+}
+
+- (id) init {
+ self = [super init];
+
+ if (self) {
+ // we lazily load the default styles, since they may not be needed
+ nodeStyles = nil;
+ edgeStyles = nil;
+ }
+
+ return self;
+}
+
+- (void) dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+#if ! __has_feature(objc_arc)
+ [nodeStyles release];
+ [edgeStyles release];
+
+ [super dealloc];
+#endif
+}
+
+- (void) loadDefaultEdgeStyles {
+#if ! __has_feature(objc_arc)
+ [edgeStyles release];
+#endif
+ edgeStyles = [[NSMutableArray alloc] initWithCapacity:3];
+
+ EdgeStyle *simple = [EdgeStyle defaultEdgeStyleWithName:@"simple"];
+ [simple setThickness:2.0f];
+ [self listenToEdgeStyle:simple];
+
+ EdgeStyle *arrow = [EdgeStyle defaultEdgeStyleWithName:@"arrow"];
+ [arrow setThickness:2.0f];
+ [arrow setDecorationStyle:ED_Arrow];
+ [self listenToEdgeStyle:arrow];
+
+ EdgeStyle *tick = [EdgeStyle defaultEdgeStyleWithName:@"tick"];
+ [tick setThickness:2.0f];
+ [tick setDecorationStyle:ED_Tick];
+ [self listenToEdgeStyle:tick];
+
+ [edgeStyles addObject:simple];
+ [edgeStyles addObject:arrow];
+ [edgeStyles addObject:tick];
+}
+
+- (void) loadDefaultNodeStyles {
+#if ! __has_feature(objc_arc)
+ [nodeStyles release];
+#endif
+ nodeStyles = [[NSMutableArray alloc] initWithCapacity:3];
+
+ NodeStyle *rn = [NodeStyle defaultNodeStyleWithName:@"rn"];
+ [rn setStrokeThickness:2];
+ [rn setStrokeColorRGB:[ColorRGB colorWithFloatRed:0 green:0 blue:0]];
+ [rn setFillColorRGB:[ColorRGB colorWithFloatRed:1 green:0 blue:0]];
+ [self listenToNodeStyle:rn];
+
+ NodeStyle *gn = [NodeStyle defaultNodeStyleWithName:@"gn"];
+ [gn setStrokeThickness:2];
+ [gn setStrokeColorRGB:[ColorRGB colorWithFloatRed:0 green:0 blue:0]];
+ [gn setFillColorRGB:[ColorRGB colorWithFloatRed:0 green:1 blue:0]];
+ [self listenToNodeStyle:gn];
+
+ NodeStyle *yn = [NodeStyle defaultNodeStyleWithName:@"yn"];
+ [yn setStrokeThickness:2];
+ [yn setStrokeColorRGB:[ColorRGB colorWithFloatRed:0 green:0 blue:0]];
+ [yn setFillColorRGB:[ColorRGB colorWithFloatRed:1 green:1 blue:0]];
+ [self listenToNodeStyle:yn];
+
+ [nodeStyles addObject:rn];
+ [nodeStyles addObject:gn];
+ [nodeStyles addObject:yn];
+}
+
+- (void) postNodeStyleAdded:(NodeStyle*)style {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"NodeStyleAdded"
+ object:self
+ userInfo:[NSDictionary dictionaryWithObject:style forKey:@"style"]];
+}
+
+- (void) postNodeStyleRemoved:(NodeStyle*)style {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"NodeStyleRemoved"
+ object:self
+ userInfo:[NSDictionary dictionaryWithObject:style forKey:@"style"]];
+}
+
+- (void) postEdgeStyleAdded:(EdgeStyle*)style {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"EdgeStyleAdded"
+ object:self
+ userInfo:[NSDictionary dictionaryWithObject:style forKey:@"style"]];
+}
+
+- (void) postEdgeStyleRemoved:(EdgeStyle*)style {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"EdgeStyleRemoved"
+ object:self
+ userInfo:[NSDictionary dictionaryWithObject:style forKey:@"style"]];
+}
+
+- (void) postNodeStylesReplaced {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"NodeStylesReplaced" object:self];
+}
+
+- (void) postEdgeStylesReplaced {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"EdgeStylesReplaced" object:self];
+}
+
+- (NSArray*) nodeStyles {
+ if (nodeStyles == nil) {
+ [self loadDefaultNodeStyles];
+ }
+ return nodeStyles;
+}
+
+- (NSArray*) edgeStyles {
+ if (edgeStyles == nil) {
+ [self loadDefaultEdgeStyles];
+ }
+ return edgeStyles;
+}
+
+- (void) _setNodeStyles:(NSMutableArray*)styles {
+ [self ignoreAllNodeStyles];
+#if ! __has_feature(objc_arc)
+ [nodeStyles release];
+ [styles retain];
+#endif
+ nodeStyles = styles;
+ for (NodeStyle *style in styles) {
+ [self listenToNodeStyle:style];
+ }
+ [self postNodeStylesReplaced];
+}
+
+- (void) _setEdgeStyles:(NSMutableArray*)styles {
+ [self ignoreAllEdgeStyles];
+#if ! __has_feature(objc_arc)
+ [edgeStyles release];
+ [styles retain];
+#endif
+ edgeStyles = styles;
+ for (EdgeStyle *style in styles) {
+ [self listenToEdgeStyle:style];
+ }
+ [self postEdgeStylesReplaced];
+}
+
+- (NodeStyle*) nodeStyleForName:(NSString*)name {
+ for (NodeStyle *s in nodeStyles) {
+ if ([[s name] isEqualToString:name]) {
+ return s;
+ }
+ }
+
+ return nil;
+}
+
+- (void) addNodeStyle:(NodeStyle*)style {
+ if (nodeStyles == nil) {
+ [self loadDefaultNodeStyles];
+ }
+ [nodeStyles addObject:style];
+ [self listenToNodeStyle:style];
+ [self postNodeStyleAdded:style];
+}
+
+- (void) removeNodeStyle:(NodeStyle*)style {
+ if (nodeStyles == nil) {
+ [self loadDefaultNodeStyles];
+ }
+
+ [self ignoreNodeStyle:style];
+#if ! __has_feature(objc_arc)
+ [style retain];
+#endif
+ [nodeStyles removeObject:style];
+ [self postNodeStyleRemoved:style];
+#if ! __has_feature(objc_arc)
+ [style release];
+#endif
+}
+
+- (EdgeStyle*) edgeStyleForName:(NSString*)name {
+ for (EdgeStyle *s in edgeStyles) {
+ if ([[s name] isEqualToString:name]) {
+ return s;
+ }
+ }
+
+ return nil;
+}
+
+- (void) addEdgeStyle:(EdgeStyle*)style {
+ if (edgeStyles == nil) {
+ [self loadDefaultEdgeStyles];
+ }
+ [edgeStyles addObject:style];
+ [self listenToEdgeStyle:style];
+ [self postEdgeStyleAdded:style];
+}
+
+- (void) removeEdgeStyle:(EdgeStyle*)style {
+ if (edgeStyles == nil) {
+ [self loadDefaultEdgeStyles];
+ }
+
+ [self ignoreEdgeStyle:style];
+#if ! __has_feature(objc_arc)
+ [style retain];
+#endif
+ [edgeStyles removeObject:style];
+ [self postEdgeStyleRemoved:style];
+#if ! __has_feature(objc_arc)
+ [style release];
+#endif
+}
+
+- (void) updateFromManager:(StyleManager*)m {
+ NSMutableArray *ns = [NSMutableArray arrayWithCapacity:[[m nodeStyles] count]];
+ for (NodeStyle *style in [m nodeStyles]) {
+ NodeStyle *currentStyle = [self nodeStyleForName:[style name]];
+ if (currentStyle != nil) {
+ [currentStyle updateFromStyle:style];
+ [ns addObject:currentStyle];
+ } else {
+#if __has_feature(objc_arc)
+ [ns addObject:[style copy]];
+#else
+ [ns addObject:[[style copy] autorelease]];
+#endif
+ }
+ }
+ NSMutableArray *es = [NSMutableArray arrayWithCapacity:[[m edgeStyles] count]];
+ for (EdgeStyle *style in [m edgeStyles]) {
+ EdgeStyle *currentStyle = [self edgeStyleForName:[style name]];
+ if (currentStyle != nil) {
+ [currentStyle updateFromStyle:style];
+ [es addObject:currentStyle];
+ } else {
+#if __has_feature(objc_arc)
+ [es addObject:[style copy]];
+#else
+ [es addObject:[[style copy] autorelease]];
+#endif
+ }
+ }
+ [self _setNodeStyles:ns];
+ [self _setEdgeStyles:es];
+}
+
+- (id) copyWithZone:(NSZone*)zone {
+ StyleManager *m = [[StyleManager allocWithZone:zone] init];
+
+ NSMutableArray *ns = [NSMutableArray arrayWithCapacity:[nodeStyles count]];
+ for (NodeStyle *style in nodeStyles) {
+#if __has_feature(objc_arc)
+ [ns addObject:[style copyWithZone:zone]];
+#else
+ [ns addObject:[[style copyWithZone:zone] autorelease]];
+#endif
+ }
+ NSMutableArray *es = [NSMutableArray arrayWithCapacity:[edgeStyles count]];
+ for (EdgeStyle *style in edgeStyles) {
+#if __has_feature(objc_arc)
+ [es addObject:[style copyWithZone:zone]];
+#else
+ [es addObject:[[style copyWithZone:zone] autorelease]];
+#endif
+ }
+ [m _setNodeStyles:ns];
+ [m _setEdgeStyles:es];
+
+ return m;
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/SupportDir.h b/tikzit-old/src/common/SupportDir.h
new file mode 100644
index 0000000..30ccbcb
--- /dev/null
+++ b/tikzit-old/src/common/SupportDir.h
@@ -0,0 +1,36 @@
+//
+// SupportDir.h
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+
+
+@interface SupportDir : NSObject {
+}
+
++ (void)createUserSupportDir;
++ (NSString*)userSupportDir;
++ (NSString*)systemSupportDir;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/SupportDir.m b/tikzit-old/src/common/SupportDir.m
new file mode 100644
index 0000000..22fed1b
--- /dev/null
+++ b/tikzit-old/src/common/SupportDir.m
@@ -0,0 +1,65 @@
+//
+// SupportDir.m
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import "SupportDir.h"
+
+#ifndef __APPLE__
+#import <glib.h>
+#import "stat.h"
+#endif
+
+@implementation SupportDir
+
++ (NSString*)userSupportDir {
+#ifdef __APPLE__
+ return [[NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,NSUserDomainMask,YES)
+ objectAtIndex:0] stringByAppendingPathComponent:@"TikZiT"];
+#else
+ return [NSString stringWithFormat:@"%s/tikzit", g_get_user_config_dir ()];
+#endif
+}
+
++ (NSString*)systemSupportDir {
+#ifdef __APPLE__
+ return [[NSBundle mainBundle] resourcePath];
+#else
+ return @TIKZITSHAREDIR;
+#endif
+}
+
++ (void)createUserSupportDir {
+#ifdef __APPLE__
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ [fileManager createDirectoryAtPath:[SupportDir userSupportDir]
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:NULL];
+#else
+ // NSFileManager is slightly dodgy on Windows
+ g_mkdir_with_parents ([[SupportDir userSupportDir] UTF8String], S_IRUSR | S_IWUSR | S_IXUSR);
+#endif
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/TikzGraphAssembler+Parser.h b/tikzit-old/src/common/TikzGraphAssembler+Parser.h
new file mode 100644
index 0000000..c9391a9
--- /dev/null
+++ b/tikzit-old/src/common/TikzGraphAssembler+Parser.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013 Alex Merry <dev@randomguy3.me.uk>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * TikzGraphAssember+Parser.h
+ *
+ * This file exposes some TikzGraphAssembler functions
+ * that are only of use to the parser.
+ */
+
+#import "TikzGraphAssembler.h"
+
+@interface TikzGraphAssembler (Parser)
+- (Graph*) graph;
+/** Store a node so that it can be looked up by name later */
+- (void) addNodeToMap:(Node*)n;
+/** Get a previously-stored node by name */
+- (Node*) nodeWithName:(NSString*)name;
+- (void) reportError:(const char *)message atLocation:(YYLTYPE*)yylloc;
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/TikzGraphAssembler.h b/tikzit-old/src/common/TikzGraphAssembler.h
new file mode 100644
index 0000000..3403969
--- /dev/null
+++ b/tikzit-old/src/common/TikzGraphAssembler.h
@@ -0,0 +1,115 @@
+//
+// TikzGraphAssembler.h
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+#import "Graph.h"
+
+/**
+ * Parses (a subset of) tikz code and produces the corresponding Graph
+ *
+ * A note on errors:
+ * If parsing fails and a non-NULL error argument is given, it will be
+ * populated with an error with domain TZErrorDomain and code TZ_ERR_PARSE
+ * (see NSError+Tikzit.h).
+ *
+ * This will have a description set, typically something like
+ * "syntax error, unexpected [, expecting ("
+ * It may also have the following keys (it will have all or none of these),
+ * where numbers are stored using NSNumber:
+ * - startLine: the line (starting at 1) containing the first character
+ * of the bad token
+ * - startColumn: the column (starting at 1; tabs count for 1) of the first
+ * character of the bad token
+ * - endLine: the line (starting at 1) containing the last character
+ * of the bad token
+ * - endColumn: the column (starting at 1; tabs count for 1) of the last
+ * character of the bad token
+ * - syntaxString: an excerpt of the input string (typically the contents
+ * from startLine to endLine) providing some context
+ * - tokenOffset: the character offset (starting at 0) of the bad token
+ * within syntaxString
+ * - tokenLength: the character length (including newlines) of the bad token
+ * within syntaxString
+ */
+@interface TikzGraphAssembler : NSObject {
+ const char *tikzStr;
+ Graph *graph;
+ void *scanner;
+ NSMutableDictionary *nodeMap;
+ NSError *lastError;
+}
+
+/**
+ * Parse tikz and place the result in gr
+ *
+ * Note that the graph must be empty; this might be used from an init
+ * method, for example, although don't forget that you can return a
+ * different object in init methods, providing you get the allocation
+ * right.
+ *
+ * @param tikz the tikz string to parse
+ * @param gr the graph to store the result in (must be empty, non-nil)
+ * @param e a location to store an error if parsing fails (may be NULL)
+ * @return YES if parsing succeeded, NO otherwise
+ */
++ (BOOL) parseTikz:(NSString*)tikz forGraph:(Graph*)gr error:(NSError**)e;
+/**
+ * Overload for -[parseTikz:forGraph:error:] with the error set to NULL
+ */
++ (BOOL) parseTikz:(NSString*)tikz forGraph:(Graph*)gr;
+/**
+ * Parse tikz
+ *
+ * @param tikz the tikz string to parse
+ * @param e a location to store an error if parsing fails (may be NULL)
+ * @return a Graph object if parsing succeeded, nil otherwise
+ */
++ (Graph*) parseTikz:(NSString*)tikz error:(NSError**)e;
+/**
+ * Overload for -[parseTikz:error:] with the error set to NULL
+ */
++ (Graph*) parseTikz:(NSString*)tikz;
+/**
+ * Validate a property string or value
+ *
+ * Wraps the string in "{" and "}" and checks it lexes completely; in other
+ * words, makes sure that "{" and "}" are balanced (ignoring escaped versions).
+ * @param tikz the string to validate
+ * @return YES if the string can be used as a property name or value, NO
+ * otherwise
+ */
++ (BOOL)validateTikzPropertyNameOrValue:(NSString*)tikz;
+
+/**
+ * Validate an edge anchor
+ *
+ * Checks that the given string will successfully lex if used as an anchor for
+ * and edge
+ * @param tikz the string to validate
+ * @return YES if the string can be used as an edge anchor, NO otherwise
+ */
++ (BOOL)validateTikzEdgeAnchor:(NSString*)tikz;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/TikzGraphAssembler.m b/tikzit-old/src/common/TikzGraphAssembler.m
new file mode 100644
index 0000000..c5d2811
--- /dev/null
+++ b/tikzit-old/src/common/TikzGraphAssembler.m
@@ -0,0 +1,310 @@
+//
+// TikzGraphAssembler.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 <http://www.gnu.org/licenses/>.
+//
+
+#import "TikzGraphAssembler.h"
+#import "tikzparserdefs.h"
+#import "tikzparser.h"
+#import "TikzGraphAssembler+Parser.h"
+#import "tikzlexer.h"
+#import "NSError+Tikzit.h"
+
+@implementation TikzGraphAssembler
+
+- (id)init {
+#if ! __has_feature(objc_arc)
+ [self release];
+#endif
+ return nil;
+}
+
+- (id)initWithGraph:(Graph*)g {
+ self = [super init];
+ if (self) {
+#if __has_feature(objc_arc)
+ graph = g;
+#else
+ graph = [g retain];
+#endif
+ nodeMap = [[NSMutableDictionary alloc] init];
+ yylex_init (&scanner);
+ yyset_extra(self, scanner);
+ }
+ return self;
+}
+
+- (void)dealloc {
+#if ! __has_feature(objc_arc)
+ [graph release];
+ [nodeMap release];
+ [lastError release];
+ yylex_destroy (scanner);
+ [super dealloc];
+#endif
+}
+
+- (BOOL) parseTikz:(NSString*)t error:(NSError**)error {
+#if ! __has_feature(objc_arc)
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+ tikzStr = [t UTF8String];
+ yy_scan_string(tikzStr, scanner);
+ int result = yyparse(scanner);
+ tikzStr = NULL;
+
+#if ! __has_feature(objc_arc)
+ [pool drain];
+#endif
+
+ if (result == 0) {
+ return YES;
+ } else {
+ if (error) {
+ if (lastError) {
+#if __has_feature(objc_arc)
+ *error = lastError;
+#else
+ *error = [[lastError retain] autorelease];
+#endif
+ } else if (result == 1) {
+ *error = [NSError errorWithMessage:@"Syntax error"
+ code:TZ_ERR_PARSE];
+ } else if (result == 2) {
+ *error = [NSError errorWithMessage:@"Insufficient memory"
+ code:TZ_ERR_PARSE];
+ } else {
+ *error = [NSError errorWithMessage:@"Unknown error"
+ code:TZ_ERR_PARSE];
+ }
+ }
+ return NO;
+ }
+}
+
++ (BOOL) parseTikz:(NSString*)tikz forGraph:(Graph*)gr {
+ return [self parseTikz:tikz forGraph:gr error:NULL];
+}
++ (Graph*) parseTikz:(NSString*)tikz error:(NSError**)e {
+ Graph *gr = [[Graph alloc] init];
+ if ([self parseTikz:tikz forGraph:gr error:e]) {
+#if __has_feature(objc_arc)
+ return gr;
+#else
+ return [gr autorelease];
+#endif
+ } else {
+#if ! __has_feature(objc_arc)
+ [gr release];
+#endif
+ return nil;
+ }
+}
++ (Graph*) parseTikz:(NSString*)tikz {
+ return [self parseTikz:tikz error:NULL];
+}
+
++ (BOOL) parseTikz:(NSString*)tikz forGraph:(Graph*)gr error:(NSError**)error {
+ if([tikz length] == 0) {
+ // empty string -> empty graph
+ return YES;
+ }
+
+ TikzGraphAssembler *assembler = [[self alloc] initWithGraph:gr];
+ BOOL success = [assembler parseTikz:tikz error:error];
+#if ! __has_feature(objc_arc)
+ [assembler release];
+#endif
+ return success;
+}
+
++ (BOOL)validateTikzPropertyNameOrValue:(NSString*)tikz {
+ BOOL valid;
+
+ NSString * testTikz = [NSString stringWithFormat: @"{%@}", tikz];
+
+ void *scanner;
+ yylex_init (&scanner);
+ yyset_extra(nil, scanner);
+ yy_scan_string([testTikz UTF8String], scanner);
+ YYSTYPE lval;
+ YYLTYPE lloc;
+ int result = yylex(&lval, &lloc, scanner);
+ valid = (result == DELIMITEDSTRING) &&
+ (yyget_leng(scanner) == [testTikz length]);
+ yylex_destroy(scanner);
+
+ return valid;
+}
+
++ (BOOL)validateTikzEdgeAnchor:(NSString*)tikz {
+ if ([tikz length] == 0)
+ return YES;
+
+ BOOL valid = YES;
+
+ NSString * testTikz = [NSString stringWithFormat: @"(1.%@)", tikz];
+
+ void *scanner;
+ yylex_init (&scanner);
+ yyset_extra(nil, scanner);
+ yy_scan_string([testTikz UTF8String], scanner);
+ YYSTYPE lval;
+ YYLTYPE lloc;
+ valid = valid && (yylex(&lval, &lloc, scanner) == LEFTPARENTHESIS);
+ valid = valid && (yylex(&lval, &lloc, scanner) == REFSTRING);
+ valid = valid && (yylex(&lval, &lloc, scanner) == FULLSTOP);
+ valid = valid && (yylex(&lval, &lloc, scanner) == REFSTRING);
+ valid = valid && (yylex(&lval, &lloc, scanner) == RIGHTPARENTHESIS);
+ valid = valid && (lloc.last_column == [testTikz length]);
+ yylex_destroy(scanner);
+
+ return valid;
+}
+
+@end
+
+@implementation TikzGraphAssembler (Parser)
+- (Graph*)graph { return graph; }
+
+- (void)addNodeToMap:(Node*)n {
+ [nodeMap setObject:n forKey:[n name]];
+}
+
+- (Node*)nodeWithName:(NSString*)name {
+ return [nodeMap objectForKey:name];
+}
+
+- (void) setLastError:(NSError*)error {
+#if ! __has_feature(objc_arc)
+ [error retain];
+ [lastError release];
+#endif
+ lastError = error;
+}
+
+- (void) reportError:(const char *)message atLocation:(YYLTYPE*)yylloc {
+ NSString *nsmsg = [NSString stringWithUTF8String:message];
+
+ const char *first_line_start = find_start_of_nth_line (
+ tikzStr, yylloc->first_line - 1);
+ const char *last_line_start = find_start_of_nth_line (
+ first_line_start, yylloc->last_line - yylloc->first_line);
+ const char *last_line_end = last_line_start;
+ while (*last_line_end && *last_line_end != '\n') {
+ // points to just after end of last line
+ ++last_line_end;
+ }
+
+ size_t context_len = last_line_end - first_line_start;
+ size_t token_offset = yylloc->first_column - 1;
+ size_t token_len = ((last_line_start - first_line_start) + yylloc->last_column) - token_offset;
+
+ if (token_offset + token_len > context_len) {
+ // error position state is corrupted
+ NSLog(@"Got bad error state for error \"%s\": start(%i,%i), end(%i,%i)",
+ message,
+ yylloc->first_line,
+ yylloc->first_column,
+ yylloc->last_line,
+ yylloc->last_column);
+ [self setLastError:[NSError errorWithMessage:nsmsg
+ code:TZ_ERR_PARSE]];
+ } else {
+ char *context = malloc (context_len + 1);
+ strncpy (context, first_line_start, context_len);
+ *(context + context_len) = '\0';
+
+ NSDictionary *userInfo =
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ nsmsg,
+ NSLocalizedDescriptionKey,
+ [NSNumber numberWithInt:yylloc->first_line],
+ @"startLine",
+ [NSNumber numberWithInt:yylloc->first_column],
+ @"startColumn",
+ [NSNumber numberWithInt:yylloc->last_line],
+ @"endLine",
+ [NSNumber numberWithInt:yylloc->last_column],
+ @"endColumn",
+ [NSString stringWithUTF8String:context],
+ @"syntaxString",
+ [NSNumber numberWithInt:token_offset],
+ @"tokenStart",
+ [NSNumber numberWithInt:token_len],
+ @"tokenLength",
+ nil];
+ [self setLastError:
+ [NSError errorWithDomain:TZErrorDomain
+ code:TZ_ERR_PARSE
+ userInfo:userInfo]];
+
+ // we can now freely edit context string
+ // we only bother printing out the first line
+ if (yylloc->last_line > yylloc->first_line) {
+ char *nlp = strchr(context, '\n');
+ if (nlp) {
+ *nlp = '\0';
+ context_len = nlp - context;
+ NSAssert2(token_offset < context_len, @"token_offset (%lu) < context_len (%lu)", token_offset, context_len);
+ if (token_offset + token_len > context_len) {
+ token_len = context_len - token_offset;
+ }
+ } else {
+ NSLog(@"Didn't find any newlines in context string!");
+ }
+ }
+ size_t token_col_offset = 0;
+ size_t token_col_len = 0;
+ for (int i = 0; i < token_offset; ++i) {
+ if (*(context + i) == '\t')
+ token_col_offset += 8;
+ else
+ ++token_col_offset;
+ }
+ for (int i = token_offset; i < token_offset + token_len; ++i) {
+ if (*(context + i) == '\t')
+ token_col_len += 8;
+ else
+ ++token_col_len;
+ }
+ NSString *pointerLinePadding =
+ [@"" stringByPaddingToLength:token_col_offset
+ withString:@" "
+ startingAtIndex:0];
+ NSString *pointerLineCarets =
+ [@"" stringByPaddingToLength:token_col_len
+ withString:@"^"
+ startingAtIndex:0];
+ NSLog(@"Parse error on line %i, starting at %i: %s\n%s\n%@%@",
+ yylloc->first_line,
+ yylloc->first_column,
+ message,
+ context,
+ pointerLinePadding,
+ pointerLineCarets);
+ free (context);
+ }
+}
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/TikzShape.h b/tikzit-old/src/common/TikzShape.h
new file mode 100644
index 0000000..6a91f91
--- /dev/null
+++ b/tikzit-old/src/common/TikzShape.h
@@ -0,0 +1,37 @@
+//
+// TikzShape.h
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+#import "Shape.h"
+
+@interface TikzShape : Shape {
+ NSString *tikzSrc;
+}
+
+@property (copy) NSString *tikzSrc;
+
+- (id)initWithTikzFile:(NSString*)file;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/TikzShape.m b/tikzit-old/src/common/TikzShape.m
new file mode 100644
index 0000000..555a7df
--- /dev/null
+++ b/tikzit-old/src/common/TikzShape.m
@@ -0,0 +1,70 @@
+//
+// TikzShape.m
+// TikZiT
+//
+// Copyright 2011 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 <http://www.gnu.org/licenses/>.
+//
+
+#import "TikzShape.h"
+#import "Graph.h"
+
+@implementation TikzShape
+
+@synthesize tikzSrc;
+
+- (id)initWithTikzFile:(NSString*)file {
+ self = [super init];
+ if (self) {
+ NSString *tikz = [NSString stringWithContentsOfFile:file
+ encoding:NSUTF8StringEncoding
+ error:NULL];
+ if (tikz == nil) return nil;
+
+ tikzSrc = [tikz copy];
+
+ Graph *graph = [Graph graphFromTikz:tikz];
+ if (graph == nil) return nil;
+
+ NSRect graphBounds = ([graph hasBoundingBox]) ? [graph boundingBox] : [graph bounds];
+
+ float sz = 0.5f;
+
+ // the "screen" coordinate space fits in the shape bounds
+ Transformer *t = [Transformer transformer];
+ float width_ratio = (2*sz) / graphBounds.size.width;
+ float height_ratio = (2*sz) / graphBounds.size.height;
+ [t setScale:MIN(width_ratio, height_ratio)];
+ NSRect bds = [t rectToScreen:graphBounds];
+ NSPoint shift = NSMakePoint(-NSMidX(bds),
+ -NSMidY(bds));
+ [t setOrigin:shift];
+ [graph applyTransformer:t];
+#if __has_feature(objc_arc)
+ paths = [graph pathCover];
+#else
+ paths = [[graph pathCover] retain];
+#endif
+ }
+ return self;
+}
+
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/Transformer.h b/tikzit-old/src/common/Transformer.h
new file mode 100644
index 0000000..1b0108a
--- /dev/null
+++ b/tikzit-old/src/common/Transformer.h
@@ -0,0 +1,154 @@
+//
+// Transformer.h
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+
+#import <Foundation/Foundation.h>
+
+extern float const PIXELS_PER_UNIT;
+
+/*!
+ @class Transformer
+ @brief Do affine coordinate transforms between an abstract co-ordinate
+ space (such as the graph's) and the screen's.
+
+ This currently allows zooming and panning.
+ */
+@interface Transformer : NSObject <NSCopying> {
+ NSPoint origin;
+ float x_scale;
+ float y_scale;
+}
+
+/*!
+ @brief The screen co-ordinate of the abstract space origin.
+ */
+@property (assign) NSPoint origin;
+
+/*!
+ @brief The scale (from abstract space to screen space)
+ @detail This is the size of a single unit (a distance of 1.0)
+ of the abstract space on the screen.
+
+ Around 50 is a reasonable value.
+ */
+@property (assign) float scale;
+
+/*!
+ @brief Whether co-ordinates are flipped about the X axis
+ @detail TikZ considers X co-ordinates to run left to right,
+ which is not necessarily how the screen views
+ them.
+ */
+@property (assign,getter=isFlippedAboutXAxis) BOOL flippedAboutXAxis;
+
+/*!
+ @brief Whether co-ordinates are flipped about the Y axis
+ @detail TikZ considers Y co-ordinates to run up the page,
+ which is not necessarily how the screen views
+ them.
+ */
+@property (assign,getter=isFlippedAboutYAxis) BOOL flippedAboutYAxis;
+
+/*!
+ @brief Transform a point from screen space to abstract space.
+ @param p a point in screen space.
+ @result A point in abstract space.
+ */
+- (NSPoint)fromScreen:(NSPoint)p;
+
+/*!
+ @brief Transform a point from abstract space to screen space.
+ @param p a point in abstract space.
+ @result A point in screen space.
+ */
+- (NSPoint)toScreen:(NSPoint)p;
+
+/*!
+ @brief Scale a distance from screen space to abstract space.
+ @param dist a distance in screen space.
+ @result A distance in abstract space.
+ */
+- (float)scaleFromScreen:(float)dist;
+
+/*!
+ @brief Scale a distance from abstract space to screen space.
+ @param dist a distance in abstract space.
+ @result A distance in screen space.
+ */
+- (float)scaleToScreen:(float)dist;
+
+/*!
+ @brief Scale a rectangle from screen space to abstract space.
+ @param r a rectangle in screen space.
+ @result A rectangle in abstract space.
+ */
+- (NSRect)rectFromScreen:(NSRect)r;
+
+/*!
+ @brief Scale a rectangle from abstract space to screen space.
+ @param r a rectangle in abstract space.
+ @result A rectangle in screen space.
+ */
+- (NSRect)rectToScreen:(NSRect)r;
+
+/*!
+ @brief Factory method to get an identity transformer.
+ @result A transformer.
+ */
++ (Transformer*)transformer;
+
+/*!
+ @brief Factory method to get a transformer identical to another
+ @result A transformer.
+ */
++ (Transformer*)transformerWithTransformer:(Transformer*)t;
+
+/*!
+ @brief Factory method to get a transformer.
+ @param o The screen co-ordinate of the abstract space origin
+ @param scale The scale (from abstract space to screen space)
+ @result A transformer.
+ */
++ (Transformer*)transformerWithOrigin:(NSPoint)o andScale:(float)scale;
+
+/*!
+ @brief Get a global 'actual size' transformer.
+ @result A transformer.
+ */
++ (Transformer*)defaultTransformer;
+
+/*!
+ @brief A transformer set up from two bounding rects.
+
+ graphRect is made as large as possible while still fitting into screenRect.
+ @result A transformer.
+ */
++ (Transformer*)transformerToFit:(NSRect)graphRect intoScreenRect:(NSRect)screenRect flippedAboutXAxis:(BOOL)flipX flippedAboutYAxis:(BOOL)flipY;
+
++ (Transformer*)transformerToFit:(NSRect)graphRect intoScreenRect:(NSRect)screenRect flippedAboutXAxis:(BOOL)flipX;
++ (Transformer*)transformerToFit:(NSRect)graphRect intoScreenRect:(NSRect)screenRect flippedAboutYAxis:(BOOL)flipY;
++ (Transformer*)transformerToFit:(NSRect)graphRect intoScreenRect:(NSRect)screenRect;
+
+@end
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/Transformer.m b/tikzit-old/src/common/Transformer.m
new file mode 100644
index 0000000..2b56813
--- /dev/null
+++ b/tikzit-old/src/common/Transformer.m
@@ -0,0 +1,231 @@
+//
+// Transformer.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 <http://www.gnu.org/licenses/>.
+//
+
+#import "Transformer.h"
+
+float const PIXELS_PER_UNIT = 50;
+
+@implementation Transformer
+
++ (Transformer*)transformer {
+#if __has_feature(objc_arc)
+ return [[Transformer alloc] init];
+#else
+ return [[[Transformer alloc] init] autorelease];
+#endif
+}
+
++ (Transformer*)transformerWithTransformer:(Transformer*)t {
+#if __has_feature(objc_arc)
+ return [t copy];
+#else
+ return [[t copy] autorelease];
+#endif
+}
+
++ (Transformer*)transformerWithOrigin:(NSPoint)o andScale:(float)scale {
+ Transformer *trans = [self transformer];
+ [trans setOrigin:o];
+ [trans setScale:scale];
+ return trans;
+}
+
++ (Transformer*)transformerToFit:(NSRect)graphRect
+ intoScreenRect:(NSRect)screenRect {
+ return [self transformerToFit:graphRect
+ intoScreenRect:screenRect
+ flippedAboutXAxis:NO
+ flippedAboutYAxis:NO];
+}
+
++ (Transformer*)transformerToFit:(NSRect)graphRect
+ intoScreenRect:(NSRect)screenRect
+ flippedAboutXAxis:(BOOL)flipX {
+ return [self transformerToFit:graphRect
+ intoScreenRect:screenRect
+ flippedAboutXAxis:flipX
+ flippedAboutYAxis:NO];
+}
+
++ (Transformer*)transformerToFit:(NSRect)graphRect
+ intoScreenRect:(NSRect)screenRect
+ flippedAboutYAxis:(BOOL)flipY {
+ return [self transformerToFit:graphRect
+ intoScreenRect:screenRect
+ flippedAboutXAxis:NO
+ flippedAboutYAxis:flipY];
+}
+
++ (Transformer*)transformerToFit:(NSRect)graphRect
+ intoScreenRect:(NSRect)screenRect
+ flippedAboutXAxis:(BOOL)flipAboutXAxis
+ flippedAboutYAxis:(BOOL)flipAboutYAxis {
+
+ const float wscale = screenRect.size.width / graphRect.size.width;
+ const float hscale = screenRect.size.height / graphRect.size.height;
+ const float scale = (wscale < hscale) ? wscale : hscale;
+ const float xpad = (screenRect.size.width - (graphRect.size.width * scale)) / 2.0;
+ const float ypad = (screenRect.size.height - (graphRect.size.height * scale)) / 2.0;
+
+ // if we are flipping, we need to calculate the origin from the opposite edge
+ const float gx = flipAboutYAxis ? -(graphRect.size.width + graphRect.origin.x)
+ : graphRect.origin.x;
+ const float gy = flipAboutXAxis ? -(graphRect.size.height + graphRect.origin.y)
+ : graphRect.origin.y;
+ const float origin_x = screenRect.origin.x - (gx * scale) + xpad;
+ const float origin_y = screenRect.origin.y - (gy * scale) + ypad;
+
+ Transformer *trans = [self transformer];
+ [trans setOrigin:NSMakePoint(origin_x, origin_y)];
+ [trans setScale:scale];
+ [trans setFlippedAboutXAxis:flipAboutXAxis];
+ [trans setFlippedAboutYAxis:flipAboutYAxis];
+ return trans;
+}
+
+- (id) init {
+ self = [super init];
+
+ if (self) {
+ origin = NSZeroPoint;
+ x_scale = 1.0f;
+ y_scale = 1.0f;
+ }
+
+ return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+ Transformer *cp = [[[self class] allocWithZone:zone] init];
+ if (cp) {
+ cp->origin = origin;
+ cp->x_scale = x_scale;
+ cp->y_scale = y_scale;
+ }
+ return cp;
+}
+
+- (NSPoint)origin { return origin; }
+- (void)setOrigin:(NSPoint)o {
+ origin = o;
+}
+
+- (float)scale { return ABS(x_scale); }
+- (void)setScale:(float)s {
+ x_scale = (x_scale < 0.0) ? -s : s;
+ y_scale = (y_scale < 0.0) ? -s : s;
+}
+
+- (BOOL)isFlippedAboutXAxis {
+ return y_scale < 0.0;
+}
+
+- (void)setFlippedAboutXAxis:(BOOL)flip {
+ if (flip != [self isFlippedAboutXAxis]) {
+ y_scale *= -1;
+ }
+}
+
+- (BOOL)isFlippedAboutYAxis {
+ return x_scale < 0.0;
+}
+
+- (void)setFlippedAboutYAxis:(BOOL)flip {
+ if (flip != [self isFlippedAboutYAxis]) {
+ x_scale *= -1;
+ }
+}
+
+- (NSPoint)fromScreen:(NSPoint)p {
+ NSPoint trans;
+ trans.x = (p.x - origin.x) / x_scale;
+ trans.y = (p.y - origin.y) / y_scale;
+ return trans;
+}
+
+- (NSPoint)toScreen:(NSPoint)p {
+ NSPoint trans;
+ trans.x = (p.x * x_scale) + origin.x;
+ trans.y = (p.y * y_scale) + origin.y;
+ return trans;
+}
+
+- (float)scaleFromScreen:(float)dist {
+ return dist / ABS(x_scale);
+}
+
+- (float)scaleToScreen:(float)dist {
+ return dist * ABS(x_scale);
+}
+
+- (NSRect)rectFromScreen:(NSRect)r {
+ NSRect r1;
+ r1.origin = [self fromScreen:r.origin];
+ r1.size.width = [self scaleFromScreen:r.size.width];
+ r1.size.height = [self scaleFromScreen:r.size.height];
+ // if we're flipped, the origin will be at a different corner
+ if ([self isFlippedAboutYAxis]) {
+ r1.origin.x -= r1.size.width;
+ }
+ if ([self isFlippedAboutXAxis]) {
+ r1.origin.y -= r1.size.height;
+ }
+ return r1;
+}
+
+- (NSRect)rectToScreen:(NSRect)r {
+ NSPoint o = r.origin;
+ // if we're flipped, the origin will be at a different corner
+ if ([self isFlippedAboutYAxis]) {
+ o.x = NSMaxX(r);
+ }
+ if ([self isFlippedAboutXAxis]) {
+ o.y = NSMaxY(r);
+ }
+ NSRect r1;
+ r1.origin = [self toScreen:o];
+ r1.size.width = [self scaleToScreen:r.size.width];
+ r1.size.height = [self scaleToScreen:r.size.height];
+ return r1;
+}
+
+- (BOOL)isEqual:(id)object {
+ Transformer *t = (Transformer*)object;
+ return ([t origin].x == [self origin].x &&
+ [t origin].y == [self origin].y &&
+ [t scale] == [self scale]);
+}
+
+Transformer *defaultTransformer = nil;
+
++ (Transformer*)defaultTransformer {
+ if (defaultTransformer == nil) {
+ defaultTransformer = [[Transformer alloc] init];
+ [defaultTransformer setScale:PIXELS_PER_UNIT];
+ }
+ return defaultTransformer;
+}
+
+@end
+
+// vi:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit-old/src/common/test/Makefile b/tikzit-old/src/common/test/Makefile
new file mode 100644
index 0000000..d158d16
--- /dev/null
+++ b/tikzit-old/src/common/test/Makefile
@@ -0,0 +1,14 @@
+OBJC = gcc -MMD -MP -DSTAND_ALONE -DGNUSTEP -DGNUSTEP_BASE_LIBRARY=1 -DGNU_RUNTIME=1 -DGNUSTEP_BASE_LIBRARY=1 -fno-strict-aliasing -fPIC -Wall -DGSWARN -DGSDIAGNOSE -Wno-import -O0 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -fgnu-runtime -fconstant-string-class=NSConstantString -I. -I.. -I/users/alemer/GNUstep/Library/Headers -std=c99 -D_GNU_SOURCE -rdynamic -fgnu-runtime -L/users/alemer/GNUstep/Library/Libraries -L/usr/local/lib64 -L/usr/lib64 -lgnustep-base -lpthread -lobjc -lm
+
+maths_test_objects = test.m maths.m ../util.m
+color_test_objects = test.m color.m ../ColorRGB.m ../util.m ../BasicMapTable.m ../RColor.m
+
+test: maths-test color-test
+ ./maths-test
+ ./color-test
+
+maths-test: $(maths_test_objects)
+ $(OBJC) $(maths_test_objects) -o $@
+
+color-test: $(color_test_objects)
+ $(OBJC) $(color_test_objects) -o $@
diff --git a/tikzit-old/src/common/test/color.m b/tikzit-old/src/common/test/color.m
new file mode 100644
index 0000000..48a6ff4
--- /dev/null
+++ b/tikzit-old/src/common/test/color.m
@@ -0,0 +1,80 @@
+//
+// color.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 <http://www.gnu.org/licenses/>.
+//
+#import "test/test.h"
+#import "ColorRGB.h"
+
+#ifdef STAND_ALONE
+void runTests() {
+#else
+void testColor() {
+#endif
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ startTestBlock(@"color");
+
+ ColorRGB *red = [ColorRGB colorWithRed:255 green:0 blue:0];
+ ColorRGB *lime = [ColorRGB colorWithRed:0 green:255 blue:0];
+ ColorRGB *green = [ColorRGB colorWithRed:0 green:128 blue:0];
+ TEST(@"Recognised red",
+ [red name] != nil &&
+ [[red name] isEqualToString:@"Red"]);
+ TEST(@"Recognised lime",
+ [lime name] != nil &&
+ [[lime name] isEqualToString:@"Lime"]);
+ TEST(@"Recognised green",
+ [green name] != nil &&
+ [[green name] isEqualToString:@"Green"]);
+
+ ColorRGB *floatRed = [ColorRGB colorWithFloatRed:1.0f green:0.0f blue:0.0f];
+ ColorRGB *floatLime = [ColorRGB colorWithFloatRed:0.0f green:1.0f blue:0.0f];
+ ColorRGB *floatGreen = [ColorRGB colorWithFloatRed:0.0f green:0.5f blue:0.0f];
+
+ TEST(@"Float red equal to int red", [floatRed isEqual:red]);
+ TEST(@"Float lime equal to int lime", [floatLime isEqual:lime]);
+ TEST(@"Float green equal to int green", [floatGreen isEqual:green]);
+
+ TEST(@"Recognised float red",
+ [floatRed name] != nil &&
+ [[floatRed name] isEqualToString:@"Red"]);
+
+ TEST(@"Recognised float lime",
+ [floatLime name] != nil &&
+ [[floatLime name] isEqualToString:@"Lime"]);
+
+ TEST(@"Recognised float green",
+ [floatGreen name] != nil &&
+ [[floatGreen name] isEqualToString:@"Green"]);
+
+ [floatRed setRedFloat:0.99f];
+ TEST(@"Nudged red, not recognised now", [floatRed name] == nil);
+ [floatRed setToClosestHashed];
+ TEST(@"Set to closest hashed, reconised again",
+ [floatRed name] != nil &&
+ [[floatRed name] isEqualToString:@"Red"]);
+
+ TEST(@"Red has correct hex (ff0000)", [[red hexName] isEqualToString:@"hexcolor0xff0000"]);
+ TEST(@"Lime has correct hex (00ff00)", [[lime hexName] isEqualToString:@"hexcolor0x00ff00"]);
+ TEST(@"Green has correct hex (008000)", [[green hexName] isEqualToString:@"hexcolor0x008000"]);
+
+ endTestBlock(@"color");
+ [pool drain];
+}
diff --git a/tikzit-old/src/common/test/common.m b/tikzit-old/src/common/test/common.m
new file mode 100644
index 0000000..c9ac980
--- /dev/null
+++ b/tikzit-old/src/common/test/common.m
@@ -0,0 +1,34 @@
+//
+// common.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 <http://www.gnu.org/licenses/>.
+//
+#import "test/test.h"
+void testParser();
+void testColor();
+void testMaths();
+
+void testCommon() {
+ startTestBlock(@"common");
+ testParser();
+ testColor();
+ testMaths();
+ endTestBlock(@"common");
+}
diff --git a/tikzit-old/src/common/test/maths.m b/tikzit-old/src/common/test/maths.m
new file mode 100644
index 0000000..a11e58e
--- /dev/null
+++ b/tikzit-old/src/common/test/maths.m
@@ -0,0 +1,562 @@
+//
+// TikZiT
+//
+// Copyright 2011 Alex Merry
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#import "../util.h"
+
+#import "test.h"
+
+void testRectAroundPoints() {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ startTestBlock(@"NSRectAroundPoints");
+
+ NSRect rect = NSRectAroundPoints (NSZeroPoint, NSZeroPoint);
+ assertRectsEqual (@"(0,0) and (0,0)", rect, NSZeroRect);
+
+ rect = NSRectAroundPoints (NSZeroPoint, NSMakePoint (1.0f, 1.0f));
+ assertRectsEqual (@"(0,0) and (1,1)", rect, NSMakeRect (0.0f, 0.0f, 1.0f, 1.0f));
+
+ rect = NSRectAroundPoints (NSMakePoint (-1.0f, 1.0f), NSMakePoint (1.0f, -1.0f));
+ assertRectsEqual (@"(-1,1) and (1,-1)", rect, NSMakeRect (-1.0f, -1.0f, 2.0f, 2.0f));
+
+ endTestBlock(@"NSRectAroundPoints");
+ [pool drain];
+}
+
+void testRectAroundPointsWithPadding() {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ startTestBlock(@"NSRectAroundPointsWithPadding");
+
+ NSRect rect = NSRectAroundPointsWithPadding (NSZeroPoint, NSZeroPoint, 0.0f);
+ assertRectsEqual (@"(0,0) and (0,0); 0 padding", rect, NSZeroRect);
+
+ rect = NSRectAroundPointsWithPadding (NSZeroPoint, NSZeroPoint, 0.2f);
+ assertRectsEqual (@"(0,0) and (0,0); 0.2 padding", rect, NSMakeRect (-0.2f, -0.2f, 0.4f, 0.4f));
+
+ rect = NSRectAroundPointsWithPadding (NSZeroPoint, NSMakePoint (1.0f, 1.0f), -0.2f);
+ assertRectsEqual (@"(0,0) and (1,1); -0.2 padding", rect, NSMakeRect (0.2f, 0.2f, 0.6f, 0.6f));
+
+ endTestBlock(@"NSRectAroundPointsWithPadding");
+ [pool drain];
+}
+
+void testGoodAtan() {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ startTestBlock(@"good_atan");
+
+ assertFloatsEqual (@"0.0, 0.0", good_atan (0.0f, 0.0f), 0.0f);
+ assertFloatsEqual (@"0.0, 1.0", good_atan (0.0f, 1.0f), 0.5f * M_PI);
+ assertFloatsEqual (@"0.0, -1.0", good_atan (0.0f, -1.0f), 1.5f * M_PI);
+ assertFloatsEqual (@"1.0, 0.0", good_atan (1.0f, 0.0f), 0.0f);
+ assertFloatsEqual (@"1.0, 0.1", good_atan (1.0f, 0.1f), 0.0996687f);
+
+ endTestBlock(@"good_atan");
+ [pool drain];
+}
+
+void testBezierInterpolate() {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ startTestBlock(@"bezierInterpolate");
+
+ assertFloatsEqual (@"0.0, (0.0, 0.1, 0.2, 0.3)", bezierInterpolate (0.0f, 0.0f, 0.1f, 0.2f, 0.3f), 0.0f);
+ assertFloatsEqual (@"1.0, (0.0, 0.1, 0.2, 0.3)", bezierInterpolate (1.0f, 0.0f, 0.1f, 0.2f, 0.3f), 0.3f);
+ assertFloatsEqual (@"0.5, (0.0, 0.1, 0.2, 0.3)", bezierInterpolate (0.5f, 0.0f, 0.1f, 0.2f, 0.3f), 0.15f);
+ // FIXME: other tests
+
+ endTestBlock(@"bezierInterpolate");
+ [pool drain];
+}
+
+void testLineSegmentsIntersect() {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ startTestBlock(@"lineSegmentsIntersect");
+
+ BOOL result = NO;
+ NSPoint intersection = NSMakePoint (-1.0f, -1.0f);
+
+ result = lineSegmentsIntersect (NSMakePoint (-1.0f, -1.0f), NSMakePoint (1.0f, 1.0f),
+ NSMakePoint (-1.0f, 1.0f), NSMakePoint (1.0f, -1.0f),
+ &intersection);
+ TEST (@"Cross at zero: has intersection", result);
+ assertPointsEqual (@"Cross at zero: intersection value", intersection, NSZeroPoint);
+
+ result = lineSegmentsIntersect (NSMakePoint (-1.0f, -1.0f), NSMakePoint (-0.5f, -0.5f),
+ NSMakePoint (-1.0f, 1.0f), NSMakePoint (1.0f, -1.0f),
+ &intersection);
+ TEST (@"Fail to cross at zero", !result);
+
+ result = lineSegmentsIntersect (NSMakePoint (1.0f, 1.0f), NSMakePoint (1.0f, -1.0f),
+ NSMakePoint (0.0f, 0.0f), NSMakePoint (1.0f, 0.0f),
+ &intersection);
+ TEST (@"Touch at one: has intersection", result);
+ assertPointsEqual (@"Touch at one: intersection value", intersection, NSMakePoint (1.0f, 0.0f));
+
+ endTestBlock(@"lineSegmentsIntersect");
+ [pool drain];
+}
+
+void testLineSegmentIntersectsRect() {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ startTestBlock(@"lineSegmentIntersectsRect");
+
+ BOOL result = NO;
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (-1.0f, -1.0f),
+ NSMakePoint (0.0f, 0.0f),
+ NSZeroRect);
+ TEST (@"Zero rect; line touches zero", result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (-1.0f, -1.0f),
+ NSMakePoint (-0.1f, -0.1f),
+ NSZeroRect);
+ TEST (@"Zero rect; line short of zero", !result);
+
+ NSRect rect = NSMakeRect (1.0f, 1.0f, 1.0f, 1.0f);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (0.0f, 0.0f),
+ NSMakePoint (3.0f, 1.0f),
+ rect);
+ TEST (@"Line underneath", !result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (0.0f, 0.0f),
+ NSMakePoint (1.0f, 3.0f),
+ rect);
+ TEST (@"Line to left", !result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (0.0f, 2.0f),
+ NSMakePoint (3.0f, 3.0f),
+ rect);
+ TEST (@"Line above", !result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (2.0f, 0.0f),
+ NSMakePoint (3.0f, 3.0f),
+ rect);
+ TEST (@"Line to right", !result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (0.0f, 0.0f),
+ NSMakePoint (0.9f, 0.9f),
+ rect);
+ TEST (@"Line short", !result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (1.1f, 1.1f),
+ NSMakePoint (1.9f, 1.9f),
+ rect);
+ TEST (@"Line inside", result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (0.0f, 1.5f),
+ NSMakePoint (3.0f, 1.5f),
+ rect);
+ TEST (@"Horizontal line through", result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (1.5f, 0.0f),
+ NSMakePoint (1.5f, 3.0f),
+ rect);
+ TEST (@"Vertical line through", result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (0.5f, 1.0f),
+ NSMakePoint (2.0f, 2.5f),
+ rect);
+ TEST (@"Cut top and left", result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (2.0f, 0.5f),
+ NSMakePoint (0.5f, 2.0f),
+ rect);
+ TEST (@"Cut bottom and left", result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (1.0f, 0.5f),
+ NSMakePoint (2.5f, 2.0f),
+ rect);
+ TEST (@"Cut bottom and right", result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (0.0f, 1.0f),
+ NSMakePoint (2.0f, 3.0f),
+ rect);
+ TEST (@"Touch top left", result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (1.0f, 0.0f),
+ NSMakePoint (3.0f, 2.0f),
+ rect);
+ TEST (@"Touch bottom right", result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (1.0f, 0.0f),
+ NSMakePoint (1.0f, 3.0f),
+ rect);
+ TEST (@"Along left side", result);
+
+ result = lineSegmentIntersectsRect (
+ NSMakePoint (0.0f, 1.0f),
+ NSMakePoint (3.0f, 1.0f),
+ rect);
+ TEST (@"Along bottom side", result);
+
+ endTestBlock(@"lineSegmentIntersectsRect");
+ [pool drain];
+}
+
+struct line_bezier_test {
+ NSString *msg;
+ NSPoint lstart;
+ NSPoint lend;
+ NSPoint c0;
+ NSPoint c1;
+ NSPoint c2;
+ NSPoint c3;
+ BOOL expectedResult;
+ float expectedT;
+ NSPoint expectedIntersect;
+};
+
+static struct line_bezier_test line_bezier_tests[] = {
+ {
+ @"Outside box",
+ {0.0f, 0.0f},
+ {1.0f, 0.0f},
+ {0.0f, 1.0f},
+ {0.0f, 2.0f},
+ {1.0f, 2.0f},
+ {1.0f, 1.0f},
+ NO,
+ -1.0f,
+ {0.0f, 0.0f}
+ },
+ {
+ @"Single intersect",
+ {100.0f, 20.0f},
+ {195.0f, 255.0f},
+ {93.0f, 163.0f},
+ {40.0f, 30.0f},
+ {270.0f, 115.0f},
+ {219.0f, 178.0f},
+ YES,
+ -0.4f,
+ {129.391693f, 92.705772f}
+ },
+ {
+ @"Double intersect",
+ {100.0f, 20.0f},
+ {195.0f, 255.0f},
+ {93.0f, 163.0f},
+ {40.0f, 30.0f},
+ {270.0f, 115.0f},
+ {154.0f, 212.0f},
+ YES,
+ -0.909f,
+ {170.740646f,194.990021f}
+ },
+ {
+ @"Near miss",
+ {100.0f, 20.0f},
+ {195.0f, 255.0f},
+ {93.0f, 163.0f},
+ {40.0f, 30.0f},
+ {176.0f, 100.0f},
+ {154.0f, 212.0f},
+ NO,
+ -1.0f,
+ {0.0f,0.0f}
+ }
+};
+static unsigned int n_line_bezier_tests = sizeof (line_bezier_tests) / sizeof (line_bezier_tests[0]);
+
+void testLineSegmentIntersectsBezier() {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ startTestBlock(@"lineSegmentIntersectsBezier");
+
+ for (unsigned int i = 0; i < n_line_bezier_tests; ++i) {
+ NSPoint intersect;
+ BOOL result = lineSegmentIntersectsBezier (
+ line_bezier_tests[i].lstart,
+ line_bezier_tests[i].lend,
+ line_bezier_tests[i].c0,
+ line_bezier_tests[i].c1,
+ line_bezier_tests[i].c2,
+ line_bezier_tests[i].c3,
+ &intersect);
+ if (result) {
+ if (line_bezier_tests[i].expectedT < 0.0f) {
+ assertPointsEqual (line_bezier_tests[i].msg, intersect, line_bezier_tests[i].expectedIntersect);
+ } else {
+ assertPointsEqual (line_bezier_tests[i].msg, intersect,
+ bezierInterpolateFull (line_bezier_tests[i].expectedT, line_bezier_tests[i].c0, line_bezier_tests[i].c1, line_bezier_tests[i].c2, line_bezier_tests[i].c3));
+ }
+ } else {
+ if (line_bezier_tests[i].expectedResult)
+ fail (line_bezier_tests[i].msg);
+ else
+ pass (line_bezier_tests[i].msg);
+ }
+ }
+
+BOOL lineSegmentIntersectsBezier (NSPoint lstart, NSPoint lend, NSPoint c0, NSPoint c1, NSPoint c2, NSPoint c3, NSPoint *result);
+ endTestBlock(@"lineSegmentIntersectsBezier");
+ [pool drain];
+}
+
+struct exit_point_test {
+ NSString *msg;
+ NSPoint rayStart;
+ float angle;
+ NSRect rect;
+ NSPoint expected;
+};
+
+static struct exit_point_test exit_point_tests[] = {
+ {
+ @"0.0 rads",
+ {0.0f, 0.0f},
+ 0.0f,
+ {{-1.0f, -1.0f}, {2.0f, 2.0f}},
+ {1.0f, 0.0f}
+ },
+ {
+ @"pi/2 rads",
+ {0.0f, 0.0f},
+ M_PI / 2.0f,
+ {{-1.0f, -1.0f}, {2.0f, 2.0f}},
+ {0.0f, 1.0f}
+ },
+ {
+ @"-pi/2 rads",
+ {0.0f, 0.0f},
+ -M_PI / 2.0f,
+ {{-1.0f, -1.0f}, {2.0f, 2.0f}},
+ {0.0f, -1.0f}
+ },
+ {
+ @"pi rads",
+ {0.0f, 0.0f},
+ M_PI,
+ {{-1.0f, -1.0f}, {2.0f, 2.0f}},
+ {-1.0f, 0.0f}
+ },
+ {
+ @"-pi rads",
+ {0.0f, 0.0f},
+ -M_PI,
+ {{-1.0f, -1.0f}, {2.0f, 2.0f}},
+ {-1.0f, 0.0f}
+ },
+ {
+ @"pi/4 rads",
+ {0.0f, 0.0f},
+ M_PI / 4.0f,
+ {{-1.0f, -1.0f}, {2.0f, 2.0f}},
+ {1.0f, 1.0f}
+ },
+ {
+ @"3pi/4 rads",
+ {0.0f, 0.0f},
+ (3.0f * M_PI) / 4.0f,
+ {{-1.0f, -1.0f}, {2.0f, 2.0f}},
+ {-1.0f, 1.0f}
+ },
+ {
+ @"-pi/4 rads",
+ {0.0f, 0.0f},
+ -M_PI / 4.0f,
+ {{-1.0f, -1.0f}, {2.0f, 2.0f}},
+ {1.0f, -1.0f}
+ },
+ {
+ @"-3pi/4 rads",
+ {0.0f, 0.0f},
+ (-3.0f * M_PI) / 4.0f,
+ {{-1.0f, -1.0f}, {2.0f, 2.0f}},
+ {-1.0f, -1.0f}
+ },
+ {
+ @"pi/8 rads",
+ {0.0f, 0.0f},
+ M_PI / 8.0f,
+ {{-1.0f, -1.0f}, {2.0f, 2.0f}},
+ {1.0f, 0.414213562373095f}
+ },
+ {
+ @"3pi/8 rads",
+ {0.0f, 0.0f},
+ 3.0f * M_PI / 8.0f,
+ {{-1.0f, -1.0f}, {2.0f, 2.0f}},
+ {0.414213562373095f, 1.0f}
+ },
+ {
+ @"-5pi/8 rads",
+ {0.0f, 0.0f},
+ -5.0f * M_PI / 8.0f,
+ {{-1.0f, -1.0f}, {2.0f, 2.0f}},
+ {-0.414213562373095f, -1.0f}
+ },
+ {
+ @"-7pi/8 rads",
+ {0.0f, 0.0f},
+ -7.0f * M_PI / 8.0f,
+ {{-1.0f, -1.0f}, {2.0f, 2.0f}},
+ {-1.0f, -0.414213562373095f}
+ },
+ {
+ @"pi/8 rads; origin (1,1)",
+ {1.0f, 1.0f},
+ M_PI / 8.0f,
+ {{0.0f, 0.0f}, {2.0f, 2.0f}},
+ {2.0f, 1.414213562373095f}
+ },
+ {
+ @"7pi/8 rads; origin (-2,2)",
+ {-2.0f, 2.0f},
+ 7.0f * M_PI / 8.0f,
+ {{-3.0f, 1.0f}, {2.0f, 2.0f}},
+ {-3.0f, 2.414213562373095f}
+ },
+ {
+ @"pi/8 rads; origin (1,1); SW of box",
+ {1.0f, 1.0f},
+ M_PI / 8.0f,
+ {{1.0f, 1.0f}, {1.0f, 1.0f}},
+ {2.0f, 1.414213562373095f}
+ },
+ {
+ @"pi/8 rads; origin (1,1); SE of box",
+ {1.0f, 1.0f},
+ M_PI / 8.0f,
+ {{0.0f, 1.0f}, {1.0f, 1.0f}},
+ {1.0f, 1.0f}
+ },
+ {
+ @"pi/8 rads; origin (1,1); NE of box",
+ {1.0f, 1.0f},
+ M_PI / 8.0f,
+ {{0.0f, 1.0f}, {1.0f, 1.0f}},
+ {1.0f, 1.0f}
+ },
+ {
+ @"pi/8 rads; origin (1,1); NW of box",
+ {1.0f, 1.0f},
+ M_PI / 8.0f,
+ {{1.0f, 0.0f}, {1.0f, 1.0f}},
+ {1.0f, 1.0f}
+ },
+ {
+ @"pi/8 rads; origin (1,1); N of box",
+ {1.0f, 1.0f},
+ M_PI / 8.0f,
+ {{0.5f, 0.0f}, {1.0f, 1.0f}},
+ {1.0f, 1.0f}
+ },
+ {
+ @"7pi/8 rads; origin (1,1); N of box",
+ {1.0f, 1.0f},
+ 7.0f * M_PI / 8.0f,
+ {{0.5f, 0.0f}, {1.0f, 1.0f}},
+ {1.0f, 1.0f}
+ },
+ {
+ @"-pi/8 rads; origin (1,1); S of box",
+ {1.0f, 1.0f},
+ -M_PI / 8.0f,
+ {{0.5f, 1.0f}, {1.0f, 1.0f}},
+ {1.0f, 1.0f}
+ },
+ {
+ @"-pi/8 rads; origin (1,1); E of box",
+ {1.0f, 1.0f},
+ -M_PI / 8.0f,
+ {{0.0f, 0.5f}, {1.0f, 1.0f}},
+ {1.0f, 1.0f}
+ },
+ {
+ @"-7pi/8 rads; origin (1,1); W of box",
+ {1.0f, 1.0f},
+ -7.0f * M_PI / 8.0f,
+ {{1.0f, 0.5f}, {1.0f, 1.0f}},
+ {1.0f, 1.0f}
+ },
+ {
+ @"7pi/8 rads; origin (1,1); W of box",
+ {1.0f, 1.0f},
+ 7.0f * M_PI / 8.0f,
+ {{1.0f, 0.5f}, {1.0f, 1.0f}},
+ {1.0f, 1.0f}
+ },
+ {
+ @"pi/8 rads; origin (1,1); leave through top",
+ {1.0f, 1.0f},
+ M_PI / 8.0f,
+ {{0.9f, 0.1f}, {1.0f, 1.0f}},
+ {1.2414213562373f, 1.1f}
+ },
+ {
+ @"0 rads; origin (1,1); N of box",
+ {1.0f, 1.0f},
+ 0.0f,
+ {{0.5f, 0.0f}, {1.0f, 1.0f}},
+ {1.5f, 1.0f}
+ }
+};
+static unsigned int n_exit_point_tests = sizeof (exit_point_tests) / sizeof (exit_point_tests[0]);
+
+void testFindExitPointOfRay() {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ startTestBlock(@"findExitPointOfRay");
+
+ for (unsigned int i = 0; i < n_exit_point_tests; ++i) {
+ NSPoint exitPoint = findExitPointOfRay (
+ exit_point_tests[i].rayStart,
+ exit_point_tests[i].angle,
+ exit_point_tests[i].rect);
+ assertPointsEqual (exit_point_tests[i].msg, exitPoint, exit_point_tests[i].expected);
+ }
+
+ endTestBlock(@"findExitPointOfRay");
+ [pool drain];
+}
+
+#ifdef STAND_ALONE
+void runTests() {
+#else
+void testMaths() {
+#endif
+ startTestBlock(@"maths");
+ testRectAroundPoints();
+ testRectAroundPointsWithPadding();
+ testGoodAtan();
+ testBezierInterpolate();
+ testLineSegmentsIntersect();
+ testLineSegmentIntersectsRect();
+ testFindExitPointOfRay();
+ testLineSegmentIntersectsBezier();
+ endTestBlock(@"maths");
+}
+
+// vim:ft=objc:ts=4:sts=4:sw=4:noet
diff --git a/tikzit-old/src/common/test/parser.m b/tikzit-old/src/common/test/parser.m
new file mode 100644
index 0000000..3346acd
--- /dev/null
+++ b/tikzit-old/src/common/test/parser.m
@@ -0,0 +1,86 @@
+//
+// parser.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 <http://www.gnu.org/licenses/>.
+//
+#import "test/test.h"
+#import "TikzGraphAssembler.h"
+
+
+#ifdef STAND_ALONE
+void runTests() {
+#else
+void testParser() {
+#endif
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ startTestBlock(@"parser");
+
+ [TikzGraphAssembler setup];
+
+ NodeStyle *rn = [NodeStyle defaultNodeStyleWithName:@"rn"];
+ NSArray *styles = [NSArray arrayWithObject:rn];
+
+ NSString *tikz =
+ @"\\begin{tikzpicture}[dotpic]"
+ @" \\begin{pgfonlayer}{foo}" //ignored
+ @" \\node [style=rn] (0) at (-2,3.4) {stuff{$\\alpha$ in here}};"
+ @" \\node (b) at (1,1) {};"
+ @" \\end{pgfonlayer}" //ignored
+ @" \\draw [bend right=20] (0) to node[tick]{-} (b.center);"
+ @"\\end{tikzpicture}";
+
+ TikzGraphAssembler *ga = [[TikzGraphAssembler alloc] init];
+ TEST(@"Parsing TikZ", [ga parseTikz:tikz]);
+
+ Graph *g = [ga graph];
+ TEST(@"Graph is non-nil", g != nil);
+ TEST(@"Graph has correct number of nodes", [[g nodes] count]==2);
+ TEST(@"Graph has correct number of edges", [[g edges] count]==1);
+
+ NSEnumerator *en = [[g nodes] objectEnumerator];
+ Node *n;
+ Node *n1, *n2;
+ while ((n=[en nextObject])) {
+ [n attachStyleFromTable:styles];
+ if ([n style] == rn) n1 = n;
+ else if ([n style] == nil) n2 = n;
+ }
+
+ TEST(@"Styles attached correctly", n1!=nil && n2!=nil);
+
+ TEST(@"Nodes labeled correctly",
+ [[n1 label] isEqualToString:@"stuff{$\\alpha$ in here}"] &&
+ [[n2 label] isEqualToString:@""]
+ );
+
+ Edge *e1 = [[[g edges] objectEnumerator] nextObject];
+
+ TEST(@"Edge has edge node", [e1 edgeNode]!=nil);
+ TEST(@"Edge node labeled correctly", [[[e1 edgeNode] label] isEqualToString:@"-"]);
+// NSString *sty = [[[[[e1 edgeNode] data] atoms] objectEnumerator] nextObject];
+// TEST(@"Edge node styled correctly", sty!=nil && [sty isEqualToString:@"tick"]);
+
+ PUTS(@"Source anchor: %@",[e1 sourceAnchor]);
+ PUTS(@"Target anchor: %@",[e1 targetAnchor]);
+
+ endTestBlock(@"parser");
+
+ [pool drain];
+}
diff --git a/tikzit-old/src/common/test/test.h b/tikzit-old/src/common/test/test.h
new file mode 100644
index 0000000..59dcdd4
--- /dev/null
+++ b/tikzit-old/src/common/test/test.h
@@ -0,0 +1,57 @@
+//
+// test.h
+// 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 <http://www.gnu.org/licenses/>.
+//
+#import <Foundation/Foundation.h>
+
+@interface Allocator : NSObject
+{}
+@end
+
+BOOL fuzzyCompare (float f1, float f2);
+BOOL fuzzyComparePoints (NSPoint p1, NSPoint p2);
+
+void setColorEnabled(BOOL b);
+
+void pass(NSString *msg);
+void fail(NSString *msg);
+void TEST(NSString *msg, BOOL test);
+void assertRectsEqual (NSString *msg, NSRect val, NSRect exp);
+void assertPointsEqual (NSString *msg, NSPoint val, NSPoint exp);
+void assertFloatsEqual (NSString *msg, float val, float exp);
+
+void startTests();
+void endTests();
+
+void startTestBlock(NSString *name);
+void endTestBlock(NSString *name);
+
+#define PUTS(fmt, ...) { \
+ NSString *_str = [[NSString alloc] initWithFormat:fmt, ##__VA_ARGS__]; \
+ printf("%s\n", [_str UTF8String]); \
+ [_str release]; }
+
+#define failFmt(fmt, ...) { \
+ NSString *_fstr = [[NSString alloc] initWithFormat:fmt, ##__VA_ARGS__]; \
+ fail(_fstr); \
+ [_fstr release]; }
+
+// vim:ft=objc:ts=4:sts=4:sw=4:noet
diff --git a/tikzit-old/src/common/test/test.m b/tikzit-old/src/common/test/test.m
new file mode 100644
index 0000000..9afcd67
--- /dev/null
+++ b/tikzit-old/src/common/test/test.m
@@ -0,0 +1,175 @@
+//
+// test.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 <http://www.gnu.org/licenses/>.
+//
+#import "test/test.h"
+
+static int PASSES;
+static int FAILS;
+
+static int ALLOC_INSTANCES = 0;
+
+static BOOL colorEnabled = YES;
+static int depth = 0;
+
+static NSString *RED, *GREEN, *BLUE, *OFF;
+
+static NSString *indents[6] =
+ {@"", @" ", @" ", @" ",
+ @" ", @" "};
+
+#define INDENT ((depth >= 6) ? indents[5] : indents[depth])
+
+
+@implementation Allocator
+
++ (id)alloc {
+ ++ALLOC_INSTANCES;
+ return [super alloc];
+}
+
+- (void)dealloc {
+ --ALLOC_INSTANCES;
+ [super dealloc];
+}
+
++ (Allocator*)allocator {
+ return [[[Allocator alloc] init] autorelease];
+}
+
+@end
+
+BOOL fuzzyCompare(float f1, float f2) {
+ return (ABS(f1 - f2) <= 0.00001f * MAX(1.0f,MIN(ABS(f1), ABS(f2))));
+}
+
+BOOL fuzzyComparePoints (NSPoint p1, NSPoint p2) {
+ return fuzzyCompare (p1.x, p2.x) && fuzzyCompare (p1.y, p2.y);
+}
+
+void pass(NSString *msg) {
+ PUTS(@"%@[%@PASS%@] %@", INDENT, GREEN, OFF, msg);
+ ++PASSES;
+}
+
+void fail(NSString *msg) {
+ PUTS(@"%@[%@FAIL%@] %@", INDENT, RED, OFF, msg);
+ ++FAILS;
+}
+
+void TEST(NSString *msg, BOOL test) {
+ if (test) {
+ pass (msg);
+ } else {
+ fail (msg);
+ }
+}
+
+void assertRectsEqual (NSString *msg, NSRect r1, NSRect r2) {
+ BOOL equal = fuzzyCompare (r1.origin.x, r2.origin.x) &&
+ fuzzyCompare (r1.origin.y, r2.origin.y) &&
+ fuzzyCompare (r1.size.width, r2.size.width) &&
+ fuzzyCompare (r1.size.height, r2.size.height);
+ if (equal) {
+ pass (msg);
+ } else {
+ failFmt(@"%@ (expected (%f,%f:%fx%f) but got (%f,%f:%fx%f))",
+ msg,
+ r2.origin.x, r2.origin.y, r2.size.width, r2.size.height,
+ r1.origin.x, r1.origin.y, r1.size.width, r1.size.height);
+ }
+}
+
+void assertPointsEqual (NSString *msg, NSPoint p1, NSPoint p2) {
+ BOOL equal = fuzzyCompare (p1.x, p2.x) && fuzzyCompare (p1.y, p2.y);
+ if (equal) {
+ pass (msg);
+ } else {
+ failFmt(@"%@ (expected (%f,%f) but got (%f,%f)",
+ msg,
+ p2.x, p2.y,
+ p1.x, p1.y);
+ }
+}
+
+void assertFloatsEqual (NSString *msg, float f1, float f2) {
+ if (fuzzyCompare (f1, f2)) {
+ pass (msg);
+ } else {
+ failFmt(@"%@ (expected %f but got %f", msg, f2, f1);
+ }
+}
+
+void startTests() {
+ PASSES = 0;
+ FAILS = 0;
+}
+
+void endTests() {
+ PUTS(@"Done testing. %@%d%@ passed, %@%d%@ failed.",
+ GREEN, PASSES, OFF,
+ RED, FAILS, OFF);
+}
+
+void startTestBlock(NSString *name) {
+ PUTS(@"%@Starting %@%@%@ tests.", INDENT, BLUE, name, OFF);
+ ++depth;
+}
+
+void endTestBlock(NSString *name) {
+ --depth;
+ PUTS(@"%@Done with %@%@%@ tests.", INDENT, BLUE, name, OFF);
+}
+
+void setColorEnabled(BOOL b) {
+ colorEnabled = b;
+ if (b) {
+ RED = @"\033[31;1m";
+ GREEN = @"\033[32;1m";
+ BLUE = @"\033[36;1m";
+ OFF = @"\033[0m";
+ } else {
+ RED = @"";
+ GREEN = @"";
+ BLUE = @"";
+ OFF = @"";
+ }
+}
+
+#ifdef STAND_ALONE
+void runTests();
+
+int main() {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ setColorEnabled (NO);
+ startTests();
+
+ runTests();
+
+ endTests();
+
+ [pool drain];
+ return 0;
+}
+#endif
+
+// vim:ft=objc:ts=4:sts=4:sw=4:noet
diff --git a/tikzit-old/src/common/tikzlexer.lm b/tikzit-old/src/common/tikzlexer.lm
new file mode 100644
index 0000000..1e92f73
--- /dev/null
+++ b/tikzit-old/src/common/tikzlexer.lm
@@ -0,0 +1,170 @@
+%{
+/*
+ * Copyright 2010 Chris Heunen
+ * Copyright 2010-2013 Aleks Kissinger
+ * Copyright 2013 K. Johan Paulsson
+ * Copyright 2013 Alex Merry <dev@randomguy3.me.uk>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+#import "tikzparserdefs.h"
+#import "tikzparser.h"
+
+#define YY_USER_ACTION \
+ yylloc->first_line = yylloc->last_line; \
+ yylloc->first_column = yylloc->last_column + 1; \
+ yylloc->last_column = yylloc->first_column + yyleng - 1;
+
+%}
+
+%option reentrant bison-bridge bison-locations 8bit
+%option nounput
+%option yylineno
+%option noyywrap
+%option header-file="common/tikzlexer.h"
+%option extra-type="TikzGraphAssembler *"
+
+
+%s props
+%s xcoord
+%s ycoord
+%s noderef
+
+FLOAT \-?[0-9]*(\.[0-9]+)?
+
+%%
+
+ /* whitespace is ignored, except for position counting; we don't
+ count formfeed and vtab as whitespace, because it's not obvious
+ how they should be dealt with and no-one actually uses them */
+
+ /* lex will take the longest-matching string */
+<INITIAL,xcoord,ycoord,props,noderef>\r\n|\r|\n {
+ yylloc->first_line += 1;
+ yylloc->last_line = yylloc->first_line;
+ yylloc->first_column = yylloc->last_column = 0;
+}
+<INITIAL,xcoord,ycoord,props,noderef>[\t ]+ { }
+
+\\begin\{tikzpicture\} { return BEGIN_TIKZPICTURE_CMD; }
+\\end\{tikzpicture\} { return END_TIKZPICTURE_CMD; }
+\\begin\{pgfonlayer\} { return BEGIN_PGFONLAYER_CMD; }
+\\end\{pgfonlayer\} { return END_PGFONLAYER_CMD; }
+\\draw { return DRAW_CMD; }
+\\node { return NODE_CMD; }
+\\path { return PATH_CMD; }
+rectangle { return RECTANGLE; }
+node { return NODE; }
+at { return AT; }
+to { return TO; }
+; { return SEMICOLON; }
+
+\([ ]*{FLOAT}[ ]*,[ ]*{FLOAT}[ ]*\) {
+ yylloc->last_column = yylloc->first_column + 1;
+ yyless(1);
+ BEGIN(xcoord);
+}
+<xcoord>{FLOAT} {
+ yylval->pt.x=(float)strtod(yytext,NULL);
+ BEGIN(ycoord);
+}
+<ycoord>, { }
+<ycoord>{FLOAT} {
+ yylval->pt.y=(float)strtod(yytext,NULL);
+}
+<ycoord>\) {
+ BEGIN(INITIAL);
+ return COORD;
+}
+
+ /* when we see "[", change parsing mode */
+\[ /*syntaxhlfix]*/ {
+ BEGIN(props);
+ return LEFTBRACKET;
+}
+<props>= { return EQUALS; }
+<props>, { return COMMA; }
+ /* technically, it is possible to have newlines in the middle of
+ property names or values, but in practice this is unlikely and
+ screws up our line counting */
+<props>[^=,\{\] \t\n]([^=,\{\]\n]*[^=,\{\] \t\n])? {
+ yylval->nsstr=[NSString stringWithUTF8String:yytext];
+ return PROPSTRING;
+}
+<props>\] {
+ BEGIN(INITIAL);
+ return RIGHTBRACKET;
+}
+
+\( {
+ BEGIN(noderef);
+ return LEFTPARENTHESIS;
+}
+<noderef>\. {
+ return FULLSTOP;
+}
+ /* we assume node names (and anchor names) never contain
+ newlines */
+<noderef>[^\.\{\)\n]+ {
+ yylval->nsstr=[NSString stringWithUTF8String:yytext];
+ return REFSTRING;
+}
+<noderef>\) {
+ BEGIN(INITIAL);
+ return RIGHTPARENTHESIS;
+}
+
+<INITIAL,props>\{ {
+ NSMutableString *buf = [NSMutableString string];
+ unsigned int brace_depth = 1;
+ unsigned int escape = 0;
+ while (1) {
+ char c = input(yyscanner);
+ // eof reached before closing brace
+ if (c == '\0' || c == EOF) {
+ return UNCLOSED_DELIM_STR;
+ }
+
+ yylloc->last_column += 1;
+ yyleng += 1;
+ if (escape) {
+ escape = 0;
+ } else if (c == '\\') {
+ escape = 1;
+ } else if (c == '{') {
+ brace_depth++;
+ } else if (c == '}') {
+ brace_depth--;
+ if (brace_depth == 0) break;
+ } else if (c == '\n') {
+ yylloc->last_line += 1;
+ yylloc->last_column = 0;
+ }
+ [buf appendFormat:@"%c", c];
+ }
+
+ yylval->nsstr = buf;
+ return DELIMITEDSTRING;
+}
+
+\\begin { return UNKNOWN_BEGIN_CMD; }
+\\end { return UNKNOWN_END_CMD; }
+\\[a-zA-Z0-9]+ { return UNKNOWN_CMD; }
+[a-zA-Z0-9]+ { return UNKNOWN_STR; }
+<INITIAL,xcoord,ycoord,props,noderef>. { return UNKNOWN_STR; }
+
+ /* vi:ft=lex:noet:ts=4:sts=4:sw=4:
+ */
diff --git a/tikzit-old/src/common/tikzparser.ym b/tikzit-old/src/common/tikzparser.ym
new file mode 100644
index 0000000..344e969
--- /dev/null
+++ b/tikzit-old/src/common/tikzparser.ym
@@ -0,0 +1,224 @@
+%{
+/*
+ * Copyright 2010 Chris Heunen
+ * Copyright 2010-2013 Aleks Kissinger
+ * Copyright 2013 K. Johan Paulsson
+ * Copyright 2013 Alex Merry <dev@randomguy3.me.uk>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "tikzparserdefs.h"
+%}
+
+/* we use features added to bison 2.4 */
+%require "2.3"
+
+%error-verbose
+/* enable maintaining locations for better error messages */
+%locations
+/* the name of the header file */
+/*%defines "common/tikzparser.h"*/
+/* make it re-entrant (no global variables) */
+%pure-parser
+/* We use a pure (re-entrant) lexer. This means yylex
+ will take a void* (opaque) type to maintain its state */
+%lex-param {void *scanner}
+/* Since this parser is also pure, yyparse needs to take
+ that lexer state as an argument */
+%parse-param {void *scanner}
+
+/* possible data types for semantic values */
+%union {
+ NSString *nsstr;
+ GraphElementProperty *prop;
+ GraphElementData *data;
+ Node *node;
+ NSPoint pt;
+ struct noderef noderef;
+}
+
+%{
+#import "GraphElementData.h"
+#import "GraphElementProperty.h"
+#import "Node.h"
+#import "Edge.h"
+
+#import "tikzlexer.h"
+#import "TikzGraphAssembler+Parser.h"
+/* the assembler (used by this parser) is stored in the lexer
+ state as "extra" data */
+#define assembler yyget_extra(scanner)
+
+/* pass errors off to the assembler */
+void yyerror(YYLTYPE *yylloc, void *scanner, const char *str) {
+ [assembler reportError:str atLocation:yylloc];
+}
+%}
+
+/* yyloc is set up with first_column = last_column = 1 by default;
+ however, it makes more sense to think of us being "before the
+ start of the line" before we parse anything */
+%initial-action {
+ yylloc.first_column = yylloc.last_column = 0;
+}
+
+
+%token BEGIN_TIKZPICTURE_CMD "\\begin{tikzpicture}"
+%token END_TIKZPICTURE_CMD "\\end{tikzpicture}"
+%token BEGIN_PGFONLAYER_CMD "\\begin{pgfonlayer}"
+%token END_PGFONLAYER_CMD "\\end{pgfonlayer}"
+%token DRAW_CMD "\\draw"
+%token NODE_CMD "\\node"
+%token PATH_CMD "\\path"
+%token RECTANGLE "rectangle"
+%token NODE "node"
+%token AT "at"
+%token TO "to"
+%token SEMICOLON ";"
+%token COMMA ","
+
+%token LEFTPARENTHESIS "("
+%token RIGHTPARENTHESIS ")"
+%token LEFTBRACKET "["
+%token RIGHTBRACKET "]"
+%token FULLSTOP "."
+%token EQUALS "="
+%token <pt> COORD "co-ordinate"
+%token <nsstr> PROPSTRING "key/value string"
+%token <nsstr> REFSTRING "string"
+%token <nsstr> DELIMITEDSTRING "{-delimited string"
+
+%token UNKNOWN_BEGIN_CMD "unknown \\begin command"
+%token UNKNOWN_END_CMD "unknown \\end command"
+%token UNKNOWN_CMD "unknown latex command"
+%token UNKNOWN_STR "unknown string"
+%token UNCLOSED_DELIM_STR "unclosed {-delimited string"
+
+%type<nsstr> nodename
+%type<nsstr> optanchor
+%type<nsstr> val
+%type<prop> property
+%type<data> extraproperties
+%type<data> properties
+%type<data> optproperties
+%type<node> optedgenode
+%type<noderef> noderef
+%type<noderef> optnoderef
+
+%%
+
+tikzpicture: "\\begin{tikzpicture}" optproperties tikzcmds "\\end{tikzpicture}"
+ {
+ if ($2) {
+ [[assembler graph] setData:$2];
+ }
+ };
+tikzcmds: tikzcmds tikzcmd | ;
+tikzcmd: node | edge | boundingbox | ignore;
+
+ignore: "\\begin{pgfonlayer}" DELIMITEDSTRING | "\\end{pgfonlayer}";
+
+optproperties:
+ "[" "]"
+ { $$ = nil; }
+ | "[" properties "]"
+ { $$ = $2; }
+ | { $$ = nil; };
+properties: extraproperties property
+ {
+ [$1 addObject:$2];
+ $$ = $1;
+ };
+extraproperties:
+ extraproperties property ","
+ {
+ [$1 addObject:$2];
+ $$ = $1;
+ }
+ | { $$ = [GraphElementData data]; };
+property:
+ val "=" val
+ { $$ = [GraphElementProperty property:$1 withValue:$3]; }
+ | val
+ { $$ = [GraphElementProperty atom:$1]; };
+val: PROPSTRING { $$ = $1; } | DELIMITEDSTRING { $$ = $1; };
+
+nodename: "(" REFSTRING ")" { $$ = $2; };
+node: "\\node" optproperties nodename "at" COORD DELIMITEDSTRING ";"
+ {
+ Node *node = [[Node alloc] init];
+ if ($2)
+ [node setData:$2];
+ [node setName:$3];
+ [node setPoint:$5];
+ [node setLabel:$6];
+ [assembler addNodeToMap:node];
+ [[assembler graph] addNode:node];
+#if ! __has_feature(objc_arc)
+ [node release];
+#endif
+ };
+
+optanchor: { $$ = nil; } | "." REFSTRING { $$ = $2; };
+noderef: "(" REFSTRING optanchor ")"
+ {
+ $$.node = [assembler nodeWithName:$2];
+ $$.anchor = $3;
+ };
+optnoderef:
+ noderef { $$ = $1; }
+ | "(" ")" { $$.node = nil; $$.anchor = nil; }
+optedgenode:
+ { $$ = nil; }
+ | "node" optproperties DELIMITEDSTRING
+ {
+ $$ = [Node node];
+ if ($2)
+ [$$ setData:$2];
+ [$$ setLabel:$3];
+ }
+edge: "\\draw" optproperties noderef "to" optedgenode optnoderef ";"
+ {
+ Edge *edge = [[Edge alloc] init];
+ if ($2)
+ [edge setData:$2];
+ [edge setSource:$3.node];
+ [edge setSourceAnchor:$3.anchor];
+ [edge setEdgeNode:$5];
+ if ($6.node) {
+ [edge setTarget:$6.node];
+ [edge setTargetAnchor:$6.anchor];
+ } else {
+ [edge setTarget:$3.node];
+ [edge setTargetAnchor:$3.anchor];
+ }
+ [edge setAttributesFromData];
+ [[assembler graph] addEdge:edge];
+#if ! __has_feature(objc_arc)
+ [edge release];
+#endif
+ };
+
+ignoreprop: val | val "=" val;
+ignoreprops: ignoreprop ignoreprops | ;
+optignoreprops: "[" ignoreprops "]";
+boundingbox:
+ "\\path" optignoreprops COORD "rectangle" COORD ";"
+ {
+ [[assembler graph] setBoundingBox:NSRectAroundPoints($3, $5)];
+ };
+
+/* vi:ft=yacc:noet:ts=4:sts=4:sw=4
+*/
diff --git a/tikzit-old/src/common/tikzparserdefs.h b/tikzit-old/src/common/tikzparserdefs.h
new file mode 100644
index 0000000..cde3345
--- /dev/null
+++ b/tikzit-old/src/common/tikzparserdefs.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2013 Alex Merry <dev@randomguy3.me.uk>
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This file sets up some defs (particularly struct noderef) needed for
+ * the tikz parser and its users.
+ *
+ * It is needed because we wish to support bison 2.3, which is the
+ * version shipped with OSX. bison 2.4 onwards allows us to put this
+ * stuff in a "%code requires" block, where it will be put in the
+ * generated header file by bison.
+ *
+ * All the types used by the %union directive in tikzparser.ym should
+ * be declared, defined or imported here.
+ */
+
+// Foundation has NSPoint and NSString
+#import <Foundation/Foundation.h>
+
+@class TikzGraphAssembler;
+@class GraphElementData;
+@class GraphElementProperty;
+@class Node;
+
+struct noderef {
+#if __has_feature(objc_arc)
+ __unsafe_unretained Node *node;
+ __unsafe_unretained NSString *anchor;
+#else
+ Node *node;
+ NSString *anchor;
+#endif
+};
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4
diff --git a/tikzit-old/src/common/util.h b/tikzit-old/src/common/util.h
new file mode 100644
index 0000000..b34f25d
--- /dev/null
+++ b/tikzit-old/src/common/util.h
@@ -0,0 +1,201 @@
+//
+// util.h
+// 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.
+//
+
+#import <Foundation/Foundation.h>
+
+#include <math.h>
+
+#ifndef M_PI
+#define M_PI 3.141592654
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/*!
+ @brief Compute a bounding rectangle for two given points.
+ @param p1 a point.
+ @param p2 another point.
+ @result A bounding rectangle for p1 and p2.
+ */
+NSRect NSRectAroundPoints(NSPoint p1, NSPoint p2);
+
+/*!
+ @brief Compute a bounding rectangle for two given points.
+ @param rect the base rectangle
+ @param the point to ensure is included
+ @result A rectangle containing rect and p
+ */
+NSRect NSRectWithPoint(NSRect rect, NSPoint p);
+
+/*!
+ @brief Compute a bounding rectangle for two given points with a given padding.
+ @param p1 a point.
+ @param p2 another point.
+ @param padding a padding.
+ @result A bounding rectangle for p1 and p2 with padding.
+ */
+NSRect NSRectAroundPointsWithPadding(NSPoint p1, NSPoint p2, float padding);
+
+/*!
+ @brief Compute a bounding rectangle for four given points.
+ @result A bounding rectangle for p1, p2, p3 and p4.
+ */
+NSRect NSRectAround4Points(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4);
+
+/*!
+ @brief Compute a bounding rectangle for four given points.
+ @param padding the amount to pad the rectangle
+ @result A bounding rectangle for p1, p2, p3 and p4 with padding
+ */
+NSRect NSRectAround4PointsWithPadding(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, float padding);
+
+/*!
+ @brief Find the distance between two points
+ @param p1 The first point
+ @param p2 The second point
+ @result The distance between p1 and p2
+ */
+float NSDistanceBetweenPoints(NSPoint p1, NSPoint p2);
+
+/*!
+ @brief Compute the 'real' arctan for two points. Always succeeds and gives a good angle,
+ regardless of sign, zeroes, etc.
+ @param dx the x distance between points.
+ @param dy the y distance between points.
+ @result An angle in radians.
+ */
+float good_atan(float dx, float dy);
+
+/*!
+ @brief Interpolate along a bezier curve to the given distance. To find the x coord,
+ use the relavant x coordinates for c0-c3, and for y use the y's.
+ @param dist a distance from 0 to 1 spanning the whole curve.
+ @param c0 the x (resp. y) coordinate of the start point.
+ @param c1 the x (resp. y) coordinate of the first control point.
+ @param c2 the x (resp. y) coordinate of the second control point.
+ @param c3 the x (resp. y) coordinate of the end point.
+ @result The x (resp. y) coordinate of the point at 'dist'.
+ */
+float bezierInterpolate (float dist, float c0, float c1, float c2, float c3);
+
+/*!
+ @brief Interpolate along a bezier curve to the given distance.
+ @param dist a distance from 0 to 1 spanning the whole curve.
+ @param c0 the x start point.
+ @param c1 the x first control point.
+ @param c2 the x second control point.
+ @param c3 the x end point.
+ @result The point at 'dist'.
+ */
+NSPoint bezierInterpolateFull (float dist, NSPoint c0, NSPoint c1, NSPoint c2, NSPoint c3);
+
+/*!
+ * @brief Find whether two line segments intersect
+ * @param l1start The starting point of line segment 1
+ * @param l1end The ending point of line segment 1
+ * @param l2start The starting point of line segment 2
+ * @param l2end The ending point of line segment 2
+ * @param result A location to store the intersection point
+ * @result YES if they intersect, NO if they do not
+ */
+BOOL lineSegmentsIntersect (NSPoint l1start, NSPoint l1end, NSPoint l2start, NSPoint l2end, NSPoint *result);
+
+/*!
+ * @brief Find whether a line segment intersects a bezier curve
+ * @detail Always finds the intersection furthest along the line segment
+ * @param lstart The starting point of the line segment
+ * @param lend The ending point of the line segment
+ * @param c0 The starting point of the bezier curve
+ * @param c1 The first control point of the bezier curve
+ * @param c2 The second control point of the bezier curve
+ * @param c3 The ending point of the bezier curve
+ * @param result A location to store the intersection point
+ * @result YES if they intersect, NO if they do not
+ */
+BOOL lineSegmentIntersectsBezier (NSPoint lstart, NSPoint lend, NSPoint c0, NSPoint c1, NSPoint c2, NSPoint c3, NSPoint *result);
+
+/*!
+ * @brief Find whether a line segment enters a rectangle
+ * @param lineStart The starting point of the line segment
+ * @param lineEnd The ending point of the line segment
+ * @param rect The rectangle
+ * @result YES if they intersect, NO if they do not
+ */
+BOOL lineSegmentIntersectsRect (NSPoint lineStart, NSPoint lineEnd, NSRect rect);
+
+/*!
+ * @brief Find where a ray exits a rectangle
+ * @param rayStart The starting point of the ray; must be contained in rect
+ * @param angle_rads The angle of the ray, in radians
+ * @param rect The rectangle
+ * @result The point at which the ray leaves the rect
+ */
+NSPoint findExitPointOfRay (NSPoint rayStart, float angle_rads, NSRect rect);
+
+/*!
+ @brief Round val to nearest stepSize
+ @param stepSize the courseness
+ @param val a value to round
+ */
+float roundToNearest(float stepSize, float val);
+
+/*!
+ @brief Convert radians into degrees
+ */
+float radiansToDegrees(float radians);
+
+/*!
+ @brief Convert degrees into radians
+ */
+float degreesToRadians(float degrees);
+
+/*!
+ @brief Normalises an angle (in degrees) to fall between -179 and 180
+ */
+int normaliseAngleDeg (int degrees);
+
+/*!
+ @brief Normalises an angle (in radians) to fall in the range (-pi,pi]
+ */
+float normaliseAngleRad (float rads);
+
+/*!
+ @brief Express a byte as alpha-only hex, with digits (0..16) -> (a..jA..F)
+ @param sh A number 0-255
+ @result A string 'aa'-'FF'
+ */
+NSString *alphaHex(unsigned short sh);
+
+const char *find_start_of_nth_line (const char * string, int line);
+
+/*!
+ @brief Formats a CGFloat as a string, removing trailing zeros
+ @detail Unlike formatting an NSNumber, or using %g, it will never
+ produce scientific notation (like "2.00e2"). Unlike %f,
+ it will not include unnecessary trailing zeros.
+ */
+NSString *formatFloat(CGFloat f, int maxdps);
+
diff --git a/tikzit-old/src/common/util.m b/tikzit-old/src/common/util.m
new file mode 100644
index 0000000..e9b8899
--- /dev/null
+++ b/tikzit-old/src/common/util.m
@@ -0,0 +1,403 @@
+//
+// util.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.
+//
+
+#import "util.h"
+#import "math.h"
+
+static BOOL fuzzyCompare(float f1, float f2) {
+ return (ABS(f1 - f2) <= 0.00001f * MIN(ABS(f1), ABS(f2)));
+}
+
+NSRect NSRectWithPoint(NSRect rect, NSPoint p) {
+ CGFloat minX = NSMinX(rect);
+ CGFloat maxX = NSMaxX(rect);
+ CGFloat minY = NSMinY(rect);
+ CGFloat maxY = NSMaxY(rect);
+ if (p.x < minX) {
+ minX = p.x;
+ } else if (p.x > maxX) {
+ maxX = p.x;
+ }
+ if (p.y < minY) {
+ minY = p.y;
+ } else if (p.y > maxY) {
+ maxY = p.y;
+ }
+ return NSMakeRect(minX, minY, maxX - minX, maxY - minY);
+}
+
+NSRect NSRectAroundPointsWithPadding(NSPoint p1, NSPoint p2, float padding) {
+ return NSMakeRect(MIN(p1.x,p2.x)-padding,
+ MIN(p1.y,p2.y)-padding,
+ ABS(p2.x-p1.x)+(2.0f*padding),
+ ABS(p2.y-p1.y)+(2.0f*padding));
+}
+
+NSRect NSRectAroundPoints(NSPoint p1, NSPoint p2) {
+ return NSRectAroundPointsWithPadding(p1, p2, 0.0f);
+}
+
+NSRect NSRectAround4PointsWithPadding(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, float padding) {
+ float leftMost = MIN(p1.x, p2.x);
+ leftMost = MIN(leftMost, p3.x);
+ leftMost = MIN(leftMost, p4.x);
+ float rightMost = MAX(p1.x, p2.x);
+ rightMost = MAX(rightMost, p3.x);
+ rightMost = MAX(rightMost, p4.x);
+ float topMost = MIN(p1.y, p2.y);
+ topMost = MIN(topMost, p3.y);
+ topMost = MIN(topMost, p4.y);
+ float bottomMost = MAX(p1.y, p2.y);
+ bottomMost = MAX(bottomMost, p3.y);
+ bottomMost = MAX(bottomMost, p4.y);
+ return NSMakeRect(leftMost-padding,
+ topMost-padding,
+ (rightMost - leftMost)+(2.0f*padding),
+ (bottomMost - topMost)+(2.0f*padding));
+}
+
+NSRect NSRectAround4Points(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4) {
+ return NSRectAround4PointsWithPadding(p1, p2, p3, p4, 0.0f);
+}
+
+float NSDistanceBetweenPoints(NSPoint p1, NSPoint p2) {
+ float dx = p2.x - p1.x;
+ float dy = p2.y - p1.y;
+ return sqrt(dx * dx + dy * dy);
+}
+
+float good_atan(float dx, float dy) {
+ if (dx > 0) {
+ return atan(dy/dx);
+ } else if (dx < 0) {
+ return M_PI + atan(dy/dx);
+ } else {
+ if (dy > 0) return 0.5 * M_PI;
+ else if (dy < 0) return 1.5 * M_PI;
+ else return 0;
+ }
+}
+
+// interpolate on a cubic bezier curve
+float bezierInterpolate(float dist, float c0, float c1, float c2, float c3) {
+ float distp = 1 - dist;
+ return (distp*distp*distp) * c0 +
+ 3 * (distp*distp) * dist * c1 +
+ 3 * (dist*dist) * distp * c2 +
+ (dist*dist*dist) * c3;
+}
+
+NSPoint bezierInterpolateFull (float dist, NSPoint c0, NSPoint c1, NSPoint c2, NSPoint c3) {
+ return NSMakePoint (bezierInterpolate (dist, c0.x, c1.x, c2.x, c3.x),
+ bezierInterpolate (dist, c0.y, c1.y, c2.y, c3.y));
+}
+
+static void lineCoeffsFromPoints(NSPoint p1, NSPoint p2, float *A, float *B, float *C) {
+ *A = p2.y - p1.y;
+ *B = p1.x - p2.x;
+ *C = (*A) * p1.x + (*B) * p1.y;
+}
+
+static void lineCoeffsFromPointAndAngle(NSPoint p, float angle, float *A, float *B, float *C) {
+ *A = sin (angle);
+ *B = -cos (angle);
+ *C = (*A) * p.x + (*B) * p.y;
+}
+
+static BOOL lineSegmentContainsPoint(NSPoint l1, NSPoint l2, float x, float y) {
+ float minX = MIN(l1.x, l2.x);
+ float maxX = MAX(l1.x, l2.x);
+ float minY = MIN(l1.y, l2.y);
+ float maxY = MAX(l1.y, l2.y);
+ return (x >= minX || fuzzyCompare (x, minX)) &&
+ (x <= maxX || fuzzyCompare (x, maxX)) &&
+ (y >= minY || fuzzyCompare (y, minY)) &&
+ (y <= maxY || fuzzyCompare (y, maxY));
+}
+
+BOOL lineSegmentsIntersect(NSPoint l1start, NSPoint l1end, NSPoint l2start, NSPoint l2end, NSPoint *result) {
+ // Ax + By = C
+ float A1, B1, C1;
+ lineCoeffsFromPoints(l1start, l1end, &A1, &B1, &C1);
+ float A2, B2, C2;
+ lineCoeffsFromPoints(l2start, l2end, &A2, &B2, &C2);
+
+ float det = A1*B2 - A2*B1;
+ if (det == 0.0f) {
+ // parallel
+ return NO;
+ } else {
+ float x = (B2*C1 - B1*C2)/det;
+ float y = (A1*C2 - A2*C1)/det;
+
+ if (lineSegmentContainsPoint(l1start, l1end, x, y) &&
+ lineSegmentContainsPoint(l2start, l2end, x, y)) {
+ if (result) {
+ (*result).x = x;
+ (*result).y = y;
+ }
+ return YES;
+ }
+ }
+ return NO;
+}
+
+BOOL lineSegmentIntersectsBezier (NSPoint lstart, NSPoint lend, NSPoint c0, NSPoint c1, NSPoint c2, NSPoint c3, NSPoint *result) {
+ NSRect curveBounds = NSRectAround4Points(c0, c1, c2, c3);
+ if (!lineSegmentIntersectsRect(lstart, lend, curveBounds))
+ return NO;
+
+ const int divisions = 20;
+ const float chunkSize = 1.0f/(float)divisions;
+ float chunkStart = 0.0f;
+ BOOL found = NO;
+
+ for (int i = 0; i < divisions; ++i) {
+ float chunkEnd = chunkStart + chunkSize;
+
+ NSPoint p1 = bezierInterpolateFull (chunkStart, c0, c1, c2, c3);
+ NSPoint p2 = bezierInterpolateFull (chunkEnd, c0, c1, c2, c3);
+
+ NSPoint p;
+ if (lineSegmentsIntersect (lstart, lend, p1, p2, &p)) {
+ lstart = p;
+ found = YES;
+ }
+
+ chunkStart = chunkEnd;
+ }
+ if (found && result) {
+ *result = lstart;
+ }
+ return found;
+}
+
+BOOL lineSegmentIntersectsRect(NSPoint lineStart, NSPoint lineEnd, NSRect rect) {
+ const float rectMaxX = NSMaxX(rect);
+ const float rectMinX = NSMinX(rect);
+ const float rectMaxY = NSMaxY(rect);
+ const float rectMinY = NSMinY(rect);
+
+ // check if the segment is entirely to one side of the rect
+ if (lineStart.x > rectMaxX && lineEnd.x > rectMaxX) {
+ return NO;
+ }
+ if (lineStart.x < rectMinX && lineEnd.x < rectMinX) {
+ return NO;
+ }
+ if (lineStart.y > rectMaxY && lineEnd.y > rectMaxY) {
+ return NO;
+ }
+ if (lineStart.y < rectMinY && lineEnd.y < rectMinY) {
+ return NO;
+ }
+
+ // Now check whether the (infinite) line intersects the rect
+ // (if it does, so does the segment, due to above checks)
+
+ // Ax + By = C
+ float A, B, C;
+ lineCoeffsFromPoints(lineStart, lineEnd, &A, &B, &C);
+
+ const float tlVal = A * rectMinX + B * rectMaxY - C;
+ const float trVal = A * rectMaxX + B * rectMaxY - C;
+ const float blVal = A * rectMinX + B * rectMinY - C;
+ const float brVal = A * rectMaxX + B * rectMinY - C;
+
+ if (tlVal < 0 && trVal < 0 && blVal < 0 && brVal < 0) {
+ // rect below line
+ return NO;
+ }
+ if (tlVal > 0 && trVal > 0 && blVal > 0 && brVal > 0) {
+ // rect above line
+ return NO;
+ }
+
+ return YES;
+}
+
+NSPoint findExitPointOfRay (NSPoint p, float angle_rads, NSRect rect) {
+ const float rectMinX = NSMinX (rect);
+ const float rectMaxX = NSMaxX (rect);
+ const float rectMinY = NSMinY (rect);
+ const float rectMaxY = NSMaxY (rect);
+
+ const float angle = normaliseAngleRad (angle_rads);
+
+ // special case the edges
+ if (p.y == rectMaxY && angle > 0 && angle < M_PI) {
+ // along the top of the box
+ return p;
+ }
+ if (p.y == rectMinY && angle < 0 && angle > -M_PI) {
+ // along the bottom of the box
+ return p;
+ }
+ if (p.x == rectMaxX && angle > -M_PI/2.0f && angle < M_PI/2.0f) {
+ // along the right of the box
+ return p;
+ }
+ if (p.x == rectMinX && (angle > M_PI/2.0f || angle < -M_PI/2.0f)) {
+ // along the left of the box
+ return p;
+ }
+
+ float A1, B1, C1;
+ lineCoeffsFromPointAndAngle(p, angle, &A1, &B1, &C1);
+ //NSLog(@"Ray is %fx + %fy = %f", A1, B1, C1);
+
+ const float tlAngle = normaliseAngleRad (good_atan (rectMinX - p.x, rectMaxY - p.y));
+ const float trAngle = normaliseAngleRad (good_atan (rectMaxX - p.x, rectMaxY - p.y));
+ if (angle <= tlAngle && angle >= trAngle) {
+ // exit top
+ float A2, B2, C2;
+ lineCoeffsFromPoints(NSMakePoint (rectMinX, rectMaxY),
+ NSMakePoint (rectMaxX, rectMaxY),
+ &A2, &B2, &C2);
+ float det = A1*B2 - A2*B1;
+ NSCAssert(det != 0.0f, @"Parallel lines?");
+ NSPoint intersect = NSMakePoint ((B2*C1 - B1*C2)/det,
+ (A1*C2 - A2*C1)/det);
+ return intersect;
+ }
+
+ const float brAngle = normaliseAngleRad (good_atan (rectMaxX - p.x, rectMinY - p.y));
+ if (angle <= trAngle && angle >= brAngle) {
+ // exit right
+ float A2, B2, C2;
+ lineCoeffsFromPoints(NSMakePoint (rectMaxX, rectMaxY),
+ NSMakePoint (rectMaxX, rectMinY),
+ &A2, &B2, &C2);
+ //NSLog(@"Edge is %fx + %fy = %f", A2, B2, C2);
+ float det = A1*B2 - A2*B1;
+ NSCAssert(det != 0.0f, @"Parallel lines?");
+ NSPoint intersect = NSMakePoint ((B2*C1 - B1*C2)/det,
+ (A1*C2 - A2*C1)/det);
+ return intersect;
+ }
+
+ const float blAngle = normaliseAngleRad (good_atan (rectMinX - p.x, rectMinY - p.y));
+ if (angle <= brAngle && angle >= blAngle) {
+ // exit bottom
+ float A2, B2, C2;
+ lineCoeffsFromPoints(NSMakePoint (rectMaxX, rectMinY),
+ NSMakePoint (rectMinX, rectMinY),
+ &A2, &B2, &C2);
+ float det = A1*B2 - A2*B1;
+ NSCAssert(det != 0.0f, @"Parallel lines?");
+ NSPoint intersect = NSMakePoint ((B2*C1 - B1*C2)/det,
+ (A1*C2 - A2*C1)/det);
+ return intersect;
+ } else {
+ // exit left
+ float A2, B2, C2;
+ lineCoeffsFromPoints(NSMakePoint (rectMinX, rectMaxY),
+ NSMakePoint (rectMinX, rectMinY),
+ &A2, &B2, &C2);
+ float det = A1*B2 - A2*B1;
+ NSCAssert(det != 0.0f, @"Parallel lines?");
+ NSPoint intersect = NSMakePoint ((B2*C1 - B1*C2)/det,
+ (A1*C2 - A2*C1)/det);
+ return intersect;
+ }
+}
+
+float roundToNearest(float stepSize, float val) {
+ if (stepSize==0.0f) return val;
+ else return round(val/stepSize)*stepSize;
+}
+
+float radiansToDegrees (float radians) {
+ return (radians * 180.0f) / M_PI;
+}
+
+float degreesToRadians(float degrees) {
+ return (degrees * M_PI) / 180.0f;
+}
+
+int normaliseAngleDeg (int degrees) {
+ while (degrees > 180) {
+ degrees -= 360;
+ }
+ while (degrees <= -180) {
+ degrees += 360;
+ }
+ return degrees;
+}
+
+float normaliseAngleRad (float rads) {
+ while (rads > M_PI) {
+ rads -= 2 * M_PI;
+ }
+ while (rads <= -M_PI) {
+ rads += 2 * M_PI;
+ }
+ return rads;
+}
+
+static char ahex[] =
+{'a','b','c','d','e','f','g','h','i','j',
+ 'A','B','C','D','E','F'};
+
+NSString *alphaHex(unsigned short sh) {
+ if (sh > 255) return @"!!";
+ return [NSString stringWithFormat:@"%c%c", ahex[sh/16], ahex[sh%16]];
+}
+
+const char *find_start_of_nth_line (const char * string, int line) {
+ int l = 0;
+ const char *lineStart = string;
+ while (*lineStart && l < line) {
+ while (*lineStart && *lineStart != '\n') {
+ ++lineStart;
+ }
+ if (*lineStart) {
+ ++l;
+ ++lineStart;
+ }
+ }
+ return lineStart;
+}
+
+NSString *formatFloat(CGFloat f, int maxdps) {
+ NSMutableString *result = [NSMutableString
+ stringWithFormat:@"%.*f", maxdps, f];
+ // delete trailing zeros
+ NSUInteger lastPos = [result length] - 1;
+ NSUInteger firstDigit = ([result characterAtIndex:0] == '-') ? 1 : 0;
+ while (lastPos > firstDigit) {
+ if ([result characterAtIndex:lastPos] == '0') {
+ [result deleteCharactersInRange:NSMakeRange(lastPos, 1)];
+ lastPos -= 1;
+ } else {
+ break;
+ }
+ }
+ if ([result characterAtIndex:lastPos] == '.') {
+ [result deleteCharactersInRange:NSMakeRange(lastPos, 1)];
+ lastPos -= 1;
+ }
+ if ([@"-0" isEqualToString:result])
+ return @"0";
+ else
+ return result;
+}
+
+// vi:ft=objc:noet:ts=4:sts=4:sw=4