From 525059964fbaf380ad2c3079b965d64e7c6d06d1 Mon Sep 17 00:00:00 2001 From: randomguy3 Date: Mon, 16 Jan 2012 16:38:35 +0000 Subject: Some cleanup before calculating proper head and tail endpoints for Edge git-svn-id: https://tikzit.svn.sourceforge.net/svnroot/tikzit/trunk@379 7c02a99a-9b00-45e3-bf44-6f3dd7fddb64 --- tikzit/src/common/Edge.h | 2 + tikzit/src/common/Edge.m | 43 +++++++++++--------- tikzit/src/common/Node.h | 43 ++++++++++++++++++++ tikzit/src/common/Node.m | 43 ++++++++++++++++++++ tikzit/src/common/NodeStyle.h | 2 + tikzit/src/common/NodeStyle.m | 3 +- tikzit/src/common/util.h | 27 +++++++++++++ tikzit/src/common/util.m | 90 +++++++++++++++++++++++++++++++++++++++++- tikzit/src/linux/Node+Render.m | 30 ++------------ tikzit/src/osx/NodeLayer.m | 2 +- 10 files changed, 235 insertions(+), 50 deletions(-) diff --git a/tikzit/src/common/Edge.h b/tikzit/src/common/Edge.h index 460ecc4..6069b9f 100644 --- a/tikzit/src/common/Edge.h +++ b/tikzit/src/common/Edge.h @@ -67,6 +67,8 @@ typedef enum { // these are all cached values computed from the above NSPoint src; NSPoint targ; + NSPoint head; + NSPoint tail; NSPoint cp1; NSPoint cp2; NSPoint mid; diff --git a/tikzit/src/common/Edge.m b/tikzit/src/common/Edge.m index c89be6d..680ddfa 100644 --- a/tikzit/src/common/Edge.m +++ b/tikzit/src/common/Edge.m @@ -72,6 +72,13 @@ return NO; } +- (NSPoint) _findContactPointOn:(Node*)node at:(float)angle { + return [node point]; + Transformer *shapeTrans = [node shapeTransformer]; + NSRect searchArea = [node boundsUsingShapeTransform:shapeTrans]; + NSPoint +} + - (void)updateControls { // check for external modification to the node locations if (src.x != [source point].x || src.y != [source point].y || @@ -90,9 +97,6 @@ float angleSrc = 0.0f; float angleTarg = 0.0f; - bend = normaliseAngleDeg (bend); - inAngle = normaliseAngleDeg (inAngle); - outAngle = normaliseAngleDeg (outAngle); if (bendMode == EdgeBendModeBasic) { float angle = good_atan(dx, dy); float bnd = (float)bend * (M_PI / 180.0f); @@ -102,6 +106,9 @@ angleSrc = (float)outAngle * (M_PI / 180.0f); angleTarg = (float)inAngle * (M_PI / 180.0f); } + + head = [self _findContactPointOn:source at:angleSrc]; + tail = [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; @@ -139,8 +146,8 @@ float angle = good_atan(dx, dy); float bnd = (float)bend * (M_PI / 180.0f); - outAngle = round((angle - bnd) * (180.0f/M_PI)); - inAngle = round((M_PI + angle + bnd) * (180.0f/M_PI)); + [self setOutAngle:round((angle - bnd) * (180.0f/M_PI))]; + [self setInAngle:round((M_PI + angle + bnd) * (180.0f/M_PI))]; dirty = YES; } @@ -155,9 +162,7 @@ bend1 = outAngle - angle; bend2 = angle - inAngle; - bend = (bend1 + bend2) / 2; - - dirty = YES; + [self setBend:(bend1 + bend2) / 2]; } - (BOOL)isSelfLoop { @@ -200,13 +205,13 @@ - (int)inAngle {return inAngle;} - (void)setInAngle:(int)a { - inAngle = a; + inAngle = normaliseAngleDeg (a); dirty = YES; } - (int)outAngle {return outAngle;} - (void)setOutAngle:(int)a { - outAngle = a; + outAngle = normaliseAngleDeg (a); dirty = YES; } @@ -218,7 +223,7 @@ - (int)bend {return bend;} - (void)setBend:(int)b { - bend = b; + bend = normaliseAngleDeg (b); dirty = YES; } @@ -368,22 +373,22 @@ bendMode = EdgeBendModeBasic; if ([data isAtomSet:@"bend left"]) { - bend = -30; + [self setBend:-30]; } else if ([data isAtomSet:@"bend right"]) { - bend = 30; + [self setBend:30]; } else if ([data propertyForKey:@"bend left"] != nil) { NSString *bnd = [data propertyForKey:@"bend left"]; - bend = -[bnd intValue]; + [self setBend:-[bnd intValue]]; } else if ([data propertyForKey:@"bend right"] != nil) { NSString *bnd = [data propertyForKey:@"bend right"]; - bend = [bnd intValue]; + [self setBend:[bnd intValue]]; } else { - bend = 0; + [self setBend:0]; if ([data propertyForKey:@"in"] != nil && [data propertyForKey:@"out"] != nil) { bendMode = EdgeBendModeInOut; - inAngle = [[data propertyForKey:@"in"] intValue]; - outAngle = [[data propertyForKey:@"out"] intValue]; + [self setInAngle:[[data propertyForKey:@"in"] intValue]]; + [self setOutAngle:[[data propertyForKey:@"out"] intValue]]; } } @@ -492,7 +497,7 @@ inAngle = outAngle; outAngle = f; - bend = -1 * bend; + [self setBend:-bend]; dirty = YES; } diff --git a/tikzit/src/common/Node.h b/tikzit/src/common/Node.h index 73a4653..c3a604d 100644 --- a/tikzit/src/common/Node.h +++ b/tikzit/src/common/Node.h @@ -29,6 +29,9 @@ #import "NodeStyle.h" #import "GraphElementData.h" +@class Shape; +@class Transformer; + /*! @class Node @brief A graph node, with associated location and style data. @@ -41,6 +44,14 @@ 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. + */ +@property (readonly) Shape *shape; + /*! @property point @brief The point where this node is located. @@ -85,6 +96,38 @@ */ - (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. diff --git a/tikzit/src/common/Node.m b/tikzit/src/common/Node.m index 3421325..8880826 100644 --- a/tikzit/src/common/Node.m +++ b/tikzit/src/common/Node.m @@ -23,6 +23,9 @@ #import "Node.h" +#import "Shape.h" +#import "ShapeNames.h" + @implementation Node @@ -41,6 +44,46 @@ return self; } +- (Shape*) shape { + if (style) { + return [Shape shapeForName:[style shapeName]]; + } else { + return [Shape shapeForName:SHAPE_CIRCLE]; + } +} + +- (Transformer*) shapeTransformerFromTransformer:(Transformer*)t { + // we take a copy to keep the reflection attributes + Transformer *transformer = [[t copy] autorelease]; + 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 { + float strokeThickness = style ? [style strokeThickness] : [NodeStyle defaultStrokeThickness]; + NSRect screenBounds = [shapeTrans rectToScreen:[[self shape] boundingRect]]; + screenBounds = NSInsetRect(screenBounds, -strokeThickness, -strokeThickness); + return screenBounds; +} + +- (NSRect) boundingRect { + return [self boundsUsingShapeTransform:[self shapeTransformer]]; +} + - (BOOL)attachStyleFromTable:(NSArray*)styles { NSString *style_name = [[[data propertyForKey:@"style"] retain] autorelease]; diff --git a/tikzit/src/common/NodeStyle.h b/tikzit/src/common/NodeStyle.h index 4c0e883..eed5b32 100644 --- a/tikzit/src/common/NodeStyle.h +++ b/tikzit/src/common/NodeStyle.h @@ -93,6 +93,8 @@ @property (readonly) BOOL strokeColorIsKnown; @property (readonly) BOOL fillColorIsKnown; ++ (int) defaultStrokeThickness; + /*! @brief Designated initializer. Construct a blank style with name 'new'. @result A default style. diff --git a/tikzit/src/common/NodeStyle.m b/tikzit/src/common/NodeStyle.m index d3e8f09..a2d22c7 100644 --- a/tikzit/src/common/NodeStyle.m +++ b/tikzit/src/common/NodeStyle.m @@ -26,11 +26,12 @@ @implementation NodeStyle ++ (int) defaultStrokeThickness { return 1; } - (id)init { self = [super initWithNotificationName:@"NodeStylePropertyChanged"]; if (self != nil) { - strokeThickness = 1; + strokeThickness = [NodeStyle defaultStrokeThickness]; scale = 1.0f; strokeColorRGB = [[ColorRGB alloc] initWithRed:0 green:0 blue:0]; fillColorRGB = [[ColorRGB alloc] initWithRed:255 green:255 blue:255]; diff --git a/tikzit/src/common/util.h b/tikzit/src/common/util.h index 74871dc..82ba9d8 100644 --- a/tikzit/src/common/util.h +++ b/tikzit/src/common/util.h @@ -26,6 +26,14 @@ #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. @@ -85,6 +93,25 @@ float good_atan(float dx, float dy); */ float bezierInterpolate(float dist, float c0, float c1, float c2, float 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 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 Round val to nearest stepSize diff --git a/tikzit/src/common/util.m b/tikzit/src/common/util.m index feef76c..0445126 100644 --- a/tikzit/src/common/util.m +++ b/tikzit/src/common/util.m @@ -82,6 +82,91 @@ 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) { + *A = p2.y - p1.y; + *B = p1.x - p2.x; + *C = (*A) * p1.x + (*B) * p1.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; + return x >= minX && x <= maxX && y >= minY && 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 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; +} + float roundToNearest(float stepSize, float val) { if (stepSize==0.0f) return val; else return round(val/stepSize)*stepSize; @@ -92,10 +177,10 @@ float radiansToDegrees (float radians) { } int normaliseAngleDeg (int degrees) { - while (degrees >= 360) { + while (degrees > 180) { degrees -= 360; } - while (degrees <= -360) { + while (degrees <= -180) { degrees += 360; } return degrees; @@ -111,3 +196,4 @@ NSString *alphaHex(unsigned short sh) { } +// vi:ft=objc:noet:ts=4:sts=4:sw=4 diff --git a/tikzit/src/linux/Node+Render.m b/tikzit/src/linux/Node+Render.m index f168924..7450dba 100644 --- a/tikzit/src/linux/Node+Render.m +++ b/tikzit/src/linux/Node+Render.m @@ -21,7 +21,6 @@ #import "Shape+Render.h" #import "ShapeNames.h" -#define DEFAULT_STROKE_WIDTH 2.0f #define MAX_LABEL_LENGTH 10 #define LABEL_PADDING_X 2 #define LABEL_PADDING_Y 2 @@ -29,30 +28,7 @@ @implementation Node (Render) - (Transformer*) shapeTransformerForSurface:(id)surface { - Transformer *transformer = [[[surface transformer] copy] autorelease]; - NSPoint screenPos = [[surface transformer] toScreen:point]; - [transformer setOrigin:screenPos]; - CGFloat scale = [[surface transformer] scale]; - if (style) { - scale *= [style scale]; - } - [transformer setScale:scale]; - return transformer; -} - -- (Shape*) shape { - if (style) { - return [Shape shapeForName:[style shapeName]]; - } else { - return [Shape shapeForName:SHAPE_CIRCLE]; - } -} - -- (NSRect) boundsUsingShapeTransform:(Transformer*)shapeTrans { - float strokeThickness = style ? [style strokeThickness] : DEFAULT_STROKE_WIDTH; - NSRect screenBounds = [shapeTrans rectToScreen:[[self shape] boundingRect]]; - screenBounds = NSInsetRect(screenBounds, -strokeThickness, -strokeThickness); - return screenBounds; + return [self shapeTransformerFromTransformer:[surface transformer]]; } - (NSRect) boundsOnSurface:(id)surface { @@ -147,7 +123,7 @@ - (void) renderToSurface:(id )surface withContext:(id)context state:(enum NodeState)state { Transformer *shapeTrans = [self shapeTransformerForSurface:surface]; - float strokeThickness = style ? [style strokeThickness] : DEFAULT_STROKE_WIDTH; + float strokeThickness = style ? [style strokeThickness] : [NodeStyle defaultStrokeThickness]; [context saveState]; @@ -185,7 +161,7 @@ return NO; } - float strokeThickness = style ? [style strokeThickness] : DEFAULT_STROKE_WIDTH; + float strokeThickness = style ? [style strokeThickness] : [NodeStyle defaultStrokeThickness]; id ctx = [surface createRenderContext]; [ctx setLineWidth:strokeThickness]; [[self shape] drawPathWithTransform:shapeTrans andContext:ctx]; diff --git a/tikzit/src/osx/NodeLayer.m b/tikzit/src/osx/NodeLayer.m index da01bec..2c37c26 100644 --- a/tikzit/src/osx/NodeLayer.m +++ b/tikzit/src/osx/NodeLayer.m @@ -73,7 +73,7 @@ if ([node style] != nil) { return [node.style strokeThickness]; } else { - return 1; + return [NodeStyle defaultStrokeThickness]; } } -- cgit v1.2.3