一些优化(不一定更好)

一些优化(不一定更好)

我想知道是否存在一种方法可以在填充形状时模拟 TikZ 中的画笔笔触。因此,给出以下内容:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\tikz{
\draw[fill=red](0,0)rectangle(10,10);
}
\end{tikzpicture}
\end{document}

我希望让它看起来像是用画笔画上去的红色部分,而不是纯红色的形状。我不太确定它看起来会是什么样子,但我猜可能是一些随机的波浪线,颜色稍深一些,有些可能稍浅一些?我想人们会希望画笔有一定的厚度,这样你就能看到一个笔触和它旁边的笔触之间的区别。

通过在线查看实际画笔笔触的示例,看起来每条线上的明暗部分都略有不同,但这可能是一个不值得追求的复杂问题。

将所有笔画都放下来就可以了,但能够指示方向就很酷了。

虽然我确信在各种图像编辑程序中都可以实现这样的操作,但我需要在 TeX/LaTeX 中将其作为自动文件生成过程的一部分来执行。

编辑:我不认为这是黑板解决方案的重复,因为黑板解决方案似乎使用了一堆小点,而画笔则涉及不同深浅的根色长波浪线。结果看起来会非常不同。黑板问题可能提供了一种方法的想法,但按原样使用它并不能解决我的问题。

更新 1:我进行了一些实验,使用带有圆角的波浪形、随机线条的 TikZ 装饰,这似乎是一种进步的方式,但我还没有做出任何看起来接近令人信服的东西。

更新 2:如下所示,这是图片的链接笔触特写。这很夸张,但表达出了要点。以下是使用波浪随机线的尝试:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing}
\begin{document}
\begin{tikzpicture}
\tikz[line width=.1mm]{
\draw[fill=red](0,0)rectangle(10,10);
\draw[line width = .5mm,decorate,decoration={random steps,segment length=12pt,amplitude=1pt},rounded corners=.1pt, color=red!50!brown] (1.5,0) -- (1.5,10);
\draw[line width = .5mm,decorate,decoration={random steps,segment length=12pt,amplitude=1pt},rounded corners=.1pt, color=red!50!brown] (1.65,0) -- (1.65,10);
\draw[line width = .5mm,decorate,decoration={random steps,segment length=12pt,amplitude=1pt},rounded corners=.1pt, color=red!50!brown] (1.8,0) -- (1.8,10);
\draw[line width = .5mm,decorate,decoration={random steps,segment length=12pt,amplitude=1pt},rounded corners=.1pt, color=red!50!brown] (1.95,0) -- (1.95,10);
}
\end{tikzpicture}
\end{document}

正如你所见,它看起来很糟糕。

红色笔触尝试

更新 3:让我更详细地介绍一下我正在寻找的东西。我正在寻找一种可以填充大型几何形状的东西,例如正方形、矩形和圆形,尺寸从半页到几乎整页,并且只使用一种颜色。我有一个可以生成多种风格音乐的程序(我是一名作曲家),但我决定也在其中添加艺术作品。到目前为止,我坚持使用 20 世纪现代主义的东西,因为其中很多东西看起来更简单。我已经做了一个蒙德里安风格的(粗交叉线上的正方形和矩形)。启发我提出这个问题的是卡西米尔·马列维奇的作品,特别是他的黑色方块黑十字红方格还有一些类似风格的。我的软件会随机生成看起来相似但不完全相同的“绘画”(例如,黑色正方形大小不同但仍然很大,红色正方形使用不同的随机四边形尺寸,黑色圆圈具有不同的随机尺寸并随机放置,等等)。我曾以为,由于这些非常简单,我的用户会喜欢用模拟的笔触看起来更有趣一些。不幸的是,我没有任何具体的绘画/画家的例子,只是一些模糊的概念,如果你仔细观察,可以在这些画作上看到笔触(虽然我不知道你是否能看到马列维奇画作中的笔触)。但是,如果做得太微妙,用户在电脑或手机上看的时候可能会迷失方向,所以有点夸张可能会更好?我还想到,也许下面画布的凸起可能有助于产生幻觉?

答案1

这是一份基于以下内容的快速撰写的提案这个答案,它利用这个答案。我计划稍后改进它。(我真的不确定是否理解关闭问题的努力。黑板帖子在我看来是相关的,但这个问题在我看来不是重复的。)

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{decorations,arrows.meta,bending}
\begin{document}
\pgfdeclarearrow{
  name=ink,
  parameters= {\the\pgfarrowlength},  
  setup code={
   \pgfarrowssettipend{0pt}
   \pgfarrowssetlineend{-\pgfarrowlength}
   \pgfarrowlinewidth=\pgflinewidth
   \pgfarrowssavethe\pgfarrowlength
  },
  drawing code={
   \pgfpathmoveto{\pgfpoint{-\pgfarrowlength}{0.5\pgflinewidth}}
   \pgfpathcurveto{\pgfpoint{-0.75\pgfarrowlength}{0.6\pgflinewidth}}{%
   \pgfpoint{-0.01\pgfarrowlength}{0.6\pgflinewidth}}{%
   \pgfpoint{0pt}{0pt}}
   \pgfpathcurveto{\pgfpoint{-0.01\pgfarrowlength}{-0.5\pgflinewidth}}{%
   \pgfpoint{-0.2\pgfarrowlength}{-(1+0.3*rnd)*\pgflinewidth}}{%
   \pgfpoint{-0.3\pgfarrowlength}{-0.8*(1+0.3*rnd)*\pgflinewidth}}
   \pgfpathcurveto{\pgfpoint{-0.4\pgfarrowlength}{-0.6*(1+0.3*rnd)*\pgflinewidth}}{%
   \pgfpoint{-0.6\pgfarrowlength}{-0.3*(1+0.3*rnd)*\pgflinewidth}}{%
   \pgfpoint{-1\pgfarrowlength}{-0.5\pgflinewidth}}
   \pgfusepathqfill
  },
  defaults = { length = 12pt }
}
\pgfkeys{/pgf/decoration/.cd,
         start color/.store in=\startcolor,
         start color=black,
         end color/.store in=\endcolor,
         end color=black,
         varying line width steps/.initial=100
}
\pgfdeclaredecoration{width and color change}{initial}{
 \state{initial}[width=0pt, next state=line, persistent precomputation={%
   \pgfmathparse{\pgfdecoratedpathlength/\pgfkeysvalueof{/pgf/decoration/varying line width steps}}%
   \let\increment=\pgfmathresult%
   \def\x{0}%
 }]{}
 \state{line}[width=\increment pt,   persistent postcomputation={%
   \pgfmathsetmacro{\x}{\x+\increment}
   },next state=line]{%
   \pgfmathparse{ifthenelse(\x<\pgfdecoratedpathlength-5mm,varyinglw(100*(\x/\pgfdecoratedpathlength)),
    varyinglw(100*((\pgfdecoratedpathlength-5mm)/\pgfdecoratedpathlength))*(\pgfdecoratedpathlength-\x)/14) )}
   \pgfmathparse{varyinglw(100*(\x/\pgfdecoratedpathlength))} %<-changed
   \pgfsetlinewidth{\pgfmathresult pt}%
   \pgfpathmoveto{\pgfpointorigin}%
   \pgfmathsetmacro{\steplength}{1.4*\increment}
   \pgfpathlineto{\pgfqpoint{\steplength pt}{0pt}}%
   \pgfmathsetmacro{\y}{100*(\x/\pgfdecoratedpathlength)}
   \pgfsetstrokecolor{\endcolor!\y!\startcolor}%
   \pgfusepath{stroke}%
 }
 \state{final}{%
   \pgfsetlinewidth{\pgflinewidth}%
   \pgfpathmoveto{\pgfpointorigin}%
   \pgfmathsetmacro{\y}{100*(\x/\pgfdecoratedpathlength)}
   \color{\endcolor!\y!\startcolor}%
   \pgfusepath{stroke}% 
 }
}


