Pie spokes don't overlap


#1

Hi,

I would like to create pie chart where spokes would not overlap.
I would like to build something similar as I did with bar graph.

` func sChart(_ chart: ShinobiChart, alter tickMark: SChartTickMark, beforeAddingTo axis: SChartAxis) {

    // insure that previous tick mark is not null.
    if previousTickMark == nil {
        previousTickMark = tickMark
    }
    
    // Check if
    // * axis is SChartCategoryAxis - this mean that we are looking at X-Axis
    if axis is SChartCategoryAxis {
        
        // because tickLable frame is to large, we need to calculate actuale size of the text that is inside.
        var tickFrame = tickMark.tickLabel!.frame
        var previousTickFrame = previousTickMark!.tickLabel!.frame
        
        // we create same frame with same center (insetBy) but shorter for width - textWidth. This is /2.0 on every side.
        tickFrame = tickFrame.insetBy(dx: (tickFrame.width - tickMark.tickLabel!.textWidth())/2.0, dy: 0)
        previousTickFrame = previousTickFrame.insetBy(dx: (previousTickFrame.width - previousTickMark!.tickLabel!.textWidth() - tickMarkPadding)/2.0, dy: 0)
        
        // Check if
        // * previous visible tick intersect with current tick
        // * is tick in ploting area.
        if tickFrame.intersects(previousTickFrame) || chart.plotAreaFrame.minX > tickFrame.minX || chart.plotAreaFrame.maxX < tickFrame.maxX {
            
            // if ticks intersect, don't show it.
            tickMark.tickEnabled = false
            
            // return so this tick (invisible) is not stored as previousTickMark
            return
        }
        
        // if tick mark was drown store it for later calculation
        previousTickMark = tickMark
    }
}`

So I try with

func sChart(_ chart: ShinobiChart, alter label: UILabel, for datapoint: SChartRadialDataPoint, atSlice index: Int, in series: SChartRadialSeries) {

But my problem is that label inside this method has always position (0,0) so I can’t calculate if they intersect. How do I catch this? Where the labels position is calculated?

Pleas help…


#2

Hi Marko,

I’m afraid you’re right and due to the issue I mentioned on your thread 'Pie chart spoke with value and name, when showing spokes the label position has not been calculated at the point of calling sChart(_ chart: ShinobiChart, alter label: UILabel, for datapoint: SChartRadialDataPoint, atSlice index: Int, in series: SChartRadialSeries).

To solve the problem of overlapping labels, you could try adjusting donutSeries.outerRadius, and donutSeries.style().spokeStyle.length to get values that work better for your chart. One option would be to alternate long and short spoke lengths on the pie as follows:

func sChart(_ chart: ShinobiChart, alter label: UILabel, for datapoint: SChartRadialDataPoint, atSlice index: Int, in series: SChartRadialSeries) {
    let pieSeries = series as! SChartDonutSeries
    let spokeStyle = pieSeries.style().spokeStyle.copy() as! SChartSpokeStyle
    
    if (index % 2 == 0) {
        spokeStyle.length = 150
    }
    else {
        spokeStyle.length = 50
    }
    
    pieSeries.setSpokeStyle(spokeStyle, forSpokeAtSliceIndex: index)
}

I hope that helps.

Alison


#3

Thank you for your reply.
I will wait for update, where this will be solved.
In a meanwhile I will probably try to calculate an angle out of the chart and data center and see if angle difference from previous datapoint is to close.

If I will manage to succeed in this approach I will post an answer.

Marko


#4

I implemented solution with angle, which is not ideal but it present quite well results.
My solution is based on premise, that if the angle between two spokes is too small, they will overlap.

`

func sChart(_ chart: ShinobiChart, alter label: UILabel, for datapoint: SChartRadialDataPoint, atSlice index: Int, in series: SChartRadialSeries) {
    
    let donut = series as! SChartDonutSeries
    
    let centerPoint = donut.getDonutCenter()
    let centerOfTheSlice = donut.getSliceCenter(index)
    

    if previousSliceCenterPoint != nil {
            
            // Put "vectors" in center
            let centered_slice = CGPoint(x: centerOfTheSlice.x - centerPoint.x, y: centerOfTheSlice.y - centerPoint.y)
            let centered_previousSlice = CGPoint(x: previousSliceCenterPoint!.x - centerPoint.x, y: previousSliceCenterPoint!.y - centerPoint.y)
            
            // Calculate angle in degree
            let engle = acos((centered_slice.x * centered_previousSlice.x + centered_slice.y * centered_previousSlice.y)/pow((donut.outerRadius + donut.innerRadius)/2.0,2))*180/CGFloat.pi
            
            if engle < 30 {
                
                label.isHidden = true
                let spokeStyle = SChartSpokeStyle()
                spokeStyle.showSpokes = false
                donut.setSpokeStyle(spokeStyle, forSpokeAtSliceIndex: index)
            } else {
                
                previousSliceCenterPoint = centerOfTheSlice
            }
            
            
        } else {
            
            previousSliceCenterPoint = centerOfTheSlice
        }
    }

    label.text = datapoint.name
}`

#5

Hi Marko,

Thanks for sharing your solution! Hopefully it will be useful to other users as well.

Kind regards,

Alison