USB-Meter / USB Meter / Model / ChartContext.swift
1 contributor
127 lines | 3.31kb
//
//  ChartContext.swift
//  USB Meter
//
//  Created by Bogdan Timofte on 14/04/2020.
//  Copyright © 2020 Bogdan Timofte. All rights reserved.
//

import CoreGraphics
import SwiftUI

class ChartContext {
    private var rect : CGRect?
    private var pad: CGFloat = 0

    var isValid: Bool {
        get {
            return rect != nil && rect!.width > 0 && rect!.height > 0
        }
    }

    var size: CGSize {
        get {
            guard rect != nil else {
                track("Invalid Context")
                return .zero
            }
            return rect!.size
        }
    }

    var origin: CGPoint {
        get {
            guard rect != nil else {
                track("Invalid Context")
                return .zero
            }
            return rect!.origin
        }
    }

    var minValue: Double {
        return rect == nil ? .nan : Double(rect!.minY)
    }

    var maxValue: Double {
        get {
            return rect == nil ? .nan : Double(rect!.maxY)
        }
    }

    func reset() {
        rect = nil
        pad = 0
    }
    func include( point: CGPoint )  {
        if rect == nil {
            rect = CGRect(origin: point, size: .zero)
            padding()
        } else {
            rect = rect!.union(CGRect(origin: point, size: .zero))
            padding()
        }
    }
    
    func padding() {
        guard rect != nil else {
            track("Invalid Context")
            pad = 0
            return
        }
        pad = rect!.size.height * Constants.chartUnderscan
    }

    func ensureMinimumSize(width minimumWidth: CGFloat = 0, height minimumHeight: CGFloat = 0) {
        guard var rect else { return }

        if rect.width < minimumWidth {
            let delta = (minimumWidth - rect.width) / 2
            rect = rect.insetBy(dx: -delta, dy: 0)
        }

        if rect.height < minimumHeight {
            let delta = (minimumHeight - rect.height) / 2
            rect = rect.insetBy(dx: 0, dy: -delta)
        }

        self.rect = rect
        padding()
    }

    func setBounds(xMin: CGFloat, xMax: CGFloat, yMin: CGFloat, yMax: CGFloat) {
        rect = CGRect(
            x: min(xMin, xMax),
            y: min(yMin, yMax),
            width: abs(xMax - xMin),
            height: max(abs(yMax - yMin), 0.1)
        )
        padding()
    }
    
    func yAxisLabel( for itemNo: Int, of items: Int ) -> Double {
        let labelSpace = Double(rect!.height) / Double(items - 1)
        let labelRelativeValue = labelSpace * Double(itemNo - 1)
        return minValue + labelRelativeValue
    }
    
    // MARK: Conversii dubioase
    func xAxisLabel( for itemNo: Int, of items: Int ) -> Double {
        let labelSpace = Double(rect!.width) / Double(items - 1)
        let labelRelativeValue = labelSpace * Double(itemNo - 1)
        return Double(rect!.origin.x) + labelRelativeValue
    }
    
    func placeInRect (point: CGPoint) -> CGPoint {
        guard let rect else {
            track("Invalid Context")
            return .zero
        }

        let width = max(rect.width, 1)
        let height = max(rect.height, 0.1)
        let x = (point.x - rect.origin.x)/width
        let y = (pad + point.y - rect.origin.y)/height
        return CGPoint(x: x, y: 1 - y * Constants.chartOverscan)
    }
}