summaryrefslogtreecommitdiff
path: root/info/asymptote-by-example-zh-cn/src/xiantu.tex
blob: e6c41b38f7bfc38364decaa5387290656d3c031a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
%# -*- coding: utf-8 -*-
% xiantu.tex
% asymptotebyexample 的一章,最基础的画图知识

\chapter{赵爽的弦图}
\label{chap:xiantu}
\nocite{quanjing}

赵爽博士是位老知识分子,研究兴趣是天文历法与算学,一生精研《周髀算经》。不过
赵老爷子年轻的时候书籍都是手写,最近才紧跟时代潮流用上了电脑。现在他要修订他
研究《周髀算经》的札记,决定使用 \LaTeX{} 来排版。

现在他遇到一个难题,就是他要画出笔记中讲解勾股定理的一幅弦图。听人介绍,几经
比较之后,他决定使用现在炒得火热的 \Asy{}。

赵博士的原图是手画的,线框多有不直不准的,\autoref{fig:xiantuancient} 就是旧
年据手稿做的雕版图,赵博士并不满意。赵爽博士理想中的图,线条要平整美观,文字
要清楚整齐,图形还要上色:朱实自然得用红色,黄实也该用黄色,以与注文一致——
就是\autoref{fig:xiantu} 的样子。

\begin{figure}
  \centering
  \includegraphics[height=\textheight-2\baselineskip]{xiantu-ancient.pdf}
  \caption{旧年做的雕版}
  \label{fig:xiantuancient}
\end{figure}
\begin{figure}
  \centering
  \includegraphics{xiantu.pdf}
  \caption{理想中的新版本}
  \label{fig:xiantu}
\end{figure}

计议已定,赵博士要开始正式的绘图了。

\section{绘图环境}

\Asy{} 的安装并不复杂,在 Windows 下面就是下载运行那个安装包,在 Linux 下面一
般也只需要下载对应的压缩包,解压就可以使用了。哦,赵博士用的就是 Windows。

点图标运行 \Asy{},就出现了交互式\index{交互式}的命令行,提示符是一个
\verb=>=。输入命令:
\begin{lstlisting}
draw((0,0) -- (3cm,4cm));   // `\color{comment}一条直线`
\end{lstlisting}
赵博士装的 GSView 立即弹了出来,里面已经画出一条倾斜的直线。再输入
|quit|\index{quit@\lstinline=quit=},程序退出,并留下了一个叫做
\verb=out.eps= 的图形文件,
小菜一碟。

这里稍稍解释一下上面的一句代码。|draw|\index{draw@\lstinline=draw=} 是画线的
命令,更准确地说,是 \Asy{} 中的函数\index{函数}:它带有一个参数\index{参数}
|(0,0) -- (3cm,4cm)|,参数外面是圆括号,整个命令以分号结束。里面的
|(0,0) -- (3cm,4cm)| 是由两个坐标\index{坐标}连接而成的直线,坐标是在直角坐标
系下的,可以带单位 |mm|, |cm|, |pt|, |bp|, |inch|, |inches|,%
\index{mm@\lstinline=mm=}\index{cm@\lstinline=cm=}\index{pt@\lstinline=pt=}%
\index{bp@\lstinline=bp=}\index{inch@\lstinline=inch=}%
\index{inches@\lstinline=inches=}%
其意义与在\TeX{} 中的一样,如果不带单位,则默认为 |bp|。行末以 |//| 开头的是
注释,另有一种在 |/* */| 之间的注释\index{注释},与 C 语言相同。

\index{编译}
不过赵博士写书讲求胸有成竹方才下笔,因此他更愿意使用更一种方式:打开一个文本
编辑器,把上面的绘图命令都录入完毕,保存为一个名为 \verb=line.asy= 的文件,最
后把这个文件拖动到 \Asy{} 的图标上面,就完成了整个作图。

赵博士的小侄子不屑于这种快捷图标的编译方式,他直接进入命令行,输入
\verb=asy=,就进入了 \Asy{} 的交互环境;要是输入 \verb=asy line=,就画出了刚
才的保存的直线。


\section{直线与绘图命令}
\label{sec:linedraw}

弦图的图形其实很简单,都是直线、方块、三角形这些,而且为了计算的简便,所有长
度也都是整数值。那么,首先就是在 \Asy{} 中来画直线。

\index{画线}
正如前面试验的时候做的那样,一条直线就是用 |--|\index{--@\lstinline=--=} 把坐
标连起来,再使用 |draw| 命令,直线就画出来了。事实上,可以用 |--| 把坐标点连
成折线:
\begin{lstlisting}
draw( (0,0) -- (1cm,0) -- (2cm,0.5cm) -- (1cm,1cm) -- (0,1cm) );
\end{lstlisting}
\begin{figure}[H]
\centering
\begin{asy}
draw( (0,0) -- (1cm,0) -- (2cm,0.5cm) -- (1cm,1cm) -- (0,1cm) );
\end{asy}
\end{figure}

