简体   繁体   English

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

[英]Defining Regions for Venn Diagram using Graphics Regions

I have created a Venn diagram using simple Graphics functions provided by WinForm in the onPaint event. 我使用onForm事件中WinForm提供的简单图形功能创建了维恩图。 Here is my code for creating the Venn. 这是我创建维恩的代码。

  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);
  }

The Venn diagram is created, but with this code my common region is the intersection of both left and right ellipses. 维恩图已创建,但是使用此代码,我的共同区域是左右两个椭圆的交点。 I want to have two separate regions out of that common area, which is separated by a line. 我想在该公共区域之外有两个单独的区域,这些区域由一条线隔开。 Here is the image for that, 这是该图片,

在此处输入图片说明

So basically, I need all these four regions separated and clickable ( different colors for each region ).. I use Region.IsVisible(e.location) in the mouse click event to handle the click event. 因此,基本上,我需要将所有这四个区域分开并单击(每个区域使用不同的颜色)。我在鼠标click事件中使用Region.IsVisible(e.location)来处理click事件。 Could someone please help? 有人可以帮忙吗?

Final solution: 最终解决方案:

cx0, cy0, radius0 center and radius of left circle cx0,cy0,radius0中心和左圆半径
cx1, cy1, radius1 center and radius of right circle cx1,cy1,radius1中心和右圆半径

The function takes the regions by 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;
        }
    }

EDIT 编辑

Region has only a Fill method and no Draw one. 区域只有填充方法,没有绘制方法。 So you cant do it with regions. 因此,您无法使用区域。 GraphicPath however HAS both Fill and Draw . 然而GraphicPath同时具有 填充绘制
You said that you need to validate if a point is inside the region BUT you can do the same with GraphicPath 您说过,您需要验证某个点是否在区域内, 但是您可以使用GraphicPath进行相同操作

myGraphicPath.IsVisible();

So, dont use regions but paths. 因此,不要使用区域,而要使用路径。 It is better for another reason. 出于另一个原因,它更好。 GraphicPath can draw AntiAlias but regions dont . GraphicPath可以借鉴消除锯齿 ,但地区 Set

g.SmoothingMode = SmoothingMode.AntiAlias;

To enable AntiAlias . 启用AntiAlias All goodies with paths! 所有有路径的东西!

Change the function name from FindRegions to FindPaths and send paths as refference: 将函数名称从FindRegions更改为FindPaths并发送路径作为参考:

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

The code is exactly the same, but add in the and: 代码完全相同,但是添加和:

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();   
}

Reference: Determine where two circles intersect 参考: 确定两个圆相交的位置

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM