Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Horizontal cubic line #935

Merged
merged 6 commits into from
Apr 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion Charts/Classes/Data/Implementations/Standard/ChartDataSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,19 @@ public class ChartDataSet: ChartBaseDataSet
/// the last end value used for calcMinMax
internal var _lastEnd: Int = 0

public var yVals: [ChartDataEntry] { return _yVals }
/// the array of y-values that this DataSet represents.
public var yVals: [ChartDataEntry]
{
get
{
return _yVals
}
set
{
_yVals = newValue
notifyDataSetChanged()
}
}

/// Use this method to tell the data set that the underlying data has changed
public override func notifyDataSetChanged()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public class LineChartDataSet: LineRadarChartDataSet, ILineChartDataSet

// MARK: - Styling functions and accessors

/// The drawing mode for this line dataset
///
/// **default**: Linear
public var mode: LineChartMode = LineChartMode.Linear

private var _cubicIntensity = CGFloat(0.2)

/// Intensity for cubic lines (min = 0.05, max = 1)
Expand All @@ -64,16 +69,36 @@ public class LineChartDataSet: LineRadarChartDataSet, ILineChartDataSet
}
}

/// If true, cubic lines are drawn instead of linear
public var drawCubicEnabled = false
@available(*, deprecated=1.0, message="Use `mode` instead.")
public var drawCubicEnabled: Bool
{
get
{
return mode == .CubicBezier
}
set
{
mode = newValue ? LineChartMode.CubicBezier : LineChartMode.Linear
}
}

/// - returns: true if drawing cubic lines is enabled, false if not.
@available(*, deprecated=1.0, message="Use `mode` instead.")
public var isDrawCubicEnabled: Bool { return drawCubicEnabled }

/// If true, stepped lines are drawn instead of linear
public var drawSteppedEnabled = false

/// - returns: true if drawing stepped lines is enabled, false if not.
@available(*, deprecated=1.0, message="Use `mode` instead.")
public var drawSteppedEnabled: Bool
{
get
{
return mode == .Stepped
}
set
{
mode = newValue ? LineChartMode.Stepped : LineChartMode.Linear
}
}

@available(*, deprecated=1.0, message="Use `mode` instead.")
public var isDrawSteppedEnabled: Bool { return drawSteppedEnabled }

/// The radius of the drawn circles.
Expand Down Expand Up @@ -167,9 +192,10 @@ public class LineChartDataSet: LineRadarChartDataSet, ILineChartDataSet
copy.cubicIntensity = cubicIntensity
copy.lineDashPhase = lineDashPhase
copy.lineDashLengths = lineDashLengths
copy.lineCapType = lineCapType
copy.drawCirclesEnabled = drawCirclesEnabled
copy.drawCubicEnabled = drawCubicEnabled
copy.drawSteppedEnabled = drawSteppedEnabled
copy.drawCircleHoleEnabled = drawCircleHoleEnabled
copy.mode = mode
return copy
}
}
22 changes: 18 additions & 4 deletions Charts/Classes/Data/Interfaces/ILineChartDataSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,42 @@
import Foundation
import CoreGraphics

@objc
public enum LineChartMode: Int
{
case Linear
case Stepped
case CubicBezier
case HorizontalBezier
}