\begin{tikzpicture}[varying arrow/.style={-{ink[length=5mm,width=3.2mm]},color=\endcolor,
postaction={/utils/exec=\pgfsetarrows{-},decorate,decoration={width and color change}}
}]
\begin{scope}[declare function={varyinglw(\x)=12+5*rnd;}]
\foreach \X in {0,...,5}
{\draw[%/pgf/decoration/start color=red,/pgf/decoration/end color=red,
decorate,decoration={width and color change,start color=red,end color=red}] 
(0,-\X*0.7-0.1+0.2*rnd) to[bend left=10-20*rnd] ++ (3,0);}
\end{scope}

\end{tikzpicture}
\end{document} 

在此处输入图片描述

更新:与您在更新中添加的链接下的图片方向有点关系。编译时间相当长,而且远不能令人满意。我发布此内容只是为了报告我去过的地方,希望其他人可能会发现其中的一些有用之处。

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{decorations,decorations.pathreplacing,calc,positioning}
\begin{document}

\pgfkeys{/brush pars/.cd,
lines/.initial=16,
color/.code={\colorlet{brushcolor}{#1}},
color=red,
distance/.initial=0.4pt
}
\tikzset{
    brush/.style={
        decorate,
        decoration={
            show path construction,
            lineto code={
                \foreach\Xbrush in{1,...,\pgfkeysvalueof{/brush pars/lines}}{
                 \pgfmathrandomitem{\c}{color}
                 \pgfmathtruncatemacro{\mix}{100-24*rnd}
                 \draw[color=brushcolor!\mix!\c,
                 shorten >={(3-4*rnd)*1pt
                    -0.5*\pgfkeysvalueof{/brush pars/lines}*\pgfkeysvalueof{/brush pars/distance}},
                 shorten <={(3-4*rnd)*1pt
                 -0.5*\pgfkeysvalueof{/brush pars/lines}*\pgfkeysvalueof{/brush pars/distance}}]
                 let \p1=($(\tikzinputsegmentlast)-(\tikzinputsegmentfirst)$),
                 \n1={90+atan2(\y1,\x1)} in
                 ($(\tikzinputsegmentfirst)+(\n1:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$)
                 --
                ($(\tikzinputsegmentlast)+(\n1:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$);
                }
            },
            curveto code={
                \foreach\Xbrush in{1,...,\pgfkeysvalueof{/brush pars/lines}}{
                 \pgfmathrandomitem{\c}{color}
                 \pgfmathtruncatemacro{\mix}{100-24*rnd}
                 \draw[color=brushcolor!\mix!\c,shorten >={(3-4*rnd)*1pt
                    -0.5*\pgfkeysvalueof{/brush pars/lines}*\pgfkeysvalueof{/brush pars/distance}},
                 shorten <={(3-4*rnd)*1pt
                    -0.5*\pgfkeysvalueof{/brush pars/lines}*\pgfkeysvalueof{/brush pars/distance}}]
                 let \p1=($(\tikzinputsegmentsupporta)-(\tikzinputsegmentfirst)$),
                 \p2=($(\tikzinputsegmentsupportb)-(\tikzinputsegmentsupporta)$),
                 \p3=($(\tikzinputsegmentlast)-(\tikzinputsegmentsupportb)$),
                 \n1={90+atan2(\y1,\x1)}, \n2={90+atan2(\y2,\x2)}, 
                 \n3={90+atan2(\y3,\x3)} in
                 ($(\tikzinputsegmentfirst)+(\n1:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$)
                 .. controls ($(\tikzinputsegmentsupporta)+(\n2:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$)
                  and ($(\tikzinputsegmentsupportb)+(\n3:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$) ..
                ($(\tikzinputsegmentlast)+(\n3:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$);
                }
            },
        }
    }
}
\tikzset{pics/.cd,
  A/.style={code={\draw[brush] 
  (0,-0.55) -- (0.3,0.4) -- (0.6,-0.55);
  \draw[brush](0.1,1/3-0.45) --
  (0.5,1/3-0.45);
  \path (0.7,0);}},
  B/.style={code={\draw[brush] (0,-0.45) -- (0,0.45)
  to[out=0,in=0,looseness=2.5]  (0,0)  to[out=0,in=0,looseness=3] cycle;}},
  C/.style={code={\draw[brush]
   (0,0) to[out=90,in=110,looseness=2]  (0.5,0.25);
   \draw[brush](0,0) to[out=-90,in=-110,looseness=2]  (0.5,-0.25);
   \path (0.7,0);}},
  D/.style={code={\draw[brush] (0,-0.45) -- (0,0.45) 
  to[out=0,in=0,looseness=2.25]   cycle;
  \path (0.7,0);}},
  E/.style={code={\draw[brush] 
  (0.5,-0.45) --(0,-0.45) -- (0,0.45)  -- (0.5,0.45);
  \draw[brush] (0,0) -- (0.5,0);
  \path (0.7,0);}},
  F/.style={code={\draw[brush] 
  (0,-0.45) -- (0,0.45)  -- (0.5,0.45);
  \draw[brush] (0,0) -- (0.5,0);
  \path (0.7,0);}},
  G/.style={code={\draw[brush]
   (0,0) to[out=90,in=110,looseness=2]  (0.5,0.25);
   \draw[brush] (0,0) to[out=-90,in=-110,looseness=2]  
   (0.5,-0.25);
   \draw[brush] (0.54,-0.25) to (0.3,-0.25);
   \path (0.7,0);}},
  H/.style={code={\draw[brush] 
  (0,-0.5) -- (0,0.5);  
  \draw[brush] (0.5,-0.5) -- (0.5,0.5);
  \draw[brush] (0,0) -- (0.5,0);
  \path (0.7,0);}},
  I/.style={code={\draw[brush] (0,-0.45) -- (0,0.45);
  \path (0.25,0);}},
  J/.style={code={\draw[brush] (0.2,0.45) -- (0.2,-0.35) to[out=-90,in=0]
  (0.1,-0.45) to[out=180,in=-90] (0,-0.35);
  \path (0.45,0);}},
  K/.style={code={\draw[brush] 
  (0,-0.45) -- (0,0.45); 
  \draw[brush] (0.4,0.45) -- (0.02,0) --  (0.4,-0.45);
  \path (0.6,0);}},
  L/.style={code={\draw[brush] 
  (0,0.5) -- (0,-0.45) -- (0.4,-0.45);
  \path (0.6,0);}},
  M/.style={code={\draw[brush] (0,-0.45) -- (0,0.45) -- 
  (0.3,0.25) -- (0.6,0.45) -- (0.6,-0.45);
  \path (0.8,0);}},
  N/.style={code={\draw[brush] (0,-0.45) -- (0,0.45) -- (0.6,-0.4) --
  (0.6,0.45);
  \path (0.8,0);}},
  O/.style={code={\draw[brush] (0.3,0) circle(0.3 and 0.48);
  \path (0.8,0);}},
  P/.style={code={\draw[brush] (0,-0.45) -- (0,0.45) 
  to[out=0,in=0,looseness=2.5]  (0,0);
  \path (0.6,0);}},
  Q/.style={code={\draw[brush] 
  (0.3,0) circle(0.3 and 0.48);
  \draw[brush](0.35,-0.25) -- (0.6,-0.45);
  \path (0.8,0);}},
  R/.style={code={\draw[brush] 
  (0,-0.45) -- (0,0.45) 
  to[out=0,in=0,looseness=2.5]  (0.05,0) -- (0.4,-0.45);
  \path (0.6,0);}},
  S/.style={code={\draw[brush] (0.5,0.4)  
  to[out=160,in=165,looseness=2]  (0.3,0)  
  to[out=-15,in=-20,looseness=2] (0.1,-0.4);
  \path (0.65,0);}},
  T/.style={code={\draw[brush] (0.35,-0.45) -- (0.35,0.45) (0,0.45) -- (0.7,0.45);
  \path (0.85,0);}},
  U/.style={code={\draw[brush] (0,0.5) -- (0,0) to[out=-90,in=-90,looseness=2.5]
  (0.6,0) -- (0.6,0.5);
  \path (0.8,0);}},
  V/.style={code={\draw[brush] (0,0.5) -- (0.3,-0.4) -- (0.6,0.5);
  \path (0.8,0);}},
  W/.style={code={\draw[brush] (0,0.45) -- (0.3,-0.4) -- (0.45,-0.1)
  -- (0.6,-0.4) -- (0.9,0.45);
  \path (1.1,0);}},
  X/.style={code={\draw[brush] 
  (0,0.45) -- (0.6,-0.45);
   \draw[brush] (0.6,0.45)
  -- (0,-0.45);
  \path (0.8,0);}},
  Y/.style={code={\draw[brush] 
  (0,0.45) -- (0.3,0);  
  \draw[brush] (0.6,0.45)
  -- (0,-0.45);
  \path (0.8,0);}},
  Z/.style={code={\draw[brush] (0,0.45) --(0.6,0.45) -- (0,-0.45)  
  -- (0.6,-0.45);
  \path (0.8,0);}},
  space/.style={code={\path (0,0) (0.2,0);}},
}
\pgfmathdeclarerandomlist{color}{{black}{white}}
\begin{tikzpicture}
 \pic[local bounding box=box1,scale=2] at (0,0) {A};
 \foreach \X [count=\Y,evaluate=\Y as \Z using {int(\Y+1)}] in {B,...,Z}
  {\edef\temp{\noexpand\pic[right=0mm of box\Y,local bounding box=box\Z,scale=2]
  {\X};}
  \temp}
\end{tikzpicture}
\end{document}

在此处输入图片描述

在此处输入图片描述

在我的计算机上编译完整字母表所需的时间:

real    0m11.438s
user    0m10.758s
sys 0m0.622s

这些信件取自这个答案并且写得非常快。(它们本来是要进入圣诞盛会但由于正当理由而未能实现。)

在我的计算机上,不到 4 分钟,你就能得到

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{decorations,decorations.pathreplacing,calc,positioning}
\pgfkeys{/brush pars/.cd,
lines/.initial=16,
color/.code={\colorlet{brushcolor}{#1}},
color=red,
distance/.initial=0.4pt
}
\tikzset{
    brush/.style={
        decorate,
        decoration={
            show path construction,
            lineto code={
                \foreach\Xbrush in{1,...,\pgfkeysvalueof{/brush pars/lines}}{
                 \pgfmathrandomitem{\c}{color}
                 \pgfmathtruncatemacro{\mix}{100-24*rnd}
                 \draw[color=brushcolor!\mix!\c,
                 shorten >={(3-4*rnd)*1pt
                    -0.5*\pgfkeysvalueof{/brush pars/lines}*\pgfkeysvalueof{/brush pars/distance}},
                 shorten <={(3-4*rnd)*1pt
                 -0.5*\pgfkeysvalueof{/brush pars/lines}*\pgfkeysvalueof{/brush pars/distance}}]
                 let \p1=($(\tikzinputsegmentlast)-(\tikzinputsegmentfirst)$),
                 \n1={90+atan2(\y1,\x1)} in
                 ($(\tikzinputsegmentfirst)+(\n1:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$)
                 --
                ($(\tikzinputsegmentlast)+(\n1:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$);
                }
            },
            curveto code={
                \foreach\Xbrush in{1,...,\pgfkeysvalueof{/brush pars/lines}}{
                 \pgfmathrandomitem{\c}{color}
                 \pgfmathtruncatemacro{\mix}{100-24*rnd}
                 \draw[color=brushcolor!\mix!\c,shorten >={(3-4*rnd)*1pt
                    -0.5*\pgfkeysvalueof{/brush pars/lines}*\pgfkeysvalueof{/brush pars/distance}},
                 shorten <={(3-4*rnd)*1pt
                    -0.5*\pgfkeysvalueof{/brush pars/lines}*\pgfkeysvalueof{/brush pars/distance}}]
                 let \p1=($(\tikzinputsegmentsupporta)-(\tikzinputsegmentfirst)$),
                 \p2=($(\tikzinputsegmentsupportb)-(\tikzinputsegmentsupporta)$),
                 \p3=($(\tikzinputsegmentlast)-(\tikzinputsegmentsupportb)$),
                 \n1={90+atan2(\y1,\x1)}, \n2={90+atan2(\y2,\x2)}, 
                 \n3={90+atan2(\y3,\x3)} in
                 ($(\tikzinputsegmentfirst)+(\n1:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$)
                 .. controls ($(\tikzinputsegmentsupporta)+(\n2:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$)
                  and ($(\tikzinputsegmentsupportb)+(\n3:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$) ..
                ($(\tikzinputsegmentlast)+(\n3:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$);
                }
            },
        }
    }
}
\pgfmathdeclarerandomlist{color}{{black}{white}}
\begin{document}
\begin{tikzpicture}
\draw[clip,postaction={fill=red}] (0,0) rectangle (10,10);
\foreach \X in {1,...,1000}
{\draw[brush] (-0.5+11*rnd,-0.5+11*rnd) to[bend left={30-60*rnd}]
++ (360*rnd:1+2*rnd);}

\end{tikzpicture}
\end{document}

在此处输入图片描述

如果你想让笔触更对齐,你可以尝试

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{decorations,decorations.pathreplacing,calc,positioning}
\pgfkeys{/brush pars/.cd,
lines/.initial=16,
color/.code={\colorlet{brushcolor}{#1}},
color=red,
distance/.initial=0.4pt
}
\tikzset{
    brush/.style={
        decorate,
        decoration={
            show path construction,
            lineto code={
                \foreach\Xbrush in{1,...,\pgfkeysvalueof{/brush pars/lines}}{
                 \pgfmathrandomitem{\c}{color}
                 \pgfmathtruncatemacro{\mix}{100-24*rnd}
                 \draw[color=brushcolor!\mix!\c,
                 shorten >={(3-4*rnd)*1pt
                    -0.5*\pgfkeysvalueof{/brush pars/lines}*\pgfkeysvalueof{/brush pars/distance}},
                 shorten <={(3-4*rnd)*1pt
                 -0.5*\pgfkeysvalueof{/brush pars/lines}*\pgfkeysvalueof{/brush pars/distance}}]
                 let \p1=($(\tikzinputsegmentlast)-(\tikzinputsegmentfirst)$),
                 \n1={90+atan2(\y1,\x1)} in
                 ($(\tikzinputsegmentfirst)+(\n1:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$)
                 --
                ($(\tikzinputsegmentlast)+(\n1:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$);
                }
            },
            curveto code={
                \foreach\Xbrush in{1,...,\pgfkeysvalueof{/brush pars/lines}}{
                 \pgfmathrandomitem{\c}{color}
                 \pgfmathtruncatemacro{\mix}{100-24*rnd}
                 \draw[color=brushcolor!\mix!\c,shorten >={(3-4*rnd)*1pt
                    -0.5*\pgfkeysvalueof{/brush pars/lines}*\pgfkeysvalueof{/brush pars/distance}},
                 shorten <={(3-4*rnd)*1pt
                    -0.5*\pgfkeysvalueof{/brush pars/lines}*\pgfkeysvalueof{/brush pars/distance}}]
                 let \p1=($(\tikzinputsegmentsupporta)-(\tikzinputsegmentfirst)$),
                 \p2=($(\tikzinputsegmentsupportb)-(\tikzinputsegmentsupporta)$),
                 \p3=($(\tikzinputsegmentlast)-(\tikzinputsegmentsupportb)$),
                 \n1={90+atan2(\y1,\x1)}, \n2={90+atan2(\y2,\x2)}, 
                 \n3={90+atan2(\y3,\x3)} in
                 ($(\tikzinputsegmentfirst)+(\n1:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$)
                 .. controls ($(\tikzinputsegmentsupporta)+(\n2:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$)
                  and ($(\tikzinputsegmentsupportb)+(\n3:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$) ..
                ($(\tikzinputsegmentlast)+(\n3:{((1.02-0.04*rnd)*\Xbrush-\pgfkeysvalueof{/brush pars/lines}/2)*\pgfkeysvalueof{/brush pars/distance}})$);
                }
            },
        }
    }
}
\pgfmathdeclarerandomlist{color}{{black}{white}}
\begin{document}
\begin{tikzpicture}[declare function={VF(\x,\y)=10*\x-4*\y+2*\x*\y;}]
\draw[clip,postaction={fill=red}] (0,0) rectangle (10,10);
\foreach \X in {1,...,1000}
{\pgfmathsetmacro{\myx}{-0.5+11*rnd}
\pgfmathsetmacro{\myy}{-0.5+11*rnd}
\draw[brush] (\myx,\myy) to[bend left={30-60*rnd}]
++ ({VF(\myx,\myy)+10-20*rnd}:1+2*rnd);}
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案2

这个需要 6 秒钟才能编译完成。它基于我之前对黑板纹理的回答。这个想法是为了dash pattern减少 for 循环。(图形卡代替 TeX 完成这项工作。)

\documentclass[border=9,tikz]{standalone}
\usetikzlibrary{decorations.pathreplacing}
\begin{document}

\def\niterate{128}
\def\rolldice{
    \pgfmathsetmacro\a{(1+rnd)/10}
    \pgfmathsetmacro\b{5+5*rnd}
    \pgfmathsetmacro\c{1+rnd}
    \pgfmathsetmacro\d{rnd*3}
    \pgfmathsetmacro\dark{rnd*50+50}
}
\tikzset{
    put dots/.style={
        /utils/exec=\rolldice,
        line width=\a,
        dash pattern=on \b off \c,
        dash phase=\c*rnd,
        shift={(rnd*360:\d pt)},
        line cap=round,
        black!\dark,
        opacity=.8
    },
    chalk/.style={
        decorate,
        decoration={
            show path construction,
            lineto code={
                \foreach\i in{1,...,\niterate}{
                    \draw[put dots]
                        (\tikzinputsegmentfirst)--(\tikzinputsegmentlast);
                }
            },
            curveto code={
                \foreach\i in{1,...,\niterate}{
                    \draw[put dots]
                        (\tikzinputsegmentfirst)..controls
                        (\tikzinputsegmentsupporta)and(\tikzinputsegmentsupportb)
                        ..(\tikzinputsegmentlast);
                }
            },
            closepath code={
                \foreach\i in{1,...,\niterate}{
                \draw[put dots]
                    (\tikzinputsegmentfirst)--(\tikzinputsegmentlast);
                }
            }
        }
    }
}

\tikz[looseness=0.25]{
    \path [chalk] (1/8,2) -- (0,1/2) arc (180:315:1/2) (-1/2,3/2)
        to [bend right] (5/8,3/2);
    \path [chalk, shift=(0:1)] (1/8,1) to [bend left] (0,0);
    \path [chalk, shift=(0:3/2)] (1/8,2) to [bend left] (0,0)
         (2/3,1) -- (1/16,2/3) -- (2/3,0);
    \path [chalk, shift=(0:5/2)] (0,1) to [bend left] (1,1)
         to [bend left] (0,0) to [bend left] (1,0);
    \path [chalk] (-1,-3/4) to [bend left] (9/2,-1/2);
}


\message{^^J^^J time = \the\dimexpr\pdfelapsedtime sp (pt means second) ^^J^^J}

\end{document}


一些优化(不一定更好)

  • 从较粗的曲线开始,曲线的偏移较少,颜色更稳定。
    填充更有效地使用画布。
  • 随着\i向上,曲线越来越细,随机性越来越强。
    这在底色之上创建了笔刷纹理。
  • 使用opacity < 1时使重叠曲线看起来像更多曲线。
  • 以下代码仅使用 50 条贝塞尔曲线替换一条贝塞尔曲线。
    编译需要 3 秒。

\documentclass[border=9,tikz]{standalone}
\usetikzlibrary{decorations.pathreplacing}
\begin{document}

\def\niterate{50}
\def\rolldice{
    \pgfmathsetmacro\rndlinewidth{6/(2+\i)}
    \pgfmathsetmacro\rndon{8+8*rnd}
    \pgfmathsetmacro\rndoff{2*rnd}
    \pgfmathsetmacro\rndshift{sqrt((1-\rndlinewidth/2)*5*rnd)}
    \pgfmathsetmacro\rndblend{50+\i*rand}
}
\tikzset{
    put dashes/.style={
        /utils/exec=\rolldice,
        line width=\rndlinewidth,
        dash pattern=on \rndon off \rndoff,
        dash phase=(\rndon+\rndoff)*rnd,
        shift={(rnd*360:\rndshift pt)},
        line cap=round,
        blue!\rndblend!green,
        opacity=.6
    },
    chalk/.style={
        decorate,
        decoration={
            show path construction,
            lineto code={
                \foreach\i in{1,...,\niterate}{
                    \draw[put dashes]
                        (\tikzinputsegmentfirst)--(\tikzinputsegmentlast);
                }
            },
            curveto code={
                \foreach\i in{1,...,\niterate}{
                    \draw[put dashes]
                        (\tikzinputsegmentfirst)..controls
                        (\tikzinputsegmentsupporta)and(\tikzinputsegmentsupportb)
                        ..(\tikzinputsegmentlast);
                }
            },
            closepath code={
                \foreach\i in{1,...,\niterate}{
                \draw[put dashes]
                    (\tikzinputsegmentfirst)--(\tikzinputsegmentlast);
                }
            }
        }
    }
}

\tikz[looseness=0.25]{
    \path [chalk] (1/8,2) -- (0,1/2) arc (180:315:1/2) (-1/2,3/2)
        to [bend right] (5/8,3/2);
    \path [chalk, shift=(0:1)] (1/8,1) to [bend left] (0,0);
    \path [chalk, shift=(0:3/2)] (1/8,2) to [bend left] (0,0)
         (2/3,1) -- (1/16,2/3) -- (2/3,0);
    \path [chalk, shift=(0:5/2)] (0,1) to [bend left] (1,1)
         to [bend left] (0,0) to [bend left] (1,0);
    \path [chalk] (-1,-3/4) to [bend left] (9/2,-1/2);
}


\message{^^J^^J time = \the\numexpr\pdfelapsedtime*1000/65536 ms ^^J^^J}

\end{document}

答案3

我终于有时间完成对这个问题的回答了。这花了一段时间,考虑到这个问题给我带来的名声,我对此感到有点内疚。无论如何,这是“最终”版本(目前):它比上一个版本更快,更可定制。通过使用宏\pgfpath…而不是\draw避免,节省了大量时间\pgfmath,这真的很慢。如果你想看旧版本,你可以看看我之前的编辑

填充正方形

由于您特别想用笔触填充某个区域,这里有一种方法可以做到这一点。以下代码在我的计算机上执行大约需要 11 秒:

\documentclass[tikz,margin=10pt]{standalone}
\usetikzlibrary{decorations.pathreplacing}

\makeatletter %% <- make @ usable in macro names
\pgfkeys{/pgf/decoration/brush/.cd,
         thickness/.initial      = 10pt,     %% <- total brush stroke width
         hair separation/.initial= .3pt,     %% <- avg. distance between hairs on the brush
         hair thickness/.initial = .4pt,     %% <- min. thickness of the individual hairs
         hair amplitude/.initial =.25pt,     %% <- amplitude of hair thickness oscillation
         min period/.initial     = 9pt,      %% <- min. value for the period of both oscillations
         max period/.initial     = 18pt,     %% <- max. value for the period of both oscillations
         period/.style           = {min period=#1,max period=#1},
         max overshoot/.initial  = 3pt,      %% <- max. distance hairs can overshoot at the end
         color 1/.initial        = red!90!black, %% <- primary colour
         color 2/.initial        = br@color1!80!black, %% <- secondary colour (slightly darker by default)
         color/.style            = {color 1=#1,color 2=br@color1!80!black}, %% color
         hair color/.initial     = black,    %% <- only used internally
         hair offset/.initial    = 0pt,      %% <- only used internally
}

%% Some fixed-point arithmetic operations using lengths
%% (N.B. both input and output are dimension registers but should be thought of as numbers)
\newcommand*\fpdivide[2]{%
  \dimexpr\numexpr #1*65536/#2\relax sp\relax
}

%% Human readable names for the dimensions used in \qsplitbezier:
\def\br@bezFrstAx {\dimen0} \def\br@bezFrstBx{ \dimen2} \def\br@bezFrstCx{\dimen4}
\def\br@bezFrstAy {\dimen6} \def\br@bezFrstBy {\dimen8} \def\br@bezFrstCy{\dimen10}
\def\br@bezScndAx{\dimen12} \def\br@bezScndBx{\dimen14} \def\br@bezThrdx {\dimen16}
\def\br@bezScndAy{\dimen18} \def\br@bezScndBy{\dimen20} \def\br@bezThrdy {\dimen22}
\newif\iffirstcomponent
%% Split up a Bézier curve with control points #2, #3, #4 and #5 at #1:
%%   (#1 is normally a parametric length between 0 and 1, but extrapolation is also possible)
\newcommand*\qsplitbezier[5]{\begingroup\edef\x{\endgroup\noexpand\qsplitbezier@{#1}#2#3#4#5\noexpand\qsplitbezier@}\x}
\def\qsplitbezier@#1(#2,#3)(#4,#5)(#6,#7)(#8,#9)\qsplitbezier@{%
  \begingroup
    \edef\s{#1}%
    %% Allow extrapolation but prevent numerical overflows:
    \ifdim\s pt>9pt \def\s{9}\fi
    \ifdim\s pt<-8pt \def\s{-8}\fi
    \edef\t{\strip@pt\dimexpr 1pt-\s pt}%
    %% Linear curves:
    \br@bezFrstAx=\dimexpr\t\dimexpr#2\relax+\s\dimexpr#4\relax
    \br@bezFrstAy=\dimexpr\t\dimexpr#3\relax+\s\dimexpr#5\relax
    \br@bezFrstBx=\dimexpr\t\dimexpr#4\relax+\s\dimexpr#6\relax
    \br@bezFrstBy=\dimexpr\t\dimexpr#5\relax+\s\dimexpr#7\relax
    \br@bezFrstCx=\dimexpr\t\dimexpr#6\relax+\s\dimexpr#8\relax
    \br@bezFrstCy=\dimexpr\t\dimexpr#7\relax+\s\dimexpr#9\relax
    %% Quadratic curves:
    \br@bezScndAx=\dimexpr\t\br@bezFrstAx+\s\br@bezFrstBx\relax
    \br@bezScndAy=\dimexpr\t\br@bezFrstAy+\s\br@bezFrstBy\relax
    \br@bezScndBx=\dimexpr\t\br@bezFrstBx+\s\br@bezFrstCx\relax
    \br@bezScndBy=\dimexpr\t\br@bezFrstBy+\s\br@bezFrstCy\relax
    %% Cubic curve:
    \br@bezThrdx=\dimexpr\t\br@bezScndAx+\s\br@bezScndBx\relax
    \br@bezThrdy=\dimexpr\t\br@bezScndAy+\s\br@bezScndBy\relax
    %% Store output in macros:
    \edef\x{\endgroup %% <-- perform assignments outside the group
      \def\noexpand\bezOneStart{#2,#3}%
      \def\noexpand\bezOneControlA{\the\br@bezFrstAx,\the\br@bezFrstAy}%
      \def\noexpand\bezOneControlB{\the\br@bezScndAx,\the\br@bezScndAy}%
      \def\noexpand\bezOneEnd{\the\br@bezThrdx,\the\br@bezThrdy}%
      \def\noexpand\bezTwoStart{\the\br@bezThrdx,\the\br@bezThrdy}%
      \def\noexpand\bezTwoControlA{\the\br@bezScndBx,\the\br@bezScndBy}%
      \def\noexpand\bezTwoControlB{\the\br@bezFrstCx,\the\br@bezFrstCy}%
      \def\noexpand\bezTwoEnd{#8,#9}%
    }\x
}
%% Split up straight lines (so we can turn them into Bézier curves)
\newcommand*\splitstraighttwice[4]{\begingroup\edef\x{\endgroup\noexpand\splitstraight@{#1}#2#3\noexpand#4\noexpand\splitstraight@}\x}
\def\splitstraight@#1(#2,#3)(#4,#5)#6\splitstraight@{%
  \begingroup
    \pgfmathsetmacro\t{#1}%
    \pgfpointlineattime{\t}{\pgfpoint{#2}{#3}}{\pgfpoint{#4}{#5}}%
    \edef#6{\the\pgf@x,\the\pgf@y}%
    \pgfmath@smuggleone#6%
   \endgroup
}
%% Orthogonal translation of the endpoints of a Bézier curve
\newcommand*\shiftbezier[6]{%
  \begingroup\edef\x{\endgroup
    %% Translate starting point
    \unexpanded{\shiftbezier@{\dimexpr#1\relax}}#3#4\unexpanded{\bezOneStart\bezOneControlA\shiftbezier@}%
    %% Translate end point
    \unexpanded{\shiftbezier@{\dimexpr#2\relax}}#5#6\unexpanded{\bezOneControlB\bezOneEnd\shiftbezier@}%
  }\x
}
\def\shiftbezier@#1(#2,#3)(#4,#5)#6#7\shiftbezier@{%
  %% This method is faster than \pgfpointnormalise + \pgfpointscale
  \begingroup
    %% Determine the angle with the positive x-axis:
    \@nameuse{pgfmathatan2@}{\strip@pt\dimexpr#5-#3\relax}{\strip@pt\dimexpr#4-#2\relax}%
    %% Construct a vector of length #1 in the same direction:
    \let\pgf@tmp\pgfmathresult
    \pgfmathcos@{\pgf@tmp}%
    \pgf@x=\pgfmathresult\dimexpr#1\relax
    \pgfmathsin@{\pgf@tmp}%
    \pgf@y=\pgfmathresult\dimexpr#1\relax
    %% Add a 90 degree rotated version of it to (#2,#3) and (#4,#5) and store in #6 resp. #7:
  \edef\x{\endgroup %% <-- perform assignments outside the group
    \def\noexpand#6{\the\dimexpr#2-\pgf@y,\the\dimexpr#3+\pgf@x}%
    \def\noexpand#7{\the\dimexpr#4-\pgf@y,\the\dimexpr#5+\pgf@x}%
  }\x
}

%% The brush hair decoration code, separated to avoid code duplication
\newcommand*\br@haircurvetocode{%
  %%%%%%%%%%%%
  %% Setup: %%
  %%%%%%%%%%%%
  \color{\pgfkeysvalueof{/pgf/decoration/brush/hair color}}
  \pgfsys@setlinewidth{\br@hairwidth}
  \edef\br@hairoffset{\pgfkeysvalueof{/pgf/decoration/brush/hair offset}}
  \pgfmathrandom{2}
  \edef\br@hairamplitude{\the\dimexpr\br@amplitude*(\pgfmathresult*2-3)}
  \edef\br@period@var{\the\dimexpr\br@period@max-\br@period@min}

  \ifdim\pgfdecoratedcompleteddistance<1pt %% <-- start of curve?
    %% Set the length of the first segment:
    \pgfmathrnd
    \edef\br@segmlength{\the\dimexpr\br@period@min+\pgfmathresult\dimexpr\br@period@var}
    %% Use a random initial phase for the thickness oscillation:
    \pgfmathrnd
    \edef\br@segmoffset{\the\dimexpr\pgfmathresult\dimexpr\br@segmlength}
    %% Introcude a random overshoot at the start:
    \pgfmathrnd
    \edef\br@extension@pre{\the\dimexpr\pgfmathresult\dimexpr\br@overshoot}
  \else                                    %% <-- not start of curve?
    %% Set appropriate values for non-initial segments:
    \let\br@segmoffset\br@segmoffset@stored
    \let\br@segmlength\br@segmlength@stored
    \let\br@hairamplitude\br@hairamplitude@stored
    \def\br@extension@pre{0pt}
  \fi
  \ifdim\dimexpr\pgfdecoratedremainingdistance-\pgfdecoratedinputsegmentlength<1pt %% <-- end of segment?
    %% Introduce a random overshoot at the end:
    \pgfmathrnd
    \edef\br@extension@post{\the\dimexpr\pgfmathresult\dimexpr\br@overshoot}
  \else
    \def\br@extension@post{0pt}
  \fi

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %% Extrapolate by \br@segmoffset at the start: %%
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  %% Make the first subsegment long enough to fit half a period:
  \edef\br@placetosplit{\strip@pt\fpdivide{-\dimexpr\br@segmoffset\relax}{\dimexpr\pgfdecoratedinputsegmentlength\relax}}
  \qsplitbezier{\br@placetosplit} {(\tikzinputsegmentfirst)}    {(\tikzinputsegmentsupporta)}
                                  {(\tikzinputsegmentsupportb)} {(\tikzinputsegmentlast)}
  %% Adjust the remaining length:
  \edef\br@remaininglength{\the\dimexpr\pgfdecoratedinputsegmentlength+\br@segmoffset}
  %% Then reduce \br@segmoffset so that slightly less will be cut off later:
  \ifdim\br@extension@pre=0pt\else
    \edef\br@segmoffset{\the\dimexpr\br@segmoffset-\br@extension@pre}
  \fi

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %% Loop until we've drawn the entire segment %%
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  \loop
    %% Split up the Bézier curve to isolate the first subsegment:
    \edef\br@placetosplit{\strip@pt\fpdivide{\dimexpr\br@segmlength\relax}{\dimexpr\br@remaininglength\relax}}
    \qsplitbezier{\br@placetosplit} {(\bezTwoStart)}    {(\bezTwoControlA)}
                                    {(\bezTwoControlB)} {(\bezTwoEnd)}
    %% Draw the central part of the hair:
    \br@haircurvetocode@{\br@hairoffset}{\br@hairoffset}
    %% Draw the oscillating part of the hair:
    \edef\br@hairoffset@first{\the\dimexpr\br@hairoffset+\br@hairamplitude}
    \edef\br@hairoffset@second{\the\dimexpr\br@hairoffset-\br@hairamplitude}
    \br@haircurvetocode@{\br@hairoffset@first}{\br@hairoffset@second}

  %% Test if the loop should be continued:
  \ifdim\br@remaininglength>\br@segmlength
    %% Adjust the remaining length:
    \edef\br@remaininglength{\the\dimexpr\br@remaininglength-\br@segmlength}
    %% Ensure that the next subsegment starts from the beginning:
    \def\br@segmoffset{0pt}
    %% Flip the hair amplitude:
    \edef\br@hairamplitude{\the\dimexpr-\br@hairamplitude}
    %% Set the length of the next subsegment: (maybe a little gratuitous?)
    \pgfmathrnd
    \edef\br@segmlength{\the\dimexpr\pgfmathresult\dimexpr\br@period@var\relax+\br@period@min}
  %% And repeat:
  \repeat
  %% Store values to be used  by the next subsegment:
  \global\let\br@segmoffset@stored\br@remaininglength
  \global\let\br@segmlength@stored\br@segmlength
  \global\let\br@hairamplitude@stored\br@hairamplitude
}

%% Separated the code that performs draws the segments to avoid code duplication:
\newcommand*\br@haircurvetocode@[2]{
  \begingroup
    %% Translate the curve's endpoints by #1 at one end and by #2 on the other:
    \shiftbezier{#1}{#2} {(\bezOneStart)} {(\bezOneControlA)} {(\bezOneControlB)} {(\bezOneEnd)}
    %% Throw away a bit at the start if this is the first segment:
    \ifdim\br@segmoffset=0pt\else
      \edef\br@placetosplit{\strip@pt\fpdivide{\dimexpr\br@segmoffset\relax}{\dimexpr\br@segmlength\relax}}
      \qsplitbezier{\br@placetosplit} {(\bezOneStart)}    {(\bezOneControlA)}
                                      {(\bezOneControlB)} {(\bezOneEnd)}
      \let\bezOneStart\bezTwoStart
      \let\bezOneEnd\bezTwoEnd
      \let\bezOneControlA\bezTwoControlA
      \let\bezOneControlB\bezTwoControlB
      \edef\br@segmlength{\the\dimexpr\br@segmlength-\br@segmoffset}
      \edef\br@remaininglength{\the\dimexpr\br@remaininglength-\br@segmoffset}
    \fi
    %% Throw away a bit at the end if this is the last segment:
    \ifdim\br@segmlength>\br@remaininglength
      \edef\br@placetosplit{\strip@pt\fpdivide{\dimexpr\br@remaininglength+\br@extension@post\relax}{\dimexpr\br@segmlength\relax}}
      \qsplitbezier{\br@placetosplit} {(\bezOneStart)}    {(\bezOneControlA)}
                                      {(\bezOneControlB)} {(\bezOneEnd)}
    \fi
    %% Draw the subsegment:
    \pgfpathmoveto{\br@pairtopgfpoint{\bezOneStart}}
    \pgfpathcurveto{\br@pairtopgfpoint{\bezOneControlA}}
                   {\br@pairtopgfpoint{\bezOneControlB}}
                   {\br@pairtopgfpoint{\bezOneEnd}}
    \pgfsetroundcap
    \pgfusepathqstroke
  \endgroup
}
\def\br@pairtopgfpoint#1{\expandafter\br@pairtopgfpoint@#1\br@pairtopgfpoint@}
\def\br@pairtopgfpoint@#1,#2\br@pairtopgfpoint@{\pgfpoint{#1}{#2}}

%% Define the brush and brush hair styles
\tikzset{
  brush hair@internal/.style={
    decorate,
    decoration={
      show path construction,
      curveto code={
        \br@haircurvetocode
      },
      lineto code={
        %% Turn this straight line into a Bézier curves and draw those
        \splitstraighttwice{0.333333}{(\tikzinputsegmentfirst)}{(\tikzinputsegmentlast)}\tikzinputsegmentsupporta
        \splitstraighttwice{0.666667}{(\tikzinputsegmentfirst)}{(\tikzinputsegmentlast)}\tikzinputsegmentsupportb
        \br@haircurvetocode
      },
      closepath code={
        \ifdim\pgfdecoratedremainingdistance<1pt\else %% <-- don't do anything if there is no distance to cover
          %% Turn this straight line into a Bézier curve and draw that
          \splitstraighttwice{0.333333}{(\tikzinputsegmentfirst)}{(\tikzinputsegmentlast)}\tikzinputsegmentsupporta
          \splitstraighttwice{0.666667}{(\tikzinputsegmentfirst)}{(\tikzinputsegmentlast)}\tikzinputsegmentsupportb
          \br@haircurvetocode
        \fi
      }
    }
  },
  brush/.code={
    %% Retrieve key values:
    \pgfqkeys{/pgf/decoration/brush}{#1}
    \colorlet{br@color1}{\pgfkeysvalueof{/pgf/decoration/brush/color 1}}
    \colorlet{br@color2}{\pgfkeysvalueof{/pgf/decoration/brush/color 2}}
    \pgfmathsetlength{\@tempdima}{\pgfkeysvalueof{/pgf/decoration/brush/hair separation}}
    \pgfmathsetcount{\@tempcnta}{\pgfkeysvalueof{/pgf/decoration/brush/thickness}/\the\@tempdima}
    \pgfmathsetlengthmacro{\br@amplitude}{\pgfkeysvalueof{/pgf/decoration/brush/hair amplitude}}
    \pgfmathsetlengthmacro{\br@period@min}{\pgfkeysvalueof{/pgf/decoration/brush/min period}}
    \pgfmathsetlengthmacro{\br@period@max}{\pgfkeysvalueof{/pgf/decoration/brush/max period}}
    \pgfmathsetlengthmacro{\br@overshoot}{\pgfkeysvalueof{/pgf/decoration/brush/max overshoot}}
    \pgfmathsetlengthmacro{\br@hairwidth}{\pgfkeysvalueof{/pgf/decoration/brush/hair thickness}}
    %% Draw brush stroke:
    \loop
      %% Randomise colour mixing:
      \pgfmathrandom{1,100}
      \begingroup\edef\x{\endgroup
        \noexpand\tikzset{postaction={
          brush hair@internal,
          /pgf/decoration/brush/hair color=br@color1!\pgfmathresult!br@color2,
          /pgf/decoration/brush/hair offset=\the\dimexpr.5\@tempdima*\@tempcnta},
        }
      }\x
      %% Abort after a central hair is drawn:
      \ifnum\@tempcnta=0
        \@tempcnta=-1
      \fi
      %% Decrement @\tempcnta every other iteration:
      \ifdim\@tempdima>0pt\else
        \advance\@tempcnta by -2
      \fi
      %% Flip the sign of the offset:
      \@tempdima=-\@tempdima
    \ifnum\@tempcnta>-1\repeat
  }
}
\makeatother

\begin{document}
%% Set up counter for timing purposes:
\newcount\lastpdfelapsedtime
\lastpdfelapsedtime=\pdfelapsedtime

\begin{tikzpicture}
  %% Clipping:
  \clip (-4.5,-4.5) rectangle (4.5,4.5);
  %% Background:
  \path[brush={color=red!90!black,
               thickness=10cm,
               hair amplitude=2.5pt,
               min period=90pt,
               max period=180pt,
               hair thickness=4.5pt,
               hair separation=3pt,
                 max overshoot=0pt,
              }] (-5.5,0) to[out=10,in=190,looseness=1] (5.5,0);
  %% Some individual strokes:
  \def\numbrushstrokes{12}
  \foreach \i in {1,...,\numbrushstrokes} {
  \typeout{\i/\numbrushstrokes} %% <- progress meter
    \pgfmathsetmacro{\outangle}{rand*30}
    \pgfmathsetmacro{\inangle}{180+rand*30}
    \pgfmathsetmacro{\curveycentre}{rand*5}
    \pgfmathsetmacro{\curveyvariation}{rand/2}
    \pgfmathsetmacro{\colormixing}{75+rnd*20}
    \pgfmathsetmacro{\blf}{rand*10}
    \pgfmathsetmacro{\curvecentre}{rand*5}
    \pgfmathsetmacro{\curvelength}{2+rnd*3}
    \path[brush={color=red!\colormixing!black,
                 thickness=2cm,
                 hair amplitude=2.5pt,
                 min period=90pt,
                 max period=180pt,
                 hair thickness=4pt,
                 hair separation=3pt,
                 max overshoot=30pt,
                }] (\curvecentre-\curvelength,\curveycentre-\curveyvariation) to[out=\outangle,in=\inangle,looseness=1] (\curvecentre+\curvelength,\curveycentre+\curveyvariation);
  }
\end{tikzpicture}

\message{Elapsed time: \the\numexpr(\pdfelapsedtime-\lastpdfelapsedtime)*1000/65536\relax\space ms.}
\end{document}

我猜这看起来有点像油漆?

序言是真的因为brush我定义和随后使用的装饰相当复杂,所以我会花很长时间。我会在这篇文章的末尾再多说一点。

注意:由于它是随机生成的,所以每次的结果看起来并不那么好。


我的原创绘画

我最初画了一个火柴人,所以我会再画一次。我无法将序言包含在本文档中,因为答案的字符限制为 30,000,所以您需要从上面复制序言。下图大约需要 5 秒钟才能制作完成。

\documentclass[tikz,margin=10pt]{standalone}
\includetikzlibrary{decorations.pathreplacing}

<Insert long preamble from before>

\begin{document}
%% Set up counter for timing purposes:
\newcount\lastpdfelapsedtime
\lastpdfelapsedtime=\pdfelapsedtime

\begin{tikzpicture}
  %% The legs: (yellow)
  \path[brush={color 1=yellow!95!red,  %% <- yellow
              }](0,-2) -- (1,0) -- (2,-2);
  %% The arms: (different orange hues)
  \path[bend right=20,
        brush={color 1=orange!70!yellow,         %% <- orange
               color 2=orange!70!red!95!black, %% <- transitioning to reddish orange
              }] (-1,2.5) to (3,2.5);
  %% The head: (red, thinner and undershoots)
  \path[brush={color=red!90!black,     %% <- darkish red
               thickness=6.7pt,        %% <- make the circle thinner
               max overshoot=-1.5mm,   %% <- negative overshoot = undershoot
              }] (1,4) circle[radius=1];
  %% The body: (green with some blue)
  \path[brush={color 1=green!80!black, %% <- darkish green
               color 2=green!70!blue!80!black, %% <- with a little blue mixed in
              }] (1,0) to[out=80,in=260,looseness=1] (1,3);
\end{tikzpicture}

\message{Elapsed time: \the\numexpr(\pdfelapsedtime-\lastpdfelapsedtime)*1000/65536\relax\space ms.}
\end{document}

真是杰作!


装饰brush

上述文档序言的唯一目的是定义brush装饰。这种装饰用一整组略微偏移的平行曲线代替一条曲线,这些曲线的厚度随点而变化。基本语法如下:

\path[<path options>,brush={color=<color>}] <path specification>;

其中<path options>可以是 之类的东西bend right=20<color>是一种颜色,<path specification>可以是(0,0) -- (1,0)。这将产生一个笔触,它由许多单独的笔触组成,这些笔触的颜色介于 和 之间<color><color>!80!black相同颜色的稍暗版本)。应该注意的是,有拐角的路径看起来不太好。

还可以指定两种颜色,如下所示:

\path[<path options>,brush={color 1=<color>, color 2=<color>}] <path specification>;

除了颜色之外,还可以提供其他一些选项:

thickness= 整个笔触的厚度
hair thickness= 每根毛发的最小厚度
hair separation= 每根毛发之间的距离
hair amplitude= 毛发厚度可以变化的量
min period= 连续的细/粗部分之间的最小距离
max period= 连续的细/粗部分之间的最大距离
max overshoot= 毛发可以超出原始曲线末端的最大量

要绘制的头发数量是根据total thickness笔触的和计算得出的hair separation。有一些限制:和hair separationhair amplitude应该小于,hair thickness以防止出现间隙。

thickness您可以通过设置来查看单个刷毛的外观0pt

\documentclass[tikz,margin=10pt]{standalone}
\includetikzlibrary{decorations.pathreplacing}

<insert long preamble from above again>

\begin{document}
\begin{tikzpicture}
  \path[brush={color=blue,thickness=0pt}] (0,0) -- (2,0);
\end{tikzpicture}
\end{document}

一根头发

它实际上由两条曲线组成:一条直线和另一条绕直线旋转的曲线。为了创建它,我不得不编写一些代码来分割任意贝塞尔曲线。我认为这部分代码本身也很有用,但目前还没有其他应用程序。


奖金

这是我在调整一些数字时意外制作的豆茎的图片。没人要求这样做,但我不能就这样把它扔掉……

\documentclass[tikz,margin=10pt]{standalone}

<insert long preamble here>

\begin{document}
\begin{tikzpicture}
  \path[brush={color 1=green!80!black,
               color 2=green!70!blue!80!black,
               hair amplitude=2.5pt,
              }] (1,0) to[out=80,in=260,looseness=1] (1,3);
\end{tikzpicture}
\end{document}

我不知道这会导致什么结果……!

相关内容