Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c7f6ad9
Improves clean Debug build time by about 30%.
drewster99 Nov 8, 2023
b620fa7
Make partitioningIndex public
ttuygun Sep 20, 2023
c14ed45
Add logic to make cornerRadius on BarChart
Mar 28, 2024
1b9bc5b
Add inverted corners for negative values
Mar 27, 2024
1c92520
Make roundedCornersInverted setter private for better protocol
Mar 29, 2024
196202d
Added tests for BarChartView
Viktor-by Aug 5, 2024
9d3e924
Charts/issues/5197 - fixed host app crash by adding a check before ca…
sorinmiroiu97 Sep 2, 2024
6d1cdfe
Charts/issues/5197 - fixed host app crash by checking if the value is…
sorinmiroiu97 Sep 2, 2024
34cae27
Added safe index subscript logic to avoid crash
tring-bhagya Sep 19, 2024
301a2bd
5197 - fixed typo for scaleY from issue comment
sorinmiroiu97 Nov 8, 2024
afc7c98
Fix FloatingPoint functions: Replace instance calls with static Doubl…
ImranQasim Feb 26, 2025
04aeec5
Fix crash in LineChartRenderer.swift when animating data set changes
StainlessStlRat Mar 18, 2025
1935638
Improved uniform spacing in pie charts
danielgindi May 12, 2025
82271c7
Merge pull request #5235 from ChartsOrg/pie_renderer_spacing_issues
danielgindi May 13, 2025
ab66734
Merge pull request #5231 from StainlessStlRat/master
danielgindi May 13, 2025
2bfd37f
Merge pull request #5226 from ImranQasim/master
danielgindi May 13, 2025
a930d59
Merge branch 'master' into release/5.0.0_updated
danielgindi May 13, 2025
019d979
Merge pull request #5200 from tring-bhagya/release/5.0.0_updated
danielgindi May 13, 2025
a6d2c0f
Merge pull request #5198 from sorinmiroiu97/sorin/crash-fix/5197
danielgindi May 13, 2025
7143ae6
Merge pull request #5192 from Viktor-by/tech/bar-chart-tests
danielgindi May 13, 2025
6626a2b
Merge pull request #5163 from crash481/feature/corner-radius
danielgindi May 13, 2025
953011d
Merge pull request #5159 from Commencis/public-internal
danielgindi May 13, 2025
86929ff
Merge pull request #5124 from drewster99/bugfix/issue5123
danielgindi May 13, 2025
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
7 changes: 6 additions & 1 deletion Charts.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,8 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_MODULE_VERIFIER = YES;
EAGER_LINKING = YES;
ENABLE_MODULE_VERIFIER = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_VERSION = A;
GCC_NO_COMMON_BLOCKS = YES;
Expand All @@ -1055,12 +1056,14 @@
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11";
MTL_ENABLE_DEBUG_INFO = YES;
OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=50";
PRODUCT_BUNDLE_IDENTIFIER = com.dcg.Charts;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_COMPILATION_MODE = singlefile;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
Expand Down Expand Up @@ -1231,6 +1234,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
EAGER_LINKING = YES;
ENABLE_MODULE_VERIFIER = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_VERSION = A;
Expand All @@ -1248,6 +1252,7 @@
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11";
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=50";
PRODUCT_BUNDLE_IDENTIFIER = com.dcg.Charts;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
Expand Down
30 changes: 25 additions & 5 deletions Source/Charts/Animation/ChartAnimationEasing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,12 @@ internal struct EasingFunctions
}

position = position - 1.0
return Double( 0.5 * (-pow(2.0, -10.0 * position) + 2.0) )

// compute partial result so Xcode's type-checker doesn't take too long
let partialResult: Double = -pow(2.0, -10.0 * position) + 2.0

// took 120ms to type check before breaking out partial result, above
return Double( 0.5 * partialResult )
}

internal static let EaseInCirc = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
Expand All @@ -265,12 +270,17 @@ internal struct EasingFunctions

internal static let EaseInOutCirc = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position: TimeInterval = elapsed / (duration / 2.0)

