tikz:均匀随机分布圆圈

tikz:均匀随机分布圆圈

我想用或多或少均匀且随机分布的圆圈填充图片的一部分,这些圆圈不应重叠。如果圆圈只有部分位于形状内,那就没问题了。密度应该像您在此页面上看到的那样http://zoada.com/html5/brute.html值为 80-100。(但我不需要那么多圆圈,30 个就足够了,我可以重复这个图案)。

我不能使用网格并稍微“摆动”圆圈:圆圈太大,而且看起来根本不随机。

目前,我正在考虑一种随机尝试:获取某个随机点,测量其与现有圆的距离,绘制或丢弃该点。尝试 100 次,并希望您获得正确数量的圆(并且不会花费太长时间)。但也许有人有更好的主意。

该示例简单地手动绘制圆圈:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{backgrounds}
\begin{document}

 \begin{tikzpicture}[framed,gridded,radius=0.5cm]
 \draw[red](0,0) rectangle (5,5);
  \draw (1,1) circle;
  \draw (2.3,1.1) circle ;
  \draw (4.5,0.8) circle ;
  \draw (5.1,1.8) circle ;
  \draw (0.4,3.3) circle ;
  \draw (2.1,2.8) circle ;
  \draw (3.8,3.5) circle ;
  \draw (4.8,4.2) circle ;
  \draw (0.8,4.9) circle ;
  \draw (2.1,4.1) circle ;
  \draw (3.8,2.0) circle ;
  \draw (3.5,0.6) circle ;
  \draw (3.0,5.0) circle ;   
  \draw (4.1,5.1) circle ;          
  \draw (0.9,2.1) circle ;
 \end{tikzpicture}
 \end{document}

答案1

这是一个实现您所描述的宏。它需要四个参数:要填充的矩形的宽度和高度、圆的半径以及尝试次数。

每次尝试都会生成一个随机位置。计算此位置与所有现有圆之间的距离,如果发生碰撞,则不绘制圆。如果圆不发生碰撞,则绘制圆,并将其坐标添加到现有圆的列表中。

这种方法效率不高,但是确实有效:

\fillrandomly{3}{2}{0.2}{50}

\pgfmathsetseed{2}
\fillrandomly{5}{5}{0.5}{300}

\documentclass{article}
\usepackage{tikz}

\begin{document}
\def\xlist{4}
\def\ylist{4}