@objc
public protocol ILineChartDataSet: ILineRadarChartDataSet
{
// MARK: - Data functions and accessors

// MARK: - Styling functions and accessors

/// The drawing mode for this line dataset
///
/// **default**: Linear
var mode: LineChartMode { get set }

/// Intensity for cubic lines (min = 0.05, max = 1)
///
/// **default**: 0.2
var cubicIntensity: CGFloat { get set }

/// If true, cubic lines are drawn instead of linear
@available(*, deprecated=1.0, message="Use `mode` instead.")
var drawCubicEnabled: Bool { get set }

/// - returns: true if drawing cubic lines is enabled, false if not.
@available(*, deprecated=1.0, message="Use `mode` instead.")
var isDrawCubicEnabled: Bool { get }

/// If true, stepped lines are drawn instead of linear
@available(*, deprecated=1.0, message="Use `mode` instead.")
var drawSteppedEnabled: Bool { get set }

/// - returns: true if drawing stepped lines is enabled, false if not.
@available(*, deprecated=1.0, message="Use `mode` instead.")
var isDrawSteppedEnabled: Bool { get }

/// The radius of the drawn circles.
Expand Down
118 changes: 90 additions & 28 deletions Charts/Classes/Renderers/LineChartRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,23 @@ public class LineChartRenderer: LineRadarChartRenderer
}

// if drawing cubic lines is enabled
if (dataSet.isDrawCubicEnabled)
switch dataSet.mode
{
drawCubic(context: context, dataSet: dataSet)
}
else
{ // draw normal (straight) lines
case .Linear: fallthrough
case .Stepped:
drawLinear(context: context, dataSet: dataSet)

case .CubicBezier:
drawCubicBezier(context: context, dataSet: dataSet)

case .HorizontalBezier:
drawHorizontalBezier(context: context, dataSet: dataSet)
}

CGContextRestoreGState(context)
}

public func drawCubic(context context: CGContext, dataSet: ILineChartDataSet)
public func drawCubicBezier(context context: CGContext, dataSet: ILineChartDataSet)
{
guard let
trans = dataProvider?.getTransformer(dataSet.axisDependency),
Expand Down Expand Up @@ -134,12 +138,12 @@ public class LineChartRenderer: LineRadarChartRenderer
// let the spline start
CGPathMoveToPoint(cubicPath, &valueToPixelMatrix, CGFloat(cur.xIndex), CGFloat(cur.value) * phaseY)

for j in minx + 1 ..< min(size, entryCount - 1)
for j in minx + 1 ..< min(size, entryCount)
{
prevPrev = prev
prev = cur
cur = next
next = dataSet.entryForIndex(j + 1)
next = entryCount > j + 1 ? dataSet.entryForIndex(j + 1) : cur

if next == nil { break }

Expand All @@ -148,29 +152,87 @@ public class LineChartRenderer: LineRadarChartRenderer
curDx = CGFloat(next.xIndex - prev.xIndex) * intensity
curDy = CGFloat(next.value - prev.value) * intensity

CGPathAddCurveToPoint(cubicPath, &valueToPixelMatrix, CGFloat(prev.xIndex) + prevDx, (CGFloat(prev.value) + prevDy) * phaseY,
CGFloat(cur.xIndex) - curDx,
(CGFloat(cur.value) - curDy) * phaseY, CGFloat(cur.xIndex), CGFloat(cur.value) * phaseY)
CGPathAddCurveToPoint(cubicPath, &valueToPixelMatrix,
CGFloat(prev.xIndex) + prevDx,
(CGFloat(prev.value) + prevDy) * phaseY,
CGFloat(cur.xIndex) - curDx,
(CGFloat(cur.value) - curDy) * phaseY,
CGFloat(cur.xIndex),
CGFloat(cur.value) * phaseY)
}
}

CGContextSaveGState(context)

if (dataSet.isDrawFilledEnabled)
{
// Copy this path because we make changes to it
let fillPath = CGPathCreateMutableCopy(cubicPath)

if (size > entryCount - 1)
drawCubicFill(context: context, dataSet: dataSet, spline: fillPath!, matrix: valueToPixelMatrix, from: minx, to: size)
}

CGContextBeginPath(context)
CGContextAddPath(context, cubicPath)
CGContextSetStrokeColorWithColor(context, drawingColor.CGColor)
CGContextStrokePath(context)

CGContextRestoreGState(context)
}

