Alles bei MetaPost

Was zeichnet man Vektorbilder? Für mich wie für viele andere ist die Antwort ziemlich offensichtlich: höchstwahrscheinlich im Illustrator. Nun oder in Inkkskape. Ich dachte auch, als mir befohlen wurde, achthundert Bilder für ein Physik-Lehrbuch zu zeichnen. Nichts dergleichen, nur schwarze und weiße technische Illustrationen mit allen möglichen Blöcken, Bällen, Federn, Linsen, Autos, Traktoren und so weiter. Es wurde vermutet, dass das Buch in der späten Folge aufgelegt werden würde, und mir wurden die Dateien mit eingefügten Bildern zur Verfügung gestellt - manchmal Bleistiftskizzen, dann Scans aus anderen Büchern - und es sah aus wie ein Manuskript in irgendeiner Form. In diesem Fall wurde der erste Gedanke - in der Inkscape zu zeichnen - Phantasien zum Thema „Wie man alles so automatisiert“ möglich. Aus irgendeinem Grund schien MetaPost zu diesem Zeitpunkt die beste Option zu sein .





Der größte Vorteil dieser Lösung ist, dass jedes Bild eine kleine Funktion mehrerer Variablen sein kann. Ein solches Bild ist zum Beispiel leicht, die Größe zu ändern und die Streifen an bestimmte unbekannte Umstände anzupassen, ohne wichtige Proportionen zu stören, was mit traditionellen Mitteln schwer zu erreichen ist. Wiederholende Elemente - diese Bälle und Federn - können sich jedoch wesentlich interessanter verhalten, als es die "menschlichen" Vektor-Editoren erlauben.

Ich wollte Bilder mit Schattierungen machen, wie in alten Büchern.



Für den Anfang war es notwendig, eine Linie mit variabler Dicke zu erhalten. Die Hauptschwierigkeit besteht hier darin, eine Kurve zu konstruieren, die mehr oder weniger parallel zu einer gegebenen ist und gegebenenfalls die Entfernung zu einer bestimmten Kurve ändert. Ich habe mich auf den wahrscheinlich primitivsten verlassenDie Art und Weise, in der die Segmente, die die Zwischenpunkte der Bezier-Kurve verbinden, einfach parallel auf diese Entfernung übertragen werden. Mit dem Unterschied, dass dieser Abstand entlang der Kurve variieren kann.



In den meisten Fällen ermöglicht dies ein erträgliches Ergebnis.



Beispielcode
Здесь и далее предполагается, что библиотека скачана и где-то есть строка input fiziko.mp;. Быстрее всего запустить и посмотреть в ConTeXt (тогда beginfig и endfig не нужны):

\starttext
\startMPcode
input fiziko.mp;
тут код
\stopMPcode
\stoptext


или в LuaLaTeX:

\documentclass{article}
\usepackage{luamplib}
\begin{document}
\begin{mplibcode}
input fiziko.mp;
тут код
\end{mplibcode}
\end{document}


beginfig(3);
path p, q; % синтаксис у метапоста, как мне кажется, довольно понятный, так что комментарии буду оставлять в основном к своим штукам
p := (0,-1/4cm){dir(30)}..(5cm, 0)..{dir(30)}(10cm, 1/4cm);
q := offsetPath(p)(1cm*sin(offsetPathLength*pi)); % первый аргумент тут — сам путь, а второй — функция от расстояния вдоль пути (offsetPathLength, меняется от 0 до 1), определяющая, на каком удалении будет проходить огибающая
draw p;
draw q dashed evenly;
endfig;



Aus zwei solchen Kurven können Sie nun die Kontur der Linie mit variabler Dicke bestimmen.



Beispielcode
beginfig(4);
path p, q[];
p := (0,-1/4cm){dir(30)}..(5cm, 0)..{dir(30)}(10cm, 1/4cm);
q1 := offsetPath(p)(1/2pt*(sin(offsetPathLength*pi)**2)); % огибающая по одну сторону пути
q2 := offsetPath(p)(-1/2pt*(sin(offsetPathLength*pi)**2)); % и по другую
fill q1--reverse(q2)--cycle;
endfig;



Die Dicke sollte auf etwas darunter begrenzt sein, da sonst zu dünne Teile der Linien während des Druckens ein Raster annehmen, und dies ist normalerweise nicht besonders schön. Eine Option besteht darin, alle Linien, deren Dicke geringer als ein bestimmter Wert ist, mit gestrichelten Linien mit der gleichen Mindestdicke herzustellen, so dass die Gesamtmenge an Farbe pro Längeneinheit durchschnittlich der der Zieldickenlinie entspricht. Anstatt die Farbmenge von den Seiten der Schnur zu verringern, nagen Sie sie mit Querstreifen.



