C#中PictureBox FillPie坐标的提示工具

4

我正在绘制一个由360个FillPie组成的圆形。每个FillPie的颜色都来自于一个列表。我想返回一个字符串,告诉鼠标所在的角度以及列表中对应的值,以便放置在工具提示上。

    List<int> datiDisco = new List<int>();

    public void Paint (Graphics grafica)
    {
        try
        {
            for (int i = 0; i < datiDisco.Count; i++)
            {
                Brush penna = new SolidBrush(Color.FromArgb(255, ScalaGrigi(valori[i]), ScalaGrigi(valori[i]), ScalaGrigi(valori[i])));
                grafica.FillPie(penna, 0, 0, 400, 400, i, 1.0f);
            }
        }
        catch
        {

        }
    }

到目前为止,代码是可行的,我成功地用正确的颜色绘制了圆圈。现在我无法想象如何获取我绘制的每个填充饼的坐标。有人能帮我吗?


你是根据某个事件在创建那个圆吗? - Failed Scientist
1
你在哪个部分遇到了困难?你需要使用某种鼠标事件。 - Sayse
在将所有的int添加到daticisco列表后,我绘制了圆形。 - Salvatore Rollo
使用“更新”按钮,我将360个整数添加到dadiDisco列表中(例如10、50、80、20、-40、50等)。然后,我调用picture box的refresh()方法,并为列表中的每个数据绘制fillPie。每个FillPie的颜色取决于列表上的值(数字越大,颜色越深)。到这里一切都好。在画完圆之后,我成功创建了工具提示,但我不知道如何根据鼠标位置在其中写入列表数据(例如,如果鼠标位于使用Listindex[45]绘制的fillPie上,则希望写入“x:45,Y:int值”)。 - Salvatore Rollo
1个回答

0

找出鼠标光标所在的饼图区域是三角函数的一个简单应用,具体来说是反正切函数(也称为arctangent或atan)的应用。

对于那些之前遇到过这个问题的人,或者对于那些还没有遇到过的人,让我们快速地看一下正切函数。三角函数处理直角三角形的几何学,根据定义,直角三角形有两条直角边和一条斜边。斜边是三角形中与直角(90°或π/2)相对的一条边的特殊名称。另外两条边很有帮助地被称为直角边。

正切函数的值是一个角度的对边与邻边的比值。反正切是其切比值相等的角度。由于函数的对称性,我们需要计算角度,然后根据象限添加或减去一个偏移量来提取“真实”的角度。以图表形式表示如下:

Diagram of arctangent values mapped to quadrants.

正切函数在几个点上有不连续性,即当相邻边的长度为0(90°和270°)时,我们必须特别处理这些点。

好了,足够的数学知识,现在进入实际应用。

对于此演示,创建一个新的C# WinForms项目,在默认的Form1上添加一个PictureBox

首先,由于我没有您的颜色生成函数,因此我使用以下值列表和辅助函数:

List<int> values = Enumerable.Range(0, 360).ToList();
int Rescale(int x) => (int)(((double)x / 360.0) * 255.0);

在构造函数中,挂接一些事件并设置一些属性:
public Form1()
{
    InitializeComponent();

    this.pictureBox1.BorderStyle = BorderStyle.Fixed3D;
    this.pictureBox1.Size = new Size(50, 50);
    this.Size = new Size(450, 450);

    this.DoubleBuffered = true;
    this.Paint += Form1_Paint;
    this.MouseMove += Form1_MouseMove;
}

为了绘制圆形,我使用了您的OnPaint处理程序的稍微修改版本:
private void Form1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.Clear(Color.Black);

    for (int i = 0; i < values.Count; i++)
    {
        Brush b = new SolidBrush(Color.FromArgb(255, Rescale(values[i]), 0, 0));
        e.Graphics.FillPie(b, 0, 0, 400, 400, (float)i, 1.0f);
    }
}

MouseMove事件中,我们进行大部分重要的工作:
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
    this.pictureBox1.Location = new Point(e.X + 5, e.Y - 5);
    int segment = (int)GetAngle(new Rectangle(0, 0, 400, 400), e.Location);
    this.pictureBox1.BackColor = Color.FromArgb(255, Rescale(segment), 0, 0);
}

你可能会注意到,由于360个楔子是以度数递增的,我只截取了角度。如果您需要更高的精度,或者决定使用大于1度的段,那么您可以使用各种舍入算法将角度舍入到最接近饼图部分的位置。

最后,我们准备实现GetAngle函数。首先,我们计算圆的中心,因为一切都是相对于它的。

int cx = (rect.Width + rect.X) / 2;
int cy = (rect.Height + rect.Y) / 2;

接下来计算鼠标位置与矩形中心的差异。(我已经反转了y坐标以使其与“标准”笛卡尔坐标系对齐,从而使事情变得更容易,并匹配你在数学教科书中看到的坐标。)
float x = pTo.X - cx;
float y = (cy - pTo.Y);

接下来检查反正切函数的未定义点(以及我们可以采取的一些快捷方式):

if ((int)x == 0)
{
    if (y > 0) return 270;
    else return 90;
}
else if ((int)y == 0)
{
    if (x > 0) return 0;
    else return 180;
}

计算内角:

float ccwAngle = (float)Math.Atan(Math.Abs(y) / Math.Abs(x));

并将该角度映射到相应的象限:

if (x > 0 && y > 0)
{

}
else if (x < 0 && y > 0)
{
    ccwAngle = (float)Math.PI - ccwAngle;
}
else if (x < 0 && y < 0)
{
    ccwAngle = ccwAngle + (float)Math.PI;
}
else if (x > 0 && y < 0)
{
    ccwAngle *= -1f;
}

将角度从度数转换为弧度并进行归一化处理(确保它在0°到360°之间)

ccwAngle *= (float)(180 / Math.PI);
while (ccwAngle > 360) ccwAngle -= 360;
while (ccwAngle < 0) ccwAngle += 360;

最后,将我们需要进行数学计算的逆时针角度转换为GDI使用的顺时针角度,并返回该值:
return 360f - ccwAngle;

所有这些组合在一起产生了最终的结果:

Screenshot of demo implementation.

(上面的代码也可以在这个Gist中作为完整示例获得)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接