如何检测饼图中的鼠标点击(圆圈片)

时间:2018-05-16 19:12:04

标签: c# windows forms windows-forms-designer

我想做一个飞镖游戏。我希望能够点击特定的馅饼给我点数。我通过连接饼图来制作飞镖靶。如何检测所选区域内是否有点击?这是我创建电路板的代码:

void draw(Color a, Color b, int j)
{
    float start_angle = -9; // offset
    float end_angle = 18;
    for (int i = 0; i < 20; i++)
    {
        pie[j, i] = e.Graphics; 
        pie[j, i].DrawPie(pen, rectangle1, start_angle, end_angle);                         
        if (i % 2 == 0)
        {
             brush.Color = a;
             pie[j, i].FillPie(brush, rectangle1, start_angle, end_angle);
        }
        else
        {
            brush.Color = b;
            pie[j, i].FillPie(brush, rectangle1, start_angle, end_angle);
        }
        start_angle = start_angle + end_angle;
    }
}

1 个答案:

答案 0 :(得分:0)

TaW关于使用GraphicsPath进行命中检测的答案很好,但这个特殊问题也可以用数学方法解决:

    /// <summary>
    /// Determine which wedge of the target a point is contained in, if any.
    /// </summary>
    /// <param name="p">The point to test.</param>
    /// <param name="targetBoundingSquare">The bounding square of the circle target.</param>
    /// <returns>
    /// -1 if point is not in the circle target,
    /// OR a number 0..19 which is the wedge that the point is in, in the same order
    /// that they are drawn.
    /// OR int.MaxValue if the point is a bullseye.
    /// </returns>
    int HitPieWedge(Point p, Rectangle targetBoundingSquare)
    {
        // precondition:
        // the calculations below assume the target is a circle, not a general ellipse
        if (targetBoundingSquare.Width != targetBoundingSquare.Height)
            throw new ArgumentException("The width and height must be equal to form a circular target.", nameof(targetBoundingSquare));

        // quick rejection test:
        // If the point isn't within the overall target bounding rectangle,
        // there's no hit
        if (!targetBoundingSquare.Contains(p))
            return -1;

        // point is within the bounding rectangle of the target, so a
        // hit is possible. now more carefully check to see if it's within
        // the circle (instead of hitting a dead zone that's inside the
        // bounding rectangle but outside of the circle)

        // calculate the center point of the target
        // could use double instead of float, but assume float is good enough precision for a game
        float radius = targetBoundingSquare.Width / 2;
        float centerX = targetBoundingSquare.X + radius;
        float centerY = targetBoundingSquare.Y + radius;

        // Use circle geometry to reject points outside the circle.
        // This is done by calculating the distance from the center of the circle to the test point.
        // If this distance is greater than the radius, we know the point
        // is outside the circle. Otherwise it is inside or just on its boundary.
        // Normally calculating the distance between two points requires a square root which is an
        // relatively expensive operation, but since we only need to know if the distance
        // exceeds the radius (and not the actual distance itself), comparing squared values
        // is just as valid and is more efficient.

        float dx = p.X - centerX;
        float dy = p.Y - centerY;

        // special case: Is point at the exact center ("bullseye")? If so,
        // it is contained in the target but not in any wedge specifically, so
        // just return an arbitrary special value.
        if (dx == 0 && dy == 0)
            return int.MaxValue;

        // actual distance: not needed since we'll use an optimized form below just for
        // relative distance comparison, which just squares both sizes of this equation.
        // distance = Math.Sqr(Math.Pow(dx,2) + Math.Pow(dy,2));

        // if point is further than radial distance from center, reject it
        if (dx * dx + dy * dy > radius * radius)
            return -1;

        // at this point we know the point is on the target somewhere
        // now determine which wedge it is in.
        // note: this part can be done faster using a lookup table,
        // but the math is kind of interesting :)

        // the sweep angle of a single pie wedge, in radians
        const double wedgeSweepAngle = 2 * Math.PI / 20;

        // determine the direction from the center of the target to the hit point
        // this will be an angle from the +X axis in radians in the range [-PI,PI]
        double hitDirection = Math.Atan2(dy, dx);

        // now the direction will be divided by the wedge angular size
        // to identify which wedge it is in, but there are a couple of tricks needed:

        // 1. Because the first wedge is centered on the positive X axis (rather than the
        //    leading edge being aligned on the axis), the direction needs to be offset by half a wedge.
        hitDirection += wedgeSweepAngle * 0.5;

        // 2. Change angle from the range [-PI,PI] to a nicer [0,2PI] range for wedge calculaton.
        //    This just maps the negative range [-PI,0] to [PI,2PI]; the positive range [0,PI] is fine as-is.
        if (hitDirection < 0)
            hitDirection += 2*Math.PI;

        // now it's just a simple division to determine the wedge index.
        // wedges will be numbered from 0 to 19 in the same order
        // they're drawn in your code above

        int wedgeIndex = (int)(hitDirection / wedgeSweepAngle);
        return wedgeIndex;
    }