像这样把坐标用 |--| 连结起来的,就成为一条路径\index{路径}。把直线而稍做修改
,在后面连上一个特殊的坐标 |cycle|\index{cycle@\lstinline=cycle=},就可以得到
一条首尾相接的闭路径\index{路径!闭路径}。如:
\begin{lstlisting}
draw( (0,0) -- (1cm,0) -- (2cm,0.5cm) -- (1cm,1cm) -- (0,1cm) -- cycle );
\end{lstlisting}
\begin{figure}[H]
\centering
\begin{asy}
draw( (0,0) -- (1cm,0) -- (2cm,0.5cm) -- (1cm,1cm) -- (0,1cm) -- cycle );
\end{asy}
\end{figure}

画一条路径可以使用不同的颜色、粗细的笔,这只要给 |draw| 命令多加一个画笔
\index{画笔}参数(多个参数用逗号分开):
\begin{lstlisting}
draw((0,0) -- (1cm,0) -- (2cm,0.5cm) -- (1cm,1cm) -- (0,1cm), darkblue+1mm);
\end{lstlisting}
\begin{figure}[H]
\centering
\begin{asy}
draw((0,0) -- (1cm,0) -- (2cm,0.5cm) -- (1cm,1cm) -- (0,1cm), darkblue+1mm);
\end{asy}
\end{figure}
这里 |darkblue| 是颜色,|1mm| 是线的粗细。|darkblue+1mm| 即指一毫米宽的深
蓝色粗线。(可用的颜色\index{颜色}名称可以参考 \cite{asyman})

赵博士画的是“勾三股四弦五”的红色三角形,这很容易:
\begin{lstlisting}
draw( (0,0) -- (4cm,0) -- (0,3cm) -- cycle, red+0.5mm );
\end{lstlisting}
\begin{figure}[H]
\centering
\begin{asy}
draw( (0,0) -- (4cm,0) -- (0,3cm) -- cycle, red+0.5mm );
\end{asy}
\end{figure}

\index{填充}
不过现在还需要的是实心的三角形,因此就需要一个新的绘图命令
|fill|\index{fill@\lstinline=fill=},即填充。于是,红色三角形就成为
\begin{lstlisting}
fill( (0,0) -- (4cm,0) -- (0,3cm) -- cycle, red );
\end{lstlisting}
\begin{figure}[H]
\centering
\begin{asy}
fill( (0,0) -- (4cm,0) -- (0,3cm) -- cycle, red );
\end{asy}
\end{figure}

但这样一来三角形的颜色就太重了,而且边界也不清楚。因此似乎应该先用浅红色填充
一遍,然后再用深红色勾边。好在可以使用一个命令
|filldraw|\index{filldraw@\lstinline=filldraw=} 同时完成这两件事情,这样就不
需要把一条路径写两遍了。即有:
\begin{lstlisting}
filldraw((0,0) -- (4cm,0) -- (0,3cm) -- cycle,
    fillpen=palered, drawpen=red+0.5mm);
\end{lstlisting}
\begin{figure}[H]
\centering
\begin{asy}
filldraw((0,0) -- (4cm,0) -- (0,3cm) -- cycle,
    fillpen=palered, drawpen=red+0.5mm);
\end{asy}
\end{figure}
这里可以简单地直接写两个参数 |palered, red+0.5mm|,不过为了清晰起见还是使用
“$\text{键}=\text{值}$”的写法,明确表示出填充的画笔和描线的画笔。

现在,赵博士的整个弦图的框架就呼之欲出了,就是画出四个三角形
(\autoref{fig:xiantuskeleton}):
\begin{lstlisting}
filldraw( (4cm,0) -- (4cm,3cm) -- (0,3cm) -- cycle,
    fillpen=palered, drawpen=red+0.5mm);
filldraw( (7cm,4cm) -- (4cm,4cm) -- (4cm,0) -- cycle,
    fillpen=palered, drawpen=red+0.5mm);
filldraw( (3cm,7cm) -- (3cm,4cm) -- (7cm,4cm) -- cycle,
    fillpen=palered, drawpen=red+0.5mm);
filldraw( (0,3cm) -- (3cm,3cm) -- (3cm,7cm) -- cycle,
    fillpen=palered, drawpen=red+0.5mm);
\end{lstlisting}
\begin{figure}
\centering
\begin{asy}
filldraw( (4cm,0) -- (4cm,3cm) -- (0,3cm) -- cycle,
    fillpen=palered, drawpen=red+0.5mm);
filldraw( (7cm,4cm) -- (4cm,4cm) -- (4cm,0) -- cycle,
    fillpen=palered, drawpen=red+0.5mm);
