Skip to content

Commit be6f6ab

Browse files
cupojoekostub
authored andcommitted
Adds a special type for inner displays. This will allow to reference the inner displays positions more easily, and point math list indexes to them (#126)
1 parent b40e3fb commit be6f6ab

File tree

6 files changed

+210
-77
lines changed

6 files changed

+210
-77
lines changed

iosMath/lib/MTMathListIndex.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ typedef NS_ENUM(unsigned int, MTMathListSubIndexType) {
4949
/// The subindex indexes into the radicand (only valid for radicals)
5050
kMTSubIndexTypeRadicand,
5151
/// The subindex indexes into the degree (only valid for radicals)
52-
kMTSubIndexTypeDegree
52+
kMTSubIndexTypeDegree,
53+
/// The subindex indexes into the inner list (only valid for inner)
54+
kMTSubIndexTypeInner
5355
};
5456

5557

iosMath/render/MTMathListDisplay.h

+24-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ typedef NS_ENUM(unsigned int, MTLinePosition) {
8888
/// Positioned at a subscript
8989
kMTLinePositionSubscript,
9090
/// Positioned at a superscript
91-
kMTLinePositionSuperscript
91+
kMTLinePositionSuperscript,
92+
/// Positioned at an inner
93+
kMTLinePositionInner
9294
};
9395

9496
/// Where the line is positioned
@@ -185,4 +187,25 @@ typedef NS_ENUM(unsigned int, MTLinePosition) {
185187

186188
@end
187189

190+
/// Rendering of an list with delimiters
191+
@interface MTInnerDisplay : MTDisplay
192+
193+
- (instancetype)init NS_UNAVAILABLE;
194+
195+
/** A display representing the inner list that can be wrapped in delimiters.
196+
It's position is relative to the parent is not treated as a sub-display.
197+
*/
198+
@property (nonatomic, readonly) MTMathListDisplay* inner;
199+
200+
/** A display representing the delimiters. Their position is relative
201+
to the parent are not treated as a sub-display.
202+
*/
203+
@property (nonatomic, readonly, nullable) MTDisplay* leftDelimiter;
204+
@property (nonatomic, readonly, nullable) MTDisplay* rightDelimiter;
205+
206+
/// Denotes the location in the parent MTList.
207+
@property (nonatomic, readonly) NSUInteger index;
208+
209+
@end
210+
188211
NS_ASSUME_NONNULL_END

iosMath/render/MTMathListDisplay.m

+105
Original file line numberDiff line numberDiff line change
@@ -798,3 +798,108 @@ - (void)draw:(CGContextRef)context
798798
CGContextRestoreGState(context);
799799
}
800800
@end
801+
802+
#pragma mark - MTInnerDisplay
803+
804+
@implementation MTInnerDisplay {
805+
MTMathListDisplay *_inner;
806+
}
807+
808+
- (instancetype) initWithInner:(MTMathListDisplay*) inner leftDelimiter:(MTDisplay*) leftDelimiter rightDelimiter:(MTDisplay*) rightDelimiter atIndex:(NSUInteger) index
809+
{
810+
self = [super init];
811+
if (self) {
812+
_leftDelimiter = leftDelimiter;
813+
_rightDelimiter = rightDelimiter;
814+
_inner = inner;
815+
_index = index;
816+
self.range = NSMakeRange(_index, 1);
817+
818+
self.width = leftDelimiter.width + inner.width + rightDelimiter.width;
819+
}
820+
return self;
821+
}
822+
823+
- (void)setPosition:(CGPoint)position
824+
{
825+
super.position = position;
826+
[self updateLeftDelimiterPosition];
827+
[self updateInnerPosition];
828+
[self updateRightDelimiterPosition];
829+
}
830+
831+
- (void) updateLeftDelimiterPosition
832+
{
833+
if (_leftDelimiter) {
834+
_leftDelimiter.position = self.position;
835+
}
836+
}
837+
838+
- (void) updateRightDelimiterPosition
839+
{
840+
if (_rightDelimiter) {
841+
_rightDelimiter.position = CGPointMake(_inner.position.x + _inner.width, self.position.y);
842+
}
843+
}
844+
845+
- (void) updateInnerPosition
846+
{
847+
if (_leftDelimiter) {
848+
_inner.position = CGPointMake(_leftDelimiter.position.x + _leftDelimiter.width, self.position.y);
849+
} else {
850+
_inner.position = self.position;
851+
}
852+
}
853+
854+
- (CGFloat)ascent
855+
{
856+
if (_leftDelimiter) {
857+
return _leftDelimiter.ascent;
858+
}
859+
if (_rightDelimiter) {
860+
return _rightDelimiter.ascent;
861+
}
862+
return _inner.ascent;
863+
}
864+
865+
- (CGFloat)descent
866+
{
867+
if (_leftDelimiter) {
868+
return _leftDelimiter.descent;
869+
}
870+
if (_rightDelimiter) {
871+
return _rightDelimiter.descent;
872+
}
873+
return _inner.descent;
874+
}
875+
876+
- (CGFloat)width
877+
{
878+
CGFloat w = _inner.width;
879+
if (_leftDelimiter) {
880+
w += _leftDelimiter.width;
881+
}
882+
if (_rightDelimiter) {
883+
w += _rightDelimiter.width;
884+
}
885+
return w;
886+
}
887+
888+
- (void)setTextColor:(MTColor *)textColor
889+
{
890+
[super setTextColor:textColor];
891+
self.leftDelimiter.textColor = textColor;
892+
self.rightDelimiter.textColor = textColor;
893+
_inner.textColor = textColor;
894+
}
895+
896+
- (void)draw:(CGContextRef)context
897+
{
898+
[super draw:context];
899+
// Draw the elements.
900+
[self.leftDelimiter draw:context];
901+
[self.rightDelimiter draw:context];
902+
[_inner draw:context];
903+
}
904+
905+
@end

iosMath/render/internal/MTMathListDisplayInternal.h

+14
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,17 @@
111111
- (instancetype)initWithAccent:(MTGlyphDisplay*) glyph accentee:(MTMathListDisplay*) accentee range:(NSRange) range NS_DESIGNATED_INITIALIZER;
112112

113113
@end
114+
115+
116+
@interface MTInnerDisplay ()
117+
118+
- (instancetype) initWithInner:(MTMathListDisplay*) inner leftDelimiter:(MTDisplay*) leftDelimiter rightDelimiter:(MTDisplay*) rightDelimiter atIndex:(NSUInteger) index NS_DESIGNATED_INITIALIZER;
119+
120+
@property (nonatomic) MTMathListDisplay* inner;
121+
122+
@property (nonatomic, nullable) MTDisplay* leftDelimiter;
123+
@property (nonatomic, nullable) MTDisplay* rightDelimiter;
124+
125+
@property (nonatomic) NSUInteger index;
126+
127+
@end

iosMath/render/internal/MTTypesetter.m

+43-47
Original file line numberDiff line numberDiff line change
@@ -707,12 +707,7 @@ - (void) createDisplayAtoms:(NSArray*) preprocessed
707707
}
708708
[self addInterElementSpace:prevNode currentType:atom.type];
709709
MTInner* inner = (MTInner*) atom;
710-
MTDisplay* display = nil;
711-
if (inner.leftBoundary || inner.rightBoundary) {
712-
display = [self makeLeftRight:inner];
713-
} else {
714-
display = [MTTypesetter createLineForMathList:inner.innerList font:_font style:_style cramped:_cramped];
715-
}
710+
MTInnerDisplay* display = [self makeInner:inner atIndex:atom.indexRange.location];
716711
display.position = _currentPosition;
717712
_currentPosition.x += display.width;
718713
[_displayAtoms addObject:display];
@@ -1507,47 +1502,6 @@ - (MTDisplay*) addLimitsToDisplay:(MTDisplay*) display forOperator:(MTLargeOpera
15071502

15081503
#pragma mark Large delimiters
15091504

1510-
// Delimiter shortfall from plain.tex
1511-
static const NSInteger kDelimiterFactor = 901;
1512-
static const NSInteger kDelimiterShortfallPoints = 5;
1513-
1514-
- (MTDisplay*) makeLeftRight:(MTInner*) inner
1515-
{
1516-
NSAssert(inner.leftBoundary || inner.rightBoundary, @"Inner should have a boundary to call this function");
1517-
1518-
MTMathListDisplay* innerListDisplay = [MTTypesetter createLineForMathList:inner.innerList font:_font style:_style cramped:_cramped spaced:YES];
1519-
CGFloat axisHeight = _styleFont.mathTable.axisHeight;
1520-
// delta is the max distance from the axis
1521-
CGFloat delta = MAX(innerListDisplay.ascent - axisHeight, innerListDisplay.descent + axisHeight);
1522-
CGFloat d1 = (delta / 500) * kDelimiterFactor; // This represents atleast 90% of the formula
1523-
CGFloat d2 = 2 * delta - kDelimiterShortfallPoints; // This represents a shortfall of 5pt
1524-
// The size of the delimiter glyph should cover at least 90% of the formula or
1525-
// be at most 5pt short.
1526-
CGFloat glyphHeight = MAX(d1, d2);
1527-
1528-
NSMutableArray* innerElements = [[NSMutableArray alloc] init];
1529-
CGPoint position = CGPointZero;
1530-
if (inner.leftBoundary && inner.leftBoundary.nucleus.length > 0) {
1531-
MTDisplay* leftGlyph = [self findGlyphForBoundary:inner.leftBoundary.nucleus withHeight:glyphHeight];
1532-
leftGlyph.position = position;
1533-
position.x += leftGlyph.width;
1534-
[innerElements addObject:leftGlyph];
1535-
}
1536-
1537-
innerListDisplay.position = position;
1538-
position.x += innerListDisplay.width;
1539-
[innerElements addObject:innerListDisplay];
1540-
1541-
if (inner.rightBoundary && inner.rightBoundary.nucleus.length > 0) {
1542-
MTDisplay* rightGlyph = [self findGlyphForBoundary:inner.rightBoundary.nucleus withHeight:glyphHeight];
1543-
rightGlyph.position = position;
1544-
position.x += rightGlyph.width;
1545-
[innerElements addObject:rightGlyph];
1546-
}
1547-
MTMathListDisplay* innerDisplay = [[MTMathListDisplay alloc] initWithDisplays:innerElements range:inner.indexRange];
1548-
return innerDisplay;
1549-
}
1550-
15511505
- (MTDisplay*) findGlyphForBoundary:(NSString*) delimiter withHeight:(CGFloat) glyphHeight
15521506
{
15531507
CGFloat glyphAscent, glyphDescent, glyphWidth;
@@ -1858,4 +1812,46 @@ - (void) positionRows:(NSArray<MTDisplay*>*) rows forTable:(MTMathTable*) table
18581812
row.position = CGPointMake(row.position.x, row.position.y - shiftDown);
18591813
}
18601814
}
1815+
1816+
#pragma mark inner
1817+
1818+
// Delimiter shortfall from plain.tex
1819+
static const NSInteger kDelimiterFactor = 901;
1820+
static const NSInteger kDelimiterShortfallPoints = 5;
1821+
1822+
- (MTInnerDisplay*) makeInner:(MTInner*) inner atIndex:(NSUInteger) index
1823+
{
1824+
NSAssert(inner.leftBoundary || inner.rightBoundary, @"Inner should have a boundary to call this function");
1825+
1826+
MTMathListDisplay* innerListDisplay = [MTTypesetter createLineForMathList:inner.innerList font:_font style:_style cramped:_cramped];
1827+
CGFloat axisHeight = _styleFont.mathTable.axisHeight;
1828+
// delta is the max distance from the axis
1829+
CGFloat delta = MAX(innerListDisplay.ascent - axisHeight, innerListDisplay.descent + axisHeight);
1830+
CGFloat d1 = (delta / 500) * kDelimiterFactor; // This represents atleast 90% of the formula
1831+
CGFloat d2 = 2 * delta - kDelimiterShortfallPoints; // This represents a shortfall of 5pt
1832+
// The size of the delimiter glyph should cover at least 90% of the formula or
1833+
// be at most 5pt short.
1834+
CGFloat glyphHeight = MAX(d1, d2);
1835+
1836+
MTDisplay* leftDelimiter = nil;
1837+
if (inner.leftBoundary && inner.leftBoundary.nucleus.length > 0) {
1838+
MTDisplay* leftGlyph = [self findGlyphForBoundary:inner.leftBoundary.nucleus withHeight:glyphHeight];
1839+
if (leftGlyph) {
1840+
leftDelimiter = leftGlyph;
1841+
}
1842+
}
1843+
1844+
MTDisplay* rightDelimiter = nil;
1845+
if (inner.rightBoundary && inner.rightBoundary.nucleus.length > 0) {
1846+
MTDisplay* rightGlyph = [self findGlyphForBoundary:inner.rightBoundary.nucleus withHeight:glyphHeight];
1847+
if (rightGlyph) {
1848+
rightDelimiter = rightGlyph;
1849+
}
1850+
}
1851+
1852+
MTInnerDisplay* innerDisplay = [[MTInnerDisplay alloc] initWithInner:innerListDisplay leftDelimiter:leftDelimiter rightDelimiter:rightDelimiter atIndex: index];
1853+
1854+
return innerDisplay;
1855+
}
1856+
18611857
@end

