重叠圆的边界


21

给定一个平面上几个点的坐标,以及围绕每个点的圆的半径,绘制代表圆和圆相交的边的多边形。直边将始终沿圆-圆相交线落下,但可能不会遵循这些线的全长。

根据mbomb007的建议,想象一下2D肥皂泡的行为。这在技术上是错误的,因为肥皂泡总是会以120°的角度相遇以最大程度地减少能量,而这些圆圈可能会以任何角度相遇。

这是一个Voronoi图,减去定义的平面。谢谢Andreas这实际上是称为功率图的Voronoi图的一般化。

例子

例如,给定两个点和两个半径,输出可能如下所示:

在此处输入图片说明

添加另一个点和半径,输出可能如下所示:

在此处输入图片说明

输入项

您可以根据需要构造输入。请通过以下输入发布结果。

测试1

  • x:10,y:10,r:10
  • x:25,y:12,r:8

测试2

  • x:8,y:10,r:6
  • x:20,y:8,r:4
  • x:18,y:20,r:12

输出量

输出应为图形,并应包含多边形边界,但不需要其他任何内容。点和交点不需要像示例中那样表示。

约束条件

  • 另一个圆的半径内将不存在任何点。
  • 标准的代码高尔夫规则。
  • 没有漏洞的答案将不被接受,但请随时尝试。

1
您应该更改标题以提及气泡。这些看起来像2D气泡。
mbomb007 '16

3
您需要给定以下几点来对飞机进行Voronoi镶嵌:en.wikipedia.org/wiki/Voronoi_diagram
Andreas

3
在Voronoi图中,“对于每个种子[点],都有一个相应的区域,该区域由比该种子更近的所有点组成。这显然不是图2中的情况下
DavidC

2
@Andreas DavidC是正确的,只有所有圆的半径都相等时,这才是Voronoi图
LLlAMnYP '16

3
这个问题要求得到圆的功率图
Anders Kaseorg '16

Answers:


18

Python 2中,473个 355字节

L=input()
m=min
a,b,c,d=eval('m(%s-r for u,v,r in L),'*4%('u','v','-u','-v'))
e=(-c-a)/499.
H=lambda x,y:x*x+y*y
I=500
J=int(2-(d+b)/e)
print'P2',I,J,255
i=I*J
P=lambda(u,v,r):H(c+i%I*e+u,b+i/I*e-v)-r*r
while i:i-=1;p,k=m((P(k)/[1,k[2]][P(k)>0],k)for k in L);u,v,r=k;print int(255*m(1,[m([-p/r]+[(P(l)-p)/H(u-l[0],v-l[1])**.5for l in L-{k}]),p][p>0]/2/e))

这将(x,y,r)在stdin上读取一组圆作为元组,并将PGM格式的图像输出到stdout。它通过在每个像素处计算图表的距离函数,并对每个像素加阴影以使其距离成比例地小于一个像素,从而大致起作用。

{(10,10,10),(25,12,8)}

输出1

{(8,10,6),(20,8,4),(18,20,12)}

输出2

{(6, 63, 4), (16, 88, 9), (64, 94, 11), (97, 96, 3), (23, 32, 13), (54, 14, 7), (41, 81, 3), (7, 7, 4), (77, 18, 8), (98, 55, 4), (2, 56, 7), (62, 18, 5), (13, 74, 2), (33, 56, 12), (49, 48, 4), (6, 76, 2), (82, 70, 9), (21, 71, 2), (27, 5, 10), (3, 32, 6), (70, 62, 6), (74, 46, 4), (21, 60, 7), (18, 47, 7), (94, 2, 4), (39, 97, 7), (62, 63, 2), (87, 29, 8), (19, 17, 4), (61, 23, 2), (73, 1, 8), (40, 17, 13), (99, 41, 4), (81, 57, 7), (1, 68, 5), (38, 3, 4), (46, 36, 9), (4, 39, 2), (73, 77, 3), (93, 19, 10), (67, 42, 3), (96, 65, 2), (2, 16, 3), (28, 92, 3), (54, 58, 2), (39, 86, 5), (84, 82, 5), (79, 43, 4), (5, 47, 1), (34, 41, 8), (65, 5, 2), (9, 44, 3), (53, 3, 6), (1, 12, 1), (81, 95, 7), (74, 31, 2), (63, 61, 1), (35, 72, 1), (44, 71, 2), (57, 35, 5), (46, 65, 6), (57, 45, 4), (93, 94, 1), (99, 81, 13), (13, 58, 4), (68, 32, 6), (11, 2, 6), (52, 98, 7), (51, 25, 5), (84, 2, 2), (44, 92, 3), (23, 72, 2), (32, 99, 7), (13, 19, 3), (97, 29, 8), (58, 80, 3), (67, 82, 5), (59, 60, 3), (86, 87, 5), (29, 73, 2), (5, 93, 4), (42, 74, 1), (75, 85, 8), (91, 53, 5), (23, 82, 4), (19, 97, 8), (51, 88, 3), (67, 12, 6), (60, 53, 1), (66, 72, 2), (57, 64, 2), (66, 49, 2), (44, 0, 4), (11, 69, 1), (93, 60, 5), (56, 50, 3), (19, 68, 3), (64, 75, 3), (6, 17, 2), (82, 5, 2)}