filldraw( (3cm,7cm) -- (3cm,4cm) -- (7cm,4cm) -- cycle,
    fillpen=palered, drawpen=red+0.5mm);
filldraw( (0,3cm) -- (3cm,3cm) -- (3cm,7cm) -- cycle,
    fillpen=palered, drawpen=red+0.5mm);
\end{asy}
\caption{弦图的初步框架}\label{fig:xiantuskeleton}
\end{figure}

还应该画出弦图的中间的“黄实”,用黄色填充。这部分是一个正方形,可以使用现成
的 \lstinline[mathescape]|box($\text{角点}$, $\text{角点}$)|
\index{box@\lstinline=box=} 命令来产生矩形的路径,因而填充正中间的正方形就可
以用:
\begin{lstlisting}
fill( box((3cm,3cm), (4cm,4cm)), yellow );
\end{lstlisting}
这个填充的命令应该放在画线之前(以免覆盖描的红线)。

回顾前面的代码,赵博士觉得连续地写四个 |filldraw| 命令太重复了。他读了 \Asy{}
的文档 \cite{asyman},才知道多条路径可以用符号 |^^|\index{^@\lstinline=^^=} 
连起来,一起使用,于是立即着手改进原来的代码。

而且,由于还打算在图的后面画出参考网格,图形的颜色还应该设置为半透明\index{透
明}的。好在这并不难实现,只要稍稍改动一下填充的画笔,使用
\lstinline[mathescape]|opacity($\text{数值}$)|
\index{opacity@\lstinline=opacity=}
来设定有一定不透明度(取值为 $0\sim1$)的画笔,并把它加在原来的画笔上。于是赵
博士最后写出了这样的代码:
\begin{lstlisting}
fill( box((3cm,3cm), (4cm,4cm)), opacity(0.5)+yellow );
filldraw( (4cm,0) -- (4cm,3cm) -- (0,3cm) -- cycle
    ^^ (7cm,4cm) -- (4cm,4cm) -- (4cm,0) -- cycle
    ^^ (3cm,7cm) -- (3cm,4cm) -- (7cm,4cm) -- cycle
    ^^ (0,3cm) -- (3cm,3cm) -- (3cm,7cm) -- cycle,
    fillpen=opacity(0.1)+red, drawpen=red+0.5mm );
\end{lstlisting}
至此,弦图的主要框架(\autoref{fig:xiantuskeleton2})就此完成。
\begin{figure}[H]
\centering
\begin{asy}
fill( box((3cm,3cm), (4cm,4cm)), opacity(0.5)+yellow );
filldraw( (4cm,0) -- (4cm,3cm) -- (0,3cm) -- cycle
    ^^ (7cm,4cm) -- (4cm,4cm) -- (4cm,0) -- cycle
    ^^ (3cm,7cm) -- (3cm,4cm) -- (7cm,4cm) -- cycle
    ^^ (0,3cm) -- (3cm,3cm) -- (3cm,7cm) -- cycle,
    fillpen=opacity(0.1)+red, drawpen=red+0.5mm );
\end{asy}
\caption{弦图的进一步优化的框架}\label{fig:xiantuskeleton2}
\end{figure}


\section{图形变换与功能模块的使用}

然后是画作为长度参考的网格。其实网格一开始就该在图中画出来,这样后面画图准确
与否才能看得清楚。不过现在赵博士是重作旧图,图样已定,网格就搁在弦图的主要图
形后面才画了。

按说这个网格是十分简单的,无非就是画一些灰色的纵横细线。比如这个 $3\times3$ 
的网格:
\begin{lstlisting}
draw( (0,0) -- (3cm,0)
    ^^ (0,1cm) -- (3cm,1cm)
    ^^ (0,2cm) -- (3cm,2cm)
    ^^ (0,3cm) -- (3cm,3cm),
    gray );
draw( (0,0) -- (0,3cm)
    ^^ (1cm,0) -- (1cm,3cm)
    ^^ (2cm,0) -- (2cm,3cm)
    ^^ (3cm,0) -- (3cm,3cm),
    gray );
\end{lstlisting}
\begin{figure}[H]
\centering
\begin{asy}
draw( (0,0) -- (3cm,0)
    ^^ (0,1cm) -- (3cm,1cm)
    ^^ (0,2cm) -- (3cm,2cm)
    ^^ (0,3cm) -- (3cm,3cm),
    gray );
draw( (0,0) -- (0,3cm)
    ^^ (1cm,0) -- (1cm,3cm)
    ^^ (2cm,0) -- (2cm,3cm)
    ^^ (3cm,0) -- (3cm,3cm),
    gray );
\end{asy}
\end{figure}

