diff --git a/Charts/Classes/Data/Implementations/Realm/RealmLineDataSet.swift b/Charts/Classes/Data/Implementations/Realm/RealmLineDataSet.swift index 04835b1e5a..344d93e3b3 100644 --- a/Charts/Classes/Data/Implementations/Realm/RealmLineDataSet.swift +++ b/Charts/Classes/Data/Implementations/Realm/RealmLineDataSet.swift @@ -59,6 +59,12 @@ public class RealmLineDataSet: RealmLineRadarDataSet, ILineChartDataSet /// - returns: true if drawing cubic lines is enabled, false if not. public var isDrawCubicEnabled: Bool { return drawCubicEnabled } + /// If true, gradient lines are drawn instead of solid + public var drawGradientEnabled = false + + /// - returns: true if drawing gradeint lines is enabled, false if not. + public var isDrawGradientEnabled: Bool { return drawGradientEnabled } + /// The radius of the drawn circles. public var circleRadius = CGFloat(8.0) @@ -114,6 +120,9 @@ public class RealmLineDataSet: RealmLineRadarDataSet, ILineChartDataSet /// [1, 3, 4, 2] will paint [- ---- - ---- ] public var lineDashLengths: [CGFloat]? + /// This is the points where gradient should change color + public var gradientPositions: [CGFloat]? + /// formatter for customizing the position of the fill-line private var _fillFormatter: ChartFillFormatter = BarLineChartFillFormatter() diff --git a/Charts/Classes/Data/Implementations/Standard/LineChartDataSet.swift b/Charts/Classes/Data/Implementations/Standard/LineChartDataSet.swift index 69894fc134..c3a1df2179 100644 --- a/Charts/Classes/Data/Implementations/Standard/LineChartDataSet.swift +++ b/Charts/Classes/Data/Implementations/Standard/LineChartDataSet.swift @@ -70,6 +70,12 @@ public class LineChartDataSet: LineRadarChartDataSet, ILineChartDataSet /// - returns: true if drawing cubic lines is enabled, false if not. public var isDrawCubicEnabled: Bool { return drawCubicEnabled } + /// If true, gradient lines are drawn instead of solid + public var drawGradientEnabled = false + + /// - returns: true if drawing gradeint lines is enabled, false if not. + public var isDrawGradientEnabled: Bool { return drawGradientEnabled } + /// The radius of the drawn circles. public var circleRadius = CGFloat(8.0) @@ -125,8 +131,12 @@ public class LineChartDataSet: LineRadarChartDataSet, ILineChartDataSet /// [1, 3, 4, 2] will paint [- ---- - ---- ] public var lineDashLengths: [CGFloat]? + /// This is the points where gradient should change color + public var gradientPositions: [CGFloat]? + /// formatter for customizing the position of the fill-line private var _fillFormatter: ChartFillFormatter = BarLineChartFillFormatter() + /// Sets a custom FillFormatter to the chart that handles the position of the filled-line for each DataSet. Set this to null to use the default logic. public var fillFormatter: ChartFillFormatter? @@ -160,6 +170,7 @@ public class LineChartDataSet: LineRadarChartDataSet, ILineChartDataSet copy.lineDashLengths = lineDashLengths copy.drawCirclesEnabled = drawCirclesEnabled copy.drawCubicEnabled = drawCubicEnabled + copy.drawGradientEnabled = drawGradientEnabled return copy } } diff --git a/Charts/Classes/Data/Interfaces/ILineChartDataSet.swift b/Charts/Classes/Data/Interfaces/ILineChartDataSet.swift index f6fb8312f1..e0243a9318 100644 --- a/Charts/Classes/Data/Interfaces/ILineChartDataSet.swift +++ b/Charts/Classes/Data/Interfaces/ILineChartDataSet.swift @@ -31,6 +31,12 @@ public protocol ILineChartDataSet: ILineRadarChartDataSet /// - returns: true if drawing cubic lines is enabled, false if not. var isDrawCubicEnabled: Bool { get } + /// If true, gradient lines are drawn instead of solid + var drawGradientEnabled: Bool { get set } + + /// - returns: true if drawing gradeint lines is enabled, false if not. + var isDrawGradientEnabled: Bool { get } + /// The radius of the drawn circles. var circleRadius: CGFloat { get set } @@ -70,6 +76,9 @@ public protocol ILineChartDataSet: ILineRadarChartDataSet /// [1, 3, 4, 2] will paint [- ---- - ---- ] var lineDashLengths: [CGFloat]? { get set } + /// This is the points where gradient should change color + var gradientPositions: [CGFloat]? { get set } + /// Sets a custom FillFormatter to the chart that handles the position of the filled-line for each DataSet. Set this to null to use the default logic. var fillFormatter: ChartFillFormatter? { get set } } diff --git a/Charts/Classes/Renderers/LineChartRenderer.swift b/Charts/Classes/Renderers/LineChartRenderer.swift index cace7bcc51..35ab72437c 100644 --- a/Charts/Classes/Renderers/LineChartRenderer.swift +++ b/Charts/Classes/Renderers/LineChartRenderer.swift @@ -196,11 +196,15 @@ public class LineChartRenderer: LineScatterCandleRadarChartRenderer drawCubicFill(context: context, dataSet: dataSet, spline: cubicPath, matrix: valueToPixelMatrix, from: minx, to: size) } - CGContextBeginPath(context) - CGContextAddPath(context, cubicPath) - CGContextSetStrokeColorWithColor(context, drawingColor.CGColor) - CGContextStrokePath(context) - + if (dataSet.isDrawGradientEnabled) + { + drawGradientLine(context: context, dataSet: dataSet, spline: cubicPath, matrix: valueToPixelMatrix) + } else { + CGContextBeginPath(context) + CGContextAddPath(context, cubicPath) + CGContextSetStrokeColorWithColor(context, drawingColor.CGColor) + CGContextStrokePath(context) + } CGContextRestoreGState(context) } @@ -352,7 +356,16 @@ public class LineChartRenderer: LineScatterCandleRadarChartRenderer { drawLinearFill(context: context, dataSet: dataSet, minx: minx, maxx: maxx, trans: trans) } - } + if (dataSet.isDrawGradientEnabled) { + let path = generatePath( + dataSet: dataSet, + fillMin: dataSet.fillFormatter?.getFillLinePosition(dataSet: dataSet, dataProvider: dataProvider!) ?? 0.0, + from: minx, + to: maxx, + matrix: trans.valueToPixelMatrix) + + drawGradientLine(context: context, dataSet: dataSet, spline: path, matrix: valueToPixelMatrix)} + } internal func drawLinearFill(context context: CGContext, dataSet: ILineChartDataSet, minx: Int, maxx: Int, trans: ChartTransformer) { @@ -414,6 +427,29 @@ public class LineChartRenderer: LineScatterCandleRadarChartRenderer return filled } + /// Generates the path that is used for gradient drawing. + private func generatePath(dataSet dataSet: ILineChartDataSet, fillMin: CGFloat, from: Int, to: Int, var matrix: CGAffineTransform) -> CGPath + { + let phaseX = _animator.phaseX + let phaseY = _animator.phaseY + + var e: ChartDataEntry! + + let generatedPath = CGPathCreateMutable() + e = dataSet.entryForIndex(from) + if e != nil + { + CGPathMoveToPoint(generatedPath, &matrix, CGFloat(e.xIndex), CGFloat(e.value) * phaseY) + } + // create a new path + for (var x = from + 1, count = Int(ceil(CGFloat(to - from) * phaseX + CGFloat(from))); x < count; x++) + { + guard let e = dataSet.entryForIndex(x) else { continue } + CGPathAddLineToPoint(generatedPath, &matrix, CGFloat(e.xIndex), CGFloat(e.value) * phaseY) + } + return generatedPath + } + public override func drawValues(context context: CGContext) { guard let dataProvider = dataProvider, lineData = dataProvider.lineData else { return } @@ -636,4 +672,75 @@ public class LineChartRenderer: LineScatterCandleRadarChartRenderer CGContextRestoreGState(context) } + + internal func drawGradientLine(context context: CGContext, dataSet: ILineChartDataSet, spline: CGPath, matrix: CGAffineTransform) + { + CGContextSaveGState(context) + let gradientPath = CGPathCreateCopyByStrokingPath(spline, nil, dataSet.lineWidth, .Butt, .Miter, 10) + CGContextAddPath(context, gradientPath) + CGContextDrawPath(context, .Fill) + + let boundingBox = CGPathGetBoundingBox(gradientPath); + let gradientStart = CGPointMake(0, CGRectGetMaxY(boundingBox)); + let gradientEnd = CGPointMake(0, CGRectGetMinY(boundingBox)); + var gradientLocations : [CGFloat] = [] + var gradientColors : [CGFloat] = [] + var cRed : CGFloat = 0 + var cGreen : CGFloat = 0 + var cBlue : CGFloat = 0 + var cAlpha : CGFloat = 0 + + //Set lower bound color + gradientLocations.append(0) + var cColor = dataSet.colorAt(0) + if cColor.getRed(&cRed, green: &cGreen, blue: &cBlue, alpha: &cAlpha) { + gradientColors += [cRed, cGreen, cBlue, cAlpha] + } + + //Set middle colors + for (var i = 0; i < dataSet.gradientPositions!.count; i++) { + var positionLocation = CGPointMake(0, dataSet.gradientPositions![i]) + positionLocation = CGPointApplyAffineTransform(positionLocation, matrix) + let normPositionLocation = (positionLocation.y - gradientStart.y) / (gradientEnd.y - gradientStart.y) + if (normPositionLocation < 0) { + gradientLocations.append(0) + } else if (normPositionLocation > 1) { + gradientLocations.append(1) + } else { + gradientLocations.append(normPositionLocation) + } + } + for (var i = 0; i < dataSet.colors.count; i++) { + cColor = dataSet.colorAt(i) + if cColor.getRed(&cRed, green: &cGreen, blue: &cBlue, alpha: &cAlpha) { + gradientColors += [cRed, cGreen, cBlue, cAlpha] + } + } + + //Set upper bound color + gradientLocations.append(1) + cColor = dataSet.colorAt(dataSet.colors.count - 1) + if cColor.getRed(&cRed, green: &cGreen, blue: &cBlue, alpha: &cAlpha) { + gradientColors += [cRed, cGreen, cBlue, cAlpha] + } + + //Define gradient + var baseSpace = CGColorSpaceCreateDeviceRGB() + var gradient : CGGradient? + if (dataSet.gradientPositions!.count > 1) { + gradient = CGGradientCreateWithColorComponents(baseSpace, gradientColors, gradientLocations, gradientColors.count / 4) + } else { + gradient = CGGradientCreateWithColorComponents(baseSpace, gradientColors, nil, gradientColors.count / 4) + } + baseSpace = nil + + //Draw gradient path + CGContextBeginPath(context) + CGContextAddPath(context, gradientPath) + CGContextClip(context) + CGContextDrawLinearGradient(context, gradient, gradientStart, gradientEnd, CGGradientDrawingOptions(rawValue: 0)) + gradient = nil + CGContextRestoreGState(context) + } + } \ No newline at end of file