Beispielcode
beginfig(5);
path p;
p := (0,-1/4cm){dir(30)}..(5cm, 0)..{dir(30)}(10cm, 1/4cm);
draw brush(p)(1pt*(sin(offsetPathLength*pi)**2)); % те же аргументы, что и у огибающей, но для толщины кисти
endfig;



Jetzt kannst du die Bälle zeichnen. Es können einfach konzentrische Kreise sein, die Dicke der Linien, die durch die Funktion der Beleuchtung des Balls an den Punkten bestimmt werden, durch die die Linien verlaufen.



Beispielcode
beginfig(6);
draw sphere.c(1.2cm);
draw sphere.c(2.4cm) shifted (2cm, 0);
endfig;



Ein weiteres praktisches Element sind Schläuche: Grob gesagt, Zylinder, die in alle Richtungen gebogen werden können. Solange sie einen konstanten Abschnitt haben, ist mit ihnen alles einfach.



Beispielcode
beginfig(7);
path p;
p := subpath (1,8) of fullcircle scaled 3cm;
draw tube.l(p)(1/2cm); % аргументы — путь и функция ширины шланга, которая здесь постоянна
endfig;



Wenn sich die Dicke ändert, ist es erforderlich, die Anzahl der Hübe entsprechend zu ändern, während die durchschnittliche Fülldichte unverändert bleibt, und Änderungen der Dicke bei der Berechnung der Beleuchtung zu berücksichtigen.



Beispielcode
beginfig(8);
path p;
p := pathSubdivide(fullcircle, 2) scaled 3cm;
draw tube.l(p)(1/2cm + 1/6cm*sin(offsetPathLength*10pi));
endfig;



Es gibt mehr Schläuche mit Schraffuren, aber das Lösen der durchschnittlichen Fülldichte erwies sich als schwieriger, so dass sie in vielen Fällen noch nicht besonders gut aussehen.



Beispielcode
beginfig(9);
path p;
p := pathSubdivide(fullcircle, 2) scaled 3cm;
draw tube.t(p)(1/2cm + 1/6cm*sin(offsetPathLength*10pi));
endfig;



Grundsätzlich kann aus Schläuchen viel gemacht werden: von Kegeln und Zylindern bis hin zu Balustern.



Beispielcode
beginfig(10);
draw tube.l ((0, 0) -- (0, 3cm))((1-offsetPathLength)*1cm) shifted (-3cm, 0); % очень простой конус
path p;
p := (-1/2cm, 0) {dir(175)} .. {dir(5)} (-1/2cm, 1/8cm) {dir(120)} .. (-2/5cm, 1/3cm) .. (-1/2cm, 3/4cm) {dir(90)} .. {dir(90)}(-1/4cm, 9/4cm){dir(175)} .. {dir(5)}(-1/4cm, 9/4cm + 1/5cm){dir(90)} .. (-2/5cm, 3cm); % огибающая балясины
p := pathSubdivide(p, 6);
draw p -- reverse(p xscaled -1) -- cycle;
tubeGenerateAlt(p, p xscaled -1, p rotated -90); % более низкоуровневая штука, чем tube.t, первые два аргумента — два пути — стороны шланга, третий — кривая огибающей.
endfig;



Etwas von dem, was aus solchen Teilen gebaut werden kann, befindet sich in der Bibliothek. Nehmen wir an, ein Globus ist im Grunde ein Ball.



Beispielcode
beginfig(11);
draw globe(1cm, -15, 0) shifted (-6/2cm, 0); % радиус, западная долгота и северная широта, десятичные
draw globe(3/2cm, -30.28367, 59.93809);
draw globe(4/3cm, -140, -30) shifted (10/3cm, 0);
endfig;



Obwohl nicht: Hier geht die Schraffur entlang der Parallelen, und es ist noch schwieriger, die Dicke des Hubs zu kontrollieren, um die Fülldichte zu erhalten, als bei den Schraffuren auf den Schläuchen. Dies ist also ein separater Ball.



Beispielcode
beginfig(12);
draw sphere.l(2cm, -60); % диаметр и широта
draw sphere.l(3cm, 45) shifted (3cm, 0);
endfig;



Ein Gewicht ist eine unkomplizierte Konstruktion von zwei Arten von Schläuchen mit variabler Dicke.



Beispielcode
beginfig(13);
draw weight.s(1cm); % высота гирьки
draw weight.s(2cm) shifted (2cm, 0);
endfig;