可无疑这个办法显得太麻烦了,赵博士要画的是 $7\times7$ 的网格,就要分别画出
$16$ 条直线。这样的代码不仅不好写,而且容易出错,修改一下也很麻烦。

赵博士看到了手册 \cite{asyman} 中讲循环语句的用法,似乎可以完成这件事。可是以
赵博士的年龄,再去看什么编程什么变量的,命令不能一条一条执行下来,很不习惯,
头脑就往往转不清楚。

\index{模块!math@\prgname{math}}
于是赵博士就去论坛上咨询,一些人劝他去用几行循环语句,甚至有人已经把完整的函
数做好了。但有一个结果特别引人注目,有人指出,在 \prgname{math} 模块中已经定
义好了一个 |grid| 函数,只要拿来用就可以了。赵博士立即精神大振,来看这个
|grid| 函数:\index{grid@\lstinline=grid=}
\begin{lstlisting}
picture grid(int Nx, int Ny, pen p=currentpen)
\end{lstlisting}
这个是在 \prgname{math} 模块中 |grid| 函数的原型。\index{原型}它说明 |grid| 
函数有 |Nx|, |Ny| 两个整数类型的必需参数,一个可选的画笔,并且返回一个
|picture|(图)\index{picture@\lstinline=picture=}类型的对象。

\index{导入}\index{模块!导入}
要使用模块的功能,需要在绘图之前导入这个模块,这只要使用
\index{import@\lstinline=import=}
\begin{lstlisting}
import `模块名`;
\end{lstlisting}
因此,要使用 \prgname{math} 模块中的 |grid| 函数,只要在代码中写
\begin{lstlisting}
import math;
\end{lstlisting}
就可以了。

|grid| 函数的行为看起来很奇怪,调用它会在一个单独的图上画出一个
|Nx|${}\times{}$|Ny| 的网格,网格的左下角在原点,间距为 $1$。要使用 |grid| 函
数画的图形,要使用 \lstinline[mathescape]|add($\text{图}$)|
\index{add@\lstinline=add=} 命令,把这个图形加在当前的图上:
\begin{lstlisting}
import math;
add(grid(10,10,gray));
\end{lstlisting}
\begin{figure}[H]
\centering
\begin{asy}
import math;
add(grid(10,10,gray));
\end{asy}
\end{figure}
不过直接这样做的结果是只能得到一个小得已经看不清的网格。因此,必须对图形进行放缩。

\index{变换}\index{仿射变换}
\index{变换!平移}\index{变换!旋转}\index{变换!放缩}
\index{变换!错切}\index{变换!反射}
\Asy{} 提供了平移、旋转、放缩、倾斜、反射等各种的仿射变换,来对坐标、路径、图
形等元素进行变换(严格的函数原型参考 \cite{asyman}):
\index{shift@\lstinline=shift=}
\index{scale@\lstinline=scale=}
\index{xscale@\lstinline=xscale=}
\index{yscale@\lstinline=yscale=}
\index{rotate@\lstinline=rotate=}
\index{slant@\lstinline=slant=}
\index{reflect@\lstinline=reflect=}
\begin{lstlisting}
shift(`坐标`)           // `\color{comment}按坐标平移`
shift(x, y)           // `\color{comment}按` (x, y) `\color{comment}平移`
scale(`倍数`)           // `\color{comment}按倍数放缩`
xscale(`倍数`)          // `\color{comment}$x$ 轴方向按倍数放缩`
yscale(`倍数`)          // `\color{comment}$y$ 轴方向按倍数放缩`
scale(x, y)           // `\color{comment}在 $x$ 轴、$y$ 轴方向分别按倍数` x, y `\color{comment}放缩`
rotate(`角度`, z=(0,0)) // `\color{comment}按角度绕中心` z`\color{comment}(默认为原点,逆时针)旋转`
slant(`因子`)           // `\color{comment}按一定因子向右倾斜`
reflect(a, b)         // `\color{comment}相对直线` a--b `\color{comment}反射`
\end{lstlisting}
使用一个变换就是把这个变换乘在被变换对象的左边。例如一个放缩一个单位正方形:
\begin{lstlisting}
draw( scale(2cm) * box((0,0), (1,1)) );
\end{lstlisting}
\begin{figure}[H]
\centering
\begin{asy}
draw( scale(2cm) * box((0,0), (1,1)) );
\end{asy}
\end{figure}
这样的变换可以连续地做下去,例如
\begin{lstlisting}
draw( rotate(90) * slant(0.3) * scale(1cm) * box((0,0), (1,1)) );
\end{lstlisting}
就是把一个单位正方形先放大,再倾斜,再旋转 $90^\circ$:
\begin{figure}[H]
\centering
\begin{asy}
draw( rotate(90) * slant(0.3) * scale(1cm) * box((0,0), (1,1)) );
\end{asy}
\end{figure}

