如何将图像设置为图表轴或沿图表轴设置?

2024-02-17

我正在尝试使用彩色光谱带作为图表的轴。这个想法是将图像上的颜色与其沿底部 x 轴相关的波长相匹配。条带需要更改大小以匹配图表区域的变化,并扩展和收缩部分以匹配图表区域中的滚动缩放。

我尝试过使用图像注释,但随着图表区域的变化,注释尺寸保持固定。此外,聚焦于鼠标位置的滚动缩放显然对注释没有影响。

最接近的方法是使用图像作为图表区域的背景。当图表区域发生变化时,这会自动缩放图像,但滚动缩放对背景图像没有影响。此外,背景最好清晰,以避免模糊数据绘图点。我可以编辑图像以使其具有较大的透明部分,并且底部只有一个彩色条带,但即使如此,该条带也可能会掩盖较低强度的数据点。

Spectrum as annotation and background: enter image description here

Annotation not scaling, background scales well: enter image description here

Both annotation and background not scaling with zooming: enter image description here


这是个好主意。

最简单的方法是将图像绘制在Paint图表中的事件,也许PrePaint.

让我们去工作吧..我们将使用DrawImage https://msdn.microsoft.com/en-us/library/ms142040(v=vs.110).aspx重载允许我们缩放和裁剪。为此我们需要两个矩形。

第一个挑战是始终获得正确的目标矩形。

为此我们需要将InnerPlotPosition从相对位置到绝对像素。

这两个功能将有助于:

RectangleF ChartAreaClientRectangle(Chart chart, ChartArea CA)
{
    RectangleF CAR = CA.Position.ToRectangleF();
    float pw = chart.ClientSize.Width / 100f;
    float ph = chart.ClientSize.Height / 100f;
    return new RectangleF(pw * CAR.X, ph * CAR.Y, pw * CAR.Width, ph * CAR.Height);
}

RectangleF InnerPlotPositionClientRectangle(Chart chart, ChartArea CA)
{
    RectangleF IPP = CA.InnerPlotPosition.ToRectangleF();
    RectangleF CArp = ChartAreaClientRectangle(chart, CA);

    float pw = CArp.Width / 100f;
    float ph = CArp.Height / 100f;

    return new RectangleF(CArp.X + pw * IPP.X, CArp.Y + ph * IPP.Y, 
                            pw * IPP.Width, ph * IPP.Height);
}

使用这些数字设置目标矩形就非常简单:

Rectangle tgtR = Rectangle.Round(new RectangleF(ipr.Left, ipr.Bottom - 15, ipr.Width, 15));

你可以选择你喜欢的高度..

下一个挑战是源矩形。

如果没有缩放,它就会简单地是:

Rectangle srcR = new Rectangle( 0, 0, bmp.Width, bmp.Height);

但对于缩放和平移,我们需要对其进行缩放;为此,我们可以使用 x 轴和ScaleView's Minimum and Maximum values.

我们计算轴上第一个和最后一个点的因子:

double f1 = ax.ScaleView.ViewMinimum / (ax.Maximum - ax.Minimum);
double f2 = ax.ScaleView.ViewMaximum / (ax.Maximum - ax.Minimum);

现在我们得到源矩形可能是这样的:

int x  = (int)(bmp.Width * f1);
int xx = (int)(bmp.Width * f2);
Rectangle srcR = new Rectangle( x, 0, xx - x, bmp.Height);

让我们把它放在一起:

private void chart_PrePaint(object sender, ChartPaintEventArgs e)
{
    // a few short names
    Graphics g = e.ChartGraphics.Graphics;  
    ChartArea ca = chart.ChartAreas[0];
    Axis ax = ca.AxisX;

    // pixels of plot area
    RectangleF ipr = InnerPlotPositionClientRectangle(chart, ca);

    // scaled first and last position
    double f1 = ax.ScaleView.ViewMinimum / (ax.Maximum - ax.Minimum);
    double f2 = ax.ScaleView.ViewMaximum / (ax.Maximum - ax.Minimum);

    // actual drawing with the zooming overload
    using (Bitmap bmp = (Bitmap)Bitmap.FromFile(imagePath))
    {
        int x  = (int)(bmp.Width * f1);
        int xx = (int)(bmp.Width * f2);
        Rectangle srcR = new Rectangle( x, 0, xx - x, bmp.Height);
        Rectangle tgtR = Rectangle.Round(
                         new RectangleF(ipr.Left , ipr.Bottom - 15, ipr.Width, 15));
        g.DrawImage(bmp, tgtR, srcR, GraphicsUnit.Pixel);
    }
}

一些注意事项:

  • 当然,我建议使用图像资源,而不是总是从磁盘加载!

  • 绘图将始终覆盖数据点和网格。你也可以..

  • 选择不同的最小值来腾出空间

  • 使图像变小

  • 将其移至 x 轴标签下方

  • 使图像半透明

  • 使 x 轴足够粗,以便可以容纳图像条:ax.LineWidth = 10

对于后一种解决方案,您需要根据缩放状态偏移 y 位置。又快又脏:int yoff = (ax.ScaleView.IsZoomed ? 12 : 5);。为了避免出现黑色条纹,还可以将轴设置为透明或 Chart.BackColor..

Update:

您还可以恢复使用StripLine。它可以扩展其BackgroundImage每当更改比例视图时(即缩放或平移时),您都必须创建合适的图像。为此,上述大部分代码将用于创建新图像。看这个帖子 https://stackoverflow.com/questions/49937962/combine-barchart-and-pointchart/49939521#49939521有关添加和替换不同的示例NamedImage到图表! (相关部分接近标记图像的结尾!)

