SwiftPlot 框架是一个跨平台库,允许您在 Swift 中本地绘制图形。 现有的 Swift 绘图框架(例如 CorePlot)仅在 iOS 或 Mac 上运行。 SwiftPlot 背后的想法是创建一个可在 iOS、Mac、Linux 和 Windows 上运行的跨平台库。
SwiftPlot 目前使用三个渲染后端来生成绘图
- Anti-Grain Geometry(AGG) C++ 渲染库
- 一个简单的 SVG 渲染器
- 一个支持 macOS、iOS、watchOS 和 tvOS 的 Core Graphics 渲染器
为了将绘图编码为 PNG 图像,它使用了 lodepng 库。
SwiftPlot 也可以在 Jupyter Notebook 中使用,并支持 Google Colab 的 Python 互操作性。
示例(演示所有功能)已包含在存储库的 Tests/SwiftPlotTests
目录下。 要运行示例,请克隆存储库,然后从包目录运行 swift test
命令。
Jupyter Notebook 示例位于 Notebooks
目录下。
生成的结果图像存储在名为 output
的目录中。 Tests
文件夹包含 Reference
目录中的参考图像集合。
SwiftPlot 采用 Apache 2.0
许可。 查看 license
将该库添加到你的项目的依赖项中,如以下 Package.swift 文件所示。
dependencies: [
.package(url: "https://github.com/KarthikRIyer/swiftplot.git", from: "2.0.0")),
],
如果您收到错误,提示找不到文件 ft2build.h,则需要安装 freetype 开发包。
Linux
sudo apt-get install libfreetype6-dev
macOS
如果上述方法不起作用,您也可以自己构建和安装 freetype。 您可以在 这里 找到源代码和构建说明。
如何在你的 Jupyter Notebook 中包含该库
将这些行添加到第一个单元格
%install-swiftpm-flags -Xcc -isystem/usr/include/freetype2 -Xswiftc -lfreetype
%install '.package(url: "https://github.com/IBM-Swift/BlueCryptor.git", from: "1.0.28")' Cryptor
%install '.package(url: "https://github.com/KarthikRIyer/swiftplot", from: "2.0.0")' SwiftPlot AGGRenderer
为了在笔记本中显示生成的绘图,请将此行添加到新单元格
%include "EnableJupyterDisplay.swift"
如果您希望在 Google Colab 环境中显示生成的绘图,请将这些行添加到新的单元格中
import Python
%include "EnableIPythonDisplay.swift"
func display(base64EncodedPNG: String) {
let displayImage = Python.import("IPython.display")
let codecs = Python.import("codecs")
let imageData = codecs.decode(Python.bytes(base64EncodedPNG, encoding: "utf8"), encoding: "base64")
displayImage.Image(data: imageData, format: "png").display()
}
请注意,由于 Google Colab 本身不支持产生丰富输出的 Swift 库,因此我们使用 Swift 的 Python 互操作性作为一种解决方法。
如何为 SwiftPlot 设置 Docker 实例
对于运行 MacOS 或 Windows 的计算机,Docker 实例易于设置和使用 swift-jupyter
。 请参考 SwiftPlot_Docker_setup.md 获取设置说明。
这里有一些示例可以为您提供使用此库的先机。 在这里,我们将仅研究使用 AGGRenderer 的绘图,但是对于 SVGRenderer,该过程将保持不变。 要在你的包中使用该库,请在 Package.swift 文件中将其作为依赖项包含在你的目标中。
更多测试可以在 SwiftPlotTests 文件夹中找到。
import SwiftPlot
import AGGRenderer
let x:[Float] = [10,100,263,489]
let y:[Float] = [10,120,500,800]
var agg_renderer: AGGRenderer = AGGRenderer()
var lineGraph = LineGraph<Float,Float>(enablePrimaryAxisGrid: true)
lineGraph.addSeries(x, y, label: "Plot 1", color: .lightBlue)
lineGraph.plotTitle.title = "SINGLE SERIES"
lineGraph.plotLabel.xLabel = "X-AXIS"
lineGraph.plotLabel.yLabel = "Y-AXIS"
lineGraph.plotLineThickness = 3.0
lineGraph.drawGraphAndOutput(fileName: filePath+"agg/"+fileName, renderer: agg_renderer)

