Skip to content

Commit def37e9

Browse files
committed
Added support for variable bar widths in range plots.
1 parent 23799c8 commit def37e9

3 files changed

Lines changed: 102 additions & 3 deletions

File tree

‎examples/CorePlotGallery/src/plots/RangePlot.m‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,11 @@ -(BOOL)plotSpace:(nonnull CPTPlotSpace *)space shouldHandlePointingDeviceUpEvent
210210
return NO;
211211
}
212212

213+
-(nullable NSNumber *)barWidthForRangePlot:(nonnull CPTRangePlot *)plot recordIndex:(NSUInteger)idx
214+
{
215+
return @(arc4random_uniform(85) + 15);
216+
}
217+
213218
#pragma mark -
214219
#pragma mark Plot Delegate Methods
215220

‎framework/Source/CPTRangePlot.h‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ extern CPTRangePlotBinding __nonnull const CPTRangePlotBindingLowValues;
1919
extern CPTRangePlotBinding __nonnull const CPTRangePlotBindingLeftValues;
2020
extern CPTRangePlotBinding __nonnull const CPTRangePlotBindingRightValues;
2121
extern CPTRangePlotBinding __nonnull const CPTRangePlotBindingBarLineStyles;
22+
extern CPTRangePlotBinding __nonnull const CPTRangePlotBindingBarWidths;
2223
/// @}
2324

2425
/**
@@ -70,6 +71,24 @@ typedef NS_ENUM (NSInteger, CPTRangePlotFillDirection) {
7071
**/
7172
-(nullable CPTLineStyle *)barLineStyleForRangePlot:(nonnull CPTRangePlot *)plot recordIndex:(NSUInteger)idx;
7273

74+
/** @brief @optional Gets an array of bar widths for the given range plot.
75+
* @param plot The range plot.
76+
* @param indexRange The range of the data indexes of interest.
77+
* @return An array of bar widths.
78+
**/
79+
-(nullable CPTNumberArray *)barWidthsForRangePlot:(nonnull CPTRangePlot *)barPlot recordIndexRange:(NSRange)indexRange;
80+
81+
/** @brief @optional Gets a bar width for the given range plot.
82+
* This method will not be called if
83+
* @link CPTBarPlotDataSource::barWidthsForBarPlot:recordIndexRange: -barWidthsForBarPlot:recordIndexRange: @endlink
84+
* is also implemented in the datasource.
85+
* @param plot The range plot.
86+
* @param idx The data index of interest.
87+
* @return The bar width for the bar with the given index. If the data source returns @nil, the default barWidth is used.
88+
* If the data source returns an NSNull object, no line is drawn.
89+
**/
90+
-(nullable NSNumber *)barWidthForRangePlot:(nonnull CPTRangePlot *)plot recordIndex:(NSUInteger)idx;
91+
7392
/// @}
7493