有了这些变换,画出一个合适大小的网格就不再是什么难事了:
\begin{lstlisting}
import math;
add( scale(5mm) * grid(4, 4, gray) );
\end{lstlisting}
\begin{figure}[H]
\centering
\begin{asy}
import math;
add( scale(5mm) * grid(4, 4, gray) );
\end{asy}
\end{figure}

赵博士的弦图有两个网格,不仅要大小合适,而且其中一个需要进行旋转和平移。平移
的位置很明显,但旋转仍然需要一些计算。当然这难不倒精研天文算学多年的赵博士,
这里弦实($5\times5$ 的正方形)可以看作是顺时针旋转得到的,从朱实的三角形容易
看出旋转的角度正好是 $\arctan(3/4)$。\Asy{} 中也可以方便地调用返回角度的反三
角函数 |aTan|\index{aTan@\lstinline=aTan=} 来计算这个角度。更详细的数学函数列
表,参看 \cite{asyman}。

于是,赵博士弦图中的网格,就可以这样方便地画出来了
(\autoref{fig:xiantugrid}):
\begin{lstlisting}
// `\color{comment}网格`
import math;
add( scale(1cm) * grid(7, 7, gray) );
add( shift(0,3cm) * rotate(-aTan(3/4)) * scale(1cm) * grid(5, 5, gray) );
// `\color{comment}弦图主体`
fill( box((3cm,3cm), (4cm,4cm)), opacity(0.5)+yellow );
filldraw( (4cm,0) -- (4cm,3cm) -- (0,3cm) -- cycle
    ^^ (7cm,4cm) -- (4cm,4cm) -- (4cm,0) -- cycle
    ^^ (3cm,7cm) -- (3cm,4cm) -- (7cm,4cm) -- cycle
    ^^ (0,3cm) -- (3cm,3cm) -- (3cm,7cm) -- cycle,
    fillpen=opacity(0.1)+red, drawpen=red+0.5mm );
\end{lstlisting}
\begin{figure}[htbp]
\centering
\begin{asy}
// 网格
import math;
add( scale(1cm) * grid(7, 7, gray) );
add( shift(0,3cm) * rotate(-aTan(3/4)) * scale(1cm) * grid(5, 5, gray) );
// 弦图主体
fill( box((3cm,3cm), (4cm,4cm)), opacity(0.5)+yellow );
filldraw( (4cm,0) -- (4cm,3cm) -- (0,3cm) -- cycle
    ^^ (7cm,4cm) -- (4cm,4cm) -- (4cm,0) -- cycle
    ^^ (3cm,7cm) -- (3cm,4cm) -- (7cm,4cm) -- cycle
    ^^ (0,3cm) -- (3cm,3cm) -- (3cm,7cm) -- cycle,
    fillpen=opacity(0.1)+red, drawpen=red+0.5mm );
\end{asy}
\caption{带网格的弦图草图}
\label{fig:xiantugrid}
\end{figure}


\section{标注文字}

\index{标注}\index{标签}
现在要进行的是文字的标注。按照勾股定理的约定,赵博士打算在一个红色三角形内标
注“朱实”,在黄色矩形处标注“黄实”,并为拼得的整个大矩形标注“弦实”;在另
一红色三角形的三边标注“勾三”、“股四”、“弦五”的尺寸;最后在图形两侧加上
说明的文字。

在标注文字之前,对于中文标签\index{标签!中文标签},应该先定义好中文环境和字体
。\Asy{} 会调用 \LaTeX{} 来进行标签的处理,因而需要设置的就是 \LaTeX{} 的编译
引擎\index{LaTeX@\LaTeX!引擎}与一般的中文 \LaTeX{} 文件导言区。在这里,赵博士决定使
用 \XeTeX{}\index{XeTeX@\XeTeX} 引擎与
\prgname{xeCJK}\index{xeCJK@\prgname{xeCJK}} 宏包来处理中文。为此,在 \Asy{} 
源文件中,他使用了下面的设置代码:
\begin{lstlisting}
settings.tex = "xelatex";
usepackage("xeCJK");
texpreamble("\setCJKmainfont{SimSun}");
\end{lstlisting}
这里第一行是设置编译时所用的 \TeX{} 引擎。后面
|usepackage|\index{usepackage@\lstinline=usepackage=} 命令就是 \LaTeX{} 中的
\texcode|\usepackage| 命令的一个包装形式,里面的字符串参数就是宏包名;而
|texpreamble|\index{texpreamble@\lstinline=texpreamble=} 命令则把接收的参数直
接放进 \LaTeX{} 的导言区。