// calculate partial result so Swift compiler doesn't lose its mind
let sqrtPartialResult: Double = sqrt(1.0 - position * position)
if position < 1.0
{
return Double( -0.5 * (sqrt(1.0 - position * position) - 1.0) )
// was 800ms to type check with inlined sqrt calculation, from above
return Double( -0.5 * (sqrtPartialResult - 1.0) )
}
position -= 2.0
return Double( 0.5 * (sqrt(1.0 - position * position) + 1.0) )
// was 1500ms to type check with inlined sqrt calculation, from above
return Double( 0.5 * (sqrtPartialResult + 1.0) )
}

internal static let EaseInElastic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
Expand Down Expand Up @@ -328,13 +338,23 @@ internal struct EasingFunctions
return Double( -0.5 * (pow(2.0, 10.0 * position) * sin((position * duration - s) * (2.0 * Double.pi) / p)) )
}
position -= 1.0
return Double( pow(2.0, -10.0 * position) * sin((position * duration - s) * (2.0 * Double.pi) / p) * 0.5 + 1.0 )

// Break out partial result so the Swift compiler doesn't lose its mind
let sinPartialResult: Double = sin((position * duration - s) * (2.0 * Double.pi) / p)

// Original expression here, with the expression above inlined, took 600ms to type check
return Double( pow(2.0, -10.0 * position) * sinPartialResult * 0.5 + 1.0 )
}

internal static let EaseInBack = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
let s: TimeInterval = 1.70158
var position: TimeInterval = elapsed / duration
return Double( position * position * ((s + 1.0) * position - s) )

// Break out partial result so the Swift compiler doesn't lose its mind
let partialResult: Double = ((s + 1.0) * position - s)

// Original expression here, with partialResult inlined, took 260ms to type check
return Double( position * position * partialResult )
}

internal static let EaseOutBack = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
Expand Down
2 changes: 1 addition & 1 deletion Source/Charts/Charts/ChartViewBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ open class ChartViewBase: NSUIView, ChartDataProvider, AnimatorDelegate
}

// set the indices to highlight
highlighted = [h]
highlighted = [h]

if callDelegate
{
Expand Down
28 changes: 28 additions & 0 deletions Source/Charts/Data/Implementations/Standard/BarChartDataSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,34 @@ open class BarChartDataSet: BarLineScatterCandleBubbleChartDataSet, BarChartData
/// the overall entry count, including counting each stack-value individually
private var _entryCountStacks = 0

/// the corner radius applied to each data set
public var cornerRadius: CGFloat = 0.0

/// array of corners to be rounded
open var roundedCorners: UIRectCorner = [] {
didSet {
var invertedCorners: UIRectCorner = []
if roundedCorners.contains(.topLeft) {
invertedCorners.insert(.bottomLeft)
}
if roundedCorners.contains(.topRight) {
invertedCorners.insert(.bottomRight)
}
if roundedCorners.contains(.bottomLeft) {
invertedCorners.insert(.topLeft)
}
if roundedCorners.contains(.bottomRight) {
invertedCorners.insert(.topRight)
}
if roundedCorners.contains(.allCorners) {
invertedCorners.insert(.allCorners)
}
roundedCornersInverted = invertedCorners
}
}

open private(set) var roundedCornersInverted: UIRectCorner = []

/// Calculates the total number of entries this DataSet represents, including
/// stacks. All values belonging to a stack are calculated separately.
private func calcEntryCountIncludingStacks(entries: [BarChartDataEntry])
Expand Down
4 changes: 4 additions & 0 deletions Source/Charts/Data/Implementations/Standard/ChartData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,10 @@ extension ChartData: MutableCollection
get { return dataSets[position] }
set { self._dataSets[position] = newValue }
}

public subscript(safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}

// MARK: RandomAccessCollection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ open class RadarChartData: ChartData

@objc open override func entry(for highlight: Highlight) -> ChartDataEntry?
{
return self[highlight.dataSetIndex].entryForIndex(Int(highlight.x))
return self[safe: highlight.dataSetIndex]?.entryForIndex(Int(highlight.x))
}
}
13 changes: 13 additions & 0 deletions Source/Charts/Data/Interfaces/BarChartDataSetProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
import Foundation
import CoreGraphics