public func drawHorizontalBezier(context context: CGContext, dataSet: ILineChartDataSet)
{
guard let
trans = dataProvider?.getTransformer(dataSet.axisDependency),
animator = animator
else { return }

let entryCount = dataSet.entryCount

guard let
entryFrom = dataSet.entryForXIndex(self.minX < 0 ? self.minX : 0, rounding: .Down),
entryTo = dataSet.entryForXIndex(self.maxX, rounding: .Up)
else { return }

let diff = (entryFrom == entryTo) ? 1 : 0
let minx = max(dataSet.entryIndex(entry: entryFrom) - diff - 1, 0)
let maxx = min(max(minx + 2, dataSet.entryIndex(entry: entryTo) + 1), entryCount)

let phaseX = animator.phaseX
let phaseY = animator.phaseY

// get the color that is specified for this position from the DataSet
let drawingColor = dataSet.colors.first!

// the path for the cubic-spline
let cubicPath = CGPathCreateMutable()

var valueToPixelMatrix = trans.valueToPixelMatrix

let size = Int(ceil(CGFloat(maxx - minx) * phaseX + CGFloat(minx)))

if (size - minx >= 2)
{
var prev: ChartDataEntry! = dataSet.entryForIndex(minx)
var cur: ChartDataEntry! = prev

if cur == nil { return }

// let the spline start
CGPathMoveToPoint(cubicPath, &valueToPixelMatrix, CGFloat(cur.xIndex), CGFloat(cur.value) * phaseY)

for j in minx + 1 ..< min(size, entryCount)
{
prevPrev = dataSet.entryForIndex(entryCount - (entryCount >= 3 ? 3 : 2))
prev = dataSet.entryForIndex(entryCount - 2)
cur = dataSet.entryForIndex(entryCount - 1)
next = cur

if prevPrev == nil || prev == nil || cur == nil { return }
prev = cur
cur = dataSet.entryForIndex(j)

prevDx = CGFloat(cur.xIndex - prevPrev.xIndex) * intensity
prevDy = CGFloat(cur.value - prevPrev.value) * intensity
curDx = CGFloat(next.xIndex - prev.xIndex) * intensity
curDy = CGFloat(next.value - prev.value) * intensity
let cpx = CGFloat(prev.xIndex) + CGFloat(cur.xIndex - prev.xIndex) / 2.0

// the last cubic
CGPathAddCurveToPoint(cubicPath, &valueToPixelMatrix, CGFloat(prev.xIndex) + prevDx, (CGFloat(prev.value) + prevDy) * phaseY,
CGFloat(cur.xIndex) - curDx,
(CGFloat(cur.value) - curDy) * phaseY, CGFloat(cur.xIndex), CGFloat(cur.value) * phaseY)
CGPathAddCurveToPoint(cubicPath,
&valueToPixelMatrix,
cpx, CGFloat(prev.value) * phaseY,
cpx, CGFloat(cur.value) * phaseY,
CGFloat(cur.xIndex), CGFloat(cur.value) * phaseY)
}
}

Expand Down Expand Up @@ -240,7 +302,7 @@ public class LineChartRenderer: LineRadarChartRenderer
let valueToPixelMatrix = trans.valueToPixelMatrix

let entryCount = dataSet.entryCount
let isDrawSteppedEnabled = dataSet.isDrawSteppedEnabled
let isDrawSteppedEnabled = dataSet.mode == .Stepped
let pointsPerEntryPair = isDrawSteppedEnabled ? 4 : 2

let phaseX = animator.phaseX
Expand Down Expand Up @@ -425,7 +487,7 @@ public class LineChartRenderer: LineRadarChartRenderer
{
let phaseX = animator?.phaseX ?? 1.0
let phaseY = animator?.phaseY ?? 1.0
let isDrawSteppedEnabled = dataSet.isDrawSteppedEnabled
let isDrawSteppedEnabled = dataSet.mode == .Stepped
var matrix = matrix

var e: ChartDataEntry!
Expand Down
71 changes: 52 additions & 19 deletions ChartsDemo/Classes/Demos/CubicLineChartViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ - (void)viewDidLoad
@{@"key": @"toggleFilled", @"label": @"Toggle Filled"},
@{@"key": @"toggleCircles", @"label": @"Toggle Circles"},
@{@"key": @"toggleCubic", @"label": @"Toggle Cubic"},
@{@"key": @"toggleHorizontalCubic", @"label": @"Toggle Horizontal Cubic"},
@{@"key": @"toggleStepped", @"label": @"Toggle Stepped"},
@{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"},
@{@"key": @"animateX", @"label": @"Animate X"},
@{@"key": @"animateY", @"label": @"Animate Y"},
Expand Down Expand Up @@ -129,25 +131,35 @@ - (void)setDataCount:(int)count range:(double)range
[yVals1 addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]];
}

LineChartDataSet *set1 = [[LineChartDataSet alloc] initWithYVals:yVals1 label:@"DataSet 1"];
set1.drawCubicEnabled = YES;
set1.cubicIntensity = 0.2;
set1.drawCirclesEnabled = NO;
set1.lineWidth = 1.8;
set1.circleRadius = 4.0;
[set1 setCircleColor:UIColor.whiteColor];
set1.highlightColor = [UIColor colorWithRed:244/255.f green:117/255.f blue:117/255.f alpha:1.f];
[set1 setColor:UIColor.whiteColor];
set1.fillColor = UIColor.whiteColor;
set1.fillAlpha = 1.f;
set1.drawHorizontalHighlightIndicatorEnabled = NO;
set1.fillFormatter = [[CubicLineSampleFillFormatter alloc] init];

LineChartData *data = [[LineChartData alloc] initWithXVals:xVals dataSet:set1];
[data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:9.f]];
[data setDrawValues:NO];

