Add Root Function and Product Function
This commit is contained in:
8
MathKit/MPFunctionLayout.m
Normal file → Executable file
8
MathKit/MPFunctionLayout.m
Normal file → Executable file
@@ -12,14 +12,18 @@
|
||||
|
||||
#import "MPFunction.h"
|
||||
#import "MPFractionFunction.h"
|
||||
#import "MPRootFunction.h"
|
||||
#import "MPParenthesisFunction.h"
|
||||
#import "MPPowerFunction.h"
|
||||
#import "MPSumFunction.h"
|
||||
#import "MPProductFunction.h"
|
||||
|
||||
#import "MPFractionFunctionLayout.h"
|
||||
#import "MPRootFunctionLayout.h"
|
||||
#import "MPParenthesisFunctionLayout.h"
|
||||
#import "MPPowerFunctionLayout.h"
|
||||
#import "MPSumFunctionLayout.h"
|
||||
#import "MPProductFunctionLayout.h"
|
||||
|
||||
#import "NSIndexPath+MPAdditions.h"
|
||||
|
||||
@@ -42,6 +46,10 @@
|
||||
return [[MPPowerFunctionLayout alloc] initWithFunction:function parent:parent];
|
||||
} else if (class == [MPFractionFunction class]) {
|
||||
return [[MPFractionFunctionLayout alloc] initWithFunction:function parent:parent];
|
||||
} else if (class == [MPProductFunction class]) {
|
||||
return [[MPProductFunctionLayout alloc] initWithFunction:function parent:parent];
|
||||
} else if (class == [MPRootFunction class]) {
|
||||
return [[MPRootFunctionLayout alloc] initWithFunction:function parent:parent];
|
||||
}
|
||||
return [[self alloc] initWithFunction:function
|
||||
parent:parent];
|
||||
|
||||
8
MathKit/MPFunctionsViewController.m
Normal file → Executable file
8
MathKit/MPFunctionsViewController.m
Normal file → Executable file
@@ -10,9 +10,11 @@
|
||||
|
||||
#import "MPFunction.h"
|
||||
#import "MPFractionFunction.h"
|
||||
#import "MPRootFunction.h"
|
||||
#import "MPParenthesisFunction.h"
|
||||
#import "MPPowerFunction.h"
|
||||
#import "MPSumFunction.h"
|
||||
#import "MPProductFunction.h"
|
||||
|
||||
#import "MPFunctionLayout.h"
|
||||
|
||||
@@ -224,16 +226,20 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
MPFunction *sumFunction = [[MPSumFunction alloc] init];
|
||||
MPFunction *productFunction = [[MPProductFunction alloc] init];
|
||||
MPFunction *parenthesisFunction = [[MPParenthesisFunction alloc] init];
|
||||
MPFunction *powerFunction = [[MPPowerFunction alloc] init];
|
||||
MPPowerFunction *squareFunction = [[MPPowerFunction alloc] init];
|
||||
squareFunction.exponentExpression = [[MPExpression alloc] initWithElement:@"2"];
|
||||
MPPowerFunction *cubicFunction = [[MPPowerFunction alloc] init];
|
||||
cubicFunction.exponentExpression = [[MPExpression alloc] initWithElement:@"3"];
|
||||
MPFunction *rootFunction = [[MPRootFunction alloc] init];
|
||||
MPFractionFunction *fractionFunction = [[MPFractionFunction alloc] init];
|
||||
self.functionPrototypes = @[
|
||||
@{@"function": sumFunction,
|
||||
@"name": NSLocalizedString(@"Sum", @"Sum Function Name")},
|
||||
@{@"function": productFunction,
|
||||
@"name": NSLocalizedString(@"Product", @"Product Function Name")},
|
||||
@{@"function": parenthesisFunction,
|
||||
@"name": NSLocalizedString(@"Parenthesis", @"Parenthesis Function Name")},
|
||||
@{@"function": squareFunction,
|
||||
@@ -242,6 +248,8 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
|
||||
@"name": NSLocalizedString(@"Cubic", @"Cubic Function Name")},
|
||||
@{@"function": powerFunction,
|
||||
@"name": NSLocalizedString(@"Power", @"Power Function Name")},
|
||||
@{@"function": rootFunction,
|
||||
@"name": NSLocalizedString(@"Root", "Root Function Name")},
|
||||
@{@"function": fractionFunction,
|
||||
@"name": NSLocalizedString(@"Fraction", @"Fraction Function Name")}
|
||||
];
|
||||
|
||||
70
MathKit/MPProductFunction.h
Executable file
70
MathKit/MPProductFunction.h
Executable file
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// MPProductFunction.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 10.01.15.
|
||||
// Copyright (c) 2015 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPFunction.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPProductFunction</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPProductFunction, MPExpression;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPProductFunction
|
||||
@abstract This class represents a product function (generally noted using a
|
||||
capital pi).
|
||||
|
||||
@discussion A product function has a start and a target expression indicating
|
||||
how often the product expression should be evaluated. Both the
|
||||
value of the start expression and the target expressions are
|
||||
included in the iterations.
|
||||
|
||||
Each iteration the iteration value is incremented by
|
||||
<code>1</code>. If the start and target expression evaluate to
|
||||
the same value the product is evaluated once.
|
||||
*/
|
||||
@interface MPProductFunction : MPFunction
|
||||
|
||||
/*!
|
||||
@property startExpression
|
||||
@abstract The value of the first iteration.
|
||||
|
||||
@discussion The start expression must define a variable that may be used in
|
||||
the sum expression. If the start expression does not define a
|
||||
variable the product function will not be evaluatable.
|
||||
*/
|
||||
@property (nonatomic, strong) MPExpression *startExpression; /* Index 0 */
|
||||
|
||||
|
||||
/*!
|
||||
@property targetExpression
|
||||
@abstract The value if the last iteration.
|
||||
|
||||
@discussion The target expression must not define a variable.
|
||||
*/
|
||||
@property (nonatomic, strong) MPExpression *targetExpression; /* Index 1 */
|
||||
|
||||
|
||||
/*!
|
||||
@property productExpression
|
||||
@abstract The product expression evaluated multiple times.
|
||||
|
||||
@discussion During evaluation of the product expression the variable defined
|
||||
in the start expression is available. That variable always
|
||||
contains the value of the current iteration.
|
||||
|
||||
The product expression itself must not define a variable.
|
||||
*/
|
||||
@property (nonatomic, strong) MPExpression *productExpression; /* Index 2 */
|
||||
|
||||
@end
|
||||
46
MathKit/MPProductFunction.m
Executable file
46
MathKit/MPProductFunction.m
Executable file
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// MPProductFunction.m
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 10.01.15.
|
||||
// Copyright (c) 2015 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPProductFunction.h"
|
||||
|
||||
#import "MPProductFunctionTerm.h"
|
||||
#import "MPExpression.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPProductFunction
|
||||
|
||||
MPFunctionAccessorImplementation(StartExpression, _startExpression)
|
||||
MPFunctionAccessorImplementation(TargetExpression, _targetExpression)
|
||||
MPFunctionAccessorImplementation(ProductExpression, _productExpression)
|
||||
|
||||
|
||||
- (NSArray *)childrenAccessors
|
||||
{
|
||||
return @[@"startExpression", @"targetExpression", @"productExpression"];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)expectsVariableDefinitionInChildAtIndex:(NSUInteger)index
|
||||
{
|
||||
return index == 0;
|
||||
}
|
||||
|
||||
|
||||
- (Class)functionTermClass
|
||||
{
|
||||
return [MPProductFunctionTerm class];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"Product(From: %@; To: %@; Using: %@)", self.startExpression, self.targetExpression, self.productExpression];
|
||||
}
|
||||
|
||||
@end
|
||||
35
MathKit/MPProductFunctionLayout.h
Executable file
35
MathKit/MPProductFunctionLayout.h
Executable file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// MPProductFunctionLayout.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 10.01.15.
|
||||
// Copyright (c) 2015 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPFunctionLayout.h"
|
||||
|
||||
|
||||
|
||||
@class MPProductFunctionLayout, MPProductFunction;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPProductFunctionLayout
|
||||
@abstract A product function layout displays a <code>@link
|
||||
//apple_ref/occ/cl/MPProductFunction@/link</code>.
|
||||
|
||||
@discussion A product is drawn using a capital greeg pi (∏) The three
|
||||
children are placed around it: The start expression below, target
|
||||
expression above and product expression to the right of it.
|
||||
*/
|
||||
@interface MPProductFunctionLayout : MPFunctionLayout
|
||||
|
||||
/*!
|
||||
@method productFunction
|
||||
@abstract Returns the <code>@link
|
||||
//apple_ref/occ/cl/MPProductFunction@/link</code> represented by
|
||||
the receiver.
|
||||
*/
|
||||
- (MPProductFunction *)productFunction;
|
||||
|
||||
@end
|
||||
175
MathKit/MPProductFunctionLayout.m
Executable file
175
MathKit/MPProductFunctionLayout.m
Executable file
@@ -0,0 +1,175 @@
|
||||
//
|
||||
// MPProductFunctionLayout.m
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 10.01.15.
|
||||
// Copyright (c) 2015 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPProductFunctionLayout.h"
|
||||
|
||||
#import "MPProductFunction.h"
|
||||
#import "NSIndexPath+MPAdditions.h"
|
||||
|
||||
|
||||
#define kProductFunctionStartExpressionOffset 0
|
||||
#define kProductFunctionTargetExpressionOffset 0
|
||||
#define kProductFunctionProductExpressionOffset 3
|
||||
#define kProductFunctionTrailingOffset 5
|
||||
|
||||
|
||||
|
||||
@implementation MPProductFunctionLayout
|
||||
|
||||
- (MPProductFunction *)productFunction
|
||||
{
|
||||
return (MPProductFunction *)self.function;
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)indexOfLeadingChild
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)indexOfTrailingChild
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index
|
||||
{
|
||||
if (index != 2) {
|
||||
return 2;
|
||||
}
|
||||
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:@"∏"
|
||||
usingFont:[NSFont fontWithName:@"Times New Roman"
|
||||
size:self.contextInferredFontSize]];
|
||||
}];
|
||||
return line;
|
||||
}
|
||||
|
||||
|
||||
- (NSRect)localLineBounds
|
||||
{
|
||||
NSRect lineBounds = CTLineGetBoundsWithOptions(self.line, 0);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
|
||||
{
|
||||
NSRect childBounds = [self childLayoutAtIndex:index].bounds;
|
||||
NSRect localLineBounds = [self localLineBounds];
|
||||
NSPoint offset;
|
||||
if (index == 0) {
|
||||
// Start Expression
|
||||
offset.x = localLineBounds.origin.x + localLineBounds.size.width / 2 - childBounds.size.width / 2;
|
||||
offset.y = localLineBounds.origin.y - kProductFunctionStartExpressionOffset - 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 = kProductFunctionTargetExpressionOffset + localLineBounds.size.height + localLineBounds.origin.y - childBounds.origin.y;
|
||||
} else {
|
||||
// Sum Expression
|
||||
MPLayout *startExpressionLayout = [self childLayoutAtIndex:0];
|
||||
MPLayout *targetExpressionLayout = [self childLayoutAtIndex:1];
|
||||
CGFloat sumWidth = MAX(MAX(localLineBounds.origin.x + localLineBounds.size.width, startExpressionLayout.bounds.size.width), targetExpressionLayout.bounds.size.width);
|
||||
offset.x = sumWidth + kProductFunctionProductExpressionOffset;
|
||||
offset.y = 0;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
|
||||
{
|
||||
return (index == 0 || index == 1) ? YES : self.usesSmallSize;
|
||||
}
|
||||
|
||||
|
||||
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
|
||||
{
|
||||
if (point.x < CTLineGetBoundsWithOptions(self.line, 0).size.width / 2) {
|
||||
return [NSIndexPath indexPathWithIndex:0];
|
||||
} else {
|
||||
return [NSIndexPath indexPathWithIndex:1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (NSRect)generateBounds
|
||||
{
|
||||
NSRect lineBounds = CTLineGetBoundsWithOptions(self.line, 0);
|
||||
NSRect startExpressionBounds = [self childLayoutAtIndex:0].bounds;
|
||||
NSRect targetExpressionBounds = [self childLayoutAtIndex:1].bounds;
|
||||
NSRect sumExpressionBounds = [self childLayoutAtIndex:2].bounds;
|
||||
NSRect bounds = lineBounds;
|
||||
|
||||
bounds.size.width = MAX(lineBounds.size.width, startExpressionBounds.size.width);
|
||||
bounds.size.height += startExpressionBounds.size.height + kProductFunctionStartExpressionOffset;
|
||||
bounds.origin.y -= startExpressionBounds.size.height + kProductFunctionStartExpressionOffset;
|
||||
|
||||
bounds.size.width = MAX(bounds.size.width, targetExpressionBounds.size.width);
|
||||
bounds.size.height += targetExpressionBounds.size.height + kProductFunctionTargetExpressionOffset;
|
||||
|
||||
bounds.size.width += kProductFunctionProductExpressionOffset + sumExpressionBounds.size.width + kProductFunctionTrailingOffset;
|
||||
CGFloat yOffsetBottom = bounds.origin.y - sumExpressionBounds.origin.y;
|
||||
CGFloat yOffsetTop = sumExpressionBounds.size.height - (yOffsetBottom + bounds.size.height);
|
||||
bounds.size.height = MAX(yOffsetBottom, 0) + bounds.size.height + MAX(yOffsetTop, 0);
|
||||
bounds.origin.y = MIN(sumExpressionBounds.origin.y, bounds.origin.y);
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
|
||||
- (void)draw
|
||||
{
|
||||
// Get the current context
|
||||
CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
// Set the text matrix
|
||||
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
|
||||
|
||||
// Draw the sum symbol
|
||||
CTLineRef line = [self line];
|
||||
CFRetain(line);
|
||||
NSRect localLineBounds = [self localLineBounds];
|
||||
CGContextSetTextPosition(context, localLineBounds.origin.x, 0);
|
||||
CTLineDraw(line, context);
|
||||
|
||||
CFRelease(line);
|
||||
}
|
||||
|
||||
@end
|
||||
37
MathKit/MPProductFunctionTerm.h
Executable file
37
MathKit/MPProductFunctionTerm.h
Executable file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// MPProductFunctionTerm.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 10.01.15.
|
||||
// Copyright (c) 2015 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPFunctionTerm.h"
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPProductFunctionTerm</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPProductFunctionTerm;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPProductFunctionTerm
|
||||
@abstract Represents and evaluates a <code>@link
|
||||
//apple_ref/occ/cl/MPProductFunction@/link</code>.
|
||||
|
||||
@discussion A product function evaluates its sum term <code>n</code> times.
|
||||
How often it actually is evaluated depends on the boundary
|
||||
expressions (start and target). Both are inclusive.
|
||||
|
||||
A product function also features a variable that contains the
|
||||
current value of the iteration. The variable is defined in the
|
||||
start expression and incremented by <code>1</code> after every
|
||||
iteration.
|
||||
*/
|
||||
@interface MPProductFunctionTerm : MPFunctionTerm
|
||||
|
||||
@end
|
||||
40
MathKit/MPProductFunctionTerm.m
Executable file
40
MathKit/MPProductFunctionTerm.m
Executable file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// MPProductFunctionTerm.m
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 10.01.15.
|
||||
// Copyright (c) 2015 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPProductFunctionTerm.h"
|
||||
|
||||
#import "MPParsedExpression.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPProductFunctionTerm
|
||||
|
||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||
{
|
||||
MPParsedExpression *startExpression = [self expressionAtIndex:0];
|
||||
NSDecimalNumber *start = [startExpression evaluate:error];
|
||||
ReturnNilIfNil(start);
|
||||
|
||||
MPEvaluateExpression(target, 1);
|
||||
|
||||
MPParsedExpression *sumExpression = [self expressionAtIndex:2];
|
||||
NSDecimalNumber *value = [NSDecimalNumber one];
|
||||
for (NSDecimalNumber *current = start;
|
||||
[current compare:target] <= 0;
|
||||
current = [current decimalNumberByAdding:[[NSDecimalNumber alloc] initWithInteger:1]]) {
|
||||
if (![self defineVariable:startExpression.definedVariable value:current error:error]) {
|
||||
return nil;
|
||||
}
|
||||
NSDecimalNumber *currentValue = [sumExpression evaluate:error];
|
||||
ReturnNilIfNil(currentValue);
|
||||
value = [value decimalNumberByMultiplyingBy:currentValue];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@end
|
||||
46
MathKit/MPRootFunction.h
Executable file
46
MathKit/MPRootFunction.h
Executable file
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// MPRootFunction.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 11.01.15.
|
||||
// Copyright (c) 2015 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPFunction.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPRootFunction</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPRootFunction, MPExpression;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPRootFunction
|
||||
@abstract This class represents an arbitrary root function.
|
||||
|
||||
@discussion A root has two children: An exponent and the expression to be
|
||||
rooted.
|
||||
*/
|
||||
@interface MPRootFunction : MPFunction
|
||||
|
||||
/*!
|
||||
@property exponentExpression
|
||||
@abstract The receiver's exponent.
|
||||
*/
|
||||
@property (nonatomic, strong) MPExpression *exponentExpression; /* Index 0 */
|
||||
|
||||
|
||||
/*!
|
||||
@property rootExpression
|
||||
@abstract The receiver's root expression.
|
||||
|
||||
@discussion The root expression is the expression that is to be rooted.
|
||||
*/
|
||||
@property (nonatomic, strong) MPExpression *rootExpression; /* Index 1 */
|
||||
|
||||
@end
|
||||
39
MathKit/MPRootFunction.m
Executable file
39
MathKit/MPRootFunction.m
Executable file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// MPRootFunction.m
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 11.01.15.
|
||||
// Copyright (c) 2015 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPRootFunction.h"
|
||||
|
||||
#import "MPRootTerm.h"
|
||||
#import "MPExpression.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPRootFunction
|
||||
|
||||
MPFunctionAccessorImplementation(ExponentExpression, _exponentExpression)
|
||||
MPFunctionAccessorImplementation(RootExpression, _rootExpression)
|
||||
|
||||
|
||||
- (NSArray *)childrenAccessors
|
||||
{
|
||||
return @[@"exponentExpression", @"rootExpression"];
|
||||
}
|
||||
|
||||
|
||||
- (Class)functionTermClass
|
||||
{
|
||||
return [MPRootTerm class];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"Root(%@, %@)", self.exponentExpression, self.rootExpression];
|
||||
}
|
||||
|
||||
@end
|
||||
42
MathKit/MPRootFunctionLayout.h
Executable file
42
MathKit/MPRootFunctionLayout.h
Executable file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// MPRootFunctionLayout.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 11.01.15.
|
||||
// Copyright (c) 2015 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPFunctionLayout.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPRootFunctionLayout</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPRootFunctionLayout, MPRootFunction;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPRootFunctionLayout
|
||||
@abstract A root function layout displays a <code>@link
|
||||
//apple_ref/occ/cl/MPRootFunction@/link</code>.
|
||||
|
||||
@discussion A root function is displayed in a very special way. In a way it
|
||||
can be described as an exponent at the left, a "V" in the middle
|
||||
and the expression that is to be rooted at the right with a bar
|
||||
over it.
|
||||
*/
|
||||
@interface MPRootFunctionLayout : MPFunctionLayout
|
||||
|
||||
/*!
|
||||
@method rootFunction
|
||||
@abstract Returns the <code>@link
|
||||
//apple_ref/occ/cl/MPRootFunction@/link</code> represented by the
|
||||
receiver.
|
||||
*/
|
||||
- (MPRootFunction *)rootFunction;
|
||||
|
||||
@end
|
||||
137
MathKit/MPRootFunctionLayout.m
Executable file
137
MathKit/MPRootFunctionLayout.m
Executable file
@@ -0,0 +1,137 @@
|
||||
//
|
||||
// MPRootFunctionLayout.m
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 11.01.15.
|
||||
// Copyright (c) 2015 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPRootFunctionLayout.h"
|
||||
|
||||
#import "MPRootFunction.h"
|
||||
|
||||
|
||||
#define kRootFunctionTrailingOffset 3
|
||||
|
||||
#define kRootFunctionLineWidth 1
|
||||
#define kRootFunctionLineFragmentLength (self.usesSmallSize ? 3 : 4)
|
||||
#define kRootFunctionVWidth 5
|
||||
|
||||
#define kRootFunctionExpressionXOffset 2
|
||||
#define kRootFunctionExponentXInset kRootFunctionLineFragmentLength
|
||||
|
||||
|
||||
@implementation MPRootFunctionLayout
|
||||
|
||||
- (MPRootFunction *)rootFunction
|
||||
{
|
||||
return (MPRootFunction *)self.function;
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)indexOfLeadingChild
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)indexOfTrailingChild
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index
|
||||
{
|
||||
return index == 0 ? 1 : NSNotFound;
|
||||
}
|
||||
|
||||
|
||||
- (NSIndexSet *)indexesOfRemainingChildren
|
||||
{
|
||||
return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
|
||||
{
|
||||
return index == 0;
|
||||
}
|
||||
|
||||
|
||||
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
|
||||
{
|
||||
NSRect exponentBounds = [self childLayoutAtIndex:0].bounds;
|
||||
NSRect rootBounds = [self childLayoutAtIndex:1].bounds;
|
||||
if (index == 0) {
|
||||
CGFloat x;
|
||||
if (exponentBounds.size.width > kRootFunctionExponentXInset) {
|
||||
x = 0;
|
||||
} else {
|
||||
x = kRootFunctionExponentXInset - exponentBounds.size.width;
|
||||
}
|
||||
return NSMakePoint(x, rootBounds.size.height / 2.0 + rootBounds.origin.y - exponentBounds.origin.y);
|
||||
} else {
|
||||
CGFloat rootSymbolXOffset = exponentBounds.size.width > kRootFunctionExponentXInset ? exponentBounds.size.width - kRootFunctionExponentXInset : 0;
|
||||
CGFloat x = rootSymbolXOffset + kRootFunctionLineFragmentLength + kRootFunctionVWidth + kRootFunctionExpressionXOffset;
|
||||
return NSMakePoint(x, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
|
||||
{
|
||||
return [[NSIndexPath indexPathWithIndex:1] indexPathByAddingIndex:0];
|
||||
}
|
||||
|
||||
- (NSRect)generateBounds
|
||||
{
|
||||
CGFloat x, y, width, height;
|
||||
NSRect exponentBounds = [self childLayoutAtIndex:0].bounds;
|
||||
NSRect rootBounds = [self childLayoutAtIndex:1].bounds;
|
||||
|
||||
x = 0;
|
||||
y = rootBounds.origin.y;
|
||||
CGFloat rootSymbolXOffset = exponentBounds.size.width > kRootFunctionLineFragmentLength ? exponentBounds.size.width : kRootFunctionLineFragmentLength;
|
||||
width = rootSymbolXOffset + kRootFunctionVWidth + kRootFunctionExpressionXOffset + rootBounds.size.width + kRootFunctionTrailingOffset;
|
||||
CGFloat heightToExponent = rootBounds.size.height / 2.0 + exponentBounds.size.height;
|
||||
height = MAX(heightToExponent, rootBounds.size.height + kRootFunctionLineWidth);
|
||||
return NSMakeRect(x, y, width, height);
|
||||
}
|
||||
|
||||
|
||||
- (void)draw
|
||||
{
|
||||
NSRect exponentBounds = [self childLayoutAtIndex:0].bounds;
|
||||
NSRect rootBounds = [self childLayoutAtIndex:1].bounds;
|
||||
|
||||
CGFloat rootSymbolXOffset = exponentBounds.size.width > kRootFunctionExponentXInset ? exponentBounds.size.width - kRootFunctionExponentXInset : 0;
|
||||
NSBezierPath *rootPath = [NSBezierPath bezierPath];
|
||||
NSPoint currentPoint = NSMakePoint(rootSymbolXOffset, rootBounds.size.height / 2.0 + rootBounds.origin.y);
|
||||
[rootPath moveToPoint:currentPoint];
|
||||
currentPoint.x += kRootFunctionLineFragmentLength;
|
||||
[rootPath lineToPoint:currentPoint];
|
||||
currentPoint.x += kRootFunctionVWidth / 2.0;
|
||||
currentPoint.y = rootBounds.origin.y;
|
||||
[rootPath lineToPoint:currentPoint];
|
||||
currentPoint.x += kRootFunctionVWidth / 2.0;
|
||||
currentPoint.y = rootBounds.size.height + rootBounds.origin.y;
|
||||
[rootPath lineToPoint:currentPoint];
|
||||
currentPoint.x += rootBounds.size.width;
|
||||
[rootPath lineToPoint:currentPoint];
|
||||
[rootPath stroke];
|
||||
}
|
||||
|
||||
@end
|
||||
33
MathKit/MPRootTerm.h
Executable file
33
MathKit/MPRootTerm.h
Executable file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// MPRootTerm.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 11.01.15.
|
||||
// Copyright (c) 2015 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPFunctionTerm.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPRootTerm</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPRootTerm;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPRootTerm
|
||||
@abstract Represents a <code>@link
|
||||
//apple_ref/occ/cl/MPRootFunction@/link</code>.
|
||||
|
||||
@discussion A root function is evaluated by evaluating the power
|
||||
<code>pow(e, 1/n)</code> where <code>e</code> is the expression
|
||||
to be rooted and <code>n</code> the exponent.
|
||||
*/
|
||||
@interface MPRootTerm : MPFunctionTerm
|
||||
|
||||
@end
|
||||
44
MathKit/MPRootTerm.m
Executable file
44
MathKit/MPRootTerm.m
Executable file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// MPRootTerm.m
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 11.01.15.
|
||||
// Copyright (c) 2015 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPRootTerm.h"
|
||||
|
||||
#import "MPParsedExpression.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPRootTerm
|
||||
|
||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||
{
|
||||
MPEvaluateExpression(exponent, 0);
|
||||
MPEvaluateExpression(root, 1);
|
||||
if ([root compare:[NSDecimalNumber zero]] < 0) {
|
||||
// There are no negative roots.
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:MPMathKitErrorDomain
|
||||
code:101
|
||||
userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"The root of a negative number is undefined.", nil)}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
if ([exponent isEqualToNumber:@(0)]) {
|
||||
// The C pow function returns 1 for pow(0, 0). Mathematically this should be undefined.
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:MPMathKitErrorDomain
|
||||
code:101
|
||||
userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"The 0th root is undefined.", nil)}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
NSDecimalNumber *actualExponent = [[NSDecimalNumber one] decimalNumberByDividingBy:exponent];
|
||||
return [[NSDecimalNumber alloc] initWithDouble:pow(root.doubleValue, actualExponent.doubleValue)];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user