7594
@end
@@ -176,6 +195,8 @@ typedef NS_ENUM (NSInteger, CPTRangePlotFillDirection) {
176195
/// @{
177196
-(void)reloadBarLineStyles;
178197
-(void)reloadBarLineStylesInIndexRange:(NSRange)indexRange;
198+
-(void)reloadBarWidths;
199+
-(void)reloadBarWidthsInIndexRange:(NSRange)indexRange;
179200
/// @}
180201

181202
@end

‎framework/Source/CPTRangePlot.m‎

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#import "CPTUtilities.h"
1414
#import "CPTXYPlotSpace.h"
1515
#import "NSCoderExtensions.h"
16+
#import "NSNumberExtensions.h"
1617
#import "tgmath.h"
1718

1819
/** @defgroup plotAnimationRangePlot Range Plot
@@ -34,6 +35,7 @@
3435
CPTRangePlotBinding const CPTRangePlotBindingLeftValues = @"leftValues"; ///< Left price values.
3536
CPTRangePlotBinding const CPTRangePlotBindingRightValues = @"rightValues"; ///< Right price values.
3637
CPTRangePlotBinding const CPTRangePlotBindingBarLineStyles = @"barLineStyles"; ///< Bar line styles.
38+
CPTRangePlotBinding const CPTRangePlotBindingBarWidths = @"barWidths"; ///< Bar widths.
3739

3840
/// @cond
3941
struct CGPointError {
@@ -64,6 +66,7 @@ -(NSInteger)extremeDrawnPointIndexForFlags:(nonnull BOOL *)pointDrawFlags number
6466

6567
-(void)drawRangeInContext:(nonnull CGContextRef)context lineStyle:(nonnull CPTLineStyle *)lineStyle viewPoint:(CGPointError *)viewPoint halfGapSize:(CGSize)halfGapSize halfBarWidth:(CGFloat)halfBarWidth alignPoints:(BOOL)alignPoints;
6668
-(CPTLineStyle *)barLineStyleForIndex:(NSUInteger)idx;
69+
-(nonnull NSNumber *)barWidthForIndex:(NSUInteger)idx;
6770

6871
@end
6972

@@ -156,6 +159,7 @@ +(void)initialize
156159
[self exposeBinding:CPTRangePlotBindingLeftValues];
157160
[self exposeBinding:CPTRangePlotBindingRightValues];
158161
[self exposeBinding:CPTRangePlotBindingBarLineStyles];
162+
[self exposeBinding:CPTRangePlotBindingBarWidths];
159163
}
160164
}
161165

@@ -170,7 +174,12 @@ +(void)initialize
170174
*
171175
* This is the designated initializer. The initialized layer will have the following properties:
172176
* - @ref barLineStyle = default line style
177+
* - @ref fillDirection = CPTRangePlotFillHorizontal
173178
* - @ref areaFill = @nil
179+
* - @ref areaBorderLineStyle = @nil
180+
* - @ref barWidth = 0.0
181+
* - @ref gapHeight = 0.0
182+
* - @ref gapWidth = 0.0
174183
* - @ref labelField = #CPTRangePlotFieldX
175184
*
176185
* @param newFrame The frame rectangle.
@@ -183,6 +192,9 @@ -(nonnull instancetype)initWithFrame:(CGRect)newFrame
183192
fillDirection = CPTRangePlotFillHorizontal;
184193
areaFill = nil;
185194
areaBorderLineStyle = nil;
195+
barWidth = CPTFloat(0.0);
196+
gapHeight = CPTFloat(0.0);
197+
gapWidth = CPTFloat(0.0);
186198

187199
pointingDeviceDownIndex = NSNotFound;
188200

@@ -204,6 +216,9 @@ -(nonnull instancetype)initWithLayer:(nonnull id)layer
204216
fillDirection = theLayer->fillDirection;
205217
areaFill = theLayer->areaFill;
206218
areaBorderLineStyle = theLayer->areaBorderLineStyle;
219+
barWidth = theLayer->barWidth;
220+
gapHeight = theLayer->gapHeight;
221+
gapWidth = theLayer->gapWidth;
207222

208223
pointingDeviceDownIndex = NSNotFound;
209224
}
@@ -545,6 +560,9 @@ -(void)reloadDataInIndexRange:(NSRange)indexRange
545560

546561
// Bar line styles
547562
[self reloadBarLineStylesInIndexRange:indexRange];
563+
564+
// Bar widths
565+
[self reloadBarWidthsInIndexRange:indexRange];
548566
}
549567

550568
-(void)reloadPlotDataInIndexRange:(NSRange)indexRange
@@ -633,6 +651,49 @@ -(void)reloadBarLineStylesInIndexRange:(NSRange)indexRange
633651
[self setNeedsDisplay];
634652
}
635653

654+
/**
655+
* @brief Reload all bar widths from the data source immediately.
656+
**/
657+
-(void)reloadBarWidths
658+
{
659+
[self reloadBarWidthsInIndexRange:NSMakeRange(0, self.cachedDataCount)];
660+
}
661+
662+
/** @brief Reload bar widths in the given index range from the data source immediately.
663+
* @param indexRange The index range to load.
664+
**/
665+
-(void)reloadBarWidthsInIndexRange:(NSRange)indexRange
666+
{
667+
id<CPTRangePlotDataSource> theDataSource = (id<CPTRangePlotDataSource>)self.dataSource;
668+
669+
if ( [theDataSource respondsToSelector:@selector(barWidthsForRangePlot:recordIndexRange:)] ) {
670+
[self cacheArray:[theDataSource barWidthsForRangePlot:self recordIndexRange:indexRange]
671+
forKey:CPTRangePlotBindingBarWidths
672+
atRecordIndex:indexRange.location];
673+
}
674+
else if ( [theDataSource respondsToSelector:@selector(barWidthForRangePlot:recordIndex:)] ) {
675+
id nilObject = [CPTPlot nilData];
676+
CPTMutableNumberArray *array = [[NSMutableArray alloc] initWithCapacity:indexRange.length];
677+
NSUInteger maxIndex = NSMaxRange(indexRange);
678+
679+
for ( NSUInteger idx = indexRange.location; idx < maxIndex; idx++ ) {
680+
NSNumber *width = [theDataSource barWidthForRangePlot:self recordIndex:idx];
681+
if ( width ) {
682+
[array addObject:width];
683+
}
684+
else {
685+
[array addObject:nilObject];
686+
}
687+
}
688+
689+
[self cacheArray:array
690+
forKey:CPTRangePlotBindingBarWidths
691+
atRecordIndex:indexRange.location];
692+
}
693+
694+
[self setNeedsDisplay];
695+
}
696+
636697
#pragma mark -
637698
#pragma mark Drawing
638699

@@ -775,11 +836,12 @@ -(void)renderAsVectorInContext:(nonnull CGContextRef)context
775836
CGPathRelease(fillPath);
776837
}
777838

778-
CGSize halfGapSize = CPTSizeMake(self.gapWidth * CPTFloat(0.5), self.gapHeight * CPTFloat(0.5));
779-
CGFloat halfBarWidth = self.barWidth * CPTFloat(0.5);
780-
BOOL alignPoints = self.alignsPointsToPixels;
839+
CGSize halfGapSize = CPTSizeMake(self.gapWidth * CPTFloat(0.5), self.gapHeight * CPTFloat(0.5));
840+
BOOL alignPoints = self.alignsPointsToPixels;
781841

782842
for ( NSUInteger i = (NSUInteger)firstDrawnPointIndex; i <= (NSUInteger)lastDrawnPointIndex; i++ ) {
843+
CGFloat halfBarWidth = [self barWidthForIndex:i].cgFloatValue * CPTFloat(0.5);
844+
783845
[self drawRangeInContext:context
784846
lineStyle:[self barLineStyleForIndex:i]
785847
viewPoint:&viewPoints[i]
@@ -967,6 +1029,17 @@ -(nullable CPTLineStyle *)barLineStyleForIndex:(NSUInteger)idx
9671029
return theBarLineStyle;
9681030
}
9691031

1032+
-(nonnull NSNumber *)barWidthForIndex:(NSUInteger)idx
1033+
{
1034+
NSNumber *theBarWidth = [self cachedValueForKey:CPTRangePlotBindingBarWidths recordIndex:idx];
1035+
1036+
if ((theBarWidth == nil) || (theBarWidth == [CPTPlot nilData])) {
1037+
theBarWidth = @(self.barWidth);
1038+
}
1039+
1040+
return theBarWidth;
1041+
}
1042+
9701043
/// @endcond
9711044

9721045
#pragma mark -

0 commit comments

Comments
 (0)