Archived
1

Internal Redesign:

- Combined MPExpression and MPMutableExpression
- Abstracted children of MPExpression into MPExpressionElement protocol
- Abstracted most of MPExpressionLayout and MPFunctionLayout into common superclass MPLayout
This commit is contained in:
Kim Wittenburg
2014-08-11 13:57:48 +02:00
parent 740c3fd80a
commit 60760b8b3d
31 changed files with 1222 additions and 1343 deletions

View File

@@ -2,45 +2,69 @@
// MPExpressionLayout.m
// MathPad
//
// Created by Kim Wittenburg on 22.04.14.
// Created by Kim Wittenburg on 07.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPExpressionLayout.h"
#import "MPExpressionStorage.h"
#import "MPFunctionLayout.h"
#import "MPModel.h"
#import "MPExpressionView.h"
@interface MPExpressionLayout (MPPathGeneration)
- (NSBezierPath *)bezierPathForChildAtIndex:(NSUInteger)index;
- (NSBezierPath *)generateBezierPathForString:(NSString *)aString;
@end
@implementation MPExpressionLayout (MPPathGeneration)
- (NSBezierPath *)bezierPathForChildAtIndex:(NSUInteger)index
{
id symbol = [self.expression elementAtIndex:index];
if ([symbol isString]) {
return [self cachableObjectForIndex:index
generator:^id{
return [self generateBezierPathForString:symbol];
}];
} else {
MPLayout *layout = [self childLayoutAtIndex:index];
return layout.bezierPath;
}
}
- (NSBezierPath *)generateBezierPathForString:(NSString *)aString
{
NSAttributedString *text = [[NSAttributedString alloc] initWithString:aString
attributes:@{NSFontAttributeName: [NSFont fontWithName:@"Lucida Grande" size:18.0]}];
self.textStorage.attributedString = text;
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
NSGlyph glyphs[glyphRange.length+1];
NSUInteger actualGlyphCount = [self.layoutManager getGlyphs:glyphs
range:glyphRange];
NSBezierPath *path = [NSBezierPath bezierPath];
[path moveToPoint:NSZeroPoint];
[path appendBezierPathWithGlyphs:glyphs
count:actualGlyphCount
inFont:[NSFont fontWithName:@"Lucida Grande" size:18.0]];
return path;
}
@end
@implementation MPExpressionLayout
#pragma mark Creation Methods
# pragma mark Creation Methods
- (instancetype)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage
{
self = [super init];
if (self) {
_symbolCache = [[NSMutableArray alloc] init];
_expressionStorage = expressionStorage;
_expressionPath = [[NSIndexPath alloc] init];
}
return self;
}
- (instancetype)initWithExpressionPath:(NSIndexPath *)expressionPath
parent:(MPFunctionLayout *)parent
{
self = [super init];
if (self) {
_symbolCache = [[NSMutableArray alloc] init];
_expressionPath = expressionPath;
_parent = parent;
}
return self;
}
#pragma mark Properties
@synthesize expressionStorage = _expressionStorage;
- (MPExpressionStorage *)expressionStorage
{
@@ -52,172 +76,50 @@
- (MPExpression *)expression
{
return [self.expressionStorage symbolAtIndexPath:self.expressionPath];
}
- (NSLayoutManager *)layoutManager
{
return self.expressionStorage.layoutManager;
}
- (NSTextContainer *)textContainer
{
return self.expressionStorage.textContainer;
}
- (NSTextStorage *)textStorage
{
return self.expressionStorage.textStorage;
return [self.expressionStorage elementAtIndexPath:self.path];
}
#pragma mark Cache Methods
// TODO: Return nil from caching with illegal index
- (void)invalidate
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
{
_valid = NO;
[self.parent invalidate];
[self.expressionView setNeedsDisplay:YES];
id cachedObject = [self cachableObjectForIndex:index generator:^id{
NSIndexPath *indexPath = [self.path indexPathByAddingIndex:index];
MPFunctionLayout *layout = [MPFunctionLayout functionLayoutForFunctionAtIndexPath:indexPath
parent:self];
return layout;
}];
if ([cachedObject isKindOfClass:[NSBezierPath class]]) {
return nil;
}
return cachedObject;
}
- (void)editedExpressionInRange:(NSRange)range replacementLength:(NSUInteger)length
- (NSSize)sizeForChildAtIndex:(NSUInteger)index
{
// TODO: New symbols may also be inserted in the middle or at the beginning
NSInteger changeInLength = length - range.length;
while (_symbolCache.count < (self.expression.numberOfSymbols + changeInLength)) {
[_symbolCache addObject:[NSNull null]];
}
NSMutableArray *newPlaceholders = [[NSMutableArray alloc] initWithCapacity:length];
while (newPlaceholders.count < length) {
[newPlaceholders addObject:[NSNull null]];
}
[_symbolCache replaceObjectsInRange:range withObjectsFromArray:newPlaceholders];
[self invalidate];
}
- (BOOL)hasCacheForSymbolAtIndex:(NSUInteger)index
{
if (index >= _symbolCache.count) {
return NO;
}
return _symbolCache[index] != [NSNull null];
}
- (MPFunctionLayout *)functionLayoutForFunctionAtIndex:(NSUInteger)index;
{
if ([self hasCacheForSymbolAtIndex:index]) {
id cacheObject = _symbolCache[index];
if ([cacheObject isKindOfClass:[NSValue class]]) {
return nil;
}
return cacheObject;
}
MPFunctionLayout *layout = [MPFunctionLayout functionLayoutForFunctionAtIndexPath:[self.expressionPath indexPathByAddingIndex:index] parent:self];
while (index >= _symbolCache.count) {
[_symbolCache addObject:[NSNull null]];
}
_symbolCache[index] = layout;
return layout;
}
- (NSSize)cachedSizeForSymbolAtIndex:(NSUInteger)index
{
id cachedSymbol = _symbolCache[index];
if ([cachedSymbol isKindOfClass:[NSValue class]]) {
return [cachedSymbol sizeValue];
}
return [(MPFunctionLayout *)cachedSymbol sizeOfFunction];
}
- (void)cacheSize:(NSSize)size forSymbolAtIndex:(NSUInteger)index
{
while (index >= _symbolCache.count) {
[_symbolCache addObject:[NSNull null]];
}
_symbolCache[index] = [NSValue valueWithSize:size];
}
#pragma mark Size Calculation Methods
- (NSSize)sizeForAllSymbols
{
if (!_valid) {
_cachedSize = [self sizeForSymbolsInRange:NSMakeRange(0, self.expression.numberOfSymbols)];
_valid = YES;
}
return _cachedSize;
}
- (NSSize)sizeForSymbolsInRange:(NSRange)range
{
NSSize size = NSMakeSize(0, 0);
for (NSUInteger index = range.location; index < NSMaxRange(range); index++) {
NSSize symbolSize = [self sizeForSymbolAtIndex:index];
size.width += symbolSize.width;
size.height = MAX(size.height, symbolSize.height);
}
return size;
}
- (NSSize)sizeForSymbolAtIndex:(NSUInteger)index
{
if ([self hasCacheForSymbolAtIndex:index]) {
return [self cachedSizeForSymbolAtIndex:index];
}
id symbol = [self.expression symbolAtIndex:index];
id symbol = [self.expression elementAtIndex:index];
if ([symbol isString]) {
[self.textStorage setString:symbol];
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
NSSize symbolSize = [self.layoutManager boundingRectForGlyphRange:glyphRange
inTextContainer:self.textContainer].size;
[self cacheSize:symbolSize
forSymbolAtIndex:index];
return symbolSize;
return [self bezierPathForChildAtIndex:index].bounds.size;
} else {
MPFunctionLayout *layout = [self functionLayoutForFunctionAtIndex:index];
return [layout sizeOfFunction];
return [self childLayoutAtIndex:index].size;
}
}
#pragma mark Drawing Methods
- (void)drawSymbolAtIndex:(NSUInteger)index
atPoint:(NSPoint)point
- (NSBezierPath *)generateBezierPath
{
id symbol = [self.expression symbolAtIndex:index];
// point.x = point.y = 0;
NSLog(@"draw Symbol: %@ at Point: (x: %f, y: %f)", symbol, point.x, point.y);
if ([symbol isString]) {
[self.textStorage setString:symbol];
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
[self.layoutManager drawGlyphsForGlyphRange:glyphRange
atPoint:point];
} else {
MPFunctionLayout *layout = [self functionLayoutForFunctionAtIndex:index];
NSLog(@"layout: %@, index: %ld", layout, index);
[layout drawFunctionAtPoint:point];
NSBezierPath *fullPath = [NSBezierPath bezierPath];
[fullPath moveToPoint:NSZeroPoint];
NSUInteger x = 0;
for (NSInteger index = 0; index < self.expression.numberOfElements; ++index) {
NSAffineTransform *transform = [NSAffineTransform transform];
// TODO: Translate by the right amount
[transform translateXBy:x yBy:0];
NSBezierPath *path = [self bezierPathForChildAtIndex:index].copy;
[path transformUsingAffineTransform:transform];
[fullPath appendBezierPath:path];
x += path.bounds.size.width;
}
}
- (void)drawSymbolsInRange:(NSRange)range
atPoint:(NSPoint)point
{
NSSize overallSize = [self sizeForSymbolsInRange:range];
CGFloat x = point.x;
for (NSUInteger index = range.location; index < NSMaxRange(range); index++) {
NSSize symbolSize = [self sizeForSymbolAtIndex:index];
CGFloat dy = (overallSize.height - symbolSize.height) / 2;
[self drawSymbolAtIndex:index
atPoint:NSMakePoint(x, point.y + dy)];
x += symbolSize.width;
}
}
- (void)drawAllSymbolsAtPoint:(NSPoint)point
{
[self drawSymbolsInRange:NSMakeRange(0, [self.expression numberOfSymbols]) atPoint:point];
return fullPath;
}
@end