输出3

在这里,距离函数已被32除以使其可见:

{(7, 9, 7), (1, 3, 2), (4, 0, 4), (9, 2, 4), (0, 8, 5)}

距离功能演示


1
保存在顶部:exec"%s=m%s(%s for u,v,r in L);"*4%('a','in','u-r','b','ax','v-r','c','in','u+r','d','ax','v+r')
Maltysen '16

9

C#〜2746

这是C#中的解决方案。可能还远未达到最佳状态,但是C#仍然无法赢得胜利。只想让自己证明我可以做到。

通过以xyr顺序指定以空格分隔的值,通过命令行输入。输出是执行目录中的文件“ l.bmp”。

程序接受任何数量的圈子。

测试1:10 10 10 25 12 8

测试2:8 10 6 20 8 4 18 20 12

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;

class Program
{
    static void Main(params string[] args) => new Program().run(args);

    class Circle
    {
        public PointF P;
        public float R;
    }

    class Line
    {
        public PointF S;
        public PointF E;
        public Circle C1;
        public Circle C2;
        public Line(Circle c1, Circle c2, PointF s, PointF e)
        {
            S = s;
            E = e;
            C1 = c1;
            C2 = c2;
        }
    }


    List<Line> lines = new List<Line>();
    List<Circle> circles = new List<Circle>();

