使用图形区域定义维恩图的区域

时间:2014-09-23 18:16:52

标签: c# winforms graphics onpaint

我使用WinForm在onPaint事件中提供的简单图形函数创建了一个维恩图。这是我创建维恩的代码。

  using (Brush brushLeft = new SolidBrush(LeftVennColor))
  {
    leftvennPath.AddEllipse(leftVenn);
    leftOnlyRegion = new Region(leftVenn);
    e.Graphics.FillEllipse(brushLeft, leftVenn);
    e.Graphics.DrawEllipse(pen, leftVenn);
  }

  using (Brush brushRight = new SolidBrush(RightVennColor))
  {
    rightvennPath.AddEllipse(rightVenn);
    rightOnlyRegion = new Region(rightVenn);
    e.Graphics.FillEllipse(brushRight, rightVenn);
    e.Graphics.DrawEllipse(pen, rightVenn);
   }

  using (GraphicsPath circle_path = new GraphicsPath())
  {
    circle_path.AddEllipse(leftVenn);
    commonRegion.Intersect(circle_path);
  }

  using (GraphicsPath circle_path = new GraphicsPath())
  {
    circle_path.AddEllipse(rightVenn);
    commonRegion.Intersect(circle_path);
  }

创建了维恩图,但是使用此代码,我的公共区域是左右椭圆的交点。我希望在该公共区域之外有两个独立的区域,这些区域由一条线分开。这是图像,

enter image description here

基本上,我需要将所有这四个区域分开并单击(每个区域的颜色不同)。我在鼠标单击事件中使用Region.IsVisible(e.location)来处理click事件。有人可以帮忙吗?

1 个答案:

答案 0 :(得分:0)

最终解决方案:

cx0,cy0,radius0 中心和左半径的半径
cx1,cy1,radius1 中心和右圆的半径

该函数通过ref。

获取区域
private void FindRegions(int cx0, int cx1, int cy0, int cy1, int radius0, int radius1, ref Region rgnLeft, ref Region rgnRight)
    {
        //Left circle
        GraphicsPath gpL = new GraphicsPath();
        //Right circle
        GraphicsPath gpR = new GraphicsPath();
        //The right small region (yellow color)
        GraphicsPath gp = new GraphicsPath();
        //Points of intersection
        PointF pnt1 = new PointF();
        PointF pnt2 = new PointF();

        Graphics g = this.CreateGraphics();

        gpL.AddEllipse(new Rectangle(cx0 - radius0, cy0 - radius0, 2 * radius0, 2 * radius0));
        gpR.AddEllipse(new Rectangle(cx1 - radius0, cy1 - radius1, 2 * radius1, 2 * radius1));

        g.DrawPath(Pens.Red, gpL);
        g.DrawPath(Pens.Blue, gpR);

        int numPoints = FindCircleCircleIntersections((single)cx0, (single)cx1, (single)cy0, (single)cy1, (single)radius0, (single)radius1, ref pnt1, ref pnt2);

        if (numPoints != 2)
        {
            //No regions
            return;
        }

        Double theta, fe;
        Double dx = (double)pnt1.X - (double)pnt2.X;
        Double dy = (double)pnt1.Y - (double)pnt2.Y;
        Double dist = Math.Sqrt(dx * dx + dy * dy);

        PointF minPoint, maxPoint;

        if (pnt2.Y < pnt1.Y)
        {
            minPoint = pnt2;
            maxPoint = pnt1;
        }
        else
        {
            minPoint = pnt1;
            maxPoint = pnt2;
        }

        //theta is the angle between the three points pnt1, pnt2 and left center
        theta = Math.Acos((dist / 2D) / 100D);
        theta = (theta * 180D) / Math.PI;
        theta = 90D - theta;
        theta *= 2D;

        //fe is the starting angle of the point(between pnt1 and pnt2) with 
        //the smaller y coordinate. The angle is measured from x axis and clockwise
        fe = Math.Asin( Math .Abs ( (-(Double)minPoint.Y + (double)cy0) )/ (double)radius0);
        fe = (fe * 180D) / Math.PI;

        if (minPoint.X > cx0 && minPoint.Y >= cy0)
        {
            //fe = (90 - fe) + 270;
        }
        else if (minPoint.X > cx0 && minPoint.Y < cy0)
        {
            fe = (90D - fe) + 270D;
        }
        else if (minPoint.X == cx0 && minPoint.Y < cy0)
        {
            fe = 270D;
        }
        else
        {
            fe += 180D;
        }

        gp.AddArc(new Rectangle(cx0 - radius0, cy0 - radius0, 2 * radius0, 2 * radius0), (float)fe, (float)theta);
        gp.AddLine(maxPoint, minPoint);

        gp.CloseFigure();

        g.DrawPath(Pens.Green, gp);

        Region rgnL = new Region(gpL);
        Region rgnR = new Region(gpR);
        Region rgnInt = new Region(gpL);
        Region rgn = new Region(gp); //right small

        rgnInt.Intersect(rgnR);

        rgnInt.Exclude(rgn); //left small

        g.FillRegion(Brushes.DarkGreen, rgnInt);
        g.FillRegion(Brushes.DarkGray, rgn);

        rgnLeft = rgnInt.Clone();
        rgnRight = rgn.Clone();

        g.Dispose();
        rgnL.Dispose();
        rgnR.Dispose();
        rgnInt.Dispose();
        rgn.Dispose();
        gpL.Dispose();
        gpR.Dispose();
        gp.Dispose(); 
    }

    private int FindCircleCircleIntersections(Single cx0, Single cx1, Single cy0, Single cy1, Single radius0, Single radius1, 
                                               ref PointF intersection1, ref PointF intersection2)
    {
        // Find the distance between the centers.
        Single dx = cx0 - cx1;
        Single dy = cy0 - cy1;
        Double dist = Math.Sqrt(dx * dx + dy * dy);

        // See how many solutions there are.
        if (dist > radius0 + radius1) 
        {
            //No solutions, the circles are too far apart.
            intersection1 = new PointF(Single.NaN, Single.NaN);
            intersection2 = new PointF(Single.NaN, Single.NaN);
            return 0;
        }
        else if (dist < Math.Abs(radius0 - radius1))
        {
            // No solutions, one circle contains the other.
            intersection1 = new PointF(Single.NaN, Single.NaN);
            intersection2 = new PointF(Single.NaN, Single.NaN);
            return 0;
        }
        else if ((dist == 0) && (radius0 == radius1)) 
        {
            // No solutions, the circles coincide.
            intersection1 = new PointF(Single.NaN, Single.NaN);
            intersection2 = new PointF(Single.NaN, Single.NaN);
            return 0;
        }
        else
        {
            // Find a and h.
            Double a  = (radius0 * radius0 - radius1 * radius1 + dist * dist) / (2 * dist);
            Double h  = Math.Sqrt(radius0 * radius0 - a * a);

            // Find P2.
            Double cx2 = cx0 + a * (cx1 - cx0) / dist;
            Double cy2 = cy0 + a * (cy1 - cy0) / dist;

            // Get the points P3.
            intersection1 = new PointF( (Single)(cx2 + h * (cy1 - cy0) / dist), (Single)(cy2 - h * (cx1 - cx0) / dist));
            intersection2 = new PointF( (Single)(cx2 - h * (cy1 - cy0) / dist), (Single)(cy2 + h * (cx1 - cx0) / dist));

            // See if we have 1 or 2 solutions.
            if (dist == radius0 + radius1) return 1;
            return 2;
        }
    }

