我需要重现各种 2D 和 3D 图形,例如下面显示的图形,这些图形原本是手绘的。每个图形的某些部分显然可以直接用TikZ
代码完成,但其他部分,尤其是不规则的“曲线”部分,则更容易通过手绘创建。
请注意,我将修改其中一些图形并创建新的图形,这些图形既有规则的、易于编码的部分(包括数学符号),也有不规则的“曲线”部分。
我设想了一个工作流程,其中这些“弯曲”的部分是使用 iPad Pro 上的绘图应用程序构建的。
问题:是否有适用于 iPad Pro 的 iOS 应用程序,可以将手绘的“曲线”部分轻松转换为TikZ
代码,然后添加到最适合直接构建的部件中TikZ
?或者有更好的方法吗?
笔记:我是不是要求提供TikZ
创建这些图形的代码!相反,我问的是导致该代码的技术方法。
答案1
不是一个答案,只是为了好玩。我知道你没有要求代码。这主要是为了证实用基本 Ti 绘制这些不规则形状并不太难的说法钾Z 语法。以下是列表中两个图形的复制品。
\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{calc,intersections,arrows.meta}
\usepackage{pgfplots}
\usepgfplotslibrary{fillbetween}
\begin{document}
\begin{tikzpicture}[long dash/.style={dash pattern=on 10pt off 2pt}]
\draw[ultra thick,long dash,name path=left,fill=orange!30] plot[smooth cycle] coordinates
{(0.3,-2) (-1,-3) (-8,-1.2) (-8.8,-0.2) (-7,0.6) (-1,-0.6)};
\draw[ultra thick,long dash,name path=left bottom] plot[smooth cycle] coordinates
{(-8,-2.8) (-9,-2.5) (-8.5,-1) (-7,0) (-6,1.7) (-5,1.7) (-4,-0) (-5.5,-2)};
\draw[ultra thick,long dash,name path=left top] plot[smooth cycle] coordinates
{(-7.2,-1) (-7.8,1) (-6.7,2) (-5.5,1) (-5,0) (-5.4,-1) (-6,-1.2)};
\path [%draw,blue,ultra thick,
name path=left arc,
intersection segments={
of=left top and left,
sequence={A1--B1}
}];
\path [%draw,red,ultra thick,
fill=red!30,
name path=left blob,
intersection segments={
of=left bottom and left arc,
sequence={A1--B0}
}];
\node[fill,circle,label=above right:$F$] at (-6.1,-0.3){};
\node at (-2.5,-1.8){$V_y$};
% right part
\path[fill=orange!30] plot[smooth cycle] coordinates
{(-1.3,2) (-0.7,3) (1,3.7) (5.2,3) (8,1.6) (8.4,1) (8,0.3) (6,0) (4,0) (2,0.3) (0,1)};
\path[fill=blue!30] plot[smooth cycle] coordinates
{(0,-2) (-0.3,-1.5) (-0.2,0) (-0.3,1) (-1,2) (0,2.8) (3,2) (7,1) (7.3,-1)
(6,-2.3) (4,-2.3) (2,-2)};
\draw[ultra thick,long dash,name path=right top] plot[smooth cycle] coordinates
{(-1.3,2) (-0.7,3) (1,3.7) (5.2,3) (8,1.6) (8.4,1) (8,0.3) (6,0) (4,0) (2,0.3) (0,1)};
\draw[ultra thick,name path=right] plot[smooth cycle] coordinates
{(0,-2) (-0.3,-1.5) (-0.2,0) (-0.3,1) (-1,2) (0,2.8) (3,2) (7,1) (7.3,-1)
(6,-2.3) (4,-2.3) (2,-2)};
\draw[ultra thick,long dash,name path=middle] plot[smooth cycle] coordinates
{(0,-3.4) (-1,-2) (-1,-0.5) (-1.5,0.4) (-1,1.6) (0,1.9) (2.1,1) (3,-1) (2.5,-3) (1,-3.7)};
\draw[ultra thick,long dash,name path=right bottom] plot[smooth cycle] coordinates
{(1,-3) (0.6,-2) (1.2,0) (3,0.8) (6,0.8) (8.5,1) (10,0) (9,-3) (7,-3.7) (5,-3.6) (2,-3.6)};
\path[name path=circle] (5.2,1.5) arc(-30:190:4mm);
\path [%draw,red,ultra thick,
name path=aux1,
intersection segments={
of=circle and right,
sequence={B1}
}];
\path [draw,blue!30,ultra thick,
name path=aux2,
intersection segments={
of=circle and aux1,
sequence={B0}
}];
\node at (4.8,1.6){$U_y$};
\node[fill,circle,label=below right:$y$] at (3.3,1.5){};
\node[fill=blue!30] at (3.7,0){$K$};
\end{tikzpicture}
\begin{tikzpicture}[thick]
\draw[-latex] (0,0) -- (5,0) node[right]{$s$};
\draw[-latex] (0,0) -- (0,7) node[left]{$y$};
\draw (4,0) -- (4,5.5);
\foreach \X in {1,2,4.5}
{\draw (0,\X) -- (4,\X);}
\foreach \X/\Y [count=\Z]in {0/0,3.5/t,5.5/1}
{
\ifnum\Z=1
\draw[very thick,fill] (0,\X) circle(1pt) node[left]{$(0,\Y)$} -- (4,\X)
coordinate[midway,below] (l1) circle(1pt)
node[below right]{$(1,\Y)$};
\else
\draw[very thick,fill] (0,\X) circle(1pt) node[left]{$(0,\Y)$} -- (4,\X)
\ifnum\Z=2
coordinate[midway,below] (l3)
\fi
\ifnum\Z=3
coordinate[midway,above] (l2)
\fi
circle(1pt)
node[right]{$(1,\Y)$};
\fi
}
\draw[fill,very thick] (1.5,3.5) circle (1pt) node[below] {$(s,t)$};
\begin{scope}[xshift=6.5cm]
\draw[very thick] plot[smooth cycle] coordinates
{(0,2) (0,5) (1.3,7) (5,7.9) (8.2,6) (8.3,3) (6,1.4) (2,1.2)};
\node[circle,fill,scale=0.6] (L) at (0.5,4){};
\node[circle,fill,scale=0.6] (R) at (7.5,4.2){};
\foreach \X in {-45,-20,-5,45,60}
{\pgfmathsetmacro{\Y}{180-\X+4*rnd}
\draw (L) to[out=\X,in=\Y,looseness=1.2] (R);
\ifnum\X=-45
\path (L) to[out=\X,in=\Y,looseness=1.2] coordinate[pos=0.5,below] (r1)
node[pos=0.6,below]{$\sigma$} (R);
\fi
\ifnum\X=60
\path (L) to[out=\X,in=\Y,looseness=1.2] coordinate[pos=0.4,above] (r2)
node[pos=0.6,above]{$\tau$} (R);
\fi
}
\draw[very thick] (L) to[out=20,in=163,looseness=1.2]
node[pos=0.2,circle,fill,scale=0.6,label=above right:$h_t(s)$]{}
coordinate[pos=0.35] (r3) (R);
\end{scope}
\draw[-latex,shorten >=2pt] (l1) to[out=14,in=220] (r1);
\draw[-latex,shorten >=2pt] (l2) to[out=24,in=140] (r2);
\draw[-latex,shorten >=2pt] (l3) to[out=-12,in=210] (r3);
\end{tikzpicture}
\end{document}
他们应该用 Ti 来说明钾Z 你可以做各种很酷的事情,比如计算线和/或面的交点。当然,你可以用一些图形软件做类似的事情,但在我看来,这种方法的优点是你只需要改变一件事就可以进行全局更改。例如,如果你不喜欢虚线的长度,你只需要重新定义样式。最后但并非最不重要的是,我认为这样做更有趣。我当然明白其他人可能不同意我的观点。
附录:将手绘图形转换为“漂亮(更好)”的提案 Ti钾Z 代码。首先以某种格式保存你的手绘图形,这里我将调用文件tmp.png
(我刚刚为其拍摄了第一张照片)。然后将其包含在 Ti钾Z 图片并读取一些坐标smooth cycles
等等。
\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
\node (tmp) {\includegraphics{tmp.png}};
\draw (tmp.south west) grid (tmp.north east);
\draw let \p1=(tmp.south west), \p2=(tmp.north east) in
\pgfextra{\pgfmathsetmacro{\xmin}{int(\x1/1cm)}
\pgfmathsetmacro{\xmax}{int(\x2/1cm)}
\pgfmathsetmacro{\ymin}{int(\y1/1cm)}
\pgfmathsetmacro{\ymax}{int(\y2/1cm)}
\typeout{\xmin,\xmax}}
\foreach \X in {\xmin,...,\xmax} {(\X,\y1) node[anchor=north] {\X}}
\foreach \Y in {\ymin,...,\ymax} {(\x1,\Y) node[anchor=east] {\Y}};
\end{tikzpicture}
\end{document}
最有可能的是,人们可以编写一个代码来提取轮廓上的一些坐标。我猜,通过机器学习,很快就可以让计算机做到这一点。是的,目前手工做到这一点很痛苦,但也不是特别痛苦。(如果有人已经用带标签的自动网格完成了这件事,我提前道歉,我找不到它,所以我自己很快写了它。在示例中,我发现刻度标签的范围是硬编码的。)
答案2
外部绘图程序(主要用鼠标,而不是手绘)
这些都列在是否有可以生成相应 LaTeX 代码的在线图表图形编辑器?或者对于 PGF/TikZ 来说,所见即所得 (WYSIWYG)?或者有哪些 GUI 应用程序可以帮助生成 TeX 图形?。
- 马查
- GeoGebra
- ktikz/qtikz
- 迪亚
- Inkscape
- 提克兹
- 电路
- 蒂克
- 更多杂项工具...
耶茲
yEd 是一款通用的图形编辑器,可以导入各种文件格式,包括手绘图形的图像。它具有将图形导出为 TikZ 代码的功能,然后您可以将其包含在 LaTeX 文档中。虽然它无法完美地重新创建手绘图形,但与在 TikZ 中手动重新创建图形相比,它可以为您节省大量时间和精力。
自由提克
答案3
您必须在最初的努力和维护的努力之间进行权衡。@marmot 回答如果您认为将来需要大量修改图表,那么这就是可行的方法。
如果你认为你永远不会改变手工制作的图形,也许一个简单的解决方案导入它们并用tikz
使用帮助网格是最快的方法。
一种可能的中间解决方案是跳转到过去并使用基于的解决方案psfrag
。请注意,此包不起作用(和无法工作) 采用现代引擎,因此必须使用一些技巧。
psfrag
很不错,因为它允许你.eps
用 LaTeX 结构(无论什么)替换文件中的字符串。我用它来增强用xcircuit
(使用起来非常快,我从未找到更好的东西;尽管我现在正试图转向,因为tkcircuitz
我有如此大的电路宝库......)。
我将通过示例向您展示。我用纯文本标签绘制了这个电路;假设它被称为buffer.eps
:
然后我为它编写了一个“替换文件”,称之为buffer-psfrag.eps
:
\mypsf{r50}{\SI{50}{\ohm}}
我有一个通用的psfragdefinitions.tex
:
%%
%% psfrag and general replacements
%%
\newcommand{\psfscale}{1.2}
\newcommand{\mypsf}[2]{%
\psfrag{#1}[Bl][Bl][\psfscale]{#2}
}
\mypsf{R}{$R$}
\mypsf{R1}{$R_1$}
\mypsf{R2}{$R_2$}
\mypsf{Rin}{$R_\mathrm{in}$}
\mypsf{Rout}{$R_\mathrm{out}$}
\mypsf{E}{$E$}
\mypsf{I}{$I$}
\mypsf{vo}{$v_o$}
\mypsf{vs}{$v_s$}
\mypsf{vi}{$v_i$}
\mypsf{v+}{$v^+$}
\mypsf{v-}{$v^-$}
\mypsf{L+}{$L^+$}
\mypsf{L-}{$L^-$}
\endinput
然后我使用我编写的一个 python 脚本(附加在这个答案的末尾),该脚本调用旧的latex
并执行dvi
-> ps
->pdf
转换,尝试嵌入和子集所有字体(我遇到了问题......):
./epsfragtopdf.py buffer.eps
获得:
一旦您拥有一组结构良好的包含脚本和 Makefile 来自动化它,它就会非常快;标签经常重复,因此您只需编写一次 psfrag 文件。
我想您可以使用任何允许您手绘、添加标签并保存到 Encapsulated Postscript 的应用程序来执行相同操作。
用 调用的脚本subdir/name.eps
将使用在、 然后和 最后psfrag
中找到的定义:./psfragdefinitios.tex
subdir/psfragdefinitions.tex
name-psfrag.tex
#!/usr/bin/env python3
#
import sys
import os
import argparse
import tempfile
import subprocess
import shutil
#
latex_pre=r"""\documentclass[12pt]{article}
\usepackage{graphicx,psfrag}
\usepackage[active,tightpage]{preview}
\usepackage{siunitx}
"""
latex_med=r"""\pagestyle {empty}
\begin{document}
\centering\null\vfill
\begin{preview}
"""
latex_post=r"""\end{preview}
\vfill
\end{document}
"""
global_def_fname="psfragdefinitions.tex"
local_def_post="-psfrag"
latex_cmd="TEXINPUTS=$TEXINPUTS:../ latex -interaction nonstopmode go.tex >/dev/null 2>&1"
dvips_cmd="dvips go.dvi >/dev/null 2>&1"
ps2pdf_cmd="ps2pdf -dPDFSETTINGS=/prepress -dEmbedAllFonts=true -dSubsetFonts=true go.ps >/dev/null 2>&1"
#
# Main
#
parser = argparse.ArgumentParser(description='Convert eps to pdf using psfrag')
parser.add_argument('--force', dest='force', action='store_true', default=False,
help='force conversion even if the PDF is newer (default: false)')
parser.add_argument('epsfiles', metavar='eps_file', nargs='+',
help='files to be converted')
args = parser.parse_args()
for filepath in args.epsfiles:
filedir, filename = os.path.split(filepath)
filebase, fileext = os.path.splitext(filename)
# print ("dir:", filedir, " base:", filebase, " ext:", fileext)
if fileext!= ".eps":
print(filepath, "does not end in .eps, skipped")
continue
if not os.path.exists(filepath):
print(filepath, "does not exists, skipped")
continue
targetfilename = filebase + ".pdf"
targetfilepath = os.path.join(filedir, targetfilename)
if os.path.exists(targetfilepath) and not args.force and os.path.getmtime(targetfilepath)>os.path.getmtime(filepath):
print(targetfilepath, "is newer than", filepath, ", skipped")
continue
print("processing", filepath, "to", targetfilepath)
tmpdir = tempfile.mkdtemp(dir=".")
# tmpdir = "tmp"
if not os.path.exists(tmpdir):
os.mkdir(tmpdir)
latexf = open(os.path.join(tmpdir,"go.tex"),"w")
latexf.write(latex_pre)
# search the file-definitions in the current dir, in the target dir
if os.path.exists(global_def_fname):
latexf.write("\\input{%s}\n" % os.path.join("..",global_def_fname))
print("including", global_def_fname)
if filedir!="" and os.path.exists(os.path.join(filedir, global_def_fname)):
latexf.write("\\input{%s}\n" % os.path.join("..", os.path.join(filedir, global_def_fname)))
print("including", os.path.join(filedir, global_def_fname))
# search specific file-definition
specfpath = os.path.join(filedir, filebase + local_def_post + ".tex")
if os.path.exists(specfpath):
latexf.write("\\input{%s}\n" % os.path.join("..", specfpath))
print("including", specfpath)
latexf.write(latex_med)
latexf.write("\\includegraphics{%s}\n" % os.path.join("..",filepath))
latexf.write(latex_post)
latexf.close()
# now we build the figure
subprocess.check_call(latex_cmd, cwd=tmpdir, shell=True)
subprocess.check_call(dvips_cmd, cwd=tmpdir, shell=True)
subprocess.check_call(ps2pdf_cmd,cwd=tmpdir, shell=True)
# now we copy the file back where it should be
shutil.copy(os.path.join(tmpdir,"go.pdf"), targetfilepath)
shutil.rmtree(tmpdir)