From c367b1dbe8e3be2ce7b9a4e4e206e79f9f4a32b0 Mon Sep 17 00:00:00 2001 From: Kim Wittenburg Date: Thu, 11 Dec 2014 15:32:06 +0100 Subject: [PATCH] Corrected MPExpression -itemAtIndex:referenceFrame:MPSymbolReferenceFrame Corrected Deformed Number Format Added "arcsin", "arccos", "arctan", "lg", "log", "ln" Elementary Functions --- MathPad/MPElementaryFunctionTerm.m | 12 ++-- MathPad/MPExpression.m | 19 +++---- MathPad/MPExpressionLayout.m | 14 ++--- MathPad/MPExpressionTokenizer.m | 4 +- MathPad/MPExpressionView.m | 90 +++++++++++++++++++++++++----- MathPad/MPFractionFunctionLayout.m | 2 +- MathPad/MPLayout.m | 2 +- 7 files changed, 105 insertions(+), 38 deletions(-) diff --git a/MathPad/MPElementaryFunctionTerm.m b/MathPad/MPElementaryFunctionTerm.m index 17f063c..d95bdf4 100644 --- a/MathPad/MPElementaryFunctionTerm.m +++ b/MathPad/MPElementaryFunctionTerm.m @@ -39,17 +39,21 @@ } else if ([function isEqualToString:@"tan"]) { func = &tan; takesArc = YES; - } else if ([function isEqualToString:@"asin"]) { + } else if ([function isEqualToString:@"asin"] || [function isEqualToString:@"arcsin"]) { func = &asin; returnsArc = YES; - } else if ([function isEqualToString:@"acos"]) { + } else if ([function isEqualToString:@"acos"] || [function isEqualToString:@"arccos"]) { func = &acos; returnsArc = YES; - } else if ([function isEqualToString:@"atan"]) { + } else if ([function isEqualToString:@"atan"] || [function isEqualToString:@"arctan"]) { func = &atan; returnsArc = YES; + } else if ([function isEqualToString:@"lg"] || [function isEqualToString:@"log"]) { + func = &log10; + } else if ([function isEqualToString:@"ln"]) { + func = &log; } else { - NSAssert(true, @"function must be one of (sin, cos, tan, asin, acos, atan)."); + NSAssert(true, @"function must be one of (sin, cos, tan, asin, acos, atan, lg, log, ln)."); } [self setFunction:func takesArcValue:takesArc diff --git a/MathPad/MPExpression.m b/MathPad/MPExpression.m index dd11926..2e33ba0 100644 --- a/MathPad/MPExpression.m +++ b/MathPad/MPExpression.m @@ -269,18 +269,17 @@ NSString *const MPIllegalElementExceptionElementKey = @"MPIllegalElementExceptio case MPSymbolReferenceFrame: { - NSUInteger location = 0; - NSUInteger elementIndex = 0; - id element = nil; - while (location < anIndex) { - element = _elements[elementIndex++]; - location += element.length; - } - if (location == anIndex && element.isFunction) { + NSUInteger offsetInElement; + NSUInteger elementIndex = [self convertIndex:anIndex + fromReferenceFrame:MPSymbolReferenceFrame + toReferenceFrame:MPElementReferenceFrame + offset:&offsetInElement]; + id element = [self elementAtIndex:elementIndex]; + if ([element isFunction]) { return element; + } else { + return [((NSString *)element) substringWithRange:NSMakeRange(offsetInElement, 1)]; } - NSUInteger indexInString = location - element.length + anIndex; - return [((NSString *)element) substringWithRange:NSMakeRange(indexInString, 1)]; } case MPTokenReferenceFrame: diff --git a/MathPad/MPExpressionLayout.m b/MathPad/MPExpressionLayout.m index a07b518..325dfbf 100644 --- a/MathPad/MPExpressionLayout.m +++ b/MathPad/MPExpressionLayout.m @@ -259,9 +259,10 @@ // Set the text matrix CGContextSetTextMatrix(context, CGAffineTransformIdentity); - // Track the x position + // Track the x position and the bounds of the last element CGFloat x = 0; - + NSRect lastElementBounds = NSMakeRect(0, kMPEmptyBoxYOrigin, kMPEmptyBoxWidth, kMPEmptyBoxHeight); + for (NSUInteger index = 0; index < self.expression.countElements; index++) { // The current element id element = [self.expression elementAtIndex:index]; @@ -279,17 +280,16 @@ CTLineDraw(line, context); CFRelease(line); - - if (index < self.expression.countElements-1 && [[self.expression elementAtIndex:index+1] isKindOfClass:[MPPowerFunction class]]) { - MPPowerFunctionLayout *layout = (MPPowerFunctionLayout *)[self childLayoutAtIndex:index+1]; - layout.baseBounds = elementBounds; - } } else { // Let the child layout draw itself MPLayout *layout = [self childLayoutAtIndex:index]; + if ([layout isKindOfClass:[MPPowerFunctionLayout class]]) { + ((MPPowerFunctionLayout *)layout).baseBounds = lastElementBounds; + } [layout drawAtPoint:NSMakePoint(x, 0)]; } x += elementBounds.size.width; + lastElementBounds = elementBounds; } CGContextRestoreGState(context); } diff --git a/MathPad/MPExpressionTokenizer.m b/MathPad/MPExpressionTokenizer.m index c424bee..dc89573 100644 --- a/MathPad/MPExpressionTokenizer.m +++ b/MathPad/MPExpressionTokenizer.m @@ -49,9 +49,9 @@ NSString *regexStringFormat = @"\\A(?:" @"([\\*∙⋅])|" @"([+-](?:\\s*[+-])*)|" - @"((?:\\d+%@(?!\\d+))|(?:(?:\\d*%@){2,}\\d*)|%@)|" // Substitute with decimal separator 3 times + @"((?:\\d+%@(?!\\d+))|(?:(?:\\d*%@){2,}\\d*)|%@(?!\\d+))|" // Substitute with decimal separator 3 times @"((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))|" // Substitute with decimal separator 2 times - @"(sin|cos|tan|asin|acos|atan)|" + @"(sin|cos|tan|asin|arcsin|acos|arccos|atan|arctan|lg|log|ln)|" @"([A-Za-z])|" @"(!)|" @"(=)|" diff --git a/MathPad/MPExpressionView.m b/MathPad/MPExpressionView.m index 42c27ee..40a04df 100644 --- a/MathPad/MPExpressionView.m +++ b/MathPad/MPExpressionView.m @@ -16,6 +16,7 @@ #import "MPParenthesisFunction.h" #import "MPFractionFunction.h" +#import "MPToken.h" #import "MPRangePath.h" #import "MPMathRules.h" @@ -648,6 +649,10 @@ [self insertParenthesisFunction:nil]; return; } + if ([characters isEqualToString:@")"]) { + [self insertClosingParenthesis]; + return; + } if (theEvent.keyCode == 10) { [self insertPowerFunction]; return; @@ -692,6 +697,29 @@ self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length); } +- (void)insertClosingParenthesis +{ + if (self.selection.length > 0) { + [self insertParenthesisFunction:self]; + } else { + NSIndexPath *currentPath = [[self.selection.location indexPathByRemovingLastIndex] indexPathByRemovingLastIndex]; + while (currentPath.length > 0) { + id element = [self.expressionStorage elementAtIndexPath:currentPath]; + if ([element isKindOfClass:[MPParenthesisFunction class]]) { + MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[currentPath indexPathByRemovingLastIndex]]; + NSUInteger selectedIndex = currentPath.lastIndex + 1; + selectedIndex = [targetExpression convertIndex:selectedIndex + fromReferenceFrame:MPElementReferenceFrame + toReferenceFrame:MPSymbolReferenceFrame]; + self.selection = MPMakeRangePath([currentPath indexPathByReplacingLastIndexWithIndex:selectedIndex], 0); + break; + } else { + currentPath = [[currentPath indexPathByRemovingLastIndex] indexPathByRemovingLastIndex]; + } + } + } +} + - (void)insertPowerFunction { MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection @@ -712,19 +740,55 @@ - (void)insertFractionFunction { - MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection - referenceFrame:MPSymbolReferenceFrame]; - MPFractionFunction *function = [[MPFractionFunction alloc] init]; - function.nominatorExpression = selectedElementsExpression; - [self.expressionStorage replaceItemsInRangePath:self.selection - referenceFrame:MPSymbolReferenceFrame - withElements:@[function]]; - MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]]; - NSUInteger functionElementIndex = [targetExpression convertIndex:self.selection.location.lastIndex - fromReferenceFrame:MPSymbolReferenceFrame - toReferenceFrame:MPElementReferenceFrame]; - NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:functionElementIndex]; - self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], 0); + if (self.selection.length == 0) { + MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]]; + NSInteger index = self.selection.location.lastIndex; + NSRange nominatorSymbols = NSMakeRange(index, 0); + while (--index >= 0) { + id symbol = [targetExpression symbolAtIndex:index]; + if ([symbol isString]) { + NSUInteger tokenIndex = [targetExpression convertIndex:index + fromReferenceFrame:MPSymbolReferenceFrame + toReferenceFrame:MPTokenReferenceFrame]; + id token = [targetExpression tokenAtIndex:tokenIndex]; + if (token.tokenType == MPNumberToken || token.tokenType == MPVariableToken || token.tokenType == MPElementaryFunctionToken) { + nominatorSymbols.location--; + nominatorSymbols.length++; + } else { + break; + } + } else if (nominatorSymbols.length == 0) { + nominatorSymbols.location--; + nominatorSymbols.length++; + } else { + break; + } + } + MPExpression *nominatorExpression = [targetExpression subexpressionWithRange:nominatorSymbols + referenceFrame:MPSymbolReferenceFrame]; + MPFractionFunction *function = [[MPFractionFunction alloc] init]; + function.nominatorExpression = nominatorExpression; + [targetExpression replaceItemsInRange:nominatorSymbols referenceFrame:MPSymbolReferenceFrame withElements:@[function]]; + NSUInteger functionElementIndex = [targetExpression convertIndex:nominatorSymbols.location + fromReferenceFrame:MPSymbolReferenceFrame + toReferenceFrame:MPElementReferenceFrame]; + NSUInteger selectedSubexpression = nominatorSymbols.length == 0 ? 0 : 1; + self.selection = MPMakeRangePath([[[self.selection.location indexPathByReplacingLastIndexWithIndex:functionElementIndex] indexPathByAddingIndex:selectedSubexpression] indexPathByAddingIndex:0], 0); + } else { + MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection + referenceFrame:MPSymbolReferenceFrame]; + MPFractionFunction *function = [[MPFractionFunction alloc] init]; + function.nominatorExpression = selectedElementsExpression; + [self.expressionStorage replaceItemsInRangePath:self.selection + referenceFrame:MPSymbolReferenceFrame + withElements:@[function]]; + MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]]; + NSUInteger functionElementIndex = [targetExpression convertIndex:self.selection.location.lastIndex + fromReferenceFrame:MPSymbolReferenceFrame + toReferenceFrame:MPElementReferenceFrame]; + NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:functionElementIndex]; + self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], 0); + } } - (void)insertNewline:(id)sender diff --git a/MathPad/MPFractionFunctionLayout.m b/MathPad/MPFractionFunctionLayout.m index 11cafdd..7683484 100644 --- a/MathPad/MPFractionFunctionLayout.m +++ b/MathPad/MPFractionFunctionLayout.m @@ -36,7 +36,7 @@ - (NSIndexSet *)indexesOfRemainingChildren { - return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 1)]; + return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]; } - (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index diff --git a/MathPad/MPLayout.m b/MathPad/MPLayout.m index 3e03d91..16da726 100644 --- a/MathPad/MPLayout.m +++ b/MathPad/MPLayout.m @@ -53,7 +53,7 @@ - (NSFont *)specialFontWithSize:(CGFloat)size { - return [NSFont fontWithName:@"CMU Sans Serif Oblique" + return [NSFont fontWithName:@"CMU Serif Italic" size:size]; }