import SwiftPlot
import AGGRenderer
import SVGRenderer
let x1:[Float] = [0,100,263,489]
let y1:[Float] = [0,320,310,170]
let x2:[Float] = [0,50,113,250]
let y2:[Float] = [0,20,100,170]
var agg_renderer: AGGRenderer = AGGRenderer()
var lineGraph = LineGraph<Float,Float>(enablePrimaryAxisGrid: true)
lineGraph.addSeries(x1, y1, label: "Plot 1", color: .lightBlue)
lineGraph.addSeries(x2, y2, label: "Plot 2", color: .orange)
lineGraph.plotTitle.title = "MULTIPLE SERIES"
lineGraph.plotLabel.xLabel = "X-AXIS"
lineGraph.plotlabel.yLabel = "Y-AXIS"
lineGraph.plotLineThickness = 3.0
lineGraph.drawGraphAndOutput(fileName: filePath+"agg/"+fileName, renderer: agg_renderer)

import SwiftPlot
import AGGRenderer
let x:[Float] = [10,100,263,489]
let y:[Float] = [10,120,500,800]
var agg_renderer: AGGRenderer = AGGRenderer()
var subplot = SubPlot(layout: .horizontal)
var lineGraph1 = LineGraph<Float,Float>(enablePrimaryAxisGrid: true)
lineGraph1.addSeries(x, y, label: "Plot 1", color: .lightBlue)
lineGraph1.plotTitle.title = "PLOT 1"
lineGraph1.plotLabel.xLabel = "X-AXIS"
lineGraph1.plotLabel.yLabel = "Y-AXIS"
lineGraph1.plotLineThickness = 3.0
var lineGraph2 = LineGraph<Float,Float>(enablePrimaryAxisGrid: true)
lineGraph2.addSeries(x, y, label: "Plot 2", color: .orange)
lineGraph2.plotTitle.title = "PLOT 2"
lineGraph2.plotLabel.xLabel = "X-AXIS"
lineGraph2.plotLabel.yLabel = "Y-AXIS"
lineGraph2.plotLineThickness = 3.0
subplot.plots = [lineGraph1, lineGraph2]
subplot.drawGraphAndOutput(fileName: "subPlotsHorizontallyStacked", renderer: agg_renderer)

import Foundation
import SwiftPlot
import AGGRenderer
func function(_ x: Float)->Float {
return 1.0/x
}
var agg_renderer: AGGRenderer = AGGRenderer()
var lineGraph = LineGraph<Float,Float>(enablePrimaryAxisGrid: true)
lineGraph.addFunction(function, minX: -5.0, maxX: 5.0, numberOfSamples: 400, clampY: -50...50, label: "Function", color: .orange)
lineGraph.plotTitle.title = "FUNCTION"
lineGraph.plotLabel.xLabel = "X-AXIS"
lineGraph.plotLabel.yLabel = "Y-AXIS"
lineGraph.drawGraphAndOutput(fileName: "functionPlotLineGraph", renderer: agg_renderer)

import SwiftPlot
import AGGRenderer
let x:[Float] = [10,100,263,489]
let y:[Float] = [10,120,500,800]
let x1:[Float] = [100,200,361,672]
let y1:[Float] = [150,250,628,800]
var agg_renderer: AGGRenderer = AGGRenderer()
var lineGraph = LineGraph<Float,Float>()
lineGraph.addSeries(x1, y1, label: "Plot 1", color: .lightBlue, axisType: .primaryAxis)
lineGraph.addSeries(x, y, label: "Plot 2", color: .orange, axisType: .secondaryAxis)
lineGraph.plotTitle.title = "SECONDARY AXIS"
lineGraph.plotLabel.xLabel = "X-AXIS"
lineGraph.plotLabel.yLabel = "Y-AXIS"
lineGraph.plotLineThickness = 3.0
lineGraph.drawGraphAndOutput(fileName: filePath+"agg/"+fileName, renderer: agg_renderer)
绘制在辅助轴上的序列以虚线绘制。

