我想知道是否存在一种方法可以在填充形状时模拟 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 separation
都hair 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}