深入了解 ScottPlot 的兴趣!ScottPlot 是一个功能强大、轻量级的 .NET 开源绘图库,专为快速、高效的 2D 数据可视化设计
深入了解 ScottPlot 的兴趣!以下是对 ScottPlot 的全面介绍,涵盖其核心特性、架构、API 使用方法、性能分析、跨平台支持、定制化能力,以及一个基于 ScottPlot 5.0.55 的完整示例代码,展示如何绘制电压曲线(结合 ResamplerMgr 类处理负值电压,确保平滑连续)丰富的图表类型:支持折线图(Scatter)、信号图(Signal)、柱状图(Bar)、饼图(Pi
深入了解 ScottPlot 的兴趣!ScottPlot 是一个功能强大、轻量级的 .NET 开源绘图库,专为快速、高效的 2D 数据可视化设计,广泛应用于科学、工程和数据分析领域。以下是对 ScottPlot 的全面介绍,涵盖其核心特性、架构、API 使用方法、性能分析、跨平台支持、定制化能力,以及一个基于 ScottPlot 5.0.55 的完整示例代码,展示如何绘制电压曲线(结合 ResamplerMgr 类处理负值电压,确保平滑连续)
1. ScottPlot 概述
-
简介:
-
ScottPlot 是一个 .NET 开源绘图库,专注于简单、高性能的 2D 图表绘制,支持折线图、散点图、柱状图、热图等。
-
开源(MIT 许可证),始于 2018 年,社区活跃,更新频繁。
-
GitHub 仓库:https://github.com/ScottPlot/ScottPlot
-
最新版本(截至 2025 年 6 月 22 日):5.0.55(2025 年 5 月发布)。
-
特点:API 简洁,性能优异,适合大数据和快速开发。
-
-
核心特性:
-
丰富的图表类型:支持折线图(Scatter)、信号图(Signal)、柱状图(Bar)、饼图(Pie)、热图(Heatmap)等。
-
高性能:Signal 模式优化大数据(支持 >10 亿点),渲染时间 <1ms。
-
跨平台:支持 Windows Forms、WPF、MAUI、Avalonia、Blazor、Console。
-
交互功能:支持缩放、平移、鼠标事件,内置右键菜单(放大、自动缩放、保存图像)。
-
图像导出:支持 PNG、JPEG、SVG、BMP 等格式。
-
简单 API:单行代码即可绘制图表,例如 plt.Add.Scatter(x, y)。
-
轻量级:依赖少,适合嵌入式和快速原型开发。
-
-
适用场景:
-
科学数据可视化(如电压曲线、传感器数据)。
-
大数据绘制(如时间序列、频谱分析)。
-
简单交互和快速开发的桌面或控制台应用。
-
跨平台数据可视化(MAUI、Avalonia)。
-
2. ScottPlot 架构
ScottPlot 采用简洁的分层设计:
-
Plot:
-
核心对象,表示整个图表,包含所有绘图元素(系列、轴、标题、图例)。
-
示例:var plt = new ScottPlot.Plot();
-
-
Plottables:
-
数据系列,表示图表中的可视化元素。
-
常见类型:
-
Scatter:散点图/折线图,适合中小数据集。
-
Signal:高效信号图,适合大数据(>百万点)。
-
Bar:柱状图。
-
Heatmap:热图。
-
-
示例:plt.Add.Scatter(x, y);
-
-
Axes:
-
坐标轴,控制 X/Y 轴的范围、标签、网格线。
-
支持多轴(Axes.AddAxis)和自定义样式。
-
示例:plt.Axes.Left.Label.Text = "电压";
-
-
Renderers:
-
负责将 Plot 渲染为图像。
-
基于 SkiaSharp(跨平台 2D 图形库),支持硬件加速。
-
示例:plt.Save("chart.png", 800, 600);
-
-
Controls:
-
平台特定的控件,负责 UI 显示和交互。
-
示例:
-
WinForms:ScottPlot.WinForms.FormsPlot。
-
WPF:ScottPlot.Wpf.WpfPlot。
-
MAUI:ScottPlot.Maui.MauiPlot。
-
-
-
Interaction:
-
内置鼠标交互(缩放、平移、右键菜单)。
-
支持自定义事件(如 MouseMove 获取坐标)。
-
3. ScottPlot API 使用方法
以下是使用 ScottPlot 5.0.55 绘制图表的基本步骤:
-
安装 NuGet 包:
-
Windows Forms:
bash
dotnet add package ScottPlot.WinForms --version 5.0.55 dotnet add package MathNet.Numerics --version 5.0.0 dotnet add package System.Drawing.Common --version 8.0.0 -
WPF(替代示例):
bash
dotnet add package ScottPlot.Wpf --version 5.0.55
-
-
创建 Plot:
-
初始化 Plot 对象:
csharp
var plt = new ScottPlot.Plot();
-
-
添加数据系列:
-
添加散点图或折线图:
csharp
var scatter = plt.Add.Scatter(x, y); scatter.MarkerStyle.Size = 5; scatter.LineStyle.Width = 2;
-
-
配置轴和样式:
-
设置标题和轴标签:
csharp
plt.Title("电压曲线"); plt.Axes.Left.Label.Text = "电压 (伏特)"; plt.Axes.Bottom.Label.Text = "时间 (秒)";
-
-
绑定到控件:
-
在 WinForms 中使用 FormsPlot:
csharp
var formsPlot = new FormsPlot { Dock = DockStyle.Fill }; formsPlot.Plot.Add.Scatter(x, y);
-
-
交互与导出:
-
添加鼠标交互:
csharp
formsPlot.MouseMove += (s, e) => { var coords = formsPlot.Plot.GetCoordinates(new Pixel(e.Location.X, e.Location.Y)); form.Text = $"时间: {coords.X:F3} 秒, 电压: {coords.Y:F3} 伏特"; }; -
导出图像:
csharp
plt.Save("chart.png", 800, 600, ImageFormat.Png);
-
4. ScottPlot 性能分析
-
中小数据集(<100,000 点):
-
性能极佳,渲染和交互无延迟,适合本例(100 点)。
-
-
大数据集(>100,000 点):
-
Signal 模式使用高效算法,渲染 >10 亿点只需数毫秒。
-
优于 OxyPlot,接近 LiveCharts2 的 Backers 包。
-
-
动态更新:
-
支持实时更新,但需手动调用 formsPlot.Refresh()。
-
性能依赖于数据量和刷新频率。
-
-
局限:
-
复杂交互(如内置 Tooltip)需手动实现。
-
复杂图表(如 3D、热图)支持有限。
-
5. 跨平台支持
ScottPlot 5.0.55 支持多种平台:
-
Windows Forms:
-
使用 ScottPlot.WinForms.FormsPlot。
-
适合传统桌面应用,交互简单。
-
-
WPF:
-
使用 ScottPlot.Wpf.WpfPlot。
-
支持 MVVM,但交互仍需手动实现。
-
-
MAUI:
-
使用 ScottPlot.Maui.MauiPlot。
-
支持 iOS、Android、Windows、macOS。
-
-
Avalonia:
-
使用 ScottPlot.Avalonia.AvaloniaPlot。
-
跨平台桌面应用。
-
-
Blazor:
-
支持 Web 应用,生成静态图像。
-
-
Console:
-
支持生成图像到文件,适合脚本和服务器端。
-
-
局限:
-
WinForms 和 WPF 交互不如 LiveCharts2 丰富。
-
MAUI 和 Avalonia 支持较新,文档较少。
-
6. 定制化能力
ScottPlot 提供灵活的定制化,但以简单性为优先:
-
样式:
-
自定义颜色、线型、标记:
csharp
scatter.Color = Colors.Blue; scatter.MarkerStyle.Size = 5; scatter.LineStyle.Width = 2;
-
-
多轴:
-
支持添加额外轴:
csharp
var secondaryAxis = plt.Axes.AddLeftAxis(); secondaryAxis.Label.Text = "功率";
-
-
交互:
-
自定义鼠标事件:
csharp
formsPlot.MouseMove += (s, e) => { /* 自定义逻辑 */ }; -
内置右键菜单(放大、自动缩放、保存)。
-
-
主题:
-
支持自定义背景、网格、字体:
csharp
plt.FigureBackground.Color = Colors.White; plt.Grid.MajorLineColor = Colors.Gray;
-
-
局限:
-
复杂交互(如 Tooltip)需手动实现。
-
不支持动态主题切换。
-
7. ScottPlot 示例代码
以下是基于 ScottPlot 5.0.55 的完整示例代码,与 OxyPlot 和 LiveCharts2 示例功能一致,绘制电压曲线(原始 vs 重采样),支持负值电压,确保平滑连续。代码基于 Windows Forms,包含鼠标交互(窗体标题显示坐标)、图像导出,并结合 ResamplerMgr 类。
环境要求
-
NuGet 包:
bash
dotnet add package ScottPlot.WinForms --version 5.0.55 dotnet add package MathNet.Numerics --version 5.0.0 dotnet add package System.Drawing.Common --version 8.0.0 -
项目:Windows Forms,.NET 8.0 或 .NET Framework 4.8。
代码
csharp
using MathNet.Numerics;
using MathNet.Numerics.Interpolation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Drawing; // 添加 System.Drawing 引用
using ScottPlot;
using ScottPlot.WinForms;
namespace ResamplerLib
{
/// <summary>
/// 重采样管理类,用于对电压数据进行插值重采样,确保负值区域平滑连续。
/// 使用 MathNet.Numerics 的样条插值,支持均匀采样和指定点采样。
/// </summary>
public static class ResamplerMgr
{
public static void Resample(int sampleCount, List<double> x, List<double> y, out List<double> xout, out List<double> yout)
{
xout = new List<double>();
yout = new List<double>();
if (!ValidateInput(x, y, sampleCount))
return;
try
{
IInterpolation ip = Interpolate.CubicSpline(x.ToArray(), y.ToArray());
double start = x[0];
double stop = x[x.Count - 1];
if (sampleCount == 1 || Math.Abs(stop - start) < 1e-10)
{
xout.Add(start);
yout.Add(y[0]);
return;
}
double step = (stop - start) / (sampleCount - 1);
for (double sx = start; sx <= stop + 1e-10; sx += step)
{
double sy = ip.Interpolate(sx);
if (double.IsNaN(sy) || double.IsInfinity(sy))
{
sy = sx <= x[0] ? y[0] : y[y.Count - 1];
Console.WriteLine($"警告:sx={sx:F3} 处插值异常,使用边界值 {sy:F3}");
}
xout.Add(sx);
yout.Add(sy);
}
}
catch (Exception ex)
{
Console.WriteLine($"重采样失败:{ex.Message}");
xout = new List<double>(x);
yout = new List<double>(y);
}
}
public static void Resample(int sampleCount, List<double> x, List<double> y, List<double> xx, out List<double> yy)
{
yy = new List<double>();
if (!ValidateInput(x, y, 1) || xx == null || xx.Count == 0)
{
Console.WriteLine("无效输入:xx 必须非空且有效");
return;
}
try
{
IInterpolation ip = Interpolate.CubicSpline(x.ToArray(), y.ToArray());
foreach (double sx in xx)
{
double sy = ip.Interpolate(sx);
if (double.IsNaN(sy) || double.IsInfinity(sy))
{
sy = sx <= x[0] ? y[0] : y[y.Count - 1];
Console.WriteLine($"警告:sx={sx:F3} 处插值异常,使用边界值 {sy:F3}");
}
yy.Add(sy);
}
}
catch (Exception ex)
{
Console.WriteLine($"重采样失败:{ex.Message}");
}
}
public static void Resample2(int sampleCount, List<double> x, List<double> y, out List<double> xout, out List<double> yout)
{
xout = new List<double>();
yout = new List<double>();
Resample(2 * sampleCount, x, y, out List<double> xTemp, out List<double> yTemp);
List<double> x2 = new List<double>(x);
List<double> y2 = new List<double>(y);
x2.AddRange(xTemp);
y2.AddRange(yTemp);
Resample(sampleCount, x2, y2, out xout, out yout);
}
private static bool ValidateInput(List<double> x, List<double> y, int sampleCount)
{
if (x == null || y == null || x.Count != y.Count || x.Count < 2 || sampleCount <= 0)
{
Console.WriteLine("无效输入:x 和 y 必须非空、长度相等、至少 2 个点,且 sampleCount > 0");
return false;
}
if (x.Any(double.IsNaN) || y.Any(double.IsNaN) || x.Any(double.IsInfinity) || y.Any(double.IsInfinity))
{
Console.WriteLine("无效输入:x 和 y 不能包含 NaN 或 Infinity");
return false;
}
for (int i = 1; i < x.Count; i++)
{
if (x[i] <= x[i - 1])
{
Console.WriteLine("无效输入:x 必须严格递增");
return false;
}
}
return true;
}
}
/// <summary>
/// 主程序:模拟电压数据,执行重采样,使用 ScottPlot 5.0.55 绘制曲线。
/// 支持鼠标交互(显示坐标),保存图像为 PNG。
/// </summary>
class Program
{
[STAThread]
static void Main()
{
// 启用 Windows Forms 视觉样式
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 创建主窗体
var form = new Form
{
Text = "电压曲线展示 - ScottPlot 5.0.55",
Width = 800,
Height = 600
};
// 创建 ScottPlot FormsPlot 控件
var formsPlot = new FormsPlot
{
Dock = DockStyle.Fill
};
form.Controls.Add(formsPlot);
// 模拟电压数据:正弦波 + 随机噪声,包含负值
List<double> x = new List<double>(); // 时间 (秒)
List<double> y = new List<double>(); // 电压 (伏特)
Random rand = new Random(42); // 固定种子
for (double t = -5.0; t <= 5.0; t += 0.5)
{
x.Add(t);
double voltage = 5.0 * Math.Sin(t) + (rand.NextDouble() - 0.5) * 0.5;
y.Add(voltage);
}
// 进行重采样
int sampleCount = 100;
ResamplerMgr.Resample(sampleCount, x, y, out List<double> xout, out List<double> yout);
// 配置 ScottPlot 图表
var plt = formsPlot.Plot;
// 添加原始数据(蓝色散点)
var scatterOriginal = plt.Add.Scatter(x.ToArray(), y.ToArray(), color: Colors.Blue);
scatterOriginal.MarkerStyle.Size = 5;
scatterOriginal.Label = "原始数据";
// 添加重采样数据(红色折线)
var scatterResampled = plt.Add.Scatter(xout.ToArray(), yout.ToArray(), color: Colors.Red);
scatterResampled.MarkerStyle.Size = 0; // 无散点
scatterResampled.LineStyle.Width = 2;
scatterResampled.Label = "重采样数据";
// 设置图表标题和轴标签
plt.Title("电压曲线(原始 vs 重采样)", size: 14);
plt.Axes.Bottom.Label.Text = "时间 (秒)";
plt.Axes.Left.Label.Text = "电压 (伏特)";
// 显示图例
plt.Legend.IsVisible = true;
plt.Legend.Alignment = Alignment.UpperRight;
// 自动调整轴范围
plt.Axes.AutoScale();
// 添加鼠标交互:显示坐标
formsPlot.MouseMove += (s, e) =>
{
var pixel = new Pixel(e.Location.X, e.Location.Y);
var coords = formsPlot.Plot.GetCoordinates(pixel);
form.Text = $"电压曲线展示 - 时间: {coords.X:F3} 秒, 电压: {coords.Y:F3} 伏特";
};
// 保存图像到文件
try
{
plt.Save("voltage_curve_scottplot.png", 800, 600, ImageFormat.Png);
Console.WriteLine("图像已保存为 voltage_curve_scottplot.png");
}
catch (Exception ex)
{
Console.WriteLine($"保存图像失败:{ex.Message}");
}
// 刷新图表
formsPlot.Refresh();
// 运行窗体
Application.Run(form);
}
}
}
示例代码中文解释
1. ResamplerMgr 类
-
功能:实现电压数据的重采样,确保负值电压区域平滑连续。
-
关键特性:
-
使用 MathNet.Numerics 的 CubicSpline 插值,优化负值区域平滑性。
-
验证输入:确保 x 严格递增,无 NaN 或无穷大。
-
处理插值异常:使用边界值。
-
支持均匀采样(Resample)、指定点采样(Resample 重载)和两次重采样(Resample2)。
-
-
方法详解:
-
Resample:生成均匀采样点。
-
Resample(重载):根据指定 xx 插值。
-
Resample2:两次重采样,增强平滑性。
-
ValidateInput:检查输入有效性。
-
2. 模拟电压数据
-
生成逻辑:
-
时间(x):-5 到 5 秒,步长 0.5,共 21 个点。
-
电压(y):正弦波(5.0 * Math.Sin(t))加噪声(±0.25V),包含负值。
-
固定种子(Random(42))确保可重复。
-
3. ScottPlot FormsPlot 控件
-
创建控件:
-
使用 ScottPlot.WinForms.FormsPlot,设置 Dock = DockStyle.Fill。
-
-
绘图配置:
-
Scatter:
-
原始数据:蓝色散点,MarkerStyle.Size = 5。
-
重采样数据:红色折线,MarkerStyle.Size = 0,LineStyle.Width = 2。
-
-
Axes:
-
X 轴:时间,Axes.Bottom.Label.Text。
-
Y 轴:电压,Axes.Left.Label.Text。
-
-
Title:设置标题,Title 方法。
-
Legend:右上角,Legend.IsVisible = true。
-
AutoScale:自动调整轴范围。
-
-
交互:
-
MouseMove:将 System.Drawing.Point 转换为 Pixel,显示坐标。
-
-
保存:
-
使用 Save 方法保存为 800x600 PNG。
-
-
代码:
csharp
var scatterOriginal = plt.Add.Scatter(x.ToArray(), y.ToArray(), color: Colors.Blue); scatterOriginal.MarkerStyle.Size = 5; scatterOriginal.Label = "原始数据"; var scatterResampled = plt.Add.Scatter(xout.ToArray(), yout.ToArray(), color: Colors.Red); scatterResampled.MarkerStyle.Size = 0; scatterResampled.LineStyle.Width = 2; scatterResampled.Label = "重采样数据";
4. 运行结果
-
输出:
-
显示 800x600 窗体,包含:
-
蓝色散点:原始数据(21 点,含噪声)。
-
红色曲线:重采样数据(100 点,平滑,负值区域无断点)。
-
-
X 轴:时间 -5 到 5 秒。
-
Y 轴:电压约 -5.5 到 5.5 伏特。
-
交互:窗体标题显示鼠标位置坐标,右键菜单支持缩放/保存。
-
保存:生成 voltage_curve_scottplot.png。
-
-
负值处理:
-
CubicSpline 插值确保负值区域平滑。
-
Axes.AutoScale() 自动包含负值。
-
8. ScottPlot vs OxyPlot vs LiveCharts2 对比
|
维度 |
ScottPlot 5.0.55 |
OxyPlot 2.1.0 |
LiveCharts2 2.0.0-rc2 |
|---|---|---|---|
|
功能 |
专注 2D 图表(散点、线、信号),功能较少 |
丰富(折线、散点、热图、3D 等高线) |
丰富(折线、柱状、饼图、热图、仪表盘) |
|
性能 |
优异, Signal 适合 >10亿点 |
中等,适合 <10,000 点 |
优异,Backers 包支持 >50M 点,<1ms 响应 |
|
易用性 |
API 简洁,学习曲线低,文档示例丰富 |
API 复杂,学习曲线高 |
中等,MVVM 友好,学习曲线适中 |
|
交互性 |
基础(缩放、平移、右键菜单),需手动实现复杂交互 |
强大(内置 Tracker、缩放、选择) |
强大(Tooltip、缩放、平移),WinForms 稍弱 |
|
跨平台 |
WinForms、WPF、MAUI、Avalonia、Blazor |
WinForms、WPF、Avalonia、Maui |
WPF、WinForms、MAUI、Uno、Avalonia、Blazor |
|
定制化 |
灵活,但复杂图表支持有限 |
高度灵活(多轴、注解、主题) |
高度灵活(主题、动画、多轴) |
|
社区与生态 |
活跃,更新频繁 |
稳定,更新较慢 |
活跃,快速更新,Beta 阶段 |
|
适用性 |
适合简单曲线、大数据、快速开发 |
适合复杂交互、工程应用 |
适合实时、跨平台、动画丰富的场景 |
|
代码复杂度 |
低,单行 API 即可绘图 |
高,需配置 PlotModel |
中等,MVVM 需额外配置 |
-
ScottPlot 优势:
-
API 极简,适合快速开发。
-
大数据性能优(Signal 模式)。
-
轻量级,依赖少。
-
-
OxyPlot 优势:
-
内置 Tracker,交互开箱即用。
-
支持复杂图表(多轴、热图)。
-
-
LiveCharts2 优势:
-
跨平台支持强大,动画流畅。
-
MVVM 友好,适合动态数据。
-
-
推荐:
-
ScottPlot:适合本例(电压曲线)中简单、高效的绘制需求,特别适合大数据和快速原型。
-
OxyPlot:适合需要高级交互和复杂图表的工程应用。
-
LiveCharts2:适合跨平台、实时更新、动画丰富的场景。
-
9. 使用说明与优化
-
运行示例:
-
创建 Windows Forms 项目。
-
安装 NuGet 包:
bash
dotnet add package ScottPlot.WinForms --version 5.0.55 dotnet add package MathNet.Numerics --version 5.0.0 dotnet add package System.Drawing.Common --version 8.0.0 -
复制代码到 Program.cs,运行。
-
检查 voltage_curve_scottplot.png 输出。
-
-
调整参数:
-
采样点:修改 sampleCount(例如 200)提高分辨率。
-
数据模拟:
csharp
double voltage = 10.0 * Math.Sin(t * 2) + (rand.NextDouble() - 0.5) * 1.0;
-
-
调试负值问题:
-
检查控制台日志,确认 NaN 警告。
-
添加日志:
csharp
xout.Add(sx); yout.Add(sy); Console.WriteLine($"点:sx={sx:F3}, sy={sy:F3}");
-
-
扩展:
-
多轴:
csharp
var secondaryAxis = plt.Axes.AddLeftAxis(); secondaryAxis.Label.Text = "功率"; var powerSeries = plt.Add.Scatter(x, powerData); powerSeries.Axes.YAxis = secondaryAxis; -
动态更新:
csharp
scatterOriginal.Data.X = newX; scatterOriginal.Data.Y = newY; formsPlot.Refresh(); -
WPF:
-
安装 ScottPlot.Wpf:
bash
dotnet add package ScottPlot.Wpf --version 5.0.55 -
使用 WpfPlot:
csharp
using ScottPlot.Wpf; var wpfPlot = new WpfPlot { Width = 800, Height = 600 };
-
-
-
性能优化:
-
大数据:使用 Signal:
csharp
plt.Add.SignalXY(xout.ToArray(), yout.ToArray()); -
禁用交互:
csharp
formsPlot.Configuration.Zoom = false; formsPlot.Configuration.Pan = false;
-
10. 负值问题解决总结
-
问题:负值电压可能导致曲线不连续,通常由插值 NaN 或输入异常引起。
-
解决:
-
使用 CubicSpline 插值,增强负值区域平滑性。
-
边界处理:插值异常时使用边界值。
-
输入验证:确保 x 递增、无 NaN。
-
增加采样点(sampleCount = 100)。
-
日志记录:便于调试。
-
11. 资源与参考
-
NuGet:https://www.nuget.org/packages/ScottPlot.WinForms/5.0.55
-
Cookbook:ScottPlot 5.0 Cookbook
-
社区:Stack Overflow(#ScottPlot)、GitHub Issues、Discord。
如果需要 WPF/MAUI 版本、动态数据示例、或特定场景优化(如多轴、大数据),请提供详情,我将提供定制代码!
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)