summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrandomguy3 <randomguy3@7c02a99a-9b00-45e3-bf44-6f3dd7fddb64>2012-01-17 18:39:31 +0000
committerrandomguy3 <randomguy3@7c02a99a-9b00-45e3-bf44-6f3dd7fddb64>2012-01-17 18:39:31 +0000
commitf90dd4ffc4b679e61a2a8cf43853b7d3c72c3e83 (patch)
tree15842d8311e621dc2776a77c9740bd72784f13f1
parent18871fdd7bbfb43eb0971ee358554f321f789eee (diff)
Calculate the head and tail of edges to be just where they contact the node (ie: behave more like tikz).
git-svn-id: https://tikzit.svn.sourceforge.net/svnroot/tikzit/trunk@388 7c02a99a-9b00-45e3-bf44-6f3dd7fddb64
-rw-r--r--tikzit/NEWS2
-rw-r--r--tikzit/src/common/Edge.h26
-rw-r--r--tikzit/src/common/Edge.m43
-rw-r--r--tikzit/src/common/Node.m14
-rw-r--r--tikzit/src/common/test/Makefile14
-rw-r--r--tikzit/src/common/test/color.m6
-rw-r--r--tikzit/src/common/test/common.m4
-rw-r--r--tikzit/src/common/test/maths.m562
-rw-r--r--tikzit/src/common/test/parser.m7
-rw-r--r--tikzit/src/common/test/test.h16
-rw-r--r--tikzit/src/common/test/test.m79
-rw-r--r--tikzit/src/common/util.h47
-rw-r--r--tikzit/src/common/util.m147
-rw-r--r--tikzit/src/linux/Edge+Render.m8
-rw-r--r--tikzit/src/linux/GraphRenderer.m8
-rw-r--r--tikzit/src/linux/Node+Render.h4
-rw-r--r--tikzit/src/linux/Node+Render.m31
17 files changed, 975 insertions, 43 deletions
diff --git a/tikzit/NEWS b/tikzit/NEWS
index 06fe57d..33686b5 100644
--- a/tikzit/NEWS
+++ b/tikzit/NEWS
@@ -6,6 +6,8 @@ tikzit 0.8 (2012-01-??):
* Add support for multiple custom preambles
* The path to pdflatex is now configurable
* Make everything look a bit better
+ * Edges now start from the edge of a node, not the centre,
+ which is what tikz does
tikzit 0.7 (2011-12-06):
* Add bounding box support
diff --git a/tikzit/src/common/Edge.h b/tikzit/src/common/Edge.h
index 6069b9f..b92d7a7 100644
--- a/tikzit/src/common/Edge.h
+++ b/tikzit/src/common/Edge.h
@@ -142,14 +142,36 @@ typedef enum {
@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. Computed from the source, target, and bend.
+ @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. Computed from the source, target, and bend.
+ @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;
diff --git a/tikzit/src/common/Edge.m b/tikzit/src/common/Edge.m
index efd9707..1878165 100644
--- a/tikzit/src/common/Edge.m
+++ b/tikzit/src/common/Edge.m
@@ -22,6 +22,7 @@
//
#import "Edge.h"
+#import "Shape.h"
#import "util.h"
@implementation Edge
@@ -73,7 +74,37 @@
}
- (NSPoint) _findContactPointOn:(Node*)node at:(float)angle {
- return [node point];
+ NSPoint rayStart = [node point];
+ Shape *shape = [node shape];
+ if (shape == nil) {
+ return rayStart;
+ }
+
+ Transformer *shapeTrans = [node shapeTransformer];
+ NSRect searchArea = [node boundsUsingShapeTransform:shapeTrans];
+ 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->head],
+ [shapeTrans toScreen:curve->cp1],
+ [shapeTrans toScreen:curve->cp2],
+ [shapeTrans toScreen:curve->tail],
+ &intersect)) {
+ // we just keep shortening the line
+ rayStart = intersect;
+ }
+ }
+ }
+
+ return rayStart;
}
- (void)updateControls {
@@ -190,6 +221,16 @@
return NSMakePoint(mid.x - (mid.y - midTan.y), mid.y + (mid.x - midTan.x));
}
+- (NSPoint) head {
+ [self updateControls];
+ return head;
+}
+
+- (NSPoint) tail {
+ [self updateControls];
+ return tail;
+}
+
- (NSPoint)cp1 {
[self updateControls];
return cp1;
diff --git a/tikzit/src/common/Node.m b/tikzit/src/common/Node.m
index 8880826..136fe20 100644
--- a/tikzit/src/common/Node.m
+++ b/tikzit/src/common/Node.m
@@ -24,7 +24,6 @@
#import "Node.h"
#import "Shape.h"
-#import "ShapeNames.h"
@implementation Node
@@ -48,7 +47,7 @@
if (style) {
return [Shape shapeForName:[style shapeName]];
} else {
- return [Shape shapeForName:SHAPE_CIRCLE];
+ return nil;
}
}
@@ -74,10 +73,13 @@
}
- (NSRect) boundsUsingShapeTransform:(Transformer*)shapeTrans {
- float strokeThickness = style ? [style strokeThickness] : [NodeStyle defaultStrokeThickness];
- NSRect screenBounds = [shapeTrans rectToScreen:[[self shape] boundingRect]];
- screenBounds = NSInsetRect(screenBounds, -strokeThickness, -strokeThickness);
- return screenBounds;
+ //if (style) {
+ return [shapeTrans rectToScreen:[[self shape] boundingRect]];
+ /*} else {
+ NSRect r = NSZeroRect;
+ r.origin = [shapeTrans toScreen:[self point]];
+ return r;
+ }*/
}
- (NSRect) boundingRect {
diff --git a/tikzit/src/common/test/Makefile b/tikzit/src/common/test/Makefile
new file mode 100644
index 0000000..d158d16
--- /dev/null
+++ b/tikzit/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/src/common/test/color.m b/tikzit/src/common/test/color.m
index e7a80ba..48a6ff4 100644
--- a/tikzit/src/common/test/color.m
+++ b/tikzit/src/common/test/color.m
@@ -23,7 +23,11 @@
#import "test/test.h"
#import "ColorRGB.h"
+#ifdef STAND_ALONE
+void runTests() {
+#else
void testColor() {
+#endif
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
startTestBlock(@"color");
@@ -73,4 +77,4 @@ void testColor() {
endTestBlock(@"color");
[pool drain];
-} \ No newline at end of file
+}
diff --git a/tikzit/src/common/test/common.m b/tikzit/src/common/test/common.m
index ee6cb5f..c9ac980 100644
--- a/tikzit/src/common/test/common.m
+++ b/tikzit/src/common/test/common.m
@@ -23,10 +23,12 @@
#import "test/test.h"
void testParser();
void testColor();
+void testMaths();
void testCommon() {
startTestBlock(@"common");
testParser();
testColor();
+ testMaths();
endTestBlock(@"common");
-} \ No newline at end of file
+}
diff --git a/tikzit/src/common/test/maths.m b/tikzit/src/common/test/maths.m
new file mode 100644
index 0000000..a11e58e
--- /dev/null
+++ b/tikzit/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/src/common/test/parser.m b/tikzit/src/common/test/parser.m
index fc9e76e..29dabe7 100644
--- a/tikzit/src/common/test/parser.m
+++ b/tikzit/src/common/test/parser.m
@@ -23,7 +23,12 @@
#import "test/test.h"
#import "TikzGraphAssembler.h"
+
+#ifdef STAND_ALONE
+void runTests() {
+#else
void testParser() {
+#endif
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
startTestBlock(@"parser");
@@ -75,4 +80,4 @@ void testParser() {
endTestBlock(@"parser");
[pool drain];
-} \ No newline at end of file
+}
diff --git a/tikzit/src/common/test/test.h b/tikzit/src/common/test/test.h
index 4fddc92..59dcdd4 100644
--- a/tikzit/src/common/test/test.h
+++ b/tikzit/src/common/test/test.h
@@ -26,8 +26,17 @@
{}
@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();
@@ -39,3 +48,10 @@ void endTestBlock(NSString *name);
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/src/common/test/test.m b/tikzit/src/common/test/test.m
index 5e05c6e..8437646 100644
--- a/tikzit/src/common/test/test.m
+++ b/tikzit/src/common/test/test.m
@@ -57,13 +57,64 @@ static NSString *indents[6] =
@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) {
- PUTS(@"%@[%@PASS%@] %@", INDENT, GREEN, OFF, msg);
- ++PASSES;
+ 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 {
- PUTS(@"%@[%@FAIL%@] %@", INDENT, RED, OFF, msg);
- ++FAILS;
+ 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);
}
}
@@ -102,3 +153,23 @@ void setColorEnabled(BOOL b) {
OFF = @"";
}
}
+
+#ifdef STAND_ALONE
+void runTests();
+
+int main() {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ setColorEnabled (YES);
+ startTests();
+
+ runTests();
+
+ endTests();
+
+ [pool drain];
+ return 0;
+}
+#endif
+
+// vim:ft=objc:ts=4:sts=4:sw=4:noet
diff --git a/tikzit/src/common/util.h b/tikzit/src/common/util.h
index 82ba9d8..809e8ea 100644
--- a/tikzit/src/common/util.h
+++ b/tikzit/src/common/util.h
@@ -91,7 +91,18 @@ float good_atan(float dx, float dy);
@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);
+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
@@ -102,7 +113,21 @@ float bezierInterpolate(float dist, float c0, float c1, float c2, float c3);
* @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);
+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
@@ -111,7 +136,16 @@ BOOL lineSegmentsIntersect(NSPoint l1start, NSPoint l1end, NSPoint l2start, NSPo
* @param rect The rectangle
* @result YES if they intersect, NO if they do not
*/
-BOOL lineSegmentIntersectsRect(NSPoint lineStart, NSPoint lineEnd, NSRect rect);
+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
@@ -126,11 +160,16 @@ float roundToNearest(float stepSize, float val);
float radiansToDegrees(float radians);
/*!
- @brief Normalises an angle (in degrees) to fall between -359 and 359
+ @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'
diff --git a/tikzit/src/common/util.m b/tikzit/src/common/util.m
index 0445126..3dec971 100644
--- a/tikzit/src/common/util.m
+++ b/tikzit/src/common/util.m
@@ -82,17 +82,28 @@ float bezierInterpolate(float dist, float c0, float c1, float c2, float c3) {
(dist*dist*dist) * c3;
}
-void lineCoeffsFromPoints(NSPoint p1, NSPoint p2, float *A, float *B, float *C) {
+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 maxX = l1.x > l2.x ? l2.x : l1.x;
- float minX = l1.x > l2.x ? l1.x : l2.x;
- float maxY = l1.y > l2.y ? l2.y : l1.y;
- float minY = l1.y > l2.y ? l1.y : l2.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 && x <= maxX && y >= minY && y <= maxY;
}
@@ -123,6 +134,36 @@ BOOL lineSegmentsIntersect(NSPoint l1start, NSPoint l1end, NSPoint l2start, NSPo
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);
@@ -167,6 +208,92 @@ BOOL lineSegmentIntersectsRect(NSPoint lineStart, NSPoint lineEnd, NSRect rect)
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;
@@ -186,6 +313,16 @@ int normaliseAngleDeg (int degrees) {
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'};
diff --git a/tikzit/src/linux/Edge+Render.m b/tikzit/src/linux/Edge+Render.m
index a6738b7..c64b35f 100644
--- a/tikzit/src/linux/Edge+Render.m
+++ b/tikzit/src/linux/Edge+Render.m
@@ -125,14 +125,14 @@ static const float cpLineWidth = 1.0;
}
- (void) createStrokePathInContext:(id<RenderContext>)context withTransformer:(Transformer*)transformer {
- NSPoint c_source = [transformer toScreen:src];
+ NSPoint c_head = [transformer toScreen:head];
NSPoint c_cp1 = [transformer toScreen:cp1];
NSPoint c_cp2 = [transformer toScreen:cp2];
- NSPoint c_target = [transformer toScreen:targ];
+ NSPoint c_tail = [transformer toScreen:tail];
[context startPath];
- [context moveTo:c_source];
- [context curveTo:c_target withCp1:c_cp1 andCp2:c_cp2];
+ [context moveTo:c_head];
+ [context curveTo:c_tail withCp1:c_cp1 andCp2:c_cp2];
if ([self style] != nil) {
// draw edge decoration
diff --git a/tikzit/src/linux/GraphRenderer.m b/tikzit/src/linux/GraphRenderer.m
index c964f1b..571390f 100644
--- a/tikzit/src/linux/GraphRenderer.m
+++ b/tikzit/src/linux/GraphRenderer.m
@@ -140,7 +140,7 @@ void graph_renderer_expose_event(GtkWidget *widget, GdkEventExpose *event);
if (node == nil) {
return;
}
- NSRect nodeRect = [node boundsWithLabelOnSurface:surface];
+ NSRect nodeRect = [node renderBoundsWithLabelForSurface:surface];
nodeRect = NSInsetRect (nodeRect, -2.0f, -2.0f);
[surface invalidateRect:nodeRect];
}
@@ -170,7 +170,7 @@ void graph_renderer_expose_event(GtkWidget *widget, GdkEventExpose *event);
}
- (BOOL) point:(NSPoint)p fuzzyHitsNode:(Node*)node {
- NSRect bounds = [node boundsOnSurface:surface];
+ NSRect bounds = [node renderBoundsForSurface:surface];
return NSPointInRect(p, bounds);
}
@@ -307,13 +307,13 @@ void graph_renderer_expose_event(GtkWidget *widget, GdkEventExpose *event);
- (void) invalidateHalfEdge {
if (halfEdgeOrigin != nil) {
NSRect invRect = NSRectAroundPoints(halfEdgeEnd, halfEdgeOriginPoint);
- invRect = NSUnionRect(invRect, [halfEdgeOrigin boundsWithLabelOnSurface:surface]);
+ invRect = NSUnionRect(invRect, [halfEdgeOrigin renderBoundsWithLabelForSurface:surface]);
NSEnumerator *enumerator = [doc nodeEnumerator];
Node *node;
while ((node = [enumerator nextObject]) != nil) {
if ([self point:halfEdgeEnd fuzzyHitsNode:node]) {
- invRect = NSUnionRect(invRect, [node boundsWithLabelOnSurface:surface]);
+ invRect = NSUnionRect(invRect, [node renderBoundsWithLabelForSurface:surface]);
}
}
[surface invalidateRect:NSInsetRect (invRect, -2.0f, -2.0f)];
diff --git a/tikzit/src/linux/Node+Render.h b/tikzit/src/linux/Node+Render.h
index 49667e1..60d2573 100644
--- a/tikzit/src/linux/Node+Render.h
+++ b/tikzit/src/linux/Node+Render.h
@@ -30,8 +30,8 @@ enum NodeState {
- (Transformer*) shapeTransformerForSurface:(id<Surface>)surface;
// the total rendered bounds, excluding label
-- (NSRect) boundsOnSurface:(id<Surface>)surface;
-- (NSRect) boundsWithLabelOnSurface:(id<Surface>)surface;
+- (NSRect) renderBoundsForSurface:(id<Surface>)surface;
+- (NSRect) renderBoundsWithLabelForSurface:(id<Surface>)surface;
- (NSString*) renderedLabel;
- (NSSize) renderedLabelSizeInContext:(id<RenderContext>)context;
- (void) renderLabelToSurface:(id<Surface>)surface withContext:(id<RenderContext>)context;
diff --git a/tikzit/src/linux/Node+Render.m b/tikzit/src/linux/Node+Render.m
index 7450dba..907d818 100644
--- a/tikzit/src/linux/Node+Render.m
+++ b/tikzit/src/linux/Node+Render.m
@@ -27,16 +27,31 @@
@implementation Node (Render)
+- (Shape*) shapeToRender {
+ if (style) {
+ return [Shape shapeForName:[style shapeName]];
+ } else {
+ return [Shape shapeForName:SHAPE_CIRCLE];
+ }
+}
+
- (Transformer*) shapeTransformerForSurface:(id<Surface>)surface {
return [self shapeTransformerFromTransformer:[surface transformer]];
}
-- (NSRect) boundsOnSurface:(id<Surface>)surface {
- return [self boundsUsingShapeTransform:[self shapeTransformerForSurface:surface]];
+- (NSRect) renderBoundsUsingShapeTransform:(Transformer*)shapeTrans {
+ float strokeThickness = style ? [style strokeThickness] : [NodeStyle defaultStrokeThickness];
+ NSRect screenBounds = [shapeTrans rectToScreen:[[self shapeToRender] boundingRect]];
+ screenBounds = NSInsetRect(screenBounds, -strokeThickness, -strokeThickness);
+ return screenBounds;
+}
+
+- (NSRect) renderBoundsForSurface:(id<Surface>)surface {
+ return [self renderBoundsUsingShapeTransform:[self shapeTransformerForSurface:surface]];
}
-- (NSRect) boundsWithLabelOnSurface:(id<Surface>)surface {
- NSRect nodeBounds = [self boundsOnSurface:surface];
+- (NSRect) renderBoundsWithLabelForSurface:(id<Surface>)surface {
+ NSRect nodeBounds = [self renderBoundsForSurface:surface];
NSRect labelRect = NSZeroRect;
if (![label isEqual:@""]) {
id<RenderContext> cr = [surface createRenderContext];
@@ -127,7 +142,7 @@
[context saveState];
- [[self shape] drawPathWithTransform:shapeTrans andContext:context];
+ [[self shapeToRender] drawPathWithTransform:shapeTrans andContext:context];
[context setLineWidth:strokeThickness];
if (!style) {
@@ -145,7 +160,7 @@
alpha = 0.25f;
RColor selectionColor = MakeSolidRColor(0.61f, 0.735f, 1.0f);
- [[self shape] drawPathWithTransform:shapeTrans andContext:context];
+ [[self shapeToRender] drawPathWithTransform:shapeTrans andContext:context];
[context strokePathWithColor:selectionColor andFillWithColor:selectionColor usingAlpha:alpha];
}
@@ -156,7 +171,7 @@
- (BOOL) hitByPoint:(NSPoint)p onSurface:(id<Surface>)surface {
Transformer *shapeTrans = [self shapeTransformerForSurface:surface];
- NSRect screenBounds = [self boundsUsingShapeTransform:shapeTrans];
+ NSRect screenBounds = [self renderBoundsUsingShapeTransform:shapeTrans];
if (!NSPointInRect(p, screenBounds)) {
return NO;
}
@@ -164,7 +179,7 @@
float strokeThickness = style ? [style strokeThickness] : [NodeStyle defaultStrokeThickness];
id<RenderContext> ctx = [surface createRenderContext];
[ctx setLineWidth:strokeThickness];
- [[self shape] drawPathWithTransform:shapeTrans andContext:ctx];
+ [[self shapeToRender] drawPathWithTransform:shapeTrans andContext:ctx];
return [ctx strokeIncludesPoint:p] || [ctx fillIncludesPoint:p];
}