Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios swift中的对数y轴?_Ios_Swift_Linechart_Logarithm - Fatal编程技术网

Ios swift中的对数y轴?

Ios swift中的对数y轴?,ios,swift,linechart,logarithm,Ios,Swift,Linechart,Logarithm,我正在开发一个有很多数字的金融应用程序 例如,我的一张图表正在从25000美元增长到200000000美元(指数增长) 正因为如此,使用任何没有对数刻度的规则图表都会导致我的大部分数据简单地“平直”,而事实上数据增长很快 这里有一个例子 (忽略尚未设置) 上面的折线图是使用 老实说,我现在还不能完全确定我是否可以问这样的问题。但我不知道从哪里开始 经过几个小时的搜索,我找不到任何库、教程、示例应用程序或任何与IOS相关的以对数轴(X或Y)为特征的图形 在我找不到任何东西之后,我意识到我必须自己

我正在开发一个有很多数字的金融应用程序

例如,我的一张图表正在从25000美元增长到200000000美元(指数增长)

正因为如此,使用任何没有对数刻度的规则图表都会导致我的大部分数据简单地“平直”,而事实上数据增长很快

这里有一个例子

(忽略尚未设置)

上面的折线图是使用

老实说,我现在还不能完全确定我是否可以问这样的问题。但我不知道从哪里开始

经过几个小时的搜索,我找不到任何库、教程、示例应用程序或任何与IOS相关的以对数轴(X或Y)为特征的图形

在我找不到任何东西之后,我意识到我必须自己实现这个功能。我找到了最适合我的应用程序的库,并且我一直在尝试如何实现一种方法,以允许y刻度以对数刻度(10的幂)进行缩放

我对原木秤的工作原理做了不少研究,并在我的大学里大量使用了它们。但我从来没有见过一个编程的例子

