在 TikZ 中复制 chronodex

在 TikZ 中复制 chronodex

我尝试在 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}

在此处输入图片描述

在此处输入图片描述

在此处输入图片描述

相关内容