Es gibt auch ein Werkzeug, um die Schläuche in Knoten zu binden.



Beispielcode, um nicht nur einen Knoten zu stören
beginfig(14);
path p;
p := (dir(90)*4/3cm) {dir(0)} .. tension 3/2 ..(dir(90 + 120)*4/3cm){dir(90 + 30)} .. tension 3/2 ..(dir(90 - 120)*4/3cm){dir(-90 - 30)} .. tension 3/2 .. cycle;
p := p scaled 6/5;
addStrandToKnot (primeOne) (p, 1/4cm, "l", "1, -1, 1"); % сначала к узлу с именем primeOne добавляется нить, идущая по пути p шириной в 1/4cm, которая будет рисоваться шлангом типа "l" (то есть tube.l, tube.t пока работает плоховато) и будет проходить в в «слоях» "1, -1, 1" в местах пересечений по ходу кривой p
draw knotFromStrands (primeOne); % затем рисуется сам узел. нитей можно добавлять несколько
endfig;



Schatten an den Knoten - Komplikationen im Beleuchtungsmodell. Grundsätzlich stört sich niemand in anderen Fällen, aber ich habe mir nicht das Ziel gesetzt, tief in die Lautstärke zu gehen. Bisher ist das nicht sehr praktisch und funktioniert nicht überall.



Beispielcode
beginfig(15);
path shadowPath[];
boolean shadowsEnabled;
numeric numberOfShadows;
shadowsEnabled := true; % тени надо включить
numberOfShadows := 1; % указать их количество
shadowPath0 := (-1cm, -2cm) -- (-1cm, 2cm) -- (-1cm +1/6cm, 2cm) -- (-1cm + 1/8cm, -2cm) -- cycle; % предмет, отбрасывающий тень, плоский и описывается замкнутым контуром
shadowDepth0 := 4/3cm; % располагается на такой-то «высоте» над предметом, на который тень падает
shadowPath1 := shadowPath0 rotated -60;
shadowDepth1 := 4/3cm;
draw sphere.c(2.4cm); % нормально тень пока отбрасывается только на шары sphere.c и шланги tube.l с постоянным сечением
fill shadowPath0 withcolor white;
draw shadowPath0;
fill shadowPath1 withcolor white;
draw shadowPath1;
endfig;



Und natürlich brauchen wir eine Holzstruktur. Der Einfluss des Charakters des Knotenwachstums auf das Muster der Jahresringe ist ein Thema für ernsthafte Forschung. Sehr einfach können Sie sich die einjährigen Ringe in parallelen Ebenen vorstellen, die die Knoten verzerren. Es reicht also aus, die Änderung der Ebene durch eine nicht sehr knifflige Funktion (Knotenfunktion) zu beschreiben und eine Reihe von Höhenlinien für die Summe der Menge solcher Funktionen als das gewünschte Jahresringmuster zu betrachten.



Beispielcode
beginfig(16);
numeric w, b;
pair A, B, C, D, A', B', C', D';
w := 4cm;
b := 1/2cm;
A := (0, 0);
A' := (b, b);
B := (0, w);
B' := (b, w-b);
C := (w, w);
C' := (w-b, w-b);
D := (w, 0);
D' := (w-b, b);
draw woodenThing(A--A'--B'--B--cycle, 0); % доска, ограниченная контуром A--A'--B'--B--cycle, с волокнами дерева под углом 0 градусов
draw woodenThing(B--B'--C'--C--cycle, 90);
draw woodenThing(C--C'--D'--D--cycle, 0);
draw woodenThing(A--A'--D'--D--cycle, 90);
eyescale := 2/3cm; % масштаб глаза
draw eye(150) shifted 1/2[A,C]; % глаз смотрит в направлении 150 градусов
endfig;



Das Auge aus dem obigen Bild kann sich etwas öffnen oder schielen, und die Pupillenbreite ändert sich. Das hat keine besondere Bedeutung, aber es erweist sich als lebendiger, als wenn solche Kleinigkeiten mechanisch überall gleich wären.



Beispielcode
beginfig(17);
eyescale := 2/3cm; % по умолчанию 1/2cm
draw eye(0) shifted (0cm, 0);
draw eye(0) shifted (1cm, 0);
draw eye(0) shifted (2cm, 0);
draw eye(0) shifted (3cm, 0);
draw eye(0) shifted (4cm, 0);
endfig;