事实上,我发现这种方式是最好的解决方案,并添加了第二个答案。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何将图像设置为图表轴或沿图表轴设置? 的相关文章

  • 地图类容器的专用功能

    我想要专门为矢量和地图之类的容器设计一个函数模板 对于向量 我可以像下面那样做 但我不知道如何才能有一个专门版本的函数 该函数仅用于像地图这样的容器 include
  • 如何配置 Ninject 来注入 NodaTime IClock

    在我的 NinjectConfigurator 中我有 container Bind
  • 二叉树和快速排序?

    我有一个家庭作业 内容如下 别生气 担心 我是not请你帮我做作业 编写一个程序 通过使用二分查找的快速排序方法对一组数字进行排序 树 推荐的实现是使用递归算法 这是什么意思 到目前为止 这是我的解释 正如我在下面解释的那样 我认为两者都有
  • 仅使用一个 #include 表达式一次包含多个头文件?

    是否有任何表达式可以使语法一次包含多个标头 而无需为每个新文件编写 include 表达式 例如 include
  • 我应该使用字节还是int?

    我记得曾在某处读到 即使您只需要字节 使用 Int32 更好 就性能而言 它 据说 仅适用于您不关心存储的情况 这是有效的吗 例如 我需要一个保存一周中某一天的变量 我是吗 int dayOfWeek or byte dayOfWeek E
  • .Net 支持柯里化泛型吗?

    假设我们有一个嵌套的泛型类 public class A
  • 如何在 C++11 中返回类成员向量

    我读了几篇关于如何从方法返回向量的文章 其中包括 c11 右值和移动语义混淆返回语句 https stackoverflow com questions 4986673 c11 rvalues and move semantics conf
  • 我想找到 C# 代码中所有后面没有括号的 if 语句。通过正则表达式

    我想找到所有if声明和for后面没有大括号的语句 当你在一个文件中写入一行时if声明您大多不会将其括在大括号中 所以我想找到所有这些if and for声明 请帮忙 就像我想捕捉这个声明 if childNode Name B return
  • 将 JavaScript 引擎嵌入到 .NET 中 [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 只是想知道是否有人尝试过将任何 js 引擎嵌入并实际集成到 net 环境中 我可以找到并实际使用 经过L
  • 如何在C++中列出Python模块的所有函数名称?

    我有一个 C 程序 我想导入一个 Python 模块并列出该模块中的所有函数名称 我该怎么做 我使用以下代码从模块中获取字典 PyDictObject pDict PyDictObject PyModule GetDict pModule
  • 向客户端发送状态码 500 时页面未呈现

    我有一个页面 通用处理程序 我想在该页面上向客户端返回状态代码 500 以指示出现问题 我这样做 Response StatusCode 500 Response StatusDescription Internal Server Erro
  • 为什么 C# 中的命名空间允许循环依赖?

    在 c 中 您可以在文件 a cs 其命名空间为 MyApp A 中拥有一条语句 using MyApp B 而文件 b cs 其名称空间为 MyApp B 已经包含该语句 using MyApp A 如果类似的依赖关系存在于不同的 dll
  • 括号内声明的对象的范围

    如果我声明一个这样的对象 void main myclass objectA anotherclass true true 0 即 我通过直接调用后者的构造函数来创建一个 objectA 和另一个对象 anotherclass anothe
  • 以编程方式阻止 Vista 桌面搜索 (WORDS) 对映射网络驱动器上的 pst 文件建立索引

    经过几天的多次尝试 我没有找到任何 100 的解决方案来解决这个问题 我的搜寻和调查范围 直接访问注册表 HKLM SOFTWARE Microsoft Windows Search CrawlScopeManager Windows Sy
  • 结构大小与 typedef 版本不同?

    我的代码中有以下结构声明和 typedef struct blockHeaderStruct bool allocated unsigned int length typedef struct blockHeaderStruct block
  • TCP/IP 传输期间套接字数据损坏

    当我通过预连接的 TCP IP 套接字发送数据时 我发现数据已损坏 Example Station1 正在向 Station2 发送数据 我已经在发送之前 在 S1 和接收之后 在 S2 打印了数据 以下是消息 S1 发送的数据是ACKS2
  • asio::this_coro::executor 的实现是什么

    在协程函数中 我们可以添加auto ex co await asio this coro executor 获取该协程的执行者 但当我想了解它的定义时 我发现了这个 Awaitable type that returns the execu
  • 对 Action 方法的两个并行 ajax 请求排队,为什么?

    我正在使用 ASP NET MVC 开发一个视频网站 我希望在我的应用程序中拥有的一项功能是转码视频 但由于转码过程可能非常耗时 我想向客户端用户展示该过程的进度 因此 我的架构是使用一个控制器操作来处理整个转码过程 并将其进度写入存储在服
  • 在for循环中声明和初始化变量

    可以简单写一下吗 for int i 0 代替 int i for i 0 在 C 或 C 中 并且会变量i只能在循环内部访问 它在 C 中有效 它在 C 的原始版本中是不合法的 但在 C99 中被采用为 C 的一部分 当时一些 C 功能被
  • 在派生类中访问基类变量

    class Program static void Main string args baseClass obj new baseClass obj intF 5 obj intS 4 child obj1 new child Consol

随机推荐