    void run(string[] args)
    {
        for (int i = 0; i < args.Length; i += 3)
            addcircle(args[i], args[i + 1], args[i + 2]);
        circles.Sort((c1, c2) => c1.P.X.CompareTo(c2.P.X));


        int mx = (int)circles.Max(c => c.P.X + c.R) + 1;
        int my = (int)circles.Max(c => c.P.Y + c.R) + 1;



        for (int i = 0; i < circles.Count; i++)
            for (int j = i + 1; j < circles.Count; j++)
            {
                var c1 = circles[i];
                var c2 = circles[j];

                var d = dist(c1.P, c2.P);
                var a = 1 / d * sqrt((-d + c1.R - c2.R) * (-d - c1.R + c2.R) * (-d + c1.R + c2.R) * (d + c1.R + c2.R));
                var x = (sqr(d) - sqr(c2.R) + sqr(c1.R)) / (2 * d);

                var ap = angle(c1.P, c2.P);
                var la = rotate(c1.P, new PointF(c1.P.X + x, c1.P.Y + a / 2), ap);
                var lb = rotate(c1.P, new PointF(c1.P.X + x, c1.P.Y - a / 2), ap);
                var l = new Line(c1, c2, la, lb);
                lines.Add(l);
            }
        foreach (Line l in lines)
            foreach (Line lo in lines)
            {
                if (l == lo) continue;
                var intersection = intersect(l, lo);

                if (intersection != null && online(intersection.Value, l) && online(intersection.Value, lo))
                {
                    foreach (Circle circle in circles)
                    {
                        if (l.C1 == circle || l.C2 == circle)
                            continue;
                        if (dist(intersection.Value, circle.P) >= circle.R)
                            continue;

                        if (dist(l.E, circle.P) < circle.R)
                            l.E = intersection.Value;

                        if (dist(l.S, circle.P) < circle.R)
                            l.S = intersection.Value;
                    }
                }
            }


        using (Bitmap bmp = new Bitmap(mx, my))
        {
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.Clear(Color.White);
                foreach (var c in circles)
                    draw(g, c);


                for (int i = 0; i < circles.Count; i++)
                {
                    var c1 = circles[i];
                    var p = new PointF(c1.P.X + c1.R, c1.P.Y);
                    for (int j = 0; j < circles.Count; j++)
                    {
                        if (i == j) continue;
                        var c2 = circles[j];
                        for (var f = 0f; f <= 360f; f += 0.1f)
                        {
                            var pl = rotate(c1.P, p, f);
                            if (dist(pl, c2.P) <= c2.R)
                            {
                                g.DrawRectangle(new Pen(Color.White), (int)pl.X, (int)pl.Y, 1, 1);
                            }

                        }
                    }
                }


                foreach (var l in lines)
                    draw(g, l);

            }
            bmp.Save("t.bmp");
        }
    }

    private float dist(PointF p1, PointF p2) => sqrt(sqr(p1.X - p2.X) + sqr(p1.Y - p2.Y));


    bool online(PointF p, Line l)
    {
        var lx = l.S.X < l.E.X ? l.S.X : l.E.X;
        var hx = l.S.X > l.E.X ? l.S.X : l.E.X;
        var ly = l.S.Y < l.E.Y ? l.S.Y : l.E.Y;
        var hy = l.S.Y > l.E.Y ? l.S.Y : l.E.Y;

        return p.X >= lx && p.X <= hx && p.Y >= ly && p.Y <= hy;
    }

    static PointF? intersect(Line l1, Line l2)
    {
        //Line1
        float A1 = l1.E.Y - l1.S.Y;
        float B1 = l1.S.X - l1.E.X;
        float C1 = A1 * l1.S.X + B1 * l1.S.Y;

        //Line2
        float A2 = l2.E.Y - l2.S.Y;
        float B2 = l2.S.X - l2.E.X;
        float C2 = A2 * l2.S.X + B2 * l2.S.Y;

        float det = A1 * B2 - A2 * B1;
        if (det == 0)
        {
            return null; //parallel lines
        }
        float x = (B2 * C1 - B1 * C2) / det;
        float y = (A1 * C2 - A2 * C1) / det;
        return new PointF(x, y);
    }

    void addcircle(string x, string y, string r)
    {
        var SCALE = 20f;
        Circle c1 = new Circle
        {
            P = new PointF(float.Parse(x) * SCALE, float.Parse(y) * SCALE),
            R = float.Parse(r) * SCALE
        };
        circles.Add(c1);
    }

    void draw(Graphics g, Line l) => g.DrawLine(new Pen(Color.Red), l.S.X, l.S.Y, l.E.X, l.E.Y);

    PointF rotate(PointF o, PointF p, float angle)
    {
        var sa = (float)Math.Sin(angle);
        var ca = (float)Math.Cos(angle);
        var dx = p.X - o.X;
        var dy = p.Y - o.Y;

        return new PointF((ca * dx - sa * dy + o.X), (sa * dx + ca * dy + o.Y));
    }

    float angle(PointF p1, PointF p2)
    {
        var dx = p2.X - p1.X;
        if (dx == 0)
            return 0f;
        return (float)Math.Atan((p2.Y - p1.Y) / dx);
    }


    void draw(Graphics g, Circle c)
    {
        g.DrawEllipse(new Pen(Color.Blue),
                      c.P.X - c.R,
                      c.P.Y - c.R,
                      c.R * 2,
                      c.R * 2);
    }

    float sqr(float d) => d * d;
    float sqrt(float d) => (float)Math.Sqrt(d);
}

这里涉及的所有数学均基于。使用链接中的公式可以轻松获得直线的坐标。但是,它们需要旋转两个相关的小柱中心之间的角度。

为了减少线的长度,我计算了它们的交点。然后,对于该交点,我检查了当前线的末端是否到达了一个圆,该圆不是“线的父级”,并且还包含了交点本身。如果是这种情况,则将线的末端缩小到交叉点的位置。

圆圈很容易绘制,“多余的”部分很难去除,因此我想出了“橡胶”解决方案,可以通过再次将其涂成白色来去除不再需要的东西。有点蛮力强迫它。这是通过沿每个圆的边缘行走并检查该像素是否在另一个圆的范围内来完成的。

最初,我想使用自己的圆图绘制方法,该方法仅以指定的角度绘制圆,但效果并不理想,并且需要更多的代码行。

如果您没有注意到,确实很难解释这一点。。。英语不是我的母亲,所以对此我感到抱歉。

打高尔夫球

