繁体   English   中英

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

[英]Defining Regions for Venn Diagram using Graphics Regions

我使用onForm事件中WinForm提供的简单图形功能创建了维恩图。 这是我创建维恩的代码。

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

维恩图已创建,但是使用此代码,我的共同区域是左右两个椭圆的交点。 我想在该公共区域之外有两个单独的区域,这些区域由一条线隔开。 这是该图片,

在此处输入图片说明

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

最终解决方案:

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

该功能通过参考获取区域。

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

编辑

区域只有填充方法,没有绘制方法。 因此,您无法使用区域。 然而GraphicPath同时具有 填充绘制
您说过,您需要验证某个点是否在区域内, 但是您可以使用GraphicPath进行相同操作

myGraphicPath.IsVisible();

因此,不要使用区域,而要使用路径。 出于另一个原因,它更好。 GraphicPath可以借鉴消除锯齿 ,但地区

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

参考: 确定两个圆相交的位置

暂无
暂无

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

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