How to draw surface contour plot (Swift language)

558 Views Asked by At

I would like to draw surface contour plot like the picture below for an iOS mobile application (swift)

enter image description here

If someone can put me on the right track, I will appreciate it.

I searched on Internet, I didn't find any obvious solution. I consider to use SceneKit or SciChart.iOS package but I'm not sure.

3

There are 3 best solutions below

0
On

You tagged [scichart] so I can post an answer.

While the WPF and Javascript versions of SciChart support contours in 2d charts, it seems SciChart's ios/android heatmap does not.

But it does have this 3d contour plot which can be viewed from above by setting Orthogonal projection & position of the camera in the 3D scene.

enter image description here

You can learn more about the 3d camera properties here. Property projectionMode sets orthogonal mode, and position / target allow you to control the view from above.

There is also a demo on the scichart examples app which allows you manipulate the 3d camera dynamically. see it here.

3
On

Charts were introduced by Apple on iOS16 and every other Apple platform (see here for a short official description: https://developer.apple.com/documentation/charts)

I found a GitHub project that aims at replicating the nice-looking charts that Apple teased at the WWDC 2022 (see here the video showing briefly the charts https://developer.apple.com/videos/play/wwdc2022/10137/)

enter image description here

Here is the link to the GitHub project by Jordi Bruin: https://github.com/jordibruin/Swift-Charts-Examples

I could extract a simplified one-file HeatMap that is close to a ContourPlot, so I guess you could start from that.

import SwiftUI
import Charts

struct Grid {
    let numRows: Int
    let numCols: Int
    var points = [Point]()

    init(numRows: Int, numCols: Int) {
        self.numRows = numRows
        self.numCols = numCols
        generateData()
    }
    
    mutating func generateData() {
        for rowIndex in 0..<numRows {
            for colIndex in 0..<numCols {
                let maxValue = numRows + numCols - 2
                let variance = Double.random(in: 0..<20) - 10
                let value = (Double(rowIndex + colIndex) * 100)/Double(maxValue) + variance
                let point = Point(x: Double(colIndex), y: Double(rowIndex), val: value)
                points.append(point)
            }
        }
    }
}

struct ContentView: View {
    @State var grid = Grid(numRows: 20, numCols: 20)
    
    var gradientColors: [Color] = [.blue, .green, .yellow, .orange, .red]

    var body: some View {
        Chart(grid.points) { point in
            Plot {
                let xVal = Int(point.x)
                let yVal = Int(point.y)
                let val = Int(point.val)
                RectangleMark(
                    xStart: PlottableValue.value("xStart", xVal),
                    xEnd: PlottableValue.value("xEnd", xVal + 1),
                    yStart: PlottableValue.value("yStart", yVal),
                    yEnd: PlottableValue.value("yEnd", yVal + 1)
                )
                .foregroundStyle(by: .value("Value", val))
            }
        }
        .chartForegroundStyleScale(range: Gradient(colors: gradientColors))
        .chartYAxis {
            AxisMarks(values: .automatic(desiredCount: grid.numRows, roundLowerBound: false, roundUpperBound: false)) { _ in
                AxisGridLine()
                AxisTick()
                AxisValueLabel(centered: true)
            }
        }
        .chartXAxis {
            AxisMarks(values: .automatic(desiredCount: grid.numCols, roundLowerBound: false, roundUpperBound: false)) { _ in
                AxisGridLine()
                AxisTick()
                AxisValueLabel(centered: true)
            }
        }
        .aspectRatio(1, contentMode: .fit)
    }

}

struct Point: Hashable, Identifiable {
    let id = UUID()
    let x: Double
    let y: Double
    let val: Double
}

Here is the result of this code on an iPhone 14 Pro simulator (iOS 16.0). I will be different for every iteration of the code you will execute since the values are input as random, just for you to get an idea.

enter image description here

0
On

I did some work on coreplot & DGCharts framework for contouring: https://github.com/swainwri/core-plot-release-2.4-sw (objc) && https://github.com/swainwri/DCGCharts (swift). These are not complete in that not covered all bases for discontinuities in the contour function. enter image description here