From af3c727241778761e6b66c48df37930e167a2fdb Mon Sep 17 00:00:00 2001 From: Jeroen Wesbeek Date: Thu, 8 Aug 2019 18:27:31 +0200 Subject: [PATCH] Fixed #4099: Line renderer did not render lines if they coordinates fell outside of the viewport, even though they might intersect the viewport. --- .../Charts/Renderers/LineChartRenderer.swift | 23 ++++++------ Source/Charts/Utils/ViewPortHandler.swift | 36 +++++++++++++++++++ 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/Source/Charts/Renderers/LineChartRenderer.swift b/Source/Charts/Renderers/LineChartRenderer.swift index 63930c19fb..031416e9a5 100644 --- a/Source/Charts/Renderers/LineChartRenderer.swift +++ b/Source/Charts/Renderers/LineChartRenderer.swift @@ -353,17 +353,20 @@ open class LineChartRenderer: LineRadarRenderer _lineSegments[i] = _lineSegments[i].applying(valueToPixelMatrix) } - if (!viewPortHandler.isInBoundsRight(_lineSegments[0].x)) - { - break - } + // Determine the start and end coordinates of the line, and make sure they differ. + guard + let firstCoordinate = _lineSegments.first, + let lastCoordinate = _lineSegments.last, + firstCoordinate != lastCoordinate else { continue } - // make sure the lines don't do shitty things outside bounds - if !viewPortHandler.isInBoundsLeft(_lineSegments[1].x) - || (!viewPortHandler.isInBoundsTop(_lineSegments[0].y) && !viewPortHandler.isInBoundsBottom(_lineSegments[1].y)) - { - continue - } + // If both points lie left of viewport, skip stroking. + if !viewPortHandler.isInBoundsLeft(firstCoordinate.x) && !viewPortHandler.isInBoundsLeft(lastCoordinate.x) { continue } + + // If both points lie right of the viewport, break out early. + if !viewPortHandler.isInBoundsRight(firstCoordinate.x) && !viewPortHandler.isInBoundsRight(lastCoordinate.x) { break } + + // Only stroke the line if it intersects with the viewport. + guard viewPortHandler.isIntersectingLine(from: firstCoordinate, to: lastCoordinate) else { continue } // get the color that is set for this line-segment context.setStrokeColor(dataSet.color(atIndex: j).cgColor) diff --git a/Source/Charts/Utils/ViewPortHandler.swift b/Source/Charts/Utils/ViewPortHandler.swift index 56e034b418..2a168dd320 100755 --- a/Source/Charts/Utils/ViewPortHandler.swift +++ b/Source/Charts/Utils/ViewPortHandler.swift @@ -416,6 +416,11 @@ open class ViewPortHandler: NSObject return isInBoundsTop(y) && isInBoundsBottom(y) } + @objc open func isInBounds(point: CGPoint) -> Bool + { + return isInBounds(x: point.x, y: point.y) + } + @objc open func isInBounds(x: CGFloat, y: CGFloat) -> Bool { return isInBoundsX(x) && isInBoundsY(y) @@ -443,6 +448,37 @@ open class ViewPortHandler: NSObject return (_contentRect.origin.y + _contentRect.size.height) >= normalizedY } + /** + A method to check whether a line between two coordinates intersects with the view port. + + - Parameters: + - startPoint: the start coordinate of the line. + - endPoint: the end coordinate of the line. + */ + @objc open func isIntersectingLine(from startPoint: CGPoint, to endPoint: CGPoint) -> Bool + { + // If the start or endpoint fall within the viewport, bail out early. + if isInBounds(point: startPoint) || isInBounds(point: endPoint) { return true } + + // Calculate the slope of the line. + let slope = (endPoint.y - startPoint.y) / (endPoint.x - startPoint.x) + + // Check for colission with left edge of the view port. + if isInBoundsY((slope * (contentRect.minX - startPoint.x)) + startPoint.y) { return true } + + // Check for colission with right edge of the view port. + if isInBoundsY((slope * (contentRect.maxX - startPoint.x)) + startPoint.y) { return true } + + // Check for colission with top edge of the view port. + if isInBoundsX(((contentRect.minY - startPoint.y) / slope) + startPoint.x) { return true } + + // Check for colission with bottom edge of the viewport. + if isInBoundsX(((contentRect.maxY - startPoint.y) / slope) + startPoint.x) { return true } + + // This line does not intersect view the view port. + return false + } + /// The current x-scale factor @objc open var scaleX: CGFloat {