您只能使用 AGGRenderer 在 Jupyter Notebook 中显示绘图。 为此,请如以上示例所示创建绘图,而不是使用 LineGraph 中的 drawGraphAndOutput
函数,而是使用 drawGraph
函数,然后从 AGGRenderer 获取 base64 编码的图像,并将其传递给显示函数,如下所示
lineGraph.drawGraph(renderer: agg_renderer)
display(base64EncodedPNG: agg_renderer.base64Png())
所有绘图代码、实用程序函数和必要的类型都包含在 SwiftPlot 模块中。 每个渲染器都作为单独的模块实现。 每个渲染器都必须以 SwiftPlot 作为其依赖项,并且必须符合 SwiftPlot 模块中 Renderer.swift 中定义的 Renderer 协议。 每种绘图类型都是接受符合协议 FloatConvertible 的数据的泛型。 目前,FloatConvertible 支持 Float 和 Double。 Renderer 协议定义了渲染器需要实现的所有必要功能。 每个绘图必须符合 Plot 协议。 目前,该协议定义了每个 Plot 必须实现的必要变量和函数,以支持 SubPlots。
您可以使用各自的函数(LineGraph 的 addSeries
)将序列添加到绘图中。 这作为 Series 对象的数组存储。 您可以设置其他属性,例如 plotTitle、plotLabel、plotDimensions 等。要实际生成绘图,您需要调用 drawGraph
或 drawGraphAndOutput
函数。 这会计算生成绘图所需的所有参数,例如边框的坐标、要绘制的缩放点等。 然后,它将此信息发送给渲染器,该渲染器具有绘制线条、矩形和文本等图元的功能。
如果渲染器在 C++ 中(此处在 AGG 的情况下),则会编写一个 C 包装器,该包装器又包装在 Swift 中。
为了在 Jupyter notebook 中显示绘图,我们将图像(采用 RGB 缓冲区形式)编码为内存中的 PNG 图像,并将编码的图像返回给 Swift 代码,在 Swift 代码中将其存储为 NSData。 然后将其编码为 base64 并传递给 swift-jupyter 中的 display 函数,该函数最终显示图像。
LineGraph<T: FloatConvertible, U: FloatConvertible>
函数 |
描述 |
init(points: [Point], width: Float = 1000, height: Float = 660, enablePrimaryAxisGrid: Bool = false, enableSecondaryAxisGrid: Bool = false) |
使用一组点初始化 LineGraph |
init(width: Float = 1000, height: Float = 660, enablePrimaryAxisGrid: Bool = false,enableSecondaryAxisGrid: Bool = false) |
初始化 LineGraph |
addSeries(_ s: Series, axisType: Axis.Location = Axis.Location.primaryAxis) |
将序列添加到绘图中 |
addSeries(points p: [Point], label: String, color: Color = Color.lightBlue, axisType: Axis<T,U>.Location = Axis<T,U>.Location.primaryAxis) |
使用一组点、一个标签和一个序列颜色将序列添加到绘图中 |
addSeries(_ x: [Float], _ y: [Float], label: String, color: Color = Color.lightBlue, axisType: Axis<T,U>.Location = Axis<T,U>.Location.primaryAxis) |
使用一组 x 和 y 坐标、一个标签和一个序列颜色将序列添加到绘图中 |
addSeries(_ y: [Float], label: String, color: Color = Color.lightBlue, axisType: Axis<T,U>.Location = Axis<T,U>.Location.primaryAxis) |
仅使用 y 坐标将序列添加到绘图中。 x 坐标会自动枚举 [1, 2, 3, ...] |
addFunction(_ function: (Float)->Float, minX: Float, maxX: Float, numberOfSamples: Int = 400, label: String, color: Color = Color.lightBlue, axisType: Axis.Location = Axis.Location.primaryAxis) |
添加要绘制的函数以及要绘制的 x 坐标范围、要绘制的函数的样本数、绘图的标签和颜色 |
drawGraphAndOutput(fileName name: String = "swift_plot_line_graph", renderer: Renderer) |
生成绘图并保存结果图像 |
drawGraph(renderer: Renderer) |
在内存中生成绘图 |
drawGraphOutput(fileName name: String = "swift_plot_line_graph", renderer: Renderer) |
将生成的绘图保存到磁盘 |
属性 |
plotTitle: PlotTitle? = nil |
plotLabel: PlotLabel? = nil |
var plotDimensions: PlotDimensions |
plotLineThickness: Float = 1.5 |
gridLineThickness: Float = 0.5 |
markerTextSize: Float = 12 |
gridColor: Color = .gray |
BarChart<T: LosslessStringConvertible, U: FloatConvertible>
函数 |
描述 |
init(width: Float = 1000, height: Float = 660, enableGrid: Bool = false) |
初始化一个 BarChart |
addSeries(_ s: Series<T,U>) |
将序列添加到绘图中。 |
addStackSeries(_ s: Series<T,U>) |
将堆叠序列添加到绘图中 |
addStackSeries(_ x: [U], label: String, color: Color = .lightBlue, hatchPattern: BarGraphSeriesOptions.Hatching = .none) |
将堆叠序列添加到绘图中 |
addSeries(values: [Pair<T,U>], label: String, color: Color = Color.lightBlue, hatchPattern: BarGraphSeriesOptions.Hatching = .none, graphOrientation: BarGraph.GraphOrientation = .vertical) |
使用 Pair 数组将序列添加到绘图中 |
addSeries(_ x: [T], _ y: [U], label: String, color: Color = Color.lightBlue, hatchPattern: BarGraphSeriesOptions.Hatching = .none, graphOrientation: BarGraph.GraphOrientation = .vertical) |
使用 Pair 数组将序列添加到绘图中 |
drawGraphAndOutput(fileName name: String = "swift_plot_bar_graph", renderer: Renderer) |
生成绘图并保存结果图像 |
drawGraph(renderer: Renderer) |
在内存中生成绘图 |
drawGraphOutput(fileName name: String = "swift_plot_bar_graph", renderer: Renderer) |
将生成的绘图保存到磁盘 |
属性 |
plotTitle: PlotTitle? = nil |
plotLabel: PlotLabel? = nil |
var plotDimensions: PlotDimensions |
space: Int = 20 (设置两个条之间的空间) |
gridLineThickness: Float = 0.5 |
markerTextSize: Float = 12 |
gridColor: Color = .gray |
Histogram<T:FloatConvertible>
函数 |
描述 |
init(width: Float = 1000, height: Float = 660, isNormalized: Bool = false, enableGrid: Bool = false) |
初始化一个 Histogram |
addSeries(_ s: HistogramSeries) |
将序列添加到绘图中。 |
addSeries(data: [T], bins: Int, label: String, color: Color = .lightBlue, histogramType: HistogramSeriesOptions.HistogramType = .bar) |
使用数组添加一个系列。 |
addStackSeries(data: [T], label: String, color: Color = .lightBlue) |
将堆叠序列添加到绘图中 |
drawGraphAndOutput(fileName name: String = "swift_plot_histogram", renderer: Renderer) |
生成绘图并保存结果图像 |
drawGraph(renderer: Renderer) |
在内存中生成绘图 |
drawGraphOutput(fileName name: String = "swift_plot_histogram", renderer: Renderer) |
将生成的绘图保存到磁盘 |
属性 |
plotTitle: PlotTitle? = nil |
plotLabel: PlotLabel? = nil |
var plotDimensions: PlotDimensions |
var strokeWidth: Float = 2 |
gridLineThickness: Float = 0.5 |
markerTextSize: Float = 12 |
gridColor: Color = .gray |
ScatterPlot<T:FloatConvertible, U:FloatConvertible>
函数 |
描述 |
init(width: Float = 1000, height: Float = 660, isNormalized: Bool = false, enableGrid: Bool = false) |
初始化一个 ScatterPlot。 |
init(points p: [Pair<T,U>], width: Float = 1000, height: Float = 660, enableGrid: Bool = false) |
初始化一个 ScatterPlot。 |
addSeries(_ s: Series<T,U>) |
将序列添加到绘图中。 |
addSeries(points: [Pair<T,U>], label: String, color: Color = .lightBlue, scatterPattern: ScatterPlotSeriesOptions.ScatterPattern = .circle) |
使用 Pairs 数组添加一个系列。 |
addSeries(_ x: [T], _ y: [U], label: String, color: Color = .lightBlue, scatterPattern: ScatterPlotSeriesOptions.ScatterPattern = .circle) |
使用单独的 x 和 y 数组添加一个系列。 |
addSeries(_ x: [T], _ y: [U], label: String, startColor: Color = .lightBlue, endColor: Color = .lightBlue, scatterPattern: ScatterPlotSeriesOptions.ScatterPattern = .circle)) |
使用单独的 x 和 y 数组添加一个系列,并为散点指定一个开始和结束颜色。 |
addSeries(_ y: [U], label: String, color: Color = .lightBlue, scatterPattern: ScatterPlotSeriesOptions.ScatterPattern = .circle) |
只使用 y 数组添加一个系列。 它将针对点的索引绘制。 |
addSeries(_ y: [U], label: String, startColor: Color = .lightBlue, endColor: Color = .lightBlue, scatterPattern: ScatterPlotSeriesOptions.ScatterPattern = .circle) |
只使用 y 数组添加一个系列。 它将针对点的索引绘制。 还要指定散点的开始和结束颜色。 |
drawGraphAndOutput(fileName name: String = "swift_plot_scatter_plot", renderer: Renderer) |
生成绘图并保存结果图像 |
drawGraph(renderer: Renderer) |
在内存中生成绘图 |
drawGraphOutput(fileName name: String = "swift_plot_scatter_plot", renderer: Renderer) |
将生成的绘图保存到磁盘 |
属性 |
plotTitle: PlotTitle? = nil |
plotLabel: PlotLabel? = nil |
plotDimensions: PlotDimensions |
scatterPatternSize: Float = 10 |
gridLineThickness: Float = 0.5 |
markerTextSize: Float = 12 |
gridColor: Color = .gray |
enum stackPattern (要代替初始化程序中的 stackPattern 传入) |
verticallyStacked |
horizontallyStacked |
gridStacked |
函数 |
描述 |
init(width: Float = 1000, height: Float = 660, numberOfPlots n: Int = 1, numberOfRows nR: Int = 1, numberOfColumns nC: Int = 1, stackPattern: Int = 0) |
初始化一个 SubPlot |
draw(plots: [Plot], renderer: Renderer, fileName: String = "subPlot_output") |
生成绘图,并将作为 Sub Plots 传入的绘图保存到磁盘 |
函数 |
描述 |
init(frameWidth : Float = 1000, frameHeight : Float = 660) |
使用帧宽度和高度创建一个 PlotDimensions 变量 |
函数 |
描述 |
init(_ x: T, _ y: T) |
使用 x 和 y 创建一个 Pair |
typealias |
Point = Pair<Float, Float> |
属性 |
zeroPoint = 点(0.0, 0.0) |
属性 |
x轴标签: 字符串 = "X轴" |
y轴标签: 字符串 = "Y轴" |
标签大小: 浮点数 = 15 |
x轴标签位置 = zeroPoint |
y轴标签位置 = zeroPoint |
属性 |
标题: 字符串 = "标题" |
标题大小: 浮点数 = 15 |
标题位置 = zeroPoint |
函数 |
描述 |
初始化(红色: 浮点数, 绿色: 浮点数, 蓝色: 浮点数, 透明度: 浮点数) |
使用红色、绿色、蓝色和透明度值创建颜色。 每个值都在0.0和1.0之间。 |
属性(仅适用于macOS和iOS) |
cgColor: CGColor(返回相应的CGColor) |
内置颜色可以在这里找到。
属性 |
x轴标签: 字符串 = "X轴" |
y轴标签: 字符串 = "Y轴" |
标签大小: 浮点数 = 15 |
x轴标签位置 = zeroPoint |
y轴标签位置 = zeroPoint |
枚举 位置(传递给 LineGraph 中的 addSeries 函数) |
主轴 |
次轴 |
枚举 散点模式(传递给 ScatterPlot 中的 addSeries 函数) |
圆形 |
正方形 |
三角形 |
菱形 |
六边形 |
五边形 |
星形 |
枚举 直方图类型: CaseIterable(传递给 Histogram 中的 addSeries 函数) |
条形 |
阶梯 |
枚举 阴影线: Int, CaseIterable(传递给 BarGraph 中的 addSeries 函数) |
无 = 0 |
正斜线 = 1 |
反斜线 = 2 |
空心圆 = 3 |
实心圆 = 4 |
垂直 = 5 |
水平 = 6 |
网格 = 7 |
交叉 = 8 |
函数 |
描述 |
encodeBase64PNG(pngBufferPointer: 不安全指针, bufferSize: Int) -> 字符串 |
将PNG图像编码为base64格式。 |
- FloatConvertible 仅支持 Float 和 Double。 我们计划将来将其扩展到 Int。
如果您想为改进此库做出贡献,请阅读我们的指南。 随时提出问题。
- Maxim Shemanarev:AGG库直接用于渲染绘图。
- Lode Vandevenne:lodepng库直接用于编码PNG图像。
- The FreeType Project:AGG 使用 FreeType 绘制文本。
- 感谢 Brad Larson 和 Marc Rasi 提供的宝贵指导。