进行上述设置后,就可以正确使用中文标签了。标注的命令很简单,就是
|label|\index{label@\lstinline=label=},参数正是标签文字和标签的位置。例如:
\begin{lstlisting}
draw( (0,0) -- (1cm,1cm) -- (2cm,0) );
label( "`\color{string}中间`", (1cm,0cm) );
\end{lstlisting}
就得到
\begin{figure}[H]
\centering
\begin{asy}
settings.tex = "xelatex";
usepackage("xeCJK");
texpreamble("\setCJKmainfont{SimSun}");
draw( (0,0) -- (1cm,1cm) -- (2cm,0) );
label( "中间", (1cm,0cm) );
\end{asy}
\end{figure}

可以在标签中使用任意的 \LaTeX{} 代码,包括数学公式,例如:
\begin{lstlisting}
label("$x = \sin\alpha$", (0,0));
\end{lstlisting}
就会正确地得到 $x = \sin\alpha$ 的标签。

现在,我们可以给弦图加上“朱实”、“黄实”和“弦实”的标签了。在前面的框架代
码后面加上
\begin{lstlisting}
label("`\color{string}朱实`", (2cm,4cm));
label("`\color{string}黄实`", (3.5cm,3.5cm));
label("`\color{string}弦实`", (5cm,4cm));
\end{lstlisting}
以及设置字体的代码,就得到\autoref{fig:xiantupartlabel} 的结果。
\begin{figure}
\centering
\begin{asy}
settings.tex = "xelatex";
usepackage("xeCJK");
texpreamble("\setCJKmainfont{SimSun}");
import math;
add( scale(1cm) * grid(7, 7, gray) );
add( shift(0,3cm) * rotate(-aTan(3/4)) * scale(1cm) * grid(5, 5, gray) );
fill( box((3cm,3cm), (4cm,4cm)), opacity(0.5)+yellow );
filldraw( (4cm,0) -- (4cm,3cm) -- (0,3cm) -- cycle
    ^^ (7cm,4cm) -- (4cm,4cm) -- (4cm,0) -- cycle
    ^^ (3cm,7cm) -- (3cm,4cm) -- (7cm,4cm) -- cycle
    ^^ (0,3cm) -- (3cm,3cm) -- (3cm,7cm) -- cycle,
    fillpen=opacity(0.1)+red, drawpen=red+0.5mm );
label("朱实", (2cm,4cm));
label("黄实", (3.5cm,3.5cm));
label("弦实", (5cm,4cm));
\end{asy}
\caption{带部分标注的弦图}\label{fig:xiantupartlabel}
\end{figure}

下面则是要给三角形的三边进行标注。

\index{标签!路径上的标签}
与前面在一个点处标注不同,这里实际是给一条路径(直线)标注标签。因此,想要得
到的是距离这条路径的中点一定方向距离加一个标签,而不是简单地取路径上的一点作
为标签的位置。好在 \Asy{} 确实也提供了这样的功能,仍然使用 |label| 命令,基本
语法是:\index{label@\lstinline=label=}
\begin{lstlisting}
label(`标签`, `路径`)
\end{lstlisting}
这里默认会在路径中间的右侧(沿着路径行进方向)加标签。例如:
\begin{lstlisting}
draw( (0,0) -- (4cm,2cm), linewidth(0.5mm) );
label("粗线条", (0,0) -- (4cm,2cm));
\end{lstlisting}
(其中的 |linewidth|\index{linewidth@\lstinline=linewidth=} 函数用来表示具有
一定线宽\index{线宽}的画笔)这段代码将得到
\begin{figure}[H]
\centering
\begin{asy}
settings.tex = "xelatex";
usepackage("xeCJK");
texpreamble("\setCJKmainfont{SimSun}");
draw( (0,0) -- (4cm,2cm), linewidth(0.5mm) );
label("粗线条", (0,0) -- (4cm,2cm));
\end{asy}
\end{figure}

现在,赵博士就可以给三角形的三边加上“勾三”、“股四”、“弦五”的标签了,只
要稍稍注意一下标签摆放的默认方向:
\begin{lstlisting}
filldraw( (4cm,0) -- (4cm,3cm) -- (0,3cm) -- cycle,
    fillpen=opacity(0.1)+red, drawpen=red+0.5mm );
label( "勾三", (4cm,3cm) -- (4cm,0) );
label( "股四", (0,3cm) -- (4cm,3cm) );
label( "弦五", (4cm,0) -- (0,3cm) );
\end{lstlisting}
\begin{figure}[H]
\centering
\begin{asy}
settings.tex = "xelatex";
usepackage("xeCJK");
texpreamble("\setCJKmainfont{SimSun}");
filldraw( (4cm,0) -- (4cm,3cm) -- (0,3cm) -- cycle,
    fillpen=opacity(0.1)+red, drawpen=red+0.5mm );
label( "勾三", (4cm,3cm) -- (4cm,0) );
label( "股四", (0,3cm) -- (4cm,3cm) );
label( "弦五", (4cm,0) -- (0,3cm) );
\end{asy}
\end{figure}

