我正在尝试在 Metapost 中创建一个齿轮,并希望为其填充颜色。我想出了以下代码:
vardef sin(expr xx) = sind(xx) enddef;
vardef cos(expr xx) = cosd(xx) enddef;
beginfig(1)
inc := 15;
inner_radius := 1.0cm;
outer_radius := 1.3cm;
for ang = 0 step 2 * inc until 360:
draw
(inner_radius * sin(ang), inner_radius * cos(ang))..
(inner_radius * sin(ang + inc), inner_radius * cos(ang + inc))--
(outer_radius * sin(ang + inc), outer_radius * cos(ang + inc))--
(outer_radius * sin(ang + 2 * inc), outer_radius * cos(ang + 2 * inc))--
(inner_radius * sin(ang + 2 * inc), inner_radius * cos(ang + 2 * inc));
endfor;
endfig;
哪个产生了我喜欢的图形,但是有没有简单的方法来调整代码以填充颜色?
答案1
该fill
语句需要循环路径参数;使用cycle
关键字关闭路径。您可以构造路径,将其存储在变量中,然后填充它:
path p;
p := for ang = 0 step 2*inc until 360 - epsilon:
(inner_radius * sin(ang), inner_radius * cos(ang)) --
(inner_radius * sin(ang + inc), inner_radius * cos(ang + inc)) --
(outer_radius * sin(ang + inc), outer_radius * cos(ang + inc)) --
(outer_radius * sin(ang + 2 * inc), outer_radius * cos(ang + 2 * inc)) --
(inner_radius * sin(ang + 2 * inc), inner_radius * cos(ang + 2 * inc)) --
endfor
cycle;
fill p withcolor 0.7white;
另一种选择是“内联”定义要填充的路径,即将循环for
直接放在fill
关键字后面:
\documentclass[border=2mm]{standalone}
\usepackage{luamplib}
\begin{document}
\begin{mplibcode}
vardef sin(expr xx) = sind(xx) enddef;
vardef cos(expr xx) = cosd(xx) enddef;
beginfig(1);
inc := 15;
inner_radius := 1.0cm;
outer_radius := 1.3cm;
fill
for ang = 0 step 2*inc until 360 - epsilon:
(inner_radius * sin(ang), inner_radius * cos(ang)) --
(inner_radius * sin(ang + inc), inner_radius * cos(ang + inc)) --
(outer_radius * sin(ang + inc), outer_radius * cos(ang + inc)) --
(outer_radius * sin(ang + 2 * inc), outer_radius * cos(ang + 2 * inc)) --
(inner_radius * sin(ang + 2 * inc), inner_radius * cos(ang + 2 * inc)) --
endfor
cycle withcolor 0.7white;
endfig;
\end{mplibcode}
\end{document}
注意:虽然这不会改变任何填充,但我epsilon
从 360 中减去,因为这样更干净(原因在Thruston 的回答,+1)epsilon
为1/65536,即MetaPost可以处理的最小正数。
答案2
让我们分阶段进行。
要制作可以填充的东西,您需要一条封闭的路径。绘制一系列独立线段的方法需要调整。因此,让我们更改代码以制作单个path
,一次构建一个“牙齿”:
path cog;
for ang = 0 step 2 * inc until 360:
cog := if known cog: cog -- fi
(inner_radius * sin(ang), inner_radius * cos(ang))..
(inner_radius * sin(ang + inc), inner_radius * cos(ang + inc))--
(outer_radius * sin(ang + inc), outer_radius * cos(ang + inc))--
(outer_radius * sin(ang + 2 * inc), outer_radius * cos(ang + 2 * inc))--
(inner_radius * sin(ang + 2 * inc), inner_radius * cos(ang + 2 * inc));
endfor
draw cog;
这会产生相同的图形——巧妙之处在于循环的第一行。请注意,您使用:=
来更新变量。在 MP 中,a=b
表示a
等于b
;但表示将a := b
的值赋给。因此,在这里,您每次循环时都会更新路径变量。实际上,您每次都通过执行来扩展路径。只是这在第一次不会起作用,因此您需要使用技巧来保护第一个赋值。循环第一次没有值,所以它不是,所以第一个赋值将符合要求。请注意,在行中展开。b
a
cog
cog := cog -- tooth
if known
cog
known
cog := tooth
if .. fi
现在你有了一条路径cog
。但为了得到fill
它,你需要关闭它,所以你需要在循环之后再执行一步: cog := cog .. cycle;
像这样
path cog;
for ang = 0 step 2 * inc until 360:
cog := if known cog: cog -- fi
(inner_radius * sin(ang), inner_radius * cos(ang))..
(inner_radius * sin(ang + inc), inner_radius * cos(ang + inc))--
(outer_radius * sin(ang + inc), outer_radius * cos(ang + inc))--
(outer_radius * sin(ang + 2 * inc), outer_radius * cos(ang + 2 * inc))--
(inner_radius * sin(ang + 2 * inc), inner_radius * cos(ang + 2 * inc));
endfor
cog := cog .. cycle;
fill cog withcolor 3/4[blue, white];
draw cog;
从而产生了这个
哎呀!循环中多了一个步骤,因为你已经从 0 转到 360,所以有一个多余的齿,并且& cycle
已经循环回来了。请注意,这对 来说没有区别fill
,但对 来说看起来很糟糕。这很容易修复。只需从 360 中draw
减去一小部分,这样我们就可以提前停止:eps
path cog;
for ang = 0 step 2 * inc until 360 - eps:
cog := if known cog: cog -- fi
(inner_radius * sin(ang), inner_radius * cos(ang))..
(inner_radius * sin(ang + inc), inner_radius * cos(ang + inc))--
(outer_radius * sin(ang + inc), outer_radius * cos(ang + inc))--
(outer_radius * sin(ang + 2 * inc), outer_radius * cos(ang + 2 * inc))--
(inner_radius * sin(ang + 2 * inc), inner_radius * cos(ang + 2 * inc));
endfor
cog := cog .. cycle;
fill cog withcolor 3/4[blue, white];
draw cog;
完成了吗?其实还没完成,因为cog := cog ..
语法有点笨拙,尤其是需要技巧if known
。还有更简单的方法。你可以把循环里面路径的定义:
path cog; cog =
for ang = 0 step 2 inc until 360 - eps:
(inner_radius * sin(ang), inner_radius * cos(ang)) --
(inner_radius * sin(ang + inc), inner_radius * cos(ang + inc)) --
(outer_radius * sin(ang + inc), outer_radius * cos(ang + inc)) --
(outer_radius * sin(ang + 2 inc), outer_radius * cos(ang + 2 inc)) --
(inner_radius * sin(ang + 2 inc), inner_radius * cos(ang + 2 inc)) &
endfor cycle;
fill cog withcolor 3/4[blue, white];
draw cog;
这更加整洁。
说了这么多,MP 的一个好处是,你可以绝不不得不对sind
和进行处理cosd
。例如,这里有一个不同的方法。
\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\begin{mplibcode}
beginfig(1)
path greatc, smallc;
greatc = fullcircle scaled 2.6 cm;
smallc = fullcircle scaled 2.0 cm;
numeric teeth; teeth = 12;
path cog; cog =
for t = 1 upto teeth:
subpath (8/teeth)*(t-1, t-1/2) of greatc --
subpath (8/teeth)*(t-1/2, t) of smallc --
endfor cycle;
fill cog withcolor 3/4[blue, white];
draw cog;
endfig;
\end{mplibcode}
\end{document}
编译此可得到lualatex
:
带有弯曲的边缘...
最后,我不是工程师,但我们画的看起来更像是新冠病毒比齿轮要小,所以你可能需要对每个齿进行倒角。也许像这样?
\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\mplibtextextlabel{enable}
\begin{mplibcode}
beginfig(1)
path greatc, smallc;
greatc = fullcircle scaled 2.6 cm;
smallc = fullcircle scaled 2.0 cm;
numeric teeth; teeth = 12;
numeric r; r = 1/24;
path cog; cog =
for t = 1 upto teeth:
subpath (8/teeth)*(t-1+r, t-1/2-r) of greatc --
subpath (8/teeth)*(t-1/2+r, t-r) of smallc --
endfor cycle;
fill cog withcolor 3/4[blue, white];
draw cog;
endfig;
\end{mplibcode}
\end{document}
编译后lualatex
得到如下结果: