License

目录

概述

SwiftPlot 框架是一个跨平台库,允许您在 Swift 中本地绘制图形。 现有的 Swift 绘图框架(例如 CorePlot)仅在 iOS 或 Mac 上运行。 SwiftPlot 背后的想法是创建一个可在 iOS、Mac、Linux 和 Windows 上运行的跨平台库。

SwiftPlot 目前使用三个渲染后端来生成绘图

为了将绘图编码为 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

brew install freetype

如果上述方法不起作用,您也可以自己构建和安装 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)

使用 LineGraph 绘制函数

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)

在 LineGraph 中使用辅助轴

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)

绘制在辅助轴上的序列以虚线绘制。

在 Jupyter Notebook 中显示绘图

您只能使用 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 等。要实际生成绘图,您需要调用 drawGraphdrawGraphAndOutput 函数。 这会计算生成绘图所需的所有参数,例如边框的坐标、要绘制的缩放点等。 然后,它将此信息发送给渲染器,该渲染器具有绘制线条、矩形和文本等图元的功能。

如果渲染器在 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

SubPlot

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 传入的绘图保存到磁盘

PlotDimensions

函数 描述
init(frameWidth : Float = 1000, frameHeight : Float = 660) 使用帧宽度和高度创建一个 PlotDimensions 变量

Pair<T,U>

属性
x: T
y: U
函数 描述
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

轴<T,U>

枚举 位置(传递给 LineGraph 中的 addSeries 函数)
主轴
次轴

散点图系列选项

枚举 散点模式(传递给 ScatterPlot 中的 addSeries 函数)
圆形
正方形
三角形
菱形
六边形
五边形
星形

直方图系列选项

枚举 直方图类型: CaseIterable(传递给 Histogram 中的 addSeries 函数)
条形
阶梯

条形图系列选项

枚举 阴影线: Int, CaseIterable(传递给 BarGraph 中的 addSeries 函数)
无 = 0
正斜线 = 1
反斜线 = 2
空心圆 = 3
实心圆 = 4
垂直 = 5
水平 = 6
网格 = 7
交叉 = 8

Base64编码器

函数 描述
encodeBase64PNG(pngBufferPointer: 不安全指针, bufferSize: Int) -> 字符串 将PNG图像编码为base64格式。

局限性

贡献者指南

如果您想为改进此库做出贡献,请阅读我们的指南。 随时提出问题

鸣谢

  1. Maxim Shemanarev:AGG库直接用于渲染绘图。
  2. Lode Vandevenne:lodepng库直接用于编码PNG图像。
  3. The FreeType Project:AGG 使用 FreeType 绘制文本。
  4. 感谢 Brad LarsonMarc Rasi 提供的宝贵指导。