¿En qué dibujar imágenes vectoriales? Para mí, como para muchos otros, la respuesta es bastante obvia: muy probablemente en ilustrador. Bueno, o en Inskape. También pensé cuando me ordenaron dibujar ochocientas piezas para un libro de texto de física. Nada de eso, solo ilustraciones técnicas en blanco y negro con todo tipo de bloques, bolas, resortes, lentes, automóviles, tractores y similares. Se suponía que el libro estaría hecho en latech, y me proporcionaron archivos de Word con imágenes insertadas, ya sea en bocetos a lápiz o escaneos de otros libros, y el manuscrito parecía estar en alguna forma. En este caso, el primer pensamiento - para dibujar en el paisaje - sucumbió a las fantasías sobre el tema "¿cómo automatizaría todo?" Por alguna razón,
MetaPost parecía ser la mejor opción en ese momento.

La ventaja más significativa de tal solución es que cada imagen puede ser una pequeña función de varias variables; Tal imagen es fácil, por ejemplo, cambiar de tamaño y ajustar las rayas a circunstancias específicas previamente desconocidas sin violar proporciones importantes, lo cual es difícil de lograr con medios más tradicionales. Y los elementos que se repiten, las mismas bolas y resortes, se pueden hacer que se comporten mucho más interesantes de lo que permiten los editores de vectores "humanos".
Quería hacer dibujos con sombreados, como el que se encuentra en los libros antiguos.

Primero, tuvimos que obtener líneas de grosor variable. La dificultad principal aquí es construir una curva que sea más o menos paralela a la dada y, según sea necesario, cambiar la distancia a la dada. Me basé en la
forma más primitiva, probablemente, en la que los segmentos que conectan los puntos intermedios de la curva de Bezier se transfieren simplemente a una distancia dada en paralelo. Con la diferencia de que esta distancia puede variar a lo largo de la curva.

En la mayoría de los casos, esto permite un resultado decente.

Código de ejemploDe aquí en adelante se supone que la biblioteca se ha
descargado y en algún lugar está la línea de
input fiziko.mp;
. La forma más rápida de comenzar y buscar en ConTeXt (luego no se necesitan
beginfig
y
endfig
):
\starttext
\startMPcode
input fiziko.mp;
\stopMPcode
\stoptext
o en 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;
Ahora dos de estas curvas se pueden usar para hacer un contorno de una línea de espesor variable.

