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 gaps in LineChart #572

Closed
shgodoroja opened this issue Nov 25, 2015 · 5 comments
Closed

Add gaps in LineChart #572

shgodoroja opened this issue Nov 25, 2015 · 5 comments

Comments

@shgodoroja
Copy link

I want to add gaps in a line chart. Is this possible to implement with the last version of the lib ?

screen shot 2015-11-26 at 00 39 09

@liuxuan30
Copy link
Member

It is possible, take a look at #533

  1. use more dataSet
  2. customize line chart renderer - drawLinear

@arjunnayak
Copy link

@liuxuan30, could you elaborate on how we would use more dataSets to produce this effect? I'm a little confused.
For example, if we use data set A to represent the data available, and data set B to represent the lack of data, wouldn't the data sets automatically connect the gaps in points?

@liuxuan30
Copy link
Member

Sorry I take some days off.
A data set means a line. so two sets, two lines, sharing the same x values. To simulate the break, let's say set A has values at xIndex [0,1,2], and set B has values at xIndex [5,6,7], so [3,4] is the empty area that looks like line breaks.
However I don't think this is a good solution. It introduces more complexity when coming to line breaks. What I have done is modifying rendering code to stop connecting dots if a break is detected.

@kocharyanvahe
Copy link

Dear @liuxuan30 I need the same thing which you mentioned. Could you please share with me what changes you made in rendering code to stop connecting dots if a break is detected?

@sashkopotapov
Copy link

sashkopotapov commented Aug 5, 2020

Wrote my own solution, hope this will help.
Uploading Screenshot 2020-08-05 at `18.54.04.png…

import CoreGraphics

class CustomLineChartRenderer: LineChartRenderer {
    override func drawHorizontalBezier(context: CGContext, dataSet: ILineChartDataSet)
    {
        guard let dataProvider = dataProvider else { return }
        
        let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency)
        
        let phaseY = animator.phaseY
        
        _xBounds.set(chart: dataProvider, dataSet: dataSet, animator: animator)
        
        let set = (dataSet as! LineChartDataSet)
        
        // 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 = CGMutablePath()
        let fillPath = CGMutablePath()
        
        let valueToPixelMatrix = trans.valueToPixelMatrix
        
        if _xBounds.range >= 1
        {
            var prev: ChartDataEntry! = dataSet.entryForIndex(_xBounds.min)
            var cur: ChartDataEntry! = prev
            
            // let the spline start
            cubicPath.move(to: CGPoint(x: CGFloat(cur.x), y: CGFloat(cur.y * phaseY)), transform: valueToPixelMatrix)
            fillPath.move(to: CGPoint(x: CGFloat(cur.x), y: CGFloat(cur.y * phaseY)), transform: valueToPixelMatrix)
            
            var xValues: [Int] = []
            set.entries.forEach { xValues.append(Int($0.x)) }
            
            for j in 1..<set.entries.count
            {
                prev = cur
                cur = dataSet.entryForIndex(j)
                
                if cur.x - prev.x > 1 {
                    fillPath.addLine(to: CGPoint(x: CGFloat(prev.x), y: CGFloat(0)), transform: valueToPixelMatrix)
                    fillPath.addLine(to: CGPoint(x: CGFloat(cur.x), y: CGFloat(0)), transform: valueToPixelMatrix)
                    fillPath.addLine(to: CGPoint(x: CGFloat(cur.x), y: CGFloat(cur.y * phaseY)), transform: valueToPixelMatrix)
                    
                    cubicPath.move(to: CGPoint(x: CGFloat(cur.x),
                                               y: CGFloat(cur.y * phaseY)),
                                   transform: valueToPixelMatrix)
                } else {
                    let cpx = CGFloat(prev.x + (cur.x - prev.x) / 2.0)
                    
                    cubicPath.addCurve(
                        to: CGPoint(
                            x: CGFloat(cur.x),
                            y: CGFloat(cur.y * phaseY)),
                        control1: CGPoint(
                            x: cpx,
                            y: CGFloat(prev.y * phaseY)),
                        control2: CGPoint(
                            x: cpx,
                            y: CGFloat(cur.y * phaseY)),
                        transform: valueToPixelMatrix)
                    
                    fillPath.addCurve(
                        to: CGPoint(
                            x: CGFloat(cur.x),
                            y: CGFloat(cur.y * phaseY)),
                        control1: CGPoint(
                            x: cpx,
                            y: CGFloat(prev.y * phaseY)),
                        control2: CGPoint(
                            x: cpx,
                            y: CGFloat(cur.y * phaseY)),
                        transform: valueToPixelMatrix)
                }
            }
        }
        
        context.saveGState()
        
        if dataSet.isDrawFilledEnabled
        {
            // Copy this path because we make changes to it
//            let fillPath = cubicPath.mutableCopy()
            
            drawCubicFill(context: context,
                          dataSet: dataSet,
                          spline: fillPath,
                          matrix: valueToPixelMatrix,
                          bounds: _xBounds)
        }
        
        context.beginPath()
        context.addPath(cubicPath)
        context.setStrokeColor(drawingColor.cgColor)
        context.strokePath()
        
        context.restoreGState()
    }
    
    override func drawCubicFill(
        context: CGContext,
        dataSet: ILineChartDataSet,
        spline: CGMutablePath,
        matrix: CGAffineTransform,
        bounds: XBounds)
    {
        guard
            let dataProvider = dataProvider
            else { return }
        
        if bounds.range <= 0
        {
            return
        }
        
        let fillMin = dataSet.fillFormatter?.getFillLinePosition(dataSet: dataSet, dataProvider: dataProvider) ?? 0.0
        
        var pt1 = CGPoint(x: CGFloat(dataSet.entryForIndex(bounds.min + bounds.range)?.x ?? 0.0), y: fillMin)
        var pt2 = CGPoint(x: CGFloat(dataSet.entryForIndex(bounds.min)?.x ?? 0.0), y: fillMin)
        pt1 = pt1.applying(matrix)
        pt2 = pt2.applying(matrix)
        
        spline.addLine(to: pt1)
        spline.addLine(to: pt2)
        spline.closeSubpath()
        
        drawFilledPath(context: context, path: spline, fillColor: dataSet.fillColor, fillAlpha: dataSet.fillAlpha)
    }
    
    override func drawFilledPath(context: CGContext,
                                 path: CGPath,
                                 fillColor: NSUIColor,
                                 fillAlpha: CGFloat)
    {
        context.saveGState()
        context.beginPath()
        context.addPath(path)
        
        // filled is usually drawn with less alpha
        context.setAlpha(fillAlpha)
        
        context.setFillColor(fillColor.cgColor)
        context.fillPath()
        
        context.restoreGState()
    }
    
}```

Just miss x values ```
        var values: [ChartDataEntry] = []
        let entry1 = ChartDataEntry(x: 0, y: 1)
        let entry2 = ChartDataEntry(x: 1, y: 2)
        let entry3 = ChartDataEntry(x: 4, y: 3)
        let entry4 = ChartDataEntry(x: 5, y: 4)

        values.append(contentsOf: [entry1, entry2, entry3, entry4])

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants