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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user