diff --git a/MathPad/MPParenthesisFunctionLayout.h b/MathPad/MPParenthesisFunctionLayout.h new file mode 100644 index 0000000..f5ba164 --- /dev/null +++ b/MathPad/MPParenthesisFunctionLayout.h @@ -0,0 +1,16 @@ +// +// MPParenthesisFunctionLayout.h +// MathPad +// +// Created by Kim Wittenburg on 17.09.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPFunctionLayout.h" +#import "MPParenthesisFunction.h" + +@interface MPParenthesisFunctionLayout : MPFunctionLayout + +- (MPParenthesisFunction *)parenthesisFunction; + +@end diff --git a/MathPad/MPParenthesisFunctionLayout.m b/MathPad/MPParenthesisFunctionLayout.m new file mode 100644 index 0000000..947cb20 --- /dev/null +++ b/MathPad/MPParenthesisFunctionLayout.m @@ -0,0 +1,189 @@ +// +// MPParenthesisFunctionLayout.m +// MathPad +// +// Created by Kim Wittenburg on 17.09.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPParenthesisFunctionLayout.h" + +#define MPParenthesisFunctionOpeningParensOffset 2 +#define MPParenthesisFunctionClosingParensOffset 0 + +@interface MPParenthesisFunctionLayout () + +- (NSBezierPath *)openingParens; +- (NSBezierPath *)closingParens; + +- (NSBezierPath *)transformedOpeningParens; +- (NSBezierPath *)transformedClosingParens; + +- (NSAffineTransform *)parenthesisTransform; + +@end + +@implementation MPParenthesisFunctionLayout + +- (MPParenthesisFunction *)parenthesisFunction +{ + return (MPParenthesisFunction *)self.function; +} + +- (NSBezierPath *)openingParens +{ + NSBezierPath *parens = [self objectForPrivateCacheIndex:0 generator:^id{ + CTLineRef line = [self createLineForString:@"("]; + CFArrayRef glyphRuns = CTLineGetGlyphRuns(line); + CFRetain(glyphRuns); + CTRunRef glyphRun = CFArrayGetValueAtIndex(glyphRuns, 0); + CFRetain(glyphRun); + + CGGlyph *glyphs = malloc(1 * sizeof(CGGlyph)); + CTRunGetGlyphs(glyphRun, CFRangeMake(0, 1), glyphs); + NSGlyph glyph = glyphs[0]; + + NSBezierPath *path = [[NSBezierPath alloc] init]; + [path moveToPoint:NSZeroPoint]; + [path appendBezierPathWithGlyph:glyph + inFont:self.font]; + + CFRelease(line); + CFRelease(glyphRuns); + CFRelease(glyphRun); + free(glyphs); + + return path; + }]; + return parens; +} + +- (NSBezierPath *)closingParens +{ + NSBezierPath *parens = [self objectForPrivateCacheIndex:1 generator:^id{ + CTLineRef line = [self createLineForString:@")"]; + CFArrayRef glyphRuns = CTLineGetGlyphRuns(line); + CFRetain(glyphRuns); + CTRunRef glyphRun = CFArrayGetValueAtIndex(glyphRuns, 0); + CFRetain(glyphRun); + + CGGlyph *glyphs = malloc(1 * sizeof(CGGlyph)); + CTRunGetGlyphs(glyphRun, CFRangeMake(0, 1), glyphs); + NSGlyph glyph = glyphs[0]; + + NSBezierPath *path = [[NSBezierPath alloc] init]; + [path moveToPoint:NSZeroPoint]; + [path appendBezierPathWithGlyph:glyph + inFont:self.font]; + + CFRelease(line); + CFRelease(glyphRuns); + CFRelease(glyphRun); + free(glyphs); + + return path; + }]; + return parens; +} + +- (NSBezierPath *)transformedOpeningParens +{ + NSBezierPath *parens = self.openingParens.copy; + [parens transformUsingAffineTransform:self.parenthesisTransform]; + return parens; +} + +- (NSBezierPath *)transformedClosingParens +{ + NSBezierPath *parens = self.closingParens.copy; + [parens transformUsingAffineTransform:self.parenthesisTransform]; + return parens; +} + +- (NSAffineTransform *)parenthesisTransform +{ + NSRect parensBounds = self.openingParens.bounds; + NSRect expressionBounds = [self childLayoutAtIndex:0].bounds; + NSAffineTransform *transform = [NSAffineTransform transform]; + if (expressionBounds.size.height >= parensBounds.size.height) { + return transform; + } + CGFloat scaleFactor = expressionBounds.size.height / parensBounds.size.height; + [transform scaleXBy:1 + yBy:scaleFactor]; + return transform; +} + +- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index +{ + if (index == 0) { + return NSMakePoint(self.openingParens.bounds.size.width + MPParenthesisFunctionOpeningParensOffset, 0); + } + return NSZeroPoint; +} + +- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point +{ +#warning Missing Implementation + return [NSIndexPath indexPathWithIndex:0]; +} + +- (NSUInteger)indexOfLeadingChild +{ + return 0; +} + +- (NSUInteger)indexOfTrailingChild +{ + return 0; +} + +- (NSIndexSet *)indexesOfRemainingChildren +{ + return [NSIndexSet indexSetWithIndex:0]; +} + +- (NSRect)generateBounds +{ + NSRect openingParensBounds = self.transformedOpeningParens.bounds; + NSRect closingParensBounds = self.transformedClosingParens.bounds; + NSRect bounds = [self childLayoutAtIndex:0].bounds; + + if (openingParensBounds.size.height > bounds.size.height) { + bounds.size.height = openingParensBounds.size.height; + } + if (closingParensBounds.size.height > bounds.size.height) { + bounds.size.height = closingParensBounds.size.height; + } + + bounds.size.width += MPParenthesisFunctionOpeningParensOffset; + bounds.size.width += openingParensBounds.size.width; + bounds.size.width += MPParenthesisFunctionClosingParensOffset; + bounds.size.width += closingParensBounds.size.width; + + return bounds; +} + +- (void)draw +{ + NSBezierPath *openingParens = self.transformedOpeningParens; + MPLayout *expressionLayout = [self childLayoutAtIndex:0]; + NSBezierPath *closingParens = self.transformedClosingParens; + + CGFloat x = 0; + + [openingParens fill]; + x += openingParens.bounds.size.width; + x += MPParenthesisFunctionOpeningParensOffset; + [expressionLayout drawAtPoint:NSMakePoint(x, 0)]; + x += expressionLayout.bounds.size.width; + x += MPParenthesisFunctionClosingParensOffset; + + NSAffineTransform *transform = [NSAffineTransform transform]; + [transform translateXBy:x + yBy:0]; + [closingParens transformUsingAffineTransform:transform]; + [closingParens fill]; +} + +@end diff --git a/MathPad/MPSumFunctionLayout.m b/MathPad/MPSumFunctionLayout.m index de60ed0..7eb9abb 100644 --- a/MathPad/MPSumFunctionLayout.m +++ b/MathPad/MPSumFunctionLayout.m @@ -11,7 +11,7 @@ #import "NSIndexPath+MPAdditions.h" -#define kSumFunctionStartExpressionOffset 3 +#define kSumFunctionStartExpressionOffset 0 #define kSumFunctionTargetExpressionOffset 0 #define kSumFunctionSumExpressionOffset 3 #define kSumFunctionTrailingOffset 5 @@ -41,10 +41,27 @@ return [super indexOfChildAfterChildAtIndex:index]; } +- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index +{ + return 0; +} + +- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index +{ + return 1; +} + +- (NSIndexSet *)indexesOfRemainingChildren +{ + return [NSIndexSet indexSetWithIndex:2]; +} + - (CTLineRef)line { CTLineRef line = [self lineForPrivateCacheIndex:0 generator:^CTLineRef{ - return [self createLineForString:@"∑"]; + return [self createLineForString:@"∑" + usingFont:[NSFont fontWithName:@"Times New Roman" + size:self.fontSize]]; }]; return line; } @@ -52,7 +69,9 @@ - (NSRect)localLineBounds { NSRect lineBounds = CTLineGetBoundsWithOptions(self.line, 0); - CGFloat width = MAX(MAX([self childLayoutAtIndex:0].bounds.size.width, [self childLayoutAtIndex:1].bounds.size.width), lineBounds.size.width); + NSRect startExpressionBounds = [self childLayoutAtIndex:0].bounds; + NSRect targetExpressionBounds = [self childLayoutAtIndex:1].bounds; + CGFloat width = MAX(MAX(startExpressionBounds.size.width, targetExpressionBounds.size.width), lineBounds.size.width); CGFloat xPosition = (width - lineBounds.size.width) / 2; return NSMakeRect(xPosition, lineBounds.origin.y, lineBounds.size.width, lineBounds.size.height); } @@ -65,11 +84,11 @@ if (index == 0) { // Start Expression offset.x = localLineBounds.origin.x + localLineBounds.size.width / 2 - childBounds.size.width / 2; - offset.y = -kSumFunctionStartExpressionOffset - childBounds.size.height; + offset.y = localLineBounds.origin.y - kSumFunctionStartExpressionOffset - childBounds.size.height - childBounds.origin.y; } else if (index == 1) { // Target Expression offset.x = localLineBounds.origin.x + localLineBounds.size.width / 2 - childBounds.size.width / 2; - offset.y = localLineBounds.size.height + kSumFunctionTargetExpressionOffset; + offset.y = kSumFunctionTargetExpressionOffset + localLineBounds.size.height + localLineBounds.origin.y - childBounds.origin.y; } else { // Sum Expression MPLayout *startExpressionLayout = [self childLayoutAtIndex:0]; @@ -81,23 +100,13 @@ return offset; } -- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point +- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index +{ + return (index == 0 || index == 1) ? YES : self.usesSmallSize; +} + +- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point { - // A single index is used to communicate back wether the - // selection should be before or after the function. - // A 0 means before, a 1 means after. - for (NSUInteger index = 0; index < self.function.numberOfChildren; index++) { - MPLayout *childLayout = [self childLayoutAtIndex:index]; - NSRect childBounds = childLayout.bounds; - NSPoint childOffset = [self offsetOfChildLayoutAtIndex:index]; - childBounds.origin.x += childOffset.x; - childBounds.origin.y += childOffset.y; - if (NSMouseInRect(point, childBounds, self.flipped)) { - NSPoint pointInChild = NSMakePoint(point.x - childOffset.x, point.y - childOffset.y); - NSIndexPath *subPath = [childLayout indexPathForMousePoint:pointInChild]; - return [subPath indexPathByPreceedingIndex:index]; - } - } if (point.x < CTLineGetBoundsWithOptions(self.line, 0).size.width / 2) { return [NSIndexPath indexPathWithIndex:0]; } else { @@ -115,20 +124,30 @@ bounds.size.width = MAX(lineBounds.size.width, startExpressionBounds.size.width); bounds.size.height += startExpressionBounds.size.height + kSumFunctionStartExpressionOffset; - + bounds.origin.y -= startExpressionBounds.size.height + kSumFunctionStartExpressionOffset; + bounds.size.width = MAX(bounds.size.width, targetExpressionBounds.size.width); bounds.size.height += targetExpressionBounds.size.height + kSumFunctionTargetExpressionOffset; bounds.size.width += kSumFunctionSumExpressionOffset + sumExpressionBounds.size.width; bounds.size.height = MAX(bounds.size.height, sumExpressionBounds.size.height); - - bounds.origin.y -= targetExpressionBounds.size.height + kSumFunctionStartExpressionOffset; bounds.size.width += kSumFunctionTrailingOffset; + + // The Bounds are a little bigger than the drawn function + bounds.size.height += 6; + bounds.origin.y -= 3; + return bounds; } - (void)draw { +#ifdef MPDEBUG_DRAW_BASELINE + [[NSColor redColor] set]; + NSRectFill(NSMakeRect(0, -1, self.bounds.size.width, 1)); + [[NSColor textColor] set]; +#endif + // Get the current context CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; @@ -142,25 +161,24 @@ CGContextSetTextPosition(context, localLineBounds.origin.x, 0); CTLineDraw(line, context); +#ifdef MPDEBUG_DRAW_FUNCTION_BOUNDS + [[NSColor blueColor] set]; + [[NSBezierPath bezierPathWithRect:localLineBounds] stroke]; + [[NSColor textColor] set]; +#endif + // Draw the start function MPLayout *startExpressionLayout = [self childLayoutAtIndex:0]; - NSPoint startExpressionLocation = NSMakePoint(localLineBounds.origin.x + localLineBounds.size.width / 2, 0); - startExpressionLocation.x -= startExpressionLayout.bounds.size.width / 2; - startExpressionLocation.y -= startExpressionLayout.bounds.size.height + kSumFunctionStartExpressionOffset; + NSPoint startExpressionLocation = [self offsetOfChildLayoutAtIndex:0]; [startExpressionLayout drawAtPoint:startExpressionLocation]; // Draw the target function MPLayout *targetExpressionLayout = [self childLayoutAtIndex:1]; - NSPoint targetExpressionLocation = NSMakePoint(localLineBounds.origin.x + localLineBounds.size.width / 2, localLineBounds.size.height); - targetExpressionLocation.x -= targetExpressionLayout.bounds.size.width / 2; - targetExpressionLocation.y += kSumFunctionTargetExpressionOffset; - [targetExpressionLayout drawAtPoint:targetExpressionLocation]; + [targetExpressionLayout drawAtPoint:[self offsetOfChildLayoutAtIndex:1]]; // Draw the sum function MPLayout *sumExpressionLayout = [self childLayoutAtIndex:2]; - CGFloat sumWidth = MAX(MAX(localLineBounds.origin.x + localLineBounds.size.width, startExpressionLayout.bounds.size.width), targetExpressionLayout.bounds.size.width); - sumWidth += kSumFunctionSumExpressionOffset; - [sumExpressionLayout drawAtPoint:NSMakePoint(sumWidth, 0)]; + [sumExpressionLayout drawAtPoint:[self offsetOfChildLayoutAtIndex:2]]; CFRelease(line); }