msdn画圆弧函数_精确之美——用TikZ画硬盘示意图
序言
備考某等級(jí)考試的時(shí)候,在教材中碰到了幾個(gè)一直不太理解的、關(guān)于硬盤的概念:磁道、柱面號(hào)、扇區(qū)。然而教材沒有配圖,無法直觀地了解這些概念的物理形態(tài)。維基百科的硬盤[1]詞條頁(yè)中倒是有一副不錯(cuò)的示意圖,我截圖搬運(yùn)了過來
機(jī)械硬盤示意圖原圖是一張 SVG 圖片,本質(zhì)上是一堆指令——也就是所謂的語(yǔ)繪啦。我是一個(gè)語(yǔ)繪愛好者,也想試試看能否用代碼畫一幅差不多的圖出來。
在舊文《程序員特有的畫圖方式——語(yǔ)繪工具小入門》[2]中,我演示過幾款寫代碼畫圖的工具,但它們都不適合用來繪制幾何圖形,所以這次它們沒有用武之地。
本來我想試試用MetaPost[3]來畫的,但鑒于“入門”了太多次,這次還是換點(diǎn)新花樣吧。這一次,我用 LaTeX+TikZ 來畫。
TikZ 是什么及光速入門
著名的壓泡面神器、麻將桌腳墊《TAOCP》的作者發(fā)明了TeX[4],知名的Raft 競(jìng)品Paxos 算法的作者在此基礎(chǔ)上創(chuàng)造了LaTeX[5],它們都是程序員簡(jiǎn)歷論文排版的好幫手。而 TikZ 則是如虎添翼地在 LaTeX 中實(shí)現(xiàn)了簡(jiǎn)單易懂的繪圖功能的一個(gè)紅包宏包(macro package,TeX 的術(shù)語(yǔ))。簡(jiǎn)而言之,TikZ 自定義了一套“語(yǔ)言”,可以在用 LaTeX 編寫的文檔中畫出各種圖形。
百聞不如一見,我演示一下如何用 TikZ 畫一條線段、一個(gè)圓,以及一段圓弧。先將下列的代碼保存到一個(gè)文件three_in_one.tex中
\documentclass{standalone}\usepackage{tikz}
\usetikzlibrary{shapes.geometric, arrows}
\begin{document}
\begin{tikzpicture}[scale=2]
%% 畫一條從原點(diǎn)指向(1, 1)的線段
\draw (0, 0) -- (1, 1);
%% 畫一個(gè)以(1, 1)為圓心,半徑為2的圓。
\draw (1, 1) circle (2);
%% 畫一段以原點(diǎn)為圓心,半徑為1,張開角度為30度的圓弧。
\draw (1, 0) arc (0:30:1);
\end{tikzpicture}
\end{document}
再使用xelatex將其編譯成 PDF 文件(xelatex可以通過安裝 TeXLive 2020 獲得)
xelatex?three_in_one.tex此時(shí)便得到了three_in_one.pdf文件。為了可以在文章中顯示,我用 ImageMagick 將其轉(zhuǎn)換為 PNG 文件
convert?three_in_one.pdf?/tmp/three_in_one.png最終的圖片如下
簡(jiǎn)單,就像畫一匹馬一樣簡(jiǎn)單。
現(xiàn)在該來試試用 TikZ 復(fù)刻維基百科上的硬盤示意圖了。
來點(diǎn)同心圓
在原圖中最引人注目的,當(dāng)屬那十幾個(gè)同心圓了。簡(jiǎn)單起見,我只畫六個(gè)圓。這六個(gè)圓的半徑相差1pt(pt是 TikZ 默認(rèn)的長(zhǎng)度單位),從3pt一直遞增到8pt,它們的圓心都在坐標(biāo)原點(diǎn)(0, 0)上。
%% 為了節(jié)省篇幅,只給出TikZ部分的代碼。\begin{tikzpicture}
\draw (0, 0) circle (3);
\draw (0, 0) circle (4);
\draw (0, 0) circle (5);
\draw (0, 0) circle (6);
\draw (0, 0) circle (7);
\draw (0, 0) circle (8);
\end{tikzpicture}
來點(diǎn)等分線
原圖中有 12 根線段,將每一個(gè)圓等分成了全等的 12 份。從前一節(jié)的內(nèi)容可知,要用\draw命令繪制線段,需要的是線段兩端的坐標(biāo),那么這批坐標(biāo)要怎么計(jì)算呢?盡管可以用三角函數(shù)計(jì)算出這些點(diǎn)的笛卡爾坐標(biāo),但在 TikZ 中可以用更方便的極坐標(biāo)來指定這些點(diǎn)。
以原圖中從 X 軸開始逆時(shí)針旋轉(zhuǎn)遇到的第一條線段為例,它在半徑為3pt的圓上的點(diǎn)的坐標(biāo)為(30:3)(30 是極坐標(biāo)中的角度,3 是半徑長(zhǎng)度),而在半徑為8pt的圓上的點(diǎn)的坐標(biāo)為(30:8),因此可以用\draw (30:3) -- (30:8)來畫出這根線段。
通過調(diào)整其中的角度可以畫出剩余的其它線段。
\begin{tikzpicture}\draw (0, 0) circle (3);
\draw (0, 0) circle (4);
\draw (0, 0) circle (5);
\draw (0, 0) circle (6);
\draw (0, 0) circle (7);
\draw (0, 0) circle (8);
\draw (0:3) -- (0:8);
\draw (30:3) -- (30:8);
\draw (60:3) -- (60:8);
\draw (90:3) -- (90:8);
\draw (120:3) -- (120:8);
\draw (150:3) -- (150:8);
\draw (180:3) -- (180:8);
\draw (210:3) -- (210:8);
\draw (240:3) -- (240:8);
\draw (270:3) -- (270:8);
\draw (300:3) -- (300:8);
\draw (330:3) -- (330:8);
\end{tikzpicture}
來張色圖
原圖大致的骨架已經(jīng)畫完了,現(xiàn)在來嘗試給它上色。在 TikZ 中,可以用\fill命令給一段封閉的曲線上色。比如用\fill[red] (0, 0) -- (1, 0) -- (1, 1) -- (0, 1) -- cycle可以將左下角在原點(diǎn)、邊長(zhǎng)為1pt的正方形涂成紅色。
先給原圖中的區(qū)域 B 上色。區(qū)域 B 是一個(gè)扇形,它由兩根長(zhǎng)度為8pt的半徑和一段夾角為 30 度的圓弧構(gòu)成。要描述這段封閉曲線,可以借助入門一節(jié)中介紹的arc命令。
\begin{tikzpicture}%% 給區(qū)域B上色。
\fill[blue] (0, 0) -- (30:8) arc (30:60:8) -- cycle;
\draw (0, 0) circle (3);
\draw (0, 0) circle (4);
\draw (0, 0) circle (5);
\draw (0, 0) circle (6);
\draw (0, 0) circle (7);
\draw (0, 0) circle (8);
\draw (0:3) -- (0:8);
\draw (30:3) -- (30:8);
\draw (60:3) -- (60:8);
\draw (90:3) -- (90:8);
\draw (120:3) -- (120:8);
\draw (150:3) -- (150:8);
\draw (180:3) -- (180:8);
\draw (210:3) -- (210:8);
\draw (240:3) -- (240:8);
\draw (270:3) -- (270:8);
\draw (300:3) -- (300:8);
\draw (330:3) -- (330:8);
\end{tikzpicture}
\fill命令那一行最后的cycle的意思,是讓曲線回到起點(diǎn)組成一個(gè)封閉的形狀。另外,\fill命令需要寫在\draw命令之前,是為了避免藍(lán)色顏料將區(qū)域內(nèi)的圓弧給蓋住了。
對(duì)于區(qū)域 C 和區(qū)域 D,方法是一樣的,只是描述封閉曲線的坐標(biāo)不同罷了。
\begin{tikzpicture}%% 給區(qū)域B上色。
\fill[blue] (0, 0) -- (30:8) arc (30:60:8) -- cycle;
%% 給區(qū)域C上色。
\fill[purple] (30:4) -- (30:5) arc (30:60:5) -- (60:4) -- (60:4) arc (60:30:4);
%% 給區(qū)域D上色。
\fill[green] (240:6) -- (240:7) arc (240:330:7) -- (330:6) -- (330:6) arc (330:240:6);
\draw (0, 0) circle (3);
\draw (0, 0) circle (4);
\draw (0, 0) circle (5);
\draw (0, 0) circle (6);
\draw (0, 0) circle (7);
\draw (0, 0) circle (8);
\draw (0:3) -- (0:8);
\draw (30:3) -- (30:8);
\draw (60:3) -- (60:8);
\draw (90:3) -- (90:8);
\draw (120:3) -- (120:8);
\draw (150:3) -- (150:8);
\draw (180:3) -- (180:8);
\draw (210:3) -- (210:8);
\draw (240:3) -- (240:8);
\draw (270:3) -- (270:8);
\draw (300:3) -- (300:8);
\draw (330:3) -- (330:8);
\end{tikzpicture}
給環(huán)形上色
聰明的讀者也許已經(jīng)發(fā)現(xiàn)了,區(qū)域 A 的環(huán)形沒辦法用這種方式來描述。不過沒關(guān)系,只要將其視為上下半兩部分,再分別上色即可。
\begin{tikzpicture}%% 環(huán)的上半部分
\fill[red] (4, 0) -- (5, 0) arc (0:180:5) -- (-4, 0) -- (-4, 0) arc (180:0:4);
%% 環(huán)的下半部分
\fill[red] (4, 0) -- (5, 0) arc (360:180:5) -- (-4, 0) -- (-4, 0) arc (180:360:4);
%% 給區(qū)域B上色。
\fill[blue] (0, 0) -- (30:8) arc (30:60:8) -- cycle;
%% 給區(qū)域C上色。
\fill[purple] (30:4) -- (30:5) arc (30:60:5) -- (60:4) -- (60:4) arc (60:30:4);
%% 給區(qū)域D上色。
\fill[green] (240:6) -- (240:7) arc (240:330:7) -- (330:6) -- (330:6) arc (330:240:6);
\draw (0, 0) circle (3);
\draw (0, 0) circle (4);
\draw (0, 0) circle (5);
\draw (0, 0) circle (6);
\draw (0, 0) circle (7);
\draw (0, 0) circle (8);
\draw (0:3) -- (0:8);
\draw (30:3) -- (30:8);
\draw (60:3) -- (60:8);
\draw (90:3) -- (90:8);
\draw (120:3) -- (120:8);
\draw (150:3) -- (150:8);
\draw (180:3) -- (180:8);
\draw (210:3) -- (210:8);
\draw (240:3) -- (240:8);
\draw (270:3) -- (270:8);
\draw (300:3) -- (300:8);
\draw (330:3) -- (330:8);
\end{tikzpicture}
潤(rùn)色一下
用 macOS 的“數(shù)碼測(cè)色計(jì)”看了一下原圖中各個(gè)區(qū)域的顏色的 RGB 值,區(qū)域 A 大概是(236, 133, 130)、區(qū)域 B 大概是(122, 127, 237)、區(qū)域 C 大概是(131, 132, 139)、區(qū)域 D 大概是(0, 151, 27)。接下來我讓 TikZ 以這四種指定的顏色填充圖中的四個(gè)區(qū)域,先用 LaTeX 的\definecolor命令定義四個(gè)新的顏色的名字。
%% 下列四行代碼置于document環(huán)境之前\definecolor{areaA}{RGB}{236,133,130}
\definecolor{areaB}{RGB}{122,127,237}
\definecolor{areaC}{RGB}{131,32,139}
\definecolor{areaD}{RGB}{0,151,27}
再替換掉\fill命令中的顏色名即可
\begin{tikzpicture}%% 環(huán)的上半部分
\fill[areaA] (4, 0) -- (5, 0) arc (0:180:5) -- (-4, 0) -- (-4, 0) arc (180:0:4);
%% 環(huán)的下半部分
\fill[areaA] (4, 0) -- (5, 0) arc (360:180:5) -- (-4, 0) -- (-4, 0) arc (180:360:4);
%% 給區(qū)域B上色。
\fill[areaB] (0, 0) -- (30:8) arc (30:60:8) -- cycle;
%% 給區(qū)域C上色。
\fill[areaC] (30:4) -- (30:5) arc (30:60:5) -- (60:4) -- (60:4) arc (60:30:4);
%% 給區(qū)域D上色。
\fill[areaD] (240:6) -- (240:7) arc (240:330:7) -- (330:6) -- (330:6) arc (330:240:6);
\draw (0, 0) circle (3);
\draw (0, 0) circle (4);
\draw (0, 0) circle (5);
\draw (0, 0) circle (6);
\draw (0, 0) circle (7);
\draw (0, 0) circle (8);
\draw (0:3) -- (0:8);
\draw (30:3) -- (30:8);
\draw (60:3) -- (60:8);
\draw (90:3) -- (90:8);
\draw (120:3) -- (120:8);
\draw (150:3) -- (150:8);
\draw (180:3) -- (180:8);
\draw (210:3) -- (210:8);
\draw (240:3) -- (240:8);
\draw (270:3) -- (270:8);
\draw (300:3) -- (300:8);
\draw (330:3) -- (330:8);
\end{tikzpicture}
圖文并茂
剩下的需要復(fù)刻的東西就是原圖中的文字以及標(biāo)注用的線了。線很容易畫,只要規(guī)定了坐標(biāo)后用\draw命令即可。比如說,我可以把四條線定義如下,其中的坐標(biāo)和線段的長(zhǎng)度純粹是個(gè)人偏好
\draw (75:4.5) -- (75:9);\draw (40:7.5) -- (40:9);
\draw (50:4.5) -- (50:9);
\draw (285:6.5) -- (285:9);
線畫完了,再到每一根線的“終點(diǎn)”標(biāo)上文字說明,這需要用到 TikZ 的node功能。用法很簡(jiǎn)單,就是在需要標(biāo)注文字的坐標(biāo)后,緊跟著關(guān)鍵字node,以及一段用花括號(hào)包裹的文本即可
\documentclass{standalone}\usepackage{tikz}
\usepackage{xeCJK}
\setCJKmainfont{Songti TC}
\usetikzlibrary{shapes.geometric, arrows}
\definecolor{areaA}{RGB}{236,133,130}
\definecolor{areaB}{RGB}{122,127,237}
\definecolor{areaC}{RGB}{131,32,139}
\definecolor{areaD}{RGB}{0,151,27}
\begin{document}
\begin{tikzpicture}
%% 環(huán)的上半部分
\fill[areaA] (4, 0) -- (5, 0) arc (0:180:5) -- (-4, 0) -- (-4, 0) arc (180:0:4);
%% 環(huán)的下半部分
\fill[areaA] (4, 0) -- (5, 0) arc (360:180:5) -- (-4, 0) -- (-4, 0) arc (180:360:4);
%% 給區(qū)域B上色。
\fill[areaB] (0, 0) -- (30:8) arc (30:60:8) -- cycle;
%% 給區(qū)域C上色。
\fill[areaC] (30:4) -- (30:5) arc (30:60:5) -- (60:4) -- (60:4) arc (60:30:4);
%% 給區(qū)域D上色。
\fill[areaD] (240:6) -- (240:7) arc (240:330:7) -- (330:6) -- (330:6) arc (330:240:6);
\draw (0, 0) circle (3);
\draw (0, 0) circle (4);
\draw (0, 0) circle (5);
\draw (0, 0) circle (6);
\draw (0, 0) circle (7);
\draw (0, 0) circle (8);
\draw (0:3) -- (0:8);
\draw (30:3) -- (30:8);
\draw (60:3) -- (60:8);
\draw (90:3) -- (90:8);
\draw (120:3) -- (120:8);
\draw (150:3) -- (150:8);
\draw (180:3) -- (180:8);
\draw (210:3) -- (210:8);
\draw (240:3) -- (240:8);
\draw (270:3) -- (270:8);
\draw (300:3) -- (300:8);
\draw (330:3) -- (330:8);
\draw (75:4.5) -- (75:9) node {磁道};
\draw (40:7.5) -- (40:9) node {扇面};
\draw (50:4.5) -- (50:9) node {扇區(qū)};
\draw (285:6.5) -- (285:9) node {簇};
\end{tikzpicture}
\end{document}
需要留意的是,我在源代碼開頭的位置,引入了xeCJK宏包(\usepackage{xeCJK}),并且指定了中文內(nèi)容用的字體為宋體(\setCJKmainfont{Songti TC}),這樣才能成功編譯。
至此,復(fù)刻算是完成了。
后記
本文只是管中窺豹,TikZ 還可以畫出其它更復(fù)雜更美輪美奐的圖形,有興趣的讀者可以移步這里[6]觀賞。此外,TikZ 也可以“編程”,比如下面的兩行代碼便足矣畫出上文中 12 行代碼才完成的等分線
\foreach \x in {0,30,60,90,120,150,180,210,240,270,300,330}\draw (\x:3) -- (\x:8);
TikZ 的更多潛力和樂趣,就由各位讀者自己探索吧。
如果你想要和我交流,歡迎點(diǎn)擊閱讀原文到我的博客上發(fā)表評(píng)論。
參考資料
[1]硬盤: https://zh.wikipedia.org/wiki/%E7%A1%AC%E7%9B%98
[2]《程序員特有的畫圖方式——語(yǔ)繪工具小入門》: https://liutos.github.io/2020/05/07/%E7%A8%8B%E5%BA%8F%E5%91%98%E7%89%B9%E6%9C%89%E7%9A%84%E7%94%BB%E5%9B%BE%E6%96%B9%E5%BC%8F%E2%80%94%E2%80%94%E8%AF%AD%E7%BB%98%E5%B7%A5%E5%85%B7%E5%B0%8F%E5%85%A5%E9%97%A8/
[3]MetaPost: https://zh.wikipedia.org/wiki/MetaPost
[4]TeX: https://zh.wikipedia.org/wiki/TeX
[5]LaTeX: https://zh.wikipedia.org/wiki/LaTeX
[6]這里: https://texample.net/tikz/examples/
點(diǎn)擊進(jìn)入留言區(qū)
總結(jié)
以上是生活随笔為你收集整理的msdn画圆弧函数_精确之美——用TikZ画硬盘示意图的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2016专接本c语言真题_云南特岗教师考
- 下一篇: C++头文件的防卫式声明(为了防止多次i