\newcommand{\fillrandomly}[4]{
    \pgfmathsetmacro\diameter{#3*2}
    \draw (0,0) rectangle (#1,#2);
    \foreach \i in {1,...,#4}{
        \pgfmathsetmacro\x{rnd*#1}
        \pgfmathsetmacro\y{rnd*#2}
        \xdef\collision{0}
        \foreach \element [count=\i] in \xlist{
            \pgfmathtruncatemacro\j{\i-1}
            \pgfmathsetmacro\checkdistance{ sqrt( ({\xlist}[\j]-(\x))^2 + ({\ylist}[\j]-(\y))^2 ) }
            \ifdim\checkdistance pt<\diameter pt
                \xdef\collision{1}
                \breakforeach
            \fi
        }
        \ifnum\collision=0
            \xdef\xlist{\xlist,\x}
            \xdef\ylist{\ylist,\y}
            \draw [red, thick] (\x,\y) circle [radius=#3];
        \fi 

    }
}

\begin{tikzpicture}
\pgfmathsetseed{2}
\fillrandomly{5}{5}{0.5}{300}

\end{tikzpicture}
\end{document}

答案2

这是不同的变体。其想法是生成一堆圆,然后剔除相交的圆。对于剔除阶段,我使用了半成品实现清扫和修剪就大型碰撞检测而言,这会加快速度n

  • 随机生成点
  • 对他们进行排序x-coordinates
  • 保存x-coordinates重叠的对以进行进一步测试,丢弃其余的
  • 对于已保存的对,确定是否y-coordinates重叠
  • 如果是这样,那么它们可能会发生碰撞,因此对它们进行实际碰撞测试
  • 如果它们发生碰撞,则丢弃其中之一

宏是\circles{n}{rad}{width}{height}\circles{500}{.1}{10}{5}给出:

在此处输入图片描述

\documentclass{article}
\usepackage{luacode}
\usepackage{tikz}
\usetikzlibrary{backgrounds}

\begin{luacode*}

local rand = math.random
local abs = math.abs
local pts = {}
local tstpairs = {}

-- generate the points
local function genpts(n,x,y)
    for i = 1,n do
        pts[i]={}
        pts[i][1] = rand()*x
        pts[i][2] = rand()*y
    end
end

-- for sorted pairs, check if x-coords overlap
-- if so, store the pair in table tstpairs
local function getpairs(t,r)
    for i = 1,#t do
        tstpairs[i] = {}
        for j = 1,#t-i do
            if t[i+j][1]-t[i][1]<2*r then
                tstpairs[i][#tstpairs[i]+1]=i+j
            else
                break
            end
        end
    end
end

-- this is the actual collision test
-- it's less expensive to use x^2+y^2<d^2 than sqrt(x^2+y^2)<d
local function testcol(a,b,r)
    local x = pts[b][1]-pts[a][1]
    local y = pts[b][2]-pts[a][2]
    if x*x+y*y<4*r*r then
        return true
    end
end

-- this is a bit of a mess, deleting pairs on the fly was causing some
-- problems, so I had to include some checks "if pts[k1]..." etc.
local function delpairs(r)
    for k1,v1 in pairs(tstpairs) do
        if pts[k1] then
            for k2,v2 in pairs(v1) do
                if pts[v2] then
                    if abs(pts[v2][2]-pts[k1][2])<2*r then
                        if testcol(k1,v2,r) then
                            pts[v2]=nil
                        end
                    end
                end
            end
        end
    end
end

-- quickSort helper
local function partition(array, p, r)
    local x = array[r][1]
    local i = p - 1
    for j = p, r - 1 do
        if array[j][1] <= x then
            i = i + 1
            local temp = array[i][1]
            array[i][1] = array[j][1]
            array[j][1] = temp
        end
    end
    local temp = array[i + 1][1]
    array[i + 1][1] = array[r][1]
    array[r][1] = temp
    return i + 1
end

-- quickSort to sort by x
-- taken from https://github.com/akosma/CodeaSort/blob/master/QuickSort.lua
local function quickSort(array, p, r)
    p = p or 1
    r = r or #array
    if p < r then
        q = partition(array, p, r)
        quickSort(array, p, q - 1)
        quickSort(array, q + 1, r)
    end
end

-- draw output
local function showout(n,r,x,y)
    tex.print("\\begin{tikzpicture}[radius="..r.."cm]")
    tex.print("\\draw[red](0,0) rectangle ("..x..","..y..");")
    for k,v in pairs(pts) do
        tex.print("\\draw ("..pts[k][1]..","..pts[k][2]..") circle ;")
    end
    tex.print("\\end{tikzpicture}")
end

-- wrapper
function circles(n,r,x,y)
    genpts(n,x,y)
    quickSort(pts)
    getpairs(pts,r)
    delpairs(r)
    showout(n,r,x,y)
end


\end{luacode*}

\def\circles#1#2#3#4{\directlua{circles(#1,#2,#3,#4)}}

\begin{document}

\circles{500}{.1}{10}{5}

\end{document}

答案3

刚刚发现这个老问题,为了完整起见,我决定添加以下答案。

泊松圆盘采样似乎是创建此类模式的正确方法。这个问题/答案在纯 lualatex 中提供了该算法的实现,允许从 pgf/tikz 中使用它。

假设您有poisson.stypoisson.lua(您可以从提到的答案中获取),以及可以正常工作的安装lualatex,则解决方案将是:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{backgrounds}
\usepackage{poisson}

\begin{document}
\edef\mylist{\poissonpointslist{5}{5}{1}{20}}  
% 5 x 5 is the size of the area to fill
% 1 is the minimum distance between centers
\begin{tikzpicture}[framed,gridded,radius=0.5cm]
    \draw[red](0,0) rectangle (5,5);
    \foreach \x/\y in \mylist \draw (\x,\y) circle;
\end{tikzpicture}
\end{document}

结果(用 编译lualatex)是:

结果

答案4

我知道这是一个很老的问题,但我一直在寻找类似的东西。杰克的代码如下几乎对我来说是可行的,但我希望更精细地控制实际出现的圆圈数量。所以我修改了 Jake 的代码,如下所示(我还通过比较平方距离而不是绝对距离进行了轻微优化):

\newcommand{\fillrandomly}[4]{
    \xdef\xlist{4}
    \xdef\ylist{4}
    \pgfmathsetmacro\diametersqr{(#3*2)^2}
    \draw (0,0) rectangle (#1,#2);
    \foreach \i in {1,...,#4}{
        \foreach \k in {1,...,20}{
            \pgfmathsetmacro\x{rnd*#1}
            \pgfmathsetmacro\y{rnd*#2}
            \xdef\collision{0}
            \foreach \element [count=\i] in \xlist{
                \pgfmathtruncatemacro\j{\i-1}
                \pgfmathsetmacro\checkdistancesqr{ ( ({\xlist}[\j]-(\x))^2 + ({\ylist}[\j]-(\y))^2 ) }
                \ifdim\checkdistancesqr pt<\diametersqr pt
                    \xdef\collision{1}
                    \breakforeach
                \fi
            }
            \ifnum\collision=0
                \xdef\xlist{\xlist,\x}
                \xdef\ylist{\ylist,\y}
                \draw [red, thick] (\x,\y) circle [radius=#3];
                \breakforeach
            \fi 
        }
    }
}

这循环尝试放置n圆圈,对于每个圆圈,它会尝试放置最多 20 次,直到放弃。如果你有紧密排列的圆圈,情况就不会好多少,但对于相对松散排列的圆圈,你可以相当肯定地在相对较短的时间内获得所需数量的圆圈。

相关内容