在我对这个问题进行过多详细的讨论之前,我想要的最终结果是像这样的图片: 我可以画出所有的东西,除了从摇摆线延伸出来的分布力箭头瓦。理想情况下,我可以创建一个宏,在两点之间绘制一系列力线,顶部是一条摆动线或预定线。
我正在编写一个程序包来绘制类似于斯坦利—我不太喜欢它的方方面面,希望我的绘图更加灵活。我重新实现了它的大部分功能,以便完成更多任务,其中一个命令 ( \distload
) 将绘制一系列箭头来表示两点之间的分布负载。我现在正在考虑创建一个\pathload
命令,它将执行相同的操作,只是力线将从某个任意预定义的路径开始(或结束)。
以下是一个 MWE,展示了我目前取得的进展。为了清晰起见,我将其拆分为单独的代码块,因此您需要将它们连接起来才能进行编译。首先是通用前言:
% arara: lualatex: { shell: true }
\documentclass{article}
\usepackage{expl3}
\usepackage{xparse}
\usepackage{etoolbox}
\usepackage{tikz}
% Lua math library
\usepgflibrary{luamath}
\pgfkeys{pgf/luamath=parser}
\usepgflibrary{fpu}
\usetikzlibrary{calc}
\usepgflibrary{arrows.meta}
\usetikzlibrary{bending}
\usetikzlibrary{decorations.markings}
\usetikzlibrary{decorations.pathmorphing}
\usetikzlibrary{decorations.pathreplacing}
\usetikzlibrary{intersections}
\usetikzlibrary{positioning}
% TikZ Styles
\tikzset{
force/.style={draw, very thick, arrows = {-Stealth[bend]}}, % Force with arrow tip at end
force'/.style={force, arrows = {Stealth[bend]-}}, % Force with arrow tip at start
force label/.style={inner sep=1pt},
}
% Distributed load parameters
\newlength{\distloadDistance}
\newlength{\distloadLength}
\setlength{\distloadDistance}{0pt}
\setlength{\distloadLength}{5mm}
\newcommand{\distloadSegments}{5}
下一个宏绘制两点之间的分布载荷,可选择指定标签、两端力线的长度、段数以及该标签的其他文本属性。它运行良好,但我可能需要优化其中的一些。
% \distload[*away]{start}{end}[label][magnitude start][magnitude end][segments][text properties]
\NewDocumentCommand{\distload}{s m m o O{\distloadLength} O{\distloadLength} O{\distloadSegments} o}{
\coordinate (distloadA1) at ($ (#2)!\distloadDistance!90:(#3) $);
\coordinate (distloadB1) at ($ (#3)!\distloadDistance!-90:(#2) $);
\coordinate (distloadA2) at ($ (#2)!{\distloadDistance+#5}!90:(#3) $);
\coordinate (distloadB2) at ($ (#3)!{\distloadDistance+#6}!-90:(#2) $);
\pgfmathsetmacro{\distloadInterval}{1/#7}
\pgfmathsetmacro{\distloadIntervalBegin}{\distloadInterval}
\pgfmathsetmacro{\distloadIntervalStep}{\distloadInterval*2}
\pgfmathsetmacro{\distloadIntervalEnd}{1-\distloadInterval}
\draw [thin] (distloadA2) -- (distloadB2);
\IfValueT{#4}{
\IfNoValueTF{#8}{
\path (distloadA2) -- (distloadB2) node[force label, sloped, above=2pt, midway]{#4};
} {
\path (distloadA2) -- (distloadB2) node[force label, sloped, above=2pt, midway, #8]{#4};
}
}
\IfBooleanTF{#1}{
\ifdimequal{#5}{0pt}{}{
\draw [force', semithick] (distloadA2) -- (distloadA1);
}
\ifdimequal{#6}{0pt}{}{
\draw [force', semithick] (distloadB2) -- (distloadB1);
}
\begin{scope}
\clip (distloadA1) -- (distloadB1) -- (distloadB2) -- (distloadA2) -- cycle;
\foreach \i in {\distloadIntervalBegin,\distloadIntervalStep,...,\distloadIntervalEnd}
\draw [force', semithick] ($(distloadA2)!\i!(distloadB2)$) -- ($(distloadA1)!\i!(distloadB1)$);
\end{scope}
}
{
\ifdimequal{#5}{0pt}{}{
\draw [force, semithick] (distloadA2) -- (distloadA1);
}
\ifdimequal{#6}{0pt}{}{
\draw [force, semithick] (distloadB2) -- (distloadB1);
}
\begin{scope}
\clip (distloadA1) -- (distloadB1) -- (distloadB2) -- (distloadA2) -- cycle;
\foreach \i in {\distloadIntervalBegin,\distloadIntervalStep,...,\distloadIntervalEnd}
\draw [force, semithick] ($(distloadA2)!\i!(distloadB2)$) -- ($(distloadA1)!\i!(distloadB1)$);
\end{scope}
}
}
下一个代码块是类似的宏,用于在两点和我正在尝试弄清楚的某条预定义线之间生成一系列力线。解决这个问题的最简单方法是从“顶线”和两点之间的假想线创建某种形状,然后剪裁箭头,但这仍然需要确定“顶线”的起点和终点。
% \pathload[*away]{start}{end}{saved path}[label][segments][text properties]
\NewDocumentCommand{\pathload}{s m m m o O{\distloadSegments} o}{
\pgfmathsetmacro{\distloadInterval}{1/#6}
\pgfmathsetmacro{\distloadIntervalBegin}{\distloadInterval}
\pgfmathsetmacro{\distloadIntervalStep}{\distloadInterval*2}
\pgfmathsetmacro{\distloadIntervalEnd}{1-\distloadInterval}
\path[name path=pathload][use path=#4]; % coordinate at (start of path) (pathloadA) coordinate at (end of path) (pathloadB);
% It works if I manually input the path, but that kind of defeats the purpose of this macro.
% \path[save path=\pathLoad, name path=pathload] (0, 1) coordinate (pathloadA) .. controls (1, 4) .. (4, 2) .. controls (6, 3) .. (10, 2) coordinate (pathloadB);
\coordinate (distloadA1) at ($ (#2)!\distloadDistance!90:(#3) $);
\coordinate (distloadB1) at ($ (#3)!\distloadDistance!-90:(#2) $);
% \draw[red] (pathloadA) -- ($(distloadA1)!(pathloadA)!(distloadB1)$);
% \draw[red] (pathloadB) -- ($(distloadA1)!(pathloadB)!(distloadB1)$);
% Two passes are probably needed. The first collects the intersections, the second draws the "force lines"?
% \foreach \i in {\distloadIntervalBegin,\distloadIntervalStep,...,\distloadIntervalEnd}
% \draw [green] ($(pathloadA)!\i!(pathloadB)$) -- ($(distloadA1)!\i!(distloadB1)$);
\draw[blue, ultra thick][use path=#4];
% Once the force lines are drawn, I can add nodes to label it using code from \distload and other macros I've written
}
最后,演示上述宏的实际文档:
\begin{document}
\section{Distributed loads}
\begin{tikzpicture}
% Uniformly distributed load
\coordinate (node-A) at (0, 0);
\coordinate (node-B) at (5, 0);
\draw[very thick, gray] (node-A) -- (node-B);
\distload{node-A}{node-B}[Force $A-B$]
% Linearly increasing load
\coordinate (node-C) at (0, -2);
\coordinate (node-D) at (7, -2);
\draw[very thick, gray] (node-C) -- (node-D);
\distload{node-C}{node-D}[Force $C-D$][0mm][15mm][4]
% Demonstrating all the options
\coordinate (node-E) at (0, -5);
\coordinate (node-F) at (8, -5);
\draw[very thick, gray] (node-E) -- (node-F);
\distload*{node-E}{node-F}[BIG FORCE][20mm][4mm][10][node font=\bfseries\huge]
% Demonstrating ALL THE POSSIBILITIES
\coordinate (node-G) at (2, -12);
\coordinate (node-H) at (6, -6);
\draw[very thick, gray] (node-G) -- (node-H);
\distload*{node-G}{node-H}[why you do this thing?][20mm][-10mm][15][red, node font=\itshape\large]
\end{tikzpicture}
\section{Loads along predefined path}
\begin{tikzpicture}
\coordinate (node-I) at (0, 0);
\coordinate (node-J) at (10, 0);
\path[save path=\pathLoad] (0, 1) .. controls (1, 4) .. (4, 2) .. controls (6, 3) .. (10, 0);
\draw[very thick, gray] (node-I) -- (node-J);
\pathload{node-I}{node-J}{\pathLoad}[Wiggle]
% \draw[blue, ultra thick][use path=\loadEdge];
% \draw[white, dashed][use path=\pathLoad];
\end{tikzpicture}
\end{document}
假设我没有叫喊XY 问题树,我基本上有三个主要问题需要解决:
- 找到两条路径之间的最大垂直距离,然后创建
\path
可用于查找交点的不可见力线(即 s) - 在“梁”和摆动线之间画出力线
- 如果摆动线太靠近光束而无法绘制完整的箭头,则剪掉一些力线
TikZ/PGF 似乎没有办法提取我想要的信息,即预定义路径的端点和局部最大值。在这个阶段,我不会担心任何健全性检查来确保摇摆路径实际上“位于”从 A 到 B 的线的“上方”。
答案1
也许这是朝着正确的方向发展的。
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\usetikzlibrary{bending}
\usetikzlibrary{calc}
\usetikzlibrary{intersections}
% TikZ Styles
\tikzset{
force/.style={draw, very thick, arrows = {-Stealth[bend]}}, % Force with arrow tip at end
force'/.style={force, arrows = {Stealth[bend]-}}, % Force with arrow tip at start
force label/.style={inner sep=1pt},
}
\tikzset{
pics/path load/.style={code={
\tikzset{path load/.cd,#1}
\def\pv##1{\pgfkeysvalueof{/tikz/path load/##1}}
\pgfmathsetmacro{\distloadInterval}{1/\pv{segments}}
\pgfmathsetmacro{\distloadIntervalBegin}{\distloadInterval}
\pgfmathsetmacro{\distloadIntervalStep}{\distloadInterval*2}
\pgfmathsetmacro{\distloadIntervalEnd}{1-\distloadInterval}
\foreach \X in {\distloadIntervalBegin,\distloadIntervalStep,...,\distloadIntervalEnd}
{
\edef\temp{\noexpand\path[name path=vert]
($\pv{start}!\X!\pv{end}$) coordinate (vertaux) --
(vertaux|-current bounding box.north);}
\temp
\path[name intersections={of=vert and \pv{path}},draw,force]
(intersection-1) -- (vertaux);}
}},path load/.cd,path/.initial=,start/.initial=,end/.initial=,label/.initial=,
segments/.initial=5}
\begin{document}
\section{Loads along predefined path}
\begin{tikzpicture}[tips=on proper draw]
\coordinate (node-I) at (0, 0);
\coordinate (node-J) at (10, 0);
\path[name path=pathLoad,save path=\pathLoad,draw,very thick,blue] (0, 1) .. controls (1, 4) .. (4, 2) .. controls (6, 3) .. (10, 0);
\draw[very thick, gray] (node-I) -- (node-J);
\pic{path load={path=pathLoad,start={(node-I)},end={(node-J)}}};
\draw[white, dashed][use path=\pathLoad];
\end{tikzpicture}
\end{document}
附录:这是一种从已保存路径中提取第一个和最后一个坐标的复杂方法。上面没有用到,但最终可能会有用。
\documentclass[tikz,border=3mm]{standalone}
\makeatletter
\tikzset{
first and last coordinates of/.code={
\begingroup
\def\pgfsyssoftpath@movetotoken##1##2{%
\pgf@xa=##1%
\pgf@ya=##2%
}%
\def\pgfsyssoftpath@curvetotoken##1##2{%
\pgf@xb=##1%
\pgf@yb=##2%
}%
\def\pgfsyssoftpath@curvetosupportatoken##1##2{%
\pgf@xb=##1%
\pgf@yb=##2%
}%
\def\pgfsyssoftpath@curvetosupportbtoken##1##2{%
\pgf@xb=##1%
\pgf@yb=##2%
}%
\def\pgfsyssoftpath@linetotoken##1##2{%
\pgf@xb=##1%
\pgf@yb=##2%
}%
#1
\tikzset{insert path={(\the\pgf@xa,\the\pgf@ya) coordinate (first)
(\the\pgf@xb,\the\pgf@yb) coordinate (last)}}
\endgroup}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\path[save path=\pathA]
(0, 1) .. controls (1, 4) .. (4, 2) .. controls (6, 3) .. (10, 0);
\draw[first and last coordinates of=\pathA,blue][use path=\pathA]
(first) node[circle,fill,red]{} (last) node[circle,fill,red]{};
\end{tikzpicture}
\end{document}