我画了一个由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
{
}
}
直到这里代码正常工作,我设法用正确的颜色绘制圆圈。现在我无法弄清楚如何获取我绘制的每个填充的坐标。有人能帮助我吗?
答案 0 :(得分:0)
确定鼠标光标所在的饼图段是三角函数的简单应用,特别是反正切(又称反正切或atan)的应用。
快速提醒那些之前遇到过这种情况的人,或者作为那些没有遇到过这种情况的人的教训,让我们快速看一下切线函数。三角函数处理直角三角形的几何,根据定义,直角三角形有两个边和一个斜边。斜边是与右侧(90°或π/ 2)角相对的三角形边的特殊名称。另外两个方面只是被称为双方。
切线函数的值是与角度相邻的一侧与角度相对的一侧的比率。反正切是角度,其切线等于该比率。由于函数的对称性,我们需要计算角度,然后根据象限添加或减去偏移量以提取“实际”角度。在图表形式中,这看起来像:
切线函数在几个点处具有不连续性,即当相邻边的长度为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;
所有这些共同产生最终结果:
(以上代码也可作为this gist中的完整示例提供)