using System;using System.Collections.Generic;using System.Drawing;using System.Drawing.Imaging;using System.Linq;class P{static void Main(params string[]args)=>new P().R(args);class C{public PointF P;public float R;}class L{public PointF S;public PointF E;public C C1;public C C2;public L(C c1,C c2,PointF s,PointF e){S=s;E=e;C1=c1;C2=c2;}}List<L>_=new List<L>();List<C>c=new List<C>();void R(string[]args){for(int i=0;i<args.Length;i+=3)A(args[i],args[i+1],args[i+2]);c.Sort((c1,c2)=>c1.P.X.CompareTo(c2.P.X));int B=(int)c.Max(c=>c.P.X+c.R)+1;int e=(int)c.Max(c=>c.P.Y+c.R)+1;for(int i=0;i++<c.Count;)for(int j=i+1;j++<c.Count;){var f=c[i];var q=c[j];var d=D(f.P,q.P);var a=1/d*S((-d+f.R-q.R)*(-d-f.R+q.R)*(-d+f.R+q.R)*(d+f.R+q.R));var x=(F(d)-F(q.R)+F(f.R))/(2*d);var h=angle(f.P,q.P);var k=R(f.P,new PointF(f.P.X+x,f.P.Y+a/2),h);var m=R(f.P,new PointF(f.P.X+x,f.P.Y-a/2),h);var l=new L(f,q,k,m);_.Add(l);}foreach(L l in _)foreach(L o in _){if(l==o)continue;var n=I(l,o);if(n !=null && O(n.Value,l)&& O(n.Value,o)){foreach(C p in c){if(l.C1==p || l.C2==p)continue;if(D(n.Value,p.P)>=p.R)continue;if(D(l.E,p.P)<p.R)l.E=n.Value;if(D(l.S,p.P)<p.R)l.S=n.Value;}}}Bitmap r=new Bitmap(B,e);Graphics g=Graphics.FromImage(r);g.Clear(Color.White);foreach(var _ in c)D(g,_);for(int i=0;i++<c.Count;){var Q=c[i];var P=new PointF(Q.P.X+Q.R,Q.P.Y);for(int j=0;j++<c.Count;){if(i==j)continue;var G=c[j];for(var f=0f;f<=360f;f+=0.1f){var H=R(Q.P,P,f);if(D(H,G.P)<=G.R){g.DrawRectangle(new Pen(Color.White),(int)H.X,(int)H.Y,1,1);}}}}foreach(var l in _)D(g,l);r.Save("t.bmp");}float D(PointF p1,PointF p2)=>S(F(p1.X-p2.X)+F(p1.Y-p2.Y));bool O(PointF p,L l){var lx=l.S.X<l.E.X ? l.S.X : l.E.X;var hx=l.S.X>l.E.X ? l.S.X : l.E.X;var ly=l.S.Y<l.E.Y ? l.S.Y : l.E.Y;var hy=l.S.Y>l.E.Y ? l.S.Y : l.E.Y;return p.X>=lx && p.X<=hx && p.Y>=ly && p.Y<=hy;}static PointF? I(L l1,L l2){float a=l1.E.Y-l1.S.Y;float b=l1.S.X-l1.E.X;float d=a*l1.S.X+b*l1.S.Y;float e=l2.E.Y-l2.S.Y;float f=l2.S.X-l2.E.X;float g=e*l2.S.X+f*l2.S.Y;float h=a*f-e*b;if(h==0)return null;float x=(f*d-b*g)/h;float y=(a*g-e*d)/h;return new PointF(x,y);}void A(string x,string y,string r){var F=20f;C _=new C{P=new PointF(float.Parse(x)*F,float.Parse(y)*F),R=float.Parse(r)*F };c.Add(_);}void D(Graphics g,L l)=>g.DrawLine(new Pen(Color.Red),l.S.X,l.S.Y,l.E.X,l.E.Y);PointF R(PointF o,PointF p,float angle){var a=(float)Math.Sin(angle);var n=(float)Math.Cos(angle);var b=p.X-o.X;var x=p.Y-o.Y;return new PointF((n*b-a*x+o.X),(a*b+n*x+o.Y));}float angle(PointF p1,PointF p2){var a=p2.X-p1.X;if(a==0)return 0f;return(float)Math.Atan((p2.Y-p1.Y)/a);}void D(Graphics g,C c){g.DrawEllipse(new Pen(Color.Blue),c.P.X-c.R,c.P.Y-c.R,c.R*2,c.R*2);}float F(float d)=>d*d;float S(float d)=>(float)Math.Sqrt(d);}

结果1 结果2

更复杂的示例(顶部圆变为y负值)

结果3 没有橡胶

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.