填写FOR循环中构建的Metapost图

填写FOR循环中构建的Metapost图

我正在尝试在 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的值赋给。因此,在这里,您每次循环时都会更新路径变量。实际上,您每次都通过执行来扩展路径。只是这在第一次不会起作用,因此您需要使用技巧来保护第一个赋值。循环第一次没有值,所以它不是,所以第一个赋值将符合要求。请注意,在行中展开。bacogcog := cog -- toothif knowncogknowncog := toothif .. 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得到如下结果:

在此处输入图片描述

相关内容