修改

Region 只有 Fill 方法,没有 Draw 方法。所以你不能用地区来做。 GraphicPath 但是 HAS 填充绘制
你说你需要验证一个点是否在区域内但是你可以用 GraphicPath

进行相同的操作
myGraphicPath.IsVisible();

所以,不要使用区域而是使用路径。其他原因更好。 GraphicPath 可以绘制 AntiAlias ,但区域。设置

g.SmoothingMode = SmoothingMode.AntiAlias;

启用 AntiAlias 。所有带路径的好东西!

将函数名称从 FindRegions 更改为 FindPaths ,并将路径作为参考发送:

private void FindPaths(int cx0, int cx1, int cy0, int cy1, int radius0, int radius1, ref GraphicsPath gpLeft, ref GraphicsPath gpRight) 

代码完全相同,但添加了和:

private void FindPaths(int cx0, int cx1, int cy0, int cy1, int radius0, int radius1, ref GraphicsPath gpLeft, ref GraphicsPath gpRight)
{
    ...
    ...
    //Above code exactly the same    

    //replace these
    //rgnLeft = rgnInt.Clone();
    //rgnRight = rgn.Clone();

    //with these
    GraphicsPath gpLeftSmall = (GraphicsPath)gp.Clone();
    Matrix matrix = new Matrix();

    PointF pntf = new PointF();

    pntf.X = (float)(Math.Min((double)pnt1.X, (double)pnt2.X) + Math.Abs((double)(pnt1.X - pnt2.X) / 2D));
    pntf.Y = (float)(Math.Min((double)pnt1.Y, (double)pnt2.Y) + Math.Abs((double)(pnt1.Y - pnt2.Y) / 2D));

    matrix.RotateAt(180, pntf);

    gpLeftSmall.Transform(matrix);

    g.DrawPath(Pens.Black, gpLeftSmall); //If you want to draw it

    //passed by refference
    gpLeft = gpLeftSmall.Clone();
    gpRight = gp.Clone();


    g.Dispose();

    rgnL.Dispose();
    rgnR.Dispose();
    rgnInt.Dispose();
    rgn.Dispose();

    gpL.Dispose();
    gpR.Dispose();
    gp.Dispose();
    gpLeftSmall.Dispose();

    matrix.Dispose();   
}

参考: Determine where two circles intersect