Meistens waren die Bilder nicht sehr komplex, aber wenn Sie sich der Sache ernsthaft nähern, müssen viele Aufgaben gelöst werden, um sie sinnvoll zu veranschaulichen. Nehmen wir an, die Aufgabe von Lopital in Bezug auf den Block (ich weiß nicht, wie man ihn auf Russisch richtig nennt, er stand nicht im Lehrbuch, nur als Beispiel): Er hängt an einem Seil der Länge l, das am Punkt A hängt, blockiert an einem anderen Seil In gleicher Höhe am Punkt B hängt eine Last C am zweiten Seil. Es wird gefragt, ob die Seile und der Block schwerelos sind, wo wird die Last liegen? Überraschenderweise sind die Lösung des Problems und die Konstruktion nicht so einfach, aber wenn Sie mit mehreren Variablen spielen, können Sie das Bild ganz genau so machen, dass es auf dem Streifen am besten aussieht, während es wahr bleibt.



Beispielcode
vardef lHopitalPulley (expr AB, l, m) = % расстояние AB между точками крепления нитей и длины l, m самих нитей. почему без единиц изменения? тут играют роль ограничения метапоста: если дальнейшие вычисления производить с такими большими числами, какими являются в этих краях сантиметры, случится arithmetic overflow.
save A, B, C, D, E, o, a, x, y, d, w, h, support;
image(
pair A, B, C, D, E, o[];
path support;
numeric a, x[], y[], d[], w, h;
x1 := (l**2 + abs(l)*((sqrt(8)*AB)++l))/4AB; % собственно, решение задачи
y1 := l+-+x1; % нахождение второй координаты блока тривиально
y2 := m - ((AB-x1)++y1); % как и нахождение второй координаты груза
A := (0, 0);
B := (AB*cm, 0);
D := (x1*cm, -y1*cm);
C := D shifted (0, -y2*cm);
d1 := 2/3cm; d2 := 1cm; d3 := 5/6d1; % диаметры блока, груза и колеса блока
w := 2/3cm; h := 1/3cm; % ширина краев доски и толщина доски. в принципе, всё это можно вынести в аргументы
o1 := (unitvector(C-D) rotated 90 scaled 1/2d3);
o2 := (unitvector(D-B) rotated 90 scaled 1/2d3);
E := whatever [D shifted o1, C shifted o1]
= whatever [D shifted o2, B shifted o2]; % место, где будет расположен центр блока
a := angle(A-D);
support := A shifted (-w, 0) -- B shifted (w, 0) -- B shifted (w, h) -- A shifted (-w, h) -- cycle;
draw woodenThing(support, 0); % доска, на которой всё висит
draw pulley (d1, a - 90) shifted E; % блок
draw image(
draw A -- D -- B withpen thickpen;
draw D -- C withpen thickpen;
) maskedWith (pulleyOutline shifted E); % нити надо прикрыть блоком
draw sphere.c(d2) shifted C shifted (0, -1/2d2); % грузом служит шар
dotlabel.llft(btex $A$ etex, A);
dotlabel.lrt(btex $B$ etex, B);
dotlabel.ulft(btex $C$ etex, C);
label.llft(btex $l$ etex, 1/2[A, D]);
)
enddef;
beginfig(18);
draw lHopitalPulley (6, 2, 11/2); % теперь можно подставлять такие параметры, с какими иллюстрации будет уютней на полосе
draw lHopitalPulley (3, 5/2, 3) shifted (8cm, 0);
endfig;



Und was ist das Lehrbuch? Als fast alle Illustrationen und Layout fertig waren, passierte etwas dort und es kam nie heraus. Wahrscheinlich, einige Zeit später, habe ich alle Hauptstücke aus der resultierenden Bibliothek erneut geschrieben und den Code in den Github eingefügt . Einige Kunshtuki betraten dort nicht: beispielsweise elektrische Schaltkreise oder eine Funktion für Ziehmaschinen und Traktoren. Einige - hinzugefügt: Knoten zum Beispiel.

Die gesamte Küche funktioniert nicht schnell: Es dauert etwa eine Minute, bis alle Bilder für diesen Artikel von LuaLaTeX auf meinem Laptop mit i5-4200U 1,6 GHz gesammelt sind. Für so viele Dinge wird ein Pseudo-Zufallszahlengenerator verwendet, so dass ähnliche Bilder nicht nur innerhalb eines Durchgangs etwas anders aussehen (dies ist eine Funktion), sondern bei jedem nächsten Durchlauf werden Bilder angezeigt, die sich vom vorherigen unterscheiden. Sie können jedoch immer in der Präambel festlegen randomseed := какому-то числу, und alle Läufe erzeugen die gleichen Bilder.

Jetzt auch beliebt: