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

Add a gradient line chart #649

Closed
wants to merge 7 commits into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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?
Expand Down Expand Up @@ -160,6 +170,7 @@ public class LineChartDataSet: LineRadarChartDataSet, ILineChartDataSet
copy.lineDashLengths = lineDashLengths
copy.drawCirclesEnabled = drawCirclesEnabled
copy.drawCubicEnabled = drawCubicEnabled
copy.drawGradientEnabled = drawGradientEnabled
return copy
}
}
9 changes: 9 additions & 0 deletions Charts/Classes/Data/Interfaces/ILineChartDataSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand Down Expand Up @@ -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 }
}
119 changes: 113 additions & 6 deletions Charts/Classes/Renderers/LineChartRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 }
Expand Down Expand Up @@ -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)
}

}