PictureBox中的工具提示FillPie协调C#

时间:2015-03-10 07:22:51

标签: c#

我画了一个由360 FillPie组成的圆圈。每个FillPie颜色都取自List。我想返回一个字符串,该字符串表示鼠标的度数以及将其放在工具提示上的列表值。

    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
        {

        }
    }

直到这里代码正常工作,我设法用正确的颜色绘制圆圈。现在我无法弄清楚如何获取我绘制的每个填充的坐标。有人能帮助我吗?

1 个答案:

答案 0 :(得分:0)

确定鼠标光标所在的饼图段是三角函数的简单应用,特别是反正切(又称反正切或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);

接下来检查arctangent的未定义点(以及我们可以采取的几个快捷方式):

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.

(以上代码也可作为this gist中的完整示例提供)