#if canImport(UIKit)
import UIKit
#endif

@objc
public protocol BarChartDataSetProtocol: BarLineScatterCandleBubbleChartDataSetProtocol
{
Expand Down Expand Up @@ -39,4 +43,13 @@ public protocol BarChartDataSetProtocol: BarLineScatterCandleBubbleChartDataSetP

/// array of labels used to describe the different values of the stacked bars
var stackLabels: [String] { get set }

/// the corner radius applied to each data set
var cornerRadius: CGFloat { get set }

/// array of corners to be rounded
var roundedCorners: UIRectCorner { get set }

/// array of corners to be rounded
var roundedCornersInverted: UIRectCorner { get }
}
33 changes: 29 additions & 4 deletions Source/Charts/Renderers/BarChartRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,17 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer
guard viewPortHandler.isInBoundsRight(barRect.origin.x) else { break }

context.setFillColor(dataSet.barShadowColor.cgColor)
context.fill(barRect)

var roundedCorners = dataSet.roundedCorners
if let i = buffer.firstIndex(of: barRect),
let entry = dataSet.entryForIndex(i),
entry.y < 0 {
roundedCorners = dataSet.roundedCornersInverted
}
let bezierPath = UIBezierPath(roundedRect: barRect, byRoundingCorners: roundedCorners,
cornerRadii: .init(width: dataSet.cornerRadius, height: dataSet.cornerRadius))
context.addPath(bezierPath.cgPath)
context.drawPath(using: .fill)
}
}

Expand Down Expand Up @@ -379,7 +389,15 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer
context.setFillColor(dataSet.color(atIndex: j).cgColor)
}

context.fill(barRect)
var roundedCorners = dataSet.roundedCorners
if let entry = dataSet.entryForIndex(j),
entry.y < 0 {
roundedCorners = dataSet.roundedCornersInverted
}
let bezierPath = UIBezierPath(roundedRect: barRect, byRoundingCorners: roundedCorners,
cornerRadii: .init(width: dataSet.cornerRadius, height: dataSet.cornerRadius))
context.addPath(bezierPath.cgPath)
context.drawPath(using: .fill)