iosMathTests/MTTypesetterTest.m

+21-28
Original file line numberDiff line numberDiff line change
@@ -993,53 +993,46 @@ - (void)testInner {
993993
XCTAssertEqual(display.subDisplays.count, 1);
994994

995995
MTDisplay* sub0 = display.subDisplays[0];
996-
XCTAssertTrue([sub0 isKindOfClass:[MTMathListDisplay class]]);
997-
MTMathListDisplay* display2 = (MTMathListDisplay*) sub0;
998-
XCTAssertEqual(display2.type, kMTLinePositionRegular);
996+
XCTAssertTrue([sub0 isKindOfClass:[MTInnerDisplay class]]);
997+
MTInnerDisplay* display2 = (MTInnerDisplay*) sub0;
999998
XCTAssertTrue(CGPointEqualToPoint(display2.position, CGPointZero));
1000999
XCTAssertTrue(NSEqualRanges(display2.range, NSMakeRange(0, 1)));
10011000
XCTAssertFalse(display2.hasScript);
1002-
XCTAssertEqual(display2.index, NSNotFound);
1003-
XCTAssertEqual(display2.subDisplays.count, 3);
1004-
1005-
MTDisplay* subLeft = display2.subDisplays[0];
1006-
XCTAssertTrue([subLeft isKindOfClass:[MTGlyphDisplay class]]);
1007-
MTGlyphDisplay* glyph = (MTGlyphDisplay*) subLeft;
1001+
XCTAssertTrue([display2.leftDelimiter isKindOfClass:[MTGlyphDisplay class]]);
1002+
1003+
MTGlyphDisplay* glyph = (MTGlyphDisplay*) display2.leftDelimiter;
10081004
XCTAssertTrue(CGPointEqualToPoint(glyph.position, CGPointZero));
10091005
XCTAssertTrue(NSEqualRanges(glyph.range, NSMakeRange(NSNotFound, 0)));
10101006
XCTAssertFalse(glyph.hasScript);
1011-
1012-
MTDisplay* sub3 = display2.subDisplays[1];
1013-
XCTAssertTrue([sub3 isKindOfClass:[MTMathListDisplay class]]);
1014-
MTMathListDisplay* display3 = (MTMathListDisplay*) sub3;
1015-
XCTAssertEqual(display3.type, kMTLinePositionRegular);
1016-
XCTAssertEqualsCGPoint(display3.position, CGPointMake(7.78, 0), 0.01);
1017-
XCTAssertTrue(NSEqualRanges(display3.range, NSMakeRange(0, 1)));
1018-
XCTAssertFalse(display3.hasScript);
1019-
XCTAssertEqual(display3.index, NSNotFound);
1020-
XCTAssertEqual(display3.subDisplays.count, 1);
1021-
1022-
MTDisplay* subsub3 = display3.subDisplays[0];
1007+
1008+
XCTAssertTrue([display2.inner isKindOfClass:[MTMathListDisplay class]]);
1009+
MTMathListDisplay* innerMathListDisplay = (MTMathListDisplay*) display2.inner;
1010+
XCTAssertEqualsCGPoint(innerMathListDisplay.position, CGPointMake(7.78, 0), 0.001);
1011+
XCTAssertTrue(NSEqualRanges(innerMathListDisplay.range, NSMakeRange(0, 1)));
1012+
XCTAssertFalse(innerMathListDisplay.hasScript);
1013+
XCTAssertEqual(innerMathListDisplay.index, NSNotFound);
1014+
XCTAssertEqual(innerMathListDisplay.subDisplays.count, 1);
1015+
1016+
MTDisplay* subsub3 = innerMathListDisplay.subDisplays[0];
10231017
XCTAssertTrue([subsub3 isKindOfClass:[MTCTLineDisplay class]]);
10241018
MTCTLineDisplay* line = (MTCTLineDisplay*) subsub3;
10251019
XCTAssertEqual(line.atoms.count, 1);
10261020
// The x is italicized
10271021
XCTAssertEqualObjects(line.attributedString.string, @"𝑥");
10281022
XCTAssertTrue(CGPointEqualToPoint(line.position, CGPointZero));
10291023
XCTAssertFalse(line.hasScript);
1030-
1031-
MTDisplay* subRight = display2.subDisplays[2];
1032-
XCTAssertTrue([subRight isKindOfClass:[MTGlyphDisplay class]]);
1033-
MTGlyphDisplay* glyph2 = (MTGlyphDisplay*) subRight;
1034-
XCTAssertEqualsCGPoint(glyph2.position, CGPointMake(19.22, 0), 0.01);
1024+
1025+
XCTAssertTrue([display2.rightDelimiter isKindOfClass:[MTGlyphDisplay class]]);
1026+
MTGlyphDisplay* glyph2 = (MTGlyphDisplay*) display2.rightDelimiter;
1027+
XCTAssertEqualsCGPoint(glyph2.position, CGPointMake(19.22, 0), 0.001);
10351028
XCTAssertTrue(NSEqualRanges(glyph2.range, NSMakeRange(NSNotFound, 0)), "Got %@ instead", NSStringFromRange(glyph2.range));
10361029
XCTAssertFalse(glyph2.hasScript);
1037-
1030+
10381031
// dimensions
10391032
XCTAssertEqual(display.ascent, display2.ascent);
10401033
XCTAssertEqual(display.descent, display2.descent);
10411034
XCTAssertEqual(display.width, display2.width);
1042-
1035+
10431036
XCTAssertEqualWithAccuracy(display.ascent, 14.97, 0.001);
10441037
XCTAssertEqualWithAccuracy(display.descent, 4.97, 0.001);
10451038
XCTAssertEqualWithAccuracy(display.width, 27, 0.01);

0 commit comments

Comments
 (0)