我尝试在 TikZ 中制作 chronodex,并且我有一个基本的骨架。我需要在其中添加一些其他内容(刻度、标签、节点)。此外,代码本身可以使用循环进行优化,但我仍在学习如何做到这一点。有人可以就这些问题给我一些建议,以便我能够得出最终的概念吗?
我的 chronodex 的代码如下:
\documentclass[border=0.5cm]{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
% Grid
\foreach \i in {1,2,...,6}
{
\draw[lightgray!40, dotted] (0,0) circle(\i);
}
\foreach \i in {0,45,90,135,...,315}
{
\draw[lightgray!20, dotted] (0,0) -- (\i:7.2) node[pos=0.96,black,fill= white]{\i};
}
%--- Tried to write a formula for the arcs
%\foreach \i in {3,4,...,5}
%{
% \foreach \j in {1,2,...,4}
% {
% \draw (90 - 30 * (\i - 3):\i) arc( : :\i);
% }
%}
\begin{scope}[black, very thick, cap=round, rounded corners=1pt]
% ARCS
%--- Inner arc EH ---%
\draw[loosely dotted] (180:2) arc(180:270:2);
%--- The full circle ---%
\draw (0:3) arc(0:360:3);
%--- Arcs w/ rad = 4 ---%
\draw (90:4) arc(90:60:4);
\draw (330:4) arc(330:360:4);
\draw (240:4) arc(240:270:4);
\draw (150:4) arc(150:180:4);
%--- Arcs w/ rad = 5 ---%
\draw (60:5) arc(60:30:5);
\draw (300:5) arc(300:330:5);
\draw (210:5) arc(210:240:5);
\draw (120:5) arc(120:150:5);
%--- Arcs w/ rad = 6 ---%
\draw (30:6) arc(30:0:6);
\draw (270:6) arc(270:300:6);
\draw (180:6) arc(180:210:6);
\draw (90:6) arc(90:120:6);
%--- Outer arc LH ---%
\draw[loosely dotted] (90:7) arc(90:180:7);
% LINES
\draw (0:3) -- (0:6);
\draw (30:3) -- (30:6);
\draw (90:3) -- (90:6);
\draw (120:3) -- (120:6);
\draw (90:3) -- (90:6);
\draw (120:3) -- (120:6);
\draw (180:3) -- (180:6);
\draw (210:3) -- (210:6);
\draw (270:3) -- (270:6);
\draw (300:3) -- (300:6);
\draw (60:3) -- (60:5);
\draw (150:3) -- (150:5);
\draw (240:3) -- (240:5);
\draw (330:3) -- (330:5);
\draw[loosely dotted] (180:2) -- (180:3);
\draw[loosely dotted] (210:2) -- (210:3);
\draw[loosely dotted] (240:2) -- (240:3);
\draw[loosely dotted] (270:2) -- (270:3);
\draw[loosely dotted] (90:6) -- (90:7);
\draw[loosely dotted] (180:6) -- (180:7);
\draw[loosely dotted] (120:6) -- (120:7);
\draw[loosely dotted] (150:5) -- (150:7);
\end{scope}
\end{tikzpicture}
\end{document}
答案1
到目前为止做得不错。请看下面我重构的代码,它用少量循环替换了许多绘制语句。
重构是一件好事。每当你完成某个部分,比如一组绘制语句,检查是否有共同点或更通用的部分。根据需要进行替换,有时还要考虑下一步。但是,要循序渐进,而不是突破性地进行。// 现在的方式可能会进一步简化,代价是代码更难阅读,引入新的来源错误。这是一个品味和经验的问题。
看起来你只需要一个概念:如何从循环中每次迭代获取多个值。看这里:
- 我想要两个表示角度的变量,
\v
比如\w
- Tikz 允许我用我喜欢的符号来分隔它们;让我们尝试一下
/
作为分隔符 - 所以每次迭代我都需要这对数据
\v/\w
- 对于我以同样的方式指定的集合
{90/60, 330/360, 240/270, 150/180}
- 剩下的就是
draw
像这样提供语句: \draw (\v:5) arc(\v:\w:5);
- 就是这样
%--- Arcs w/ rad = 4 ---%
\foreach \v/\w in {90/60, 330/360, 240/270, 150/180}
\draw ( \v:4) arc( \v: \w:4);
%\documentclass[border=0.5cm]{standalone}
\documentclass[10pt,border=5mm,tikz]{standalone}
\usepackage{tikz}
\begin{document}
% ~~~ refactored code ~~~~~~~~~~~~~~~
\begin{tikzpicture}
% Grid
\foreach \i in {1,2,...,6}
% ~~~ make it visible for me ~~~~
\draw[lightgray!40!red, dotted] (0,0) circle(\i);
\foreach \i in {0,45,90,135,...,315}
\draw[lightgray!20!blue, dotted] (0,0) -- (\i:7.2)
node[pos=0.96,black,fill= white]{\i};
\begin{scope}[black, very thick, cap=round, rounded corners=1pt]
% ARCS
%--- Inner arc EH ---%
\draw[loosely dotted] (180:2) arc(180:270:2);
%--- The full circle ---%
\draw (0:3) arc(0:360:3);
%--- Arcs w/ rad = 4 ---%
\foreach \v/\w in {90/60, 330/360, 240/270, 150/180}
\draw ( \v:4) arc( \v: \w:4);
%--- Arcs w/ rad = 5 ---%
\foreach \v/\w in {60/30, 300/330, 210/240, 120/150}
\draw ( \v:5) arc( \v: \w:5);
%--- Arcs w/ rad = 6 ---%
\foreach \v/\w in {30/0, 270/300, 180/210, 90/120}
\draw ( \v:6) arc( \v: \w:6);
%--- Outer arc LH ---%
\draw[loosely dotted] (90:7) arc(90:180:7);
% LINES
\foreach \w in {0,30,90,120,90,120,180,210,270,300}
\draw (\w:3) -- (\w:6);
\foreach \w in {60,150,240,330}
\draw (\w:3) -- (\w:5);
\foreach \w in {180,210,240,270}
\draw [loosely dotted] (\w:2) -- (\w:3);
\foreach \w in {30,180,120}
\draw [loosely dotted] (\w:6) -- (\w:7);
\draw[loosely dotted] (150:5) -- (150:7);
\end{scope}
\end{tikzpicture}
% ~~~ original post ~~~~~~~~~~~~~~~
\begin{tikzpicture}
% Grid
\foreach \i in {1,2,...,6}
{
\draw[lightgray!40, dotted] (0,0) circle(\i);
}
\foreach \i in {0,45,90,135,...,315}
{
\draw[lightgray!20, dotted] (0,0) -- (\i:7.2) node[pos=0.96,black,fill= white]{\i};
}
%--- Tried to write a formula for the arcs
%\foreach \i in {3,4,...,5}
%{
% \foreach \j in {1,2,...,4}
% {
% \draw (90 - 30 * (\i - 3):\i) arc( : :\i);
% }
%}
\begin{scope}[black, very thick, cap=round, rounded corners=1pt]
% ARCS
%--- Inner arc EH ---%
\draw[loosely dotted] (180:2) arc(180:270:2);
%--- The full circle ---%
\draw (0:3) arc(0:360:3);
%--- Arcs w/ rad = 4 ---%
\draw (90:4) arc(90:60:4);
\draw (330:4) arc(330:360:4);
\draw (240:4) arc(240:270:4);
\draw (150:4) arc(150:180:4);
%--- Arcs w/ rad = 5 ---%
\draw (60:5) arc(60:30:5);
\draw (300:5) arc(300:330:5);
\draw (210:5) arc(210:240:5);
\draw (120:5) arc(120:150:5);
%--- Arcs w/ rad = 6 ---%
\draw (30:6) arc(30:0:6);
\draw (270:6) arc(270:300:6);
\draw (180:6) arc(180:210:6);
\draw (90:6) arc(90:120:6);
%--- Outer arc LH ---%
\draw[loosely dotted] (90:7) arc(90:180:7);
% LINES
\draw (0:3) -- (0:6);
\draw (30:3) -- (30:6);
\draw (90:3) -- (90:6);
\draw (120:3) -- (120:6);
\draw (90:3) -- (90:6);
\draw (120:3) -- (120:6);
\draw (180:3) -- (180:6);
\draw (210:3) -- (210:6);
\draw (270:3) -- (270:6);
\draw (300:3) -- (300:6);
\draw (60:3) -- (60:5);
\draw (150:3) -- (150:5);
\draw (240:3) -- (240:5);
\draw (330:3) -- (330:5);
\draw[loosely dotted] (180:2) -- (180:3);
\draw[loosely dotted] (210:2) -- (210:3);
\draw[loosely dotted] (240:2) -- (240:3);
\draw[loosely dotted] (270:2) -- (270:3);
\draw[loosely dotted] (90:6) -- (90:7);
\draw[loosely dotted] (180:6) -- (180:7);
\draw[loosely dotted] (120:6) -- (120:7);
\draw[loosely dotted] (150:5) -- (150:7);
\end{scope}
\end{tikzpicture}
\end{document}
答案2
我从头开始重新创建了所有内容,并使其\pic
成为高度可定制的,并允许使用 24 小时或上午/下午格式。它使用calendar
库,可以轻松创建由以下\pic
内容组成的完整日历:
\documentclass[border=10pt, tikz]{standalone}
\usetikzlibrary{calendar}
\newcount\chronodexcurrentdate
\newcount\chronodexcurrentweekday
\tikzset{
pics/chronodex/.style={
code={
\tikzset{chronodex/.cd, #1}
% inner segments
\fill[chronodex/segment 6 to 7 am]
(270:3) arc[start angle=270, end angle=240, radius=3] --
(240:2) arc[start angle=240, end angle=270, radius=2] -- cycle;
\fill[chronodex/segment 7 to 8 am]
(240:3) arc[start angle=240, end angle=210, radius=3] --
(210:2) arc[start angle=210, end angle=240, radius=2] -- cycle;
\fill[chronodex/segment 8 to 9 am]
(210:3) arc[start angle=210, end angle=180, radius=3] --
(180:2) arc[start angle=180, end angle=210, radius=2] -- cycle;
\draw[chronodex/inner ring]
(90:2) arc[start angle=90, end angle=-180, radius=2];
\foreach \i [count=\a from 0] in {90,60,...,-180} {
\draw[chronodex/inner line]
(\i:3) -- (\i:2);
\pgfmathparse{int(\a > 0 && \a < 7 ? 1 : 0)}
\ifnum\pgfmathresult=1\relax
\node[rotate={\i}, anchor=south east, chronodex/inner label]
at (\i:3) {\a\pgfkeysvalueof{/tikz/chronodex/am suffix}};
\fi
\pgfmathparse{int(\a > 6 && \a < 9 ? 1 : 0)}
\ifnum\pgfmathresult=1\relax
\node[rotate={\i-180}, anchor=south west, chronodex/inner label]
at (\i:3) {\a\pgfkeysvalueof{/tikz/chronodex/am suffix}};
\fi
}
% outer segments
\fill[chronodex/segment 9 to 10 pm]
(180:7) arc[start angle=180, end angle=150, radius=7] --
(150:6) arc[start angle=150, end angle=180, radius=6] -- cycle;
\fill[chronodex/segment 10 to 11 pm]
(150:7) arc[start angle=150, end angle=120, radius=7] --
(120:6) arc[start angle=120, end angle=150, radius=6] -- cycle;
\fill[chronodex/segment 11 pm to 12 am]
(120:7) arc[start angle=120, end angle=90, radius=7] --
(90:6) arc[start angle=90, end angle=120, radius=6] -- cycle;
\draw[chronodex/outer ring]
(180:7) arc[start angle=180, end angle=90, radius=7]
(90:6) arc[start angle=90, end angle=180, radius=6];
\foreach \o [count=\p from 0] in {180,150,...,90} {
\draw[chronodex/outer line]
(\o:7) -- (\o:6);
\ifnum\p<3\relax
\pgfmathsetmacro{\h}{int(\p+9+\pgfkeysvalueof{/tikz/chronodex/24 hours conversion})}
\node[rotate={\o-180}, anchor=south west, chronodex/outer label]
at (\o:7) {\h\pgfkeysvalueof{/tikz/chronodex/pm suffix}};
\fi
}
% base segments
\foreach \b [count=\a from 0] in {180,90,...,-90} {
\draw[chronodex/additional ring]
(\b:5) arc[start angle={\b}, end angle={\b-30}, radius=5]
(\b:6) arc[start angle={\b}, end angle={\b-60}, radius=6]
({\b-30}:5) -- ({\b-30}:6);
\fill[chronodex/base segments]
(\b:4) arc[start angle={\b}, end angle={\b-30}, radius=4] --
({\b-30}:5) arc[start angle={\b-30}, end angle={\b-60}, radius=5] --
({\b-60}:6) arc[start angle={\b-60}, end angle={\b-90}, radius=6] --
({\b-90}:3) arc[start angle={\b-90}, end angle={\b}, radius=3] -- (\b:3) -- cycle;
\draw[chronodex/primary ring]
(\b:3) -- (\b:4)
arc[start angle={\b}, end angle={\b-30}, radius=4] -- ({\b-30}:3);
\draw[chronodex/primary ring]
({\b-30}:3) -- ({\b-30}:5)
arc[start angle={\b-30}, end angle={\b-60}, radius=5] -- ({\b-60}:3);
\draw[chronodex/secondary ring]
({\b-30}:4) arc[start angle={\b-30}, end angle={\b-60}, radius=4];
\draw[chronodex/primary ring]
({\b-60}:3) -- ({\b-60}:6)
arc[start angle={\b-60}, end angle={\b-90}, radius=6] -- ({\b-90}:3);
\draw[chronodex/secondary ring]
({\b-60}:4) arc[start angle={\b-60}, end angle={\b-90}, radius=4]
({\b-60}:5) arc[start angle={\b-60}, end angle={\b-90}, radius=5];
\foreach \n in {7.5,15,22.5} {
\foreach \t/\a in {4/0, 5/30, 6/60} {
\tikzset{chronodex/tick bottom start={3}}
\draw[rotate={\b-\a-\n}, chronodex/primary tick]
\pgfkeysvalueof{/tikz/chronodex/tick bottom code};
\tikzset{chronodex/tick top start={\t}}
\draw[rotate={\b-\a-\n}, chronodex/primary tick]
\pgfkeysvalueof{/tikz/chronodex/tick top code};
}
\tikzset{chronodex/tick middle start={4}}
\draw[rotate={\b-30-\n}, chronodex/secondary tick]
\pgfkeysvalueof{/tikz/chronodex/tick middle code};
\draw[rotate={\b-60-\n}, chronodex/secondary tick]
\pgfkeysvalueof{/tikz/chronodex/tick middle code};
\tikzset{chronodex/tick middle start={5}}
\draw[rotate={\b-60-\n}, chronodex/secondary tick]
\pgfkeysvalueof{/tikz/chronodex/tick middle code};
}
\ifnum\a=0\relax
\node[rotate={\b-180}, anchor=south west, chronodex/base label]
at (\b:4) {9\pgfkeysvalueof{/tikz/chronodex/am suffix}};
\node[rotate={\b-210}, anchor=south west, chronodex/base label]
at ({\b-30}:5) {10\pgfkeysvalueof{/tikz/chronodex/am suffix}};
\node[rotate={\b-240}, anchor=south west, chronodex/base label]
at ({\b-60}:6) {11\pgfkeysvalueof{/tikz/chronodex/am suffix}};
\node[rotate={\b-270}, anchor=south west, chronodex/base label]
at ({\b-90}:6) {12\pgfkeysvalueof{/tikz/chronodex/noon suffix}};
\else
\ifnum\a=3\relax
\pgfmathsetmacro{\h}{int(\a*3-2+\pgfkeysvalueof{/tikz/chronodex/24 hours conversion})}
\node[rotate={\b-210}, anchor=south west, chronodex/base label]
at ({\b-30}:5) {\h\pgfkeysvalueof{/tikz/chronodex/pm suffix}};
\pgfmathsetmacro{\h}{int(\a*3-1+\pgfkeysvalueof{/tikz/chronodex/24 hours conversion})}
\node[rotate={\b-240}, anchor=south west, chronodex/base label]
at ({\b-60}:6) {\h\pgfkeysvalueof{/tikz/chronodex/pm suffix}};
\else
\pgfmathsetmacro{\h}{int(\a*3-2+\pgfkeysvalueof{/tikz/chronodex/24 hours conversion})}
\node[rotate={\b-30}, anchor=south east, chronodex/base label]
at ({\b-30}:4) {\h\pgfkeysvalueof{/tikz/chronodex/pm suffix}};
\pgfmathsetmacro{\h}{int(\a*3-1+\pgfkeysvalueof{/tikz/chronodex/24 hours conversion})}
\node[rotate={\b-60}, anchor=south east, chronodex/base label]
at ({\b-60}:5) {\h\pgfkeysvalueof{/tikz/chronodex/pm suffix}};
\pgfmathsetmacro{\h}{int(\a*3+\pgfkeysvalueof{/tikz/chronodex/24 hours conversion})}
\node[rotate={\b-90}, anchor=south east, chronodex/base label]
at ({\b-90}:6) {\h\pgfkeysvalueof{/tikz/chronodex/pm suffix}};
\fi
\fi
}
\draw[chronodex/base ring]
(0:0) circle[radius=3];
% day and weekday
\ifdefined\chronodexcurrentday
\pgfmathtruncatemacro{\d}{\chronodexcurrentday.0}
\node[chronodex/day]
at (0,0) {\d};
\node[chronodex/weekday]
at (0,0) {\pgfcalendarweekdayname{\chronodexcurrentweekday}};
\fi
}
},
chronodex/date/.code={
\pgfcalendardatetojulian{#1}{\chronodexcurrentdate}
\pgfcalendarjuliantodate{\chronodexcurrentdate}{\chronodexcurrentyear}{\chronodexcurrentmonth}{\chronodexcurrentday}
\pgfcalendarjuliantoweekday{\chronodexcurrentdate}{\chronodexcurrentweekday}
},
chronodex/date/.initial={},
chronodex/am suffix/.initial={\,am},
chronodex/pm suffix/.initial={\,pm},
chronodex/noon suffix/.initial={\,noon},
chronodex/24 hours conversion/.initial={0},
chronodex/24 hours/.style={
am suffix={:00},
pm suffix={:00},
noon suffix={:00},
24 hours conversion={12},
},
chronodex/minor tick length/.initial={5},
chronodex/major tick length/.initial={7},
chronodex/tick top start/.initial={0},
chronodex/tick middle start/.initial={0},
chronodex/tick bottom start/.initial={0},
chronodex/tick top code/.initial={
(0:\pgfkeysvalueof{/tikz/chronodex/tick top start}) --
++(0:{(\n == 15 ? \pgfkeysvalueof{/tikz/chronodex/major tick length} : \pgfkeysvalueof{/tikz/chronodex/minor tick length})*-1pt})
},
chronodex/tick middle code/.initial={
([shift={(0:{(\n == 15 ? \pgfkeysvalueof{/tikz/chronodex/major tick length} : \pgfkeysvalueof{/tikz/chronodex/minor tick length})*-0.5pt})}]0:\pgfkeysvalueof{/tikz/chronodex/tick middle start}) --
++(0:{(\n == 15 ? \pgfkeysvalueof{/tikz/chronodex/major tick length} : \pgfkeysvalueof{/tikz/chronodex/minor tick length})*1pt})
},
chronodex/tick bottom code/.initial={
(0:\pgfkeysvalueof{/tikz/chronodex/tick bottom start}) --
++(0:{(\n == 15 ? \pgfkeysvalueof{/tikz/chronodex/major tick length} : \pgfkeysvalueof{/tikz/chronodex/minor tick length})*1pt})
},
chronodex/base ring/.style={
thick,
},
chronodex/base segments/.style={
white!0,
},
chronodex/primary ring/.style={
thick,
},
chronodex/primary tick/.style={
},
chronodex/secondary ring/.style={
gray,
},
chronodex/secondary tick/.style={
},
chronodex/inner ring/.style={
gray,
densely dashed,
},
chronodex/inner line/.style={
gray,
},
chronodex/outer ring/.style={
gray,
densely dashed,
},
chronodex/additional ring/.style={
gray,
densely dotted,
},
chronodex/outer line/.style={
gray,
},
chronodex/label/.style={
font=\footnotesize,
},
chronodex/base label/.style={
chronodex/label,
},
chronodex/inner label/.style={
chronodex/label,
gray,
},
chronodex/outer label/.style={
chronodex/label,
gray,
},
chronodex/segment 6 to 7 am/.style={
gray!25,
},
chronodex/segment 7 to 8 am/.style={
gray!15,
},
chronodex/segment 8 to 9 am/.style={
gray!5,
},
chronodex/segment 9 to 10 pm/.style={
white!0,
},
chronodex/segment 10 to 11 pm/.style={
white!0,
},
chronodex/segment 11 pm to 12 am/.style={
white!0,
},
chronodex/day/.style={
anchor=south,
font=\Huge,
},
chronodex/weekday/.style={
anchor=north,
font=\large,
},
}
\begin{document}
\begin{tikzpicture}
\pic {chronodex};
\end{tikzpicture}
\begin{tikzpicture}
\pic {chronodex={date=2023-06-08}};
\end{tikzpicture}
\begin{tikzpicture}
\pic {chronodex={24 hours,
tick middle code={
(0:\pgfkeysvalueof{/tikz/chronodex/tick middle start}) node[circle, inner sep=1.25pt, fill=cyan] {}
},
base ring/.append style={cyan, very thick},
base segments/.append style={cyan!10},
primary ring/.append style={cyan, thick},
primary tick/.append style={magenta!50},
secondary tick/.append style={cyan},
segment 6 to 7 am/.append style={magenta!25},
segment 7 to 8 am/.append style={magenta!15},
segment 8 to 9 am/.append style={magenta!5},
segment 9 to 10 pm/.append style={magenta!25},
segment 10 to 11 pm/.append style={magenta!15},
segment 11 pm to 12 am/.append style={magenta!5},
label/.append style={magenta, font=\sffamily}}};
\end{tikzpicture}
\end{document}