if drawBorder
{
Expand Down Expand Up @@ -701,7 +719,7 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer
for high in indices
{
guard
let set = barData[high.dataSetIndex] as? BarChartDataSetProtocol,
let set = barData[safe: high.dataSetIndex] as? BarChartDataSetProtocol,
set.isHighlightEnabled
else { continue }

Expand Down Expand Up @@ -744,7 +762,14 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer

setHighlightDrawPos(highlight: high, barRect: barRect)

context.fill(barRect)
var roundedCorners = set.roundedCorners
if e.y < 0 {
roundedCorners = set.roundedCornersInverted
}
let bezierPath = UIBezierPath(roundedRect: barRect, byRoundingCorners: roundedCorners,
cornerRadii: .init(width: set.cornerRadius, height: set.cornerRadius))
context.addPath(bezierPath.cgPath)
context.drawPath(using: .fill)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ open class BarLineScatterCandleBubbleRenderer: NSObject, DataRenderer
open func isDrawingValuesAllowed(dataProvider: ChartDataProvider?) -> Bool
{
guard let data = dataProvider?.data else { return false }
return data.entryCount < Int(CGFloat(dataProvider?.maxVisibleCount ?? 0) * viewPortHandler.scaleX)
let count = CGFloat(dataProvider?.maxVisibleCount ?? 0) * viewPortHandler.scaleX
guard count < CGFloat.infinity, !count.isNaN else { return false }
return data.entryCount < Int(count)
}

/// Class representing the bounds of the current viewport in terms of indices in the values array of a DataSet.
Expand Down
2 changes: 1 addition & 1 deletion Source/Charts/Renderers/BubbleChartRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ open class BubbleChartRenderer: BarLineScatterCandleBubbleRenderer
for high in indices
{
guard
let dataSet = bubbleData[high.dataSetIndex] as? BubbleChartDataSetProtocol,
let dataSet = bubbleData[safe: high.dataSetIndex] as? BubbleChartDataSetProtocol,
dataSet.isHighlightEnabled,
let entry = dataSet.entryForXValue(high.x, closestToY: high.y) as? BubbleChartDataEntry,
isInBoundsX(entry: entry, dataSet: dataSet)
Expand Down
2 changes: 1 addition & 1 deletion Source/Charts/Renderers/CandleStickChartRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ open class CandleStickChartRenderer: LineScatterCandleRadarRenderer
for high in indices
{
guard
let set = candleData[high.dataSetIndex] as? CandleChartDataSetProtocol,
let set = candleData[safe: high.dataSetIndex] as? CandleChartDataSetProtocol,
set.isHighlightEnabled
else { continue }

Expand Down
4 changes: 3 additions & 1 deletion Source/Charts/Renderers/CombinedChartRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,9 @@ open class CombinedChartRenderer: NSObject, DataRenderer
open func isDrawingValuesAllowed(dataProvider: ChartDataProvider?) -> Bool
{
guard let data = dataProvider?.data else { return false }
return data.entryCount < Int(CGFloat(dataProvider?.maxVisibleCount ?? 0) * viewPortHandler.scaleX)
let count = CGFloat(dataProvider?.maxVisibleCount ?? 0) * viewPortHandler.scaleX
guard count < CGFloat.infinity, !count.isNaN else { return false }
return data.entryCount < Int(count)
}

/// All sub-renderers.
Expand Down
27 changes: 22 additions & 5 deletions Source/Charts/Renderers/HorizontalBarChartRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,15 @@ open class HorizontalBarChartRenderer: BarChartRenderer
_barShadowRectBuffer.size.width = viewPortHandler.contentWidth

context.setFillColor(dataSet.barShadowColor.cgColor)
context.fill(_barShadowRectBuffer)

var roundedCorners = dataSet.roundedCorners
if e.x < 0 {
roundedCorners = dataSet.roundedCornersInverted
}
let bezierPath = UIBezierPath(roundedRect: _barShadowRectBuffer, byRoundingCorners: roundedCorners,
cornerRadii: .init(width: dataSet.cornerRadius, height: dataSet.cornerRadius))
context.addPath(bezierPath.cgPath)
context.drawPath(using: .fill)
}
}

Expand Down Expand Up @@ -265,7 +273,15 @@ open class HorizontalBarChartRenderer: BarChartRenderer
context.setFillColor(dataSet.color(atIndex: j).cgColor)
}

context.fill(barRect)
var roundedCorners = dataSet.roundedCorners
if let entry = dataSet.entryForIndex(j),
entry.x < 0 {
roundedCorners = dataSet.roundedCornersInverted
}
let bezierPath = UIBezierPath(roundedRect: barRect, byRoundingCorners: roundedCorners,
cornerRadii: .init(width: dataSet.cornerRadius, height: dataSet.cornerRadius))
context.addPath(bezierPath.cgPath)
context.drawPath(using: .fill)

if drawBorder
{
Expand Down Expand Up @@ -614,9 +630,10 @@ open class HorizontalBarChartRenderer: BarChartRenderer

open override func isDrawingValuesAllowed(dataProvider: ChartDataProvider?) -> Bool
{
guard let data = dataProvider?.data
else { return false }
return data.entryCount < Int(CGFloat(dataProvider?.maxVisibleCount ?? 0) * self.viewPortHandler.scaleY)
guard let data = dataProvider?.data else { return false }
let count = CGFloat(dataProvider?.maxVisibleCount ?? 0) * viewPortHandler.scaleY
guard count < CGFloat.infinity, !count.isNaN else { return false }
return data.entryCount < Int(count)
}

/// Sets the drawing position of the highlight object based on the riven bar-rect.
Expand Down
2 changes: 1 addition & 1 deletion Source/Charts/Renderers/LineChartRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,7 @@ open class LineChartRenderer: LineRadarRenderer

for high in indices
{
guard let set = lineData[high.dataSetIndex] as? LineChartDataSetProtocol,
guard let set = lineData[safe: high.dataSetIndex] as? LineChartDataSetProtocol,
set.isHighlightEnabled
else { continue }

Expand Down
Loading
Loading