我发现一个是用JavaScript写的(我想)。但不幸的是,我没有web开发经验,无法正确翻译:(

我真的不喜欢把别人的代码放在上面,但我真的不知道从哪里开始

如果这是不允许的,请发表评论,我会立即处理它

这一切都归功于这个家伙:

下面是我认为需要实现日志规模的代码

private func drawChart() {

    drawingHeight = bounds.height - bottomInset - topInset
    drawingWidth = bounds.width

    let minMax = getMinMax()
    min = minMax.min
    max = minMax.max

    highlightShapeLayer = nil

    // Remove things before drawing, e.g. when changing orientation

    for view in self.subviews {
        view.removeFromSuperview()
    }
    for layer in layerStore {
        layer.removeFromSuperlayer()
    }
    layerStore.removeAll()

    // Draw content

    for (index, series) in enumerate(self.series) {

        // Separate each line in multiple segments over and below the x axis
        var segments = Chart.segmentLine(series.data as ChartLineSegment)

        for (i, segment) in enumerate(segments) {
            let scaledXValues = scaleValuesOnXAxis( segment.map( { return $0.x } ) )
            let scaledYValues = scaleValuesOnYAxis( segment.map( { return $0.y } ) )

            if series.line {
                drawLine(xValues: scaledXValues, yValues: scaledYValues, seriesIndex: index)
            }
            if series.area {
                drawArea(xValues: scaledXValues, yValues: scaledYValues, seriesIndex: index)
            }
        }
    }

    drawAxes()

    if xLabels != nil || series.count > 0 {
        drawLabelsAndGridOnXAxis()
    }
    if yLabels != nil || series.count > 0 {
        drawLabelsAndGridOnYAxis()
    }

}

// MARK: - Scaling

private func getMinMax() -> (min: ChartPoint, max: ChartPoint) {

    // Start with user-provided values

    var min = (x: minX, y: minY)
    var max = (x: maxX, y: maxY)

    // Check in datasets

    for series in self.series {
        let xValues =  series.data.map( { (point: ChartPoint) -> Float in
            return point.x } )
        let yValues =  series.data.map( { (point: ChartPoint) -> Float in
            return point.y } )

        let newMinX = minElement(xValues)
        let newMinY = minElement(yValues)
        let newMaxX = maxElement(xValues)
        let newMaxY = maxElement(yValues)

        if min.x == nil || newMinX < min.x! { min.x = newMinX }
        if min.y == nil || newMinY < min.y! { min.y = newMinY }
        if max.x == nil || newMaxX > max.x! { max.x = newMaxX }
        if max.y == nil || newMaxY > max.y! { max.y = newMaxY }
    }

    // Check in labels

    if xLabels != nil {
        let newMinX = minElement(xLabels!)
        let newMaxX = maxElement(xLabels!)
        if min.x == nil || newMinX < min.x { min.x = newMinX }
        if max.x == nil || newMaxX > max.x { max.x = newMaxX }
    }

    if yLabels != nil {
        let newMinY = minElement(yLabels!)
        let newMaxY = maxElement(yLabels!)
        if min.y == nil || newMinY < min.y { min.y = newMinY }
        if max.y == nil || newMaxY > max.y { max.y = newMaxY }
    }

    if min.x == nil { min.x = 0 }
    if min.y == nil { min.y = 0 }
    if max.x == nil { max.x = 0 }
    if max.y == nil { max.y = 0 }

    return (min: (x: min.x!, y: min.y!), max: (x: max.x!, max.y!))

}

private func scaleValuesOnXAxis(values: Array<Float>) -> Array<Float> {
    let width = Float(drawingWidth)

    var factor: Float
    if max.x - min.x == 0 { factor = 0 }
    else { factor = width / (max.x - min.x) }

    let scaled = values.map { factor * ($0 - self.min.x) }
    return scaled
}

private func scaleValuesOnYAxis(values: Array<Float>) -> Array<Float> {

    let height = Float(drawingHeight)
    var factor: Float
    if max.y - min.y == 0 { factor = 0 }
    else { factor = height / (max.y - min.y) }

    let scaled = values.map { Float(self.topInset) + height - factor * ($0 - self.min.y) }

    return scaled
}

private func scaleValueOnYAxis(value: Float) -> Float {

    let height = Float(drawingHeight)
    var factor: Float
    if max.y - min.y == 0 { factor = 0 }
    else { factor = height / (max.y - min.y) }

    let scaled = Float(self.topInset) + height - factor * (value - min.y)
    return scaled
}

private func getZeroValueOnYAxis() -> Float {
    if min.y > 0 {
        return scaleValueOnYAxis(min.y)
    }
    else {
        return scaleValueOnYAxis(0)
    }

}

// MARK: - Drawings

private func isVerticalSegmentAboveXAxis(yValues: Array<Float>) -> Bool {

    // YValues are "reverted" from top to bottom, so min is actually the maxz
    let min = maxElement(yValues)
    let zero = getZeroValueOnYAxis()

    return min <= zero

}

private func drawLine(#xValues: Array<Float>, yValues: Array<Float>, seriesIndex: Int) -> CAShapeLayer {

    let isAboveXAxis = isVerticalSegmentAboveXAxis(yValues)
    let path = CGPathCreateMutable()

    CGPathMoveToPoint(path, nil, CGFloat(xValues.first!), CGFloat(yValues.first!))

    for i in 1..<yValues.count {
        let y = yValues[i]
        CGPathAddLineToPoint(path, nil, CGFloat(xValues[i]), CGFloat(y))
    }

    var lineLayer = CAShapeLayer()
    lineLayer.frame = self.bounds
    lineLayer.path = path

    if isAboveXAxis {
        lineLayer.strokeColor = series[seriesIndex].colors.above.CGColor
    }
    else {
        lineLayer.strokeColor = series[seriesIndex].colors.below.CGColor
    }
    lineLayer.fillColor = nil
    lineLayer.lineWidth = lineWidth
    lineLayer.lineJoin = kCALineJoinBevel

    self.layer.addSublayer(lineLayer)

    layerStore.append(lineLayer)

    return lineLayer
}

private func drawArea(#xValues: Array<Float>, yValues: Array<Float>, seriesIndex: Int) {
    let isAboveXAxis = isVerticalSegmentAboveXAxis(yValues)
    let area = CGPathCreateMutable()
    let zero = CGFloat(getZeroValueOnYAxis())

    CGPathMoveToPoint(area, nil, CGFloat(xValues[0]), zero)

    for i in 0..<xValues.count {
        CGPathAddLineToPoint(area, nil, CGFloat(xValues[i]), CGFloat(yValues[i]))
    }

    CGPathAddLineToPoint(area, nil, CGFloat(xValues.last!), zero)

    var areaLayer = CAShapeLayer()
    areaLayer.frame = self.bounds
    areaLayer.path = area
    areaLayer.strokeColor = nil
    if isAboveXAxis {
        areaLayer.fillColor = series[seriesIndex].colors.above.colorWithAlphaComponent(areaAlphaComponent).CGColor
    }
    else {
        areaLayer.fillColor = series[seriesIndex].colors.below.colorWithAlphaComponent(areaAlphaComponent).CGColor
    }
    areaLayer.lineWidth = 0

    self.layer.addSublayer(areaLayer)

    layerStore.append(areaLayer)
}

private func drawAxes() {

    let context = UIGraphicsGetCurrentContext()
    CGContextSetStrokeColorWithColor(context, axesColor.CGColor)
    CGContextSetLineWidth(context, 0.5)

    // horizontal axis at the bottom
    CGContextMoveToPoint(context, 0, drawingHeight + topInset)
    CGContextAddLineToPoint(context, drawingWidth, drawingHeight + topInset)
    CGContextStrokePath(context)

    // horizontal axis at the top
    CGContextMoveToPoint(context, 0, 0)
    CGContextAddLineToPoint(context, drawingWidth, 0)
    CGContextStrokePath(context)

    // horizontal axis when y = 0
    if min.y < 0 && max.y > 0 {
        let y = CGFloat(getZeroValueOnYAxis())
        CGContextMoveToPoint(context, 0, y)
        CGContextAddLineToPoint(context, drawingWidth, y)
        CGContextStrokePath(context)
    }

    // vertical axis on the left
    CGContextMoveToPoint(context, 0, 0)
    CGContextAddLineToPoint(context, 0, drawingHeight + topInset)
    CGContextStrokePath(context)


    // vertical axis on the right
    CGContextMoveToPoint(context, drawingWidth, 0)
    CGContextAddLineToPoint(context, drawingWidth, drawingHeight + topInset)
    CGContextStrokePath(context)

}
private func drawChart(){
绘图高度=边界高度-底部插图-顶部插图
drawingWidth=bounds.width
设minMax=getMinMax()
最小值=最小值最大值最小值
max=minMax.max
highlightShapeLayer=零
//在绘图前(例如更改方向时)移除物品
用于在self.subview中查看{
view.removeFromSuperview()视图
}
用于layerStore中的图层{
layer.removeFromSuperlayer()
}
layerStore.removeAll()
//绘制内容
用于枚举(self.series)中的(索引,序列){
//在x轴上方和下方的多个线段中分隔每条线
var segments=Chart.segmentLine(series.data作为ChartLineSegment)
枚举(段)中的(i,段){
设scaledXValues=scaleValuesOnXAxis(segment.map({return$0.x}))
设scaledYValues=scaleValuesOnYAxis(segment.map({return$0.y}))
if系列.line{
绘制线(X值:缩放X值,Y值:缩放Y值,系列索引:索引)
}
if系列面积{
绘图区域(X值:缩放X值,Y值:缩放Y值,系列索引:索引)
}
}
}
牵引轴()
如果xLabels!=nil | | series.count>0{
drawLabelsAndGridOnXAxis()
}
如果yLabels!=nil | | series.count>0{
drawLabelsAndGridOnYAxis()
}
}
//标记:-缩放
私有函数getMinMax()->(最小值:ChartPoint,最大值:ChartPoint){
//从用户提供的值开始
变量min=(x:minX,y:minY)
变量max=(x:maxX,y:maxY)
//签入数据集
对于self.series中的系列{
让xValues=series.data.map({(point:ChartPoint)->浮入
返回点(x})
让yValues=series.data.map({(点:ChartPoint)->浮入
返回点(y})
设newMinX=minElement(xValues)
设newMinY=minElement(y值)
设newMaxX=maxElement(xValues)
设newMaxY=maxElement(yValue)
如果min.x==nil | | newMinXmax.x!{max.x=newMaxX}
如果max.y==nil | | newMaxY>max.y!{max.y=newMaxY}
}
//签入标签
如果xLabels!=nil{
让newMinX=minElement(xLabels!)
设newMaxX=maxElement(xLabels!)
如果min.x==nil | | newMinXmax.x{max.x=newMaxX}
}
如果为零{
让newMinY=minElement(yLabels!)
设newMaxY=maxElement(yLabels!)
如果min.y==nil | | newMinYmax.y{max.y=newMaxY}
}
如果min.x==nil{min.x=0}
如果min.y==nil{min.y=0}
如果max.x==nil{max.x=0}
如果max.y==nil{max.y=0}
返回值(min:(x:min.x!,y:min.y!),max:(x:max.x!,max.y!))
}
私有函数scaleValuesOnXAxis(值:数组)->Array{
让宽度=浮动(drawingWidth)
风险系数:浮动
如果max.x-min.x==0{factor=0}
else{系数=宽度/(max.x-min.x)}
设scaled=values.map{factor*($0-self.min.x)}
返回标度
}
私有函数scaleValuesOnYAxis(值:数组)->Array{
让高度=浮动(图纸高度)
风险系数:浮动
如果max.y-min.y==0{factor=0}
else{系数=高度/(最大y-最小y)}
设scaled=values.map{Float(self.topInset)+height-factor*($0-self.min.y)}
返回标度
}
私有函数scaleValueOnAxis(值:Float)->Float{
让高度=浮动(图纸高度)
风险系数:浮动
如果max.y-min.y==0{factor=0}
else{系数=高度/(最大y-最小y)}
让缩放=浮动(自顶插图)+高度-系数*(值-最小y)
返回标度
}
private func getZeroValueOnYAxis()->Float{
如果最小y>0{
返回刻度值轴(最小y)
}
否则{
返回ScaleValueOnAxis(0)
}
}
//标记:-图纸
私有函数是X轴上的垂直分段(Y值:数组)->Bool{
//Y值从上到下“还原”,因此min实际上是maxz