最近想用TikZ画一下化学中的一些器材,通过参考latex-glassware和tikz-labo实现了试管、烧瓶等的绘制。现有一个疑惑,当给scope传入rotate旋转参数后,也会造成液面的统一旋...
最近想用TikZ画一下化学中的一些器材,通过参考latex-glassware和tikz-labo实现了试管、烧瓶等的绘制。
现有一个疑惑,当给scope传入rotate旋转参数后,也会造成液面的统一旋转,从而无法保持液面的水平状态。
一个简单的想法是在scope内能取得传入rotate旋转角度,然后对溶液和液面椭圆进行反向旋转。
为验证想法,用45度进行了测试,反旋转可以实现液面保持水平。
但是,没有找到如何在scope内获取rotate旋转角度的方法,各位网友可否指点一二?
目前的绘制结果为:
实现的MWE为:
\documentclass[margin=3pt,
convert,
convert={
outext=.png,
command=\unexpanded{
pdftocairo -r 600 -png \infile % 将生成的pdf文件转换为png图像
}
}
]{standalone}
\usepackage{ctex}
\usepackage{ifthen}
\newcommand{\ifnonzero}[2]{
\ifthenelse{\equal{#1}{0}}{}{#2}
}
% \usepackage{calc}
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{intersections}
% 烧瓶
% 根据梯形面积等分刻度
% 用定积分法计算等分线:
% 瓶底半径:1
% 瓶口半径:0.25
% 高度:2(不含颈部)
% 顺时针转90度,用两点公式写出直线方程:y=(-3/8)*x + 1
% 求积分:(-3/16)*x*x + x
% 0-2定积分得总面积为:5/4
% 根据传入的#3比例参数,确定当前面积为:#3 * 5 / 4
% 解方程:(-3/16)*x*x + x - #3 * 5 / 4 = 0
% 可得用于绘制的液面高度
\newcommand{\flask}[3][]{
\begin{scope}[shift={(#2)},glassware,#1]
% 方程系数
\pgfmathsetmacro{\a}{-3/16}
\pgfmathsetmacro{\b}{1}
\pgfmathsetmacro{\m}{-1 * \b / (2 * \a)}
% 液面系数为不0
\ifnonzero{#3}{
% 烧瓶外框路径,并构成裁剪区域
\clip[rounded corners, name path = P1] (-0.4, 3)--++(0.15,-0.1)
--++(0,-0.9)--++(-0.75, -2.0)to[bend right=10pt]++(2.0, 0.0)--++(-0.75, 2.0)
--++(0.0, 0.9)--++(0.15, 0.10)
++(-0.4, 0.0) circle [x radius=0.4, y radius=0.065]--cycle;%
% 计算液面高度
\pgfmathsetmacro{\c}{-1 * #3 * 5 / 4}
\pgfmathsetmacro{\delta}{\b * \b - 4 * \a * \c}
\pgfmathsetmacro{\n}{sqrt(abs(\delta)) / (2 * abs(\a))}
\pgfmathsetmacro{\i}{\m - \n}
% 绘制液面矩形并被烧瓶裁剪
% TODO:如何获得给scope转入的rotate旋转参数
% 从而进行反旋转以保持液面水平?
\fill[name path=P2] (-1.0, -3.5pt) rectangle (1.0, \i);
% 绘制液面椭圆标志
% 求交点
\path [name intersections={of=P1 and P2, by={E,F}}];
% 计算中点
\node (M) at ($(E)!0.5!(F)$){};
% 计算半径并绘制液面椭圆标志
\fill[draw=white, very thin] (E) let \p1 = ($(M)-(E)$),
\n2 = {veclen(\x1,\y1)}
in ++(\n2, 0) circle[x radius = \n2, y radius = 2.5pt];
}
% 绘制刻度线(用积分法计算出刻度位置)
\pgfmathtruncatemacro{\lticklable}{1}
% 计算刻度位置
\pgfmathsetmacro{\c}{-1 * 5 / 24}
\pgfmathsetmacro{\delta}{\b * \b - 4 * \a * \c}
\pgfmathsetmacro{\n}{sqrt(abs(\delta)) / (2 * abs(\a))}
\pgfmathsetmacro{\i}{\m - \n}
% 多带带处理第1个坐标刻度,以便在循环中可以处理大刻度(mod 5运算)
\draw (-0.1, \i)--(0.1, \i) node[right, xshift=-3pt](\lticklable){\tiny{}\lticklable};
\foreach \y[count=\x] in {6,7,...,25}% 刻度位置占有单位面积数
{
\pgfmathsetmacro{\c}{-1 * \y / 24}
\pgfmathsetmacro{\delta}{\b * \b - 4 * \a * \c}
\pgfmathsetmacro{\n}{sqrt(abs(\delta)) / (2 * abs(\a))}
\pgfmathsetmacro{\i}{\m - \n}
\pgfmathtruncatemacro{\ltick}{mod(\x, 5)}
\ifnum\ltick=0
\pgfmathtruncatemacro{\lticklable}{\x / 5 + 1}
\draw (-0.1,\i)--(0.1,\i) node[right, xshift=-3pt](\lticklable){\tiny{}\lticklable};
\else
\draw (-0.05,\i)--(0.05,\i);
\fi
}
% 绘制烧瓶外框
\draw[rounded corners] (-0.4, 3)--++(0.15,-0.1)
--++(0,-0.9)--++(-0.75, -2.0)to[bend right=10pt]++(2.0, 0.0)--++(-0.75, 2.0)
--++(0.0, 0.9)--++(0.15, 0.10)
++(-0.4, 0.0) circle [x radius=0.4, y radius=0.065]--cycle;%
\end{scope}
}
\newcommand{\iflask}[3][]{
\begin{scope}[shift={(#2)},glassware,#1]
% 方程系数
\pgfmathsetmacro{\a}{-3/16}
\pgfmathsetmacro{\b}{1}
\pgfmathsetmacro{\m}{-1 * \b / (2 * \a)}
% 液面系数为不0
\ifnonzero{#3}{
% 烧瓶外框路径,并构成裁剪区域
\clip[rounded corners, name path = P1] (-0.4, 3)--++(0.15,-0.1)
--++(0,-0.9)--++(-0.75, -2.0)to[bend right=10pt]++(2.0, 0.0)--++(-0.75, 2.0)
--++(0.0, 0.9)--++(0.15, 0.10)
++(-0.4, 0.0) circle [x radius=0.4, y radius=0.065]--cycle;%
% 计算液面高度
\pgfmathsetmacro{\c}{-1 * #3 * 5 / 4}
\pgfmathsetmacro{\delta}{\b * \b - 4 * \a * \c}
\pgfmathsetmacro{\n}{sqrt(abs(\delta)) / (2 * abs(\a))}
\pgfmathsetmacro{\i}{\m - \n}
% 绘制液面矩形并被烧瓶裁剪
% 旋转
\fill[rotate=45,name path=P2] (-3.0, -25pt) rectangle (3.0, \i);
% 绘制液面椭圆标志
% 反向旋转45度
\path [name intersections={of=P1 and P2, by={E,F}}];
% 计算中点
\node (M) at ($(E)!0.5!(F)$){};
% 计算半径并绘制液面椭圆标志
% 反向旋转45度
\fill[rotate = 45, draw=white, very thin] (E) let \p1 = ($(M)-(E)$),
\n2 = {veclen(\x1,\y1)}
in ++(\n2, 0) circle[x radius = \n2, y radius = 2.5pt];
}
% 绘制刻度线(用积分法计算出刻度位置)
\pgfmathtruncatemacro{\lticklable}{1}
% 计算刻度位置
\pgfmathsetmacro{\c}{-1 * 5 / 24}
\pgfmathsetmacro{\delta}{\b * \b - 4 * \a * \c}
\pgfmathsetmacro{\n}{sqrt(abs(\delta)) / (2 * abs(\a))}
\pgfmathsetmacro{\i}{\m - \n}
% 多带带处理第1个坐标刻度,以便在循环中可以处理大刻度(mod 5运算)
\draw (-0.1, \i)--(0.1, \i) node[right, xshift=-3pt](\lticklable){\tiny{}\lticklable};
\foreach \y[count=\x] in {6,7,...,25}% 刻度位置占有单位面积数
{
\pgfmathsetmacro{\c}{-1 * \y / 24}
\pgfmathsetmacro{\delta}{\b * \b - 4 * \a * \c}
\pgfmathsetmacro{\n}{sqrt(abs(\delta)) / (2 * abs(\a))}
\pgfmathsetmacro{\i}{\m - \n}
\pgfmathtruncatemacro{\ltick}{mod(\x, 5)}
\ifnum\ltick=0
\pgfmathtruncatemacro{\lticklable}{\x / 5 + 1}
\draw (-0.1,\i)--(0.1,\i) node[right, xshift=-3pt](\lticklable){\tiny{}\lticklable};
\else
\draw (-0.05,\i)--(0.05,\i);
\fi
}
% 绘制烧瓶外框
\draw[rounded corners] (-0.4, 3)--++(0.15,-0.1)
--++(0,-0.9)--++(-0.75, -2.0)to[bend right=10pt]++(2.0, 0.0)--++(-0.75, 2.0)
--++(0.0, 0.9)--++(0.15, 0.10)
++(-0.4, 0.0) circle [x radius=0.4, y radius=0.065]--cycle;%
\end{scope}
}
\begin{document}
\begin{tikzpicture}
\tikzstyle{glassware} = [fill=magenta!15]
\flask{0,0}{0.50}
\flask[rotate=45]{4,0.5}{0.80}
\iflask[rotate=-45]{6,0.5}{0.60}
\end{tikzpicture}
\end{document}
可以使用选项 /tikz/reset cm 把变换矩阵还原为单位矩阵。
\newcommand{\flask}[3][]{
\begin{scope}[reset cm,#2,fill=magenta!15,#1]
%..........................................................省略
% TODO:如何获得给scope转入的rotate旋转参数
% 从而进行反旋转以保持液面水平?
\begin{scope}[reset cm]
\fill[name path=P2] (current bounding box.south west) rectangle (current bounding box.south east |- 0, \i);
% 绘制液面椭圆标志
% 求交点
\path [name intersections={of=P1 and P2, by={E,F}}];
% 计算中点
\node (M) at ($(E)!0.5!(F)$){};
% 计算半径并绘制液面椭圆标志
\fill[draw=white, very thin] (E) let \p1 = ($(M)-(E)$),
\n2 = {veclen(\x1,\y1)}
in ++(\n2, 0) circle[x radius = \n2, y radius = 2.5pt];
\end{scope}
%.................................................................省略
\end{scope}
}
然后效果是:
\begin{tikzpicture}
\flask{rotate=69}{0.50}
\end{tikzpicture}
图形中这个倾斜角度有一点问题(杯口那里),要避免这个问题,可能需要一些复杂的计算。
我对TikZ变换的了解是:
1.如果想获得当前的变换矩阵,可以使用命令 \pgfgettransform,变换矩阵的主要元素是 \pgf@pt@aa, \pgf@pt@ab, \pgf@pt@ba, \pgf@pt@ba 这4个宏(数值),和 \pgf@pt@x, \pgf@pt@y 这2个尺寸寄存器。
2.直接改变变换矩阵的是命令 \pgftransformcm。
3.顶层的 TikZ 命令、变换选项不会直接把变换矩阵应用于点的坐标,对点坐标应用变换矩阵的是底层的 PGF 命令,例如\pgfpathmoveto, 它会调用命令 \pgfpointtransformed 做变换,变换后的坐标构成软路径。
4.TikZ的变换选项基本上都调用内部命令 \tikz@addtransform 来重定义宏 \tikz@transform, 宏 \tikz@transform 保存一系列的底层变换命令。当宏 \tikz@transform 被执行时,其中的各个变换命令依次起作用,也就是依次修改变换矩阵。
5.如果只用TikZ的命令来画“烧杯”,那么TikZ的变换选项差不多就够用了,不过有时候使用底层的命令会更方便。
我的理解可能不对,仅供参考。
一周热门 更多>