_chartView.data = data;
LineChartDataSet *set1 = nil;
if (_chartView.data.dataSetCount > 0)
{
set1 = (LineChartDataSet *)_chartView.data.dataSets[0];
set1.yVals = yVals1;
[_chartView notifyDataSetChanged];
}
else
{
set1 = [[LineChartDataSet alloc] initWithYVals:yVals1 label:@"DataSet 1"];
set1.drawCubicEnabled = YES;
set1.cubicIntensity = 0.2;
set1.drawCirclesEnabled = NO;
set1.lineWidth = 1.8;
set1.circleRadius = 4.0;
[set1 setCircleColor:UIColor.whiteColor];
set1.highlightColor = [UIColor colorWithRed:244/255.f green:117/255.f blue:117/255.f alpha:1.f];
[set1 setColor:UIColor.whiteColor];
set1.fillColor = UIColor.whiteColor;
set1.fillAlpha = 1.f;
set1.drawHorizontalHighlightIndicatorEnabled = NO;
set1.fillFormatter = [[CubicLineSampleFillFormatter alloc] init];

LineChartData *data = [[LineChartData alloc] initWithXVals:xVals dataSet:set1];
[data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:9.f]];
[data setDrawValues:NO];

_chartView.data = data;
}
}

- (void)optionTapped:(NSString *)key
Expand Down Expand Up @@ -185,6 +197,27 @@ - (void)optionTapped:(NSString *)key
return;
}

if ([key isEqualToString:@"toggleStepped"])
{
for (id<ILineChartDataSet> set in _chartView.data.dataSets)
{
set.drawSteppedEnabled = !set.isDrawSteppedEnabled;
}

[_chartView setNeedsDisplay];
}

if ([key isEqualToString:@"toggleHorizontalCubic"])
{
for (id<ILineChartDataSet> set in _chartView.data.dataSets)
{
set.mode = set.mode == LineChartModeCubicBezier ? LineChartModeHorizontalBezier : LineChartModeCubicBezier;
}

[_chartView setNeedsDisplay];
return;
}

[super handleOption:key forChartView:_chartView];
}

Expand Down
Loading