Archived
1
This repository has been archived on 2022-08-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
mathpad/MathPad/MPExpressionLayout.m
Kim Wittenburg 740c3fd80a Some small corrections
Added example expression (hardcoded for development)
Added example change for expressions (hardcoded for development)
2014-05-18 01:10:50 +02:00

224 lines
6.4 KiB
Objective-C

//
// MPExpressionLayout.m
// MathPad
//
// Created by Kim Wittenburg on 22.04.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPExpressionLayout.h"
#import "MPExpressionStorage.h"
#import "MPFunctionLayout.h"
#import "MPModel.h"
#import "MPExpressionView.h"
@implementation MPExpressionLayout
#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
{
if (_expressionStorage) {
return _expressionStorage;
}
return self.parent.expressionStorage;
}
- (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;
}
#pragma mark Cache Methods
// TODO: Return nil from caching with illegal index
- (void)invalidate
{
_valid = NO;
[self.parent invalidate];
[self.expressionView setNeedsDisplay:YES];
}
- (void)editedExpressionInRange:(NSRange)range replacementLength:(NSUInteger)length
{
// 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];
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;
} else {
MPFunctionLayout *layout = [self functionLayoutForFunctionAtIndex:index];
return [layout sizeOfFunction];
}
}
#pragma mark Drawing Methods
- (void)drawSymbolAtIndex:(NSUInteger)index
atPoint:(NSPoint)point
{
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];
}
}
- (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];
}
@end