不过,为了得到正确的标签位置,不得不把原来逆时针画的线用顺时针方向重写,这多
少让赵博士有些恼火:为什么不能在路径的左边标注标签呢?确实可以,很简单,只要
给 |label| 命令再加上 |align=LeftSide|\index{align@\lstinline=align=}
\index{LeftSide@\lstinline=LeftSide=} 选项(或者简单地只用 |LeftSide|)就指定
了在左边放置对齐。同理,还有向右对齐的
|RightSide|\index{RightSide@\lstinline=RightSide=},在中间对齐的
|Center|\index{Center@\lstinline=Center=} 以及一般意义的相对方向
\lstinline[mathescape]|Relative($\text{方向}$)|。%
\index{Relative@\lstinline=Relative=}%
例如:
\begin{lstlisting}
draw( (0,0) -- (4cm,2cm), blue, Arrow );
label( "LeftSide", (0,0) -- (4cm,2cm), align=LeftSide );
label( "RightSide", (0,0) -- (4cm,2cm), align=RightSide );
label( "Center", (0,0) -- (4cm,2cm), align=Center );

draw( (6cm,0)--(8cm,2cm), blue, Arrow );
label( "E", (6cm,0)--(8cm,2cm), Relative(E) );
label( "S", (6cm,0)--(8cm,2cm), Relative(S) );
label( "W", (6cm,0)--(8cm,2cm), Relative(W) );
label( "N", (6cm,0)--(8cm,2cm), Relative(N) );
\end{lstlisting}
\begin{figure}[H]
\centering
\begin{asy}
draw( (0,0) -- (4cm,2cm), blue, Arrow );
label( "LeftSide", (0,0) -- (4cm,2cm), align=LeftSide );
label( "RightSide", (0,0) -- (4cm,2cm), align=RightSide );
label( "Center", (0,0) -- (4cm,2cm), align=Center );

draw( (6cm,0)--(8cm,2cm), blue, Arrow );
label( "E", (6cm,0)--(8cm,2cm), Relative(E) );
label( "S", (6cm,0)--(8cm,2cm), Relative(S) );
label( "W", (6cm,0)--(8cm,2cm), Relative(W) );
label( "N", (6cm,0)--(8cm,2cm), Relative(N) );
\end{asy}
\end{figure}
|E|, |S|, |W|, |N|\index{E@\lstinline=E=}\index{S@\lstinline=S=}
\index{W@\lstinline=W=}\index{N@\lstinline=N=} 分别是东南西北四个罗盘方向,%
\index{罗盘方向}用在 |Relative| 函数里面就表示相对于路径方向的四个方向。为明
确,这里用 |Arrow|\index{Arrow@\lstinline=Arrow=} 选项在画线时加了箭头。
\index{箭头}

不过,这样的标签还是不能令赵博士满意。赵博士给三角形加标签,还希望标签随着三
角形的边作旋转,使标签沿着边排列。为此,赵博士不得不又仔细查看了
\cite{asyman},在讲标注文字一节(label)他找到了一个构造标签的更高级的办法,
即不仅仅是使用一个简单的字符串,而是使用\index{Label@\lstinline=Label=}
\begin{lstlisting}
Label(`标签`)
\end{lstlisting}
函数进行构造。里面的“标签”参数仍然可以是原来的字符串,或是通过这个函数构造
出来的高级的标签。这个函数可以带许多可选的其他参数,如
\lstinline[mathescape]|position=$\text{位置}$|
\index{position@\lstinline=position=} 的参数就可以指定标签放在路径中点之外的
其他地方;而 \lstinline[mathescape]|embed=$\text{嵌入变换方式}$|
\index{embed@\lstinline=embed=} 的参数则可以解决标签自动旋转的问题。

这里暂且放下 |position| 参数。只来看 |embed| 参数的一个特例:%
\lstinline[mathescape]|Rotate($\text{方向}$)|%
\index{Rotate@\lstinline=Rotate=}。这个参数会让标签向着给定的方向旋转,如:
\begin{lstlisting}
draw( (0,0)--(4cm,2cm), blue, Arrow );
label( Label("Rotate", Rotate((4,2))),
    (0,0)--(4cm,2cm) );
\end{lstlisting}
\begin{figure}[H]
\centering
\begin{asy}
draw( (0,0)--(4cm,2cm), blue, Arrow );
label( Label("Rotate", Rotate((4,2))),
    (0,0)--(4cm,2cm) );
\end{asy}
\end{figure}
这里坐标 $(4,2)$ 正是这条直线的绘制方向。

终于,使用了上面的全部功能,赵博士完成了全部的图形标注工作
(\autoref{fig:xiantulabel}):
\begin{lstlisting}
settings.tex = "xelatex";
usepackage("xeCJK");
texpreamble("\setCJKmainfont{SimSun}");