Código de ejemplobeginfig(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;
El grosor debe ser algo limitado desde abajo, de lo contrario, el ráster tomará partes demasiado delgadas de las líneas al imprimir, y esto generalmente no es muy hermoso. Una opción es dibujar todas las líneas, cuyo grosor es menor que un cierto valor, por líneas discontinuas del mismo grosor mínimo, de modo que la cantidad total de pintura por unidad de longitud en promedio corresponda a la de la línea del grosor objetivo. Es decir, en lugar de reducir la cantidad de pintura de los lados de la línea, comience a roerla con franjas transversales.

Código de ejemplobeginfig(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;
Ahora puedes dibujar bolas. Pueden ser solo círculos concéntricos, cuyo grosor de las líneas está determinado por la función de la iluminación de la pelota en los puntos a través de los cuales pasan las líneas.

Código de ejemplobeginfig(6);
draw sphere.c(1.2cm);
draw sphere.c(2.4cm) shifted (2cm, 0);
endfig;
Otra primitiva conveniente son las "mangueras": en términos generales, cilindros que pueden doblarse de cualquier manera. Mientras sean de sección transversal constante, todo es simple con ellos.

Código de ejemplobeginfig(7);
path p;
p := subpath (1,8) of fullcircle scaled 3cm;
draw tube.l(p)(1/2cm); % — ,
endfig;
Si el grosor cambia, entonces el número de golpes debe cambiarse en consecuencia, manteniendo la densidad de relleno promedio sin cambios, y también tenga en cuenta los cambios en el grosor al calcular la iluminación.

Código de ejemplobeginfig(8);
path p;
p := pathSubdivide(fullcircle, 2) scaled 3cm;
draw tube.l(p)(1/2cm + 1/6cm*sin(offsetPathLength*10pi));
endfig;
Todavía hay mangueras con trama transversal, pero para ellos fue más difícil resolver el problema de preservar la densidad de llenado promedio, por lo que en muchos casos todavía no se ven muy bien.

Código de ejemplobeginfig(9);
path p;
p := pathSubdivide(fullcircle, 2) scaled 3cm;
draw tube.t(p)(1/2cm + 1/6cm*sin(offsetPathLength*10pi));
endfig;
En principio, se pueden hacer muchas cosas solo con mangueras: desde conos y cilindros hasta balaustres.

Código de ejemplobeginfig(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;
Algo de lo que se puede construir a partir de tales partes está en la biblioteca. Digamos que un globo es básicamente una pelota.

Código de ejemplobeginfig(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;
Aunque no: aquí la eclosión corre en paralelo, y controlar el grosor del trazo para mantener la densidad de llenado es aún más difícil que en el caso de la eclosión transversal en las mangueras, por lo que este es un tipo de bola separada.

Código de ejemplobeginfig(12);
draw sphere.l(2cm, -60); %
draw sphere.l(3cm, 45) shifted (3cm, 0);
endfig;
Y el peso es un diseño sencillo de dos tipos de mangueras de espesor variable.

Código de ejemplobeginfig(13);
draw weight.s(1cm); %
draw weight.s(2cm) shifted (2cm, 0);
endfig;
Todavía hay una herramienta para atar las mangueras en nudos.

Código de muestra para no saturar, solo un nodobeginfig(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;
Las sombras de los nodos son una complicación en el modelo de iluminación. En principio, nadie se molesta en usarlos en otros casos, pero no establecí una meta para profundizar en el volumen, por lo que si bien esto no es muy conveniente, no funciona en todas partes.

Código de ejemplobeginfig(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;
Y, por supuesto, necesitas una textura de madera. La influencia de la naturaleza del crecimiento de los nudos en el patrón de las secciones de los anillos de los árboles es un tema de investigación seria. Muy simplificado, podemos imaginar anillos anuales en planos paralelos en los que se introducen distorsiones de los nudos. Por lo tanto, es suficiente describir el cambio en el plano mediante una función no muy sofisticada (la función de nudo) y considerar una serie de isolinas para la suma del conjunto de funciones como el patrón deseado de anillos de árbol.

Código de ejemplobeginfig(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, wb);
C := (w, w);
C' := (wb, wb);
D := (w, 0);
D' := (wb, 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;
El ojo de la imagen de arriba puede abrirse ligeramente, luego entrecerrar los ojos y cambiar el ancho de la pupila. No tiene un significado especial en esto, pero resulta más vívido que si tales pequeñeces fueran mecánicamente iguales en todas partes.

Código de ejemplobeginfig(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;
En la mayoría de los casos, las imágenes no eran muy complicadas, pero si aborda el asunto con toda seriedad, es necesario resolver muchas tareas para ilustrarlas de manera significativa. Aquí, digamos, la tarea de Lopital sobre el bloque (no sé cómo se llama correctamente en ruso, no estaba en el libro de texto, es solo un ejemplo): el bloque cuelga de una cuerda de longitud l suspendida en el punto A, se engancha en otra cuerda suspendido a la misma altura en el punto B, la carga C cuelga de la segunda cuerda. La pregunta es, si las cuerdas y el bloque no tienen peso, ¿dónde estará la carga? Sorprendentemente, tanto la solución del problema como la construcción no son tan elementales, pero, jugando con varias variables, puede hacer que la imagen sea exactamente lo que se verá mejor en la tira, sin dejar de ser cierta.

Código de ejemplovardef 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(CD) rotated 90 scaled 1/2d3);
o2 := (unitvector(DB) rotated 90 scaled 1/2d3);
E := whatever [D shifted o1, C shifted o1]
= whatever [D shifted o2, B shifted o2]; % ,
a := angle(AD);
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;
¿Qué es un libro de texto? Por desgracia, cuando casi todas las ilustraciones y el diseño estaban listos, algo sucedió allí y nunca salió. Por lo tanto, probablemente, algún tiempo después, reescribí todas las cosas principales de la biblioteca resultante nuevamente y
publiqué el código en el github . Algunos kunshtyuki no entraron allí: por ejemplo, circuitos eléctricos o una función para dibujar automóviles y tractores. Algunos - agregados: nodos, por ejemplo.
Toda esta cocina no funciona rápidamente: toma aproximadamente un minuto recopilar todas las imágenes de este artículo con LuaLaTeX en mi computadora portátil con el i5-4200U 1.6 GHz. Para muchas cosas, se usa un generador de números pseudoaleatorios, por lo que las imágenes similares se verán un poco diferentes no solo dentro de una ejecución (esta es una característica), sino que cada ejecución posterior dará imágenes diferentes de la anterior. Pero siempre puede establecer
randomseed := -
en el preámbulo, y todas las mismas ejecuciones producirán las mismas imágenes.