import math;
add( scale(1cm) * grid(7, 7, gray) );
add( shift(0,3cm) * rotate(-aTan(3/4)) * scale(1cm) * grid(5, 5, gray) );

fill( box((3cm,3cm), (4cm,4cm)), opacity(0.5)+yellow );
filldraw( (4cm,0) -- (4cm,3cm) -- (0,3cm) -- cycle
    ^^ (7cm,4cm) -- (4cm,4cm) -- (4cm,0) -- cycle
    ^^ (3cm,7cm) -- (3cm,4cm) -- (7cm,4cm) -- cycle
    ^^ (0,3cm) -- (3cm,3cm) -- (3cm,7cm) -- cycle,
    fillpen=opacity(0.1)+red, drawpen=red+0.5mm );

label("朱实", (2cm,4cm));
label("黄实", (3.5cm,3.5cm));
label("弦实", (5cm,4cm));
label( Label("勾三",Rotate(S)), (4cm,0)--(4cm,3cm), LeftSide );
label( Label("股四",Rotate(E)), (4cm,3cm)--(0,3cm), LeftSide );
label( Label("弦五",Rotate((4,-3))), (0,3cm)--(4cm,0), LeftSide );
\end{lstlisting}
\begin{figure}[htbp]
\centering
\begin{asy}
settings.tex = "xelatex";
usepackage("xeCJK");
texpreamble("\setCJKmainfont{SimSun}");

import math;
add( scale(1cm) * grid(7, 7, gray) );
add( shift(0,3cm) * rotate(-aTan(3/4)) * scale(1cm) * grid(5, 5, gray) );

fill( box((3cm,3cm), (4cm,4cm)), opacity(0.5)+yellow );
filldraw( (4cm,0) -- (4cm,3cm) -- (0,3cm) -- cycle
    ^^ (7cm,4cm) -- (4cm,4cm) -- (4cm,0) -- cycle
    ^^ (3cm,7cm) -- (3cm,4cm) -- (7cm,4cm) -- cycle
    ^^ (0,3cm) -- (3cm,3cm) -- (3cm,7cm) -- cycle,
    fillpen=opacity(0.1)+red, drawpen=red+0.5mm );

label("朱实", (2cm,4cm));
label("黄实", (3.5cm,3.5cm));
label("弦实", (5cm,4cm));
label( Label("勾三",Rotate(S)), (4cm,0)--(4cm,3cm), LeftSide );
label( Label("股四",Rotate(E)), (4cm,3cm)--(0,3cm), LeftSide );
label( Label("弦五",Rotate((4,-3))), (0,3cm)--(4cm,0), LeftSide );
\end{asy}
\caption{带标注的完整弦图}
\label{fig:xiantulabel}
\end{figure}


\section*{习题}

\begin{enumerate}
  \item 查阅参考手册 \cite{asyman},看看都有哪些颜色可用。看看你的系统中安装
    了哪些中文字体。然后修改\autoref{fig:xiantulabel},使用另一种你喜欢的字体
    进行标注;并且将“朱实”、“黄实”和“弦实”分别用红、黄、橙色标注。

  \item 查阅参考手册 \cite{asyman},看看 |draw|、|fill|、|label| 等命令都可以
    带哪些可选的参数,并自己举例子试验一下效果。

  \item 代码
\begin{lstlisting}
path zhushi = (4cm,0) -- (4cm,3cm) -- (0,3cm) -- cycle;
\end{lstlisting}
    可以把一个三角形的路径保存在变量 |zhushi| 中,以后就可以使用
    |draw(zhushi)| 这样的命令对此路径进行操作了\footnote{关于变量定义和使用的
    详细内容,参考后面的章节或手册 \cite{asyman}。}。

    考虑如何利用平移和旋转变换,只定义一个三角形的路径,就把弦图中的四个红色
    三角形都画出来。

  \item 查阅手册 \cite{asyman} 和 \Asy{} 自带的例子,研究模块
    \prgname{roundedpath} 的用法。并利用它尝试画出下面的图形:
\begin{figure}[H]
\centering
\begin{asy}
import roundedpath;
pen thick = linewidth(0.8pt);
path p = (0,0) -- (0,2cm) -- (1cm,3.25cm) -- (2cm,2cm) -- (2cm,0)
	-- (0,2cm) -- (2cm, 2cm) -- (0,0) -- (2cm,0);
draw(roundedpath(p, 8pt), thick);
\end{asy}
\end{figure}

    看看 \Asy{} 中还有什么用法简单而又有趣的模块。

  \item (较难)研究在 \LaTeX{} 里中文直排的方法,尽量精确地复现出赵博士理想
    中的弦图效果(\autoref{fig:xiantu})。
\end{enumerate}

\endinput

% vim:tw=77: