跳过正文
  1. 技术博客/

Modern Swjtu Thesis开发

·3916 字·8 分钟·
Somnus
作者
Somnus
记录学习、项目、实验和一些被密码保护起来的日常。

最近正在准备写本科毕业设计论文,不想用微软的word写,因为感觉不够极客 😧 ,也不想使用LaTex,因为LaTex太笨重了,编译速度太慢了,在线服务overleaf又是收费的(帮黄学长报销这个来着,好像一个月要三百多 🙀) 在网上搜罗搜罗发现了typst,一个基于rust内核的开源的标记语言排版系统,速度非常快,功能我觉得的也足够强(公式什么的都能很好地显示),同时我在他的模板库也找到了其他大学的毕业论文模板(nju),于是基于这个模板,我便衍生做一个swjtu的版本,最后,感谢nju-lug。

基础介绍
#

先简单介绍一下typst,是一款可用于出版的可编程标记语言,拥有变量、函数与包管理等现代编程语言特点,同时又注重于科学写作领域,与LaTex相似。

  • 语法简洁:上手难度与markdown相当,文本的源码阅读性很高
  • 编译速度快:typst用的是rust编写的,即typ(esetting+ru)st,运行平台是WASM,
  • 环境搭建简单:不需要像LaTex折腾几个G的开发环境,可以在Web运行,还可以用vscode安装插件开发。

Typst 简明语法指南
#

Typst 是一款现代、高效的排版系统,旨在提供比 LaTeX 更简单的语法和更快的渲染速度。其语法主要分为:标记模式 (Markup)数学模式 (Math)脚本模式 (Scripting)

基础文本排版 (Markup Mode)
#

标记模式用于文档的正文结构,类似于 Markdown。

功能语法示例说明
标题= 一级标题, == 二级标题= 越多层级越深,等号后需空格
加粗*加粗内容*使用星号包裹
斜体_斜体内容_使用下划线包裹
代码块` `行内代码` ` 或 ``` 块状代码 ```支持多种语言高亮
无序列表- 列表项自动层级缩进
有序列表+ 列表项自动编号
术语列表/ 术语: 定义描述常用于名词解释
链接https://google.com自动识别或使用 #link 函数
引用> 引用文本用于长段引用
注释// 单行/* 多行 */不会显示在输出文件中

数学公式 (Math Mode)
#

使用美元符号 $ 进入数学模式。

  • 行内公式: $a + b = c$
  • 块级公式: $ v = s / t $ (在 $ 符号内侧保留空格)

常用数学语法:
#

  • 上下标: x^2, y_i
  • 分式: 1/2fract(a, b)
  • 根号: sqrt(x)
  • 希腊字母: 直接输入名称,如 alpha, beta, phi, Delta
  • 矩阵: mat(1, 2; 3, 4) (分号分隔行)
  • 求和/积分: sum, integral, product
  • 限制范围: sum_(i=0)^n x_i

函数与脚本 (Scripting Mode)
#

在 Typst 中,所有以 # 开头的标识符都代表函数调用或代码逻辑。

常用内置函数
#

  • 图片: #image("logo.png", width: 40%)
  • 表格:
    1#table(
    2  columns: (1fr, auto, 1fr),
    3  [], [], [],
    4  [1], [2], [3],
    5)
  • 形状与间隔: #rect[], #circle[], #h(1cm) (水平间距), #v(2pt) (垂直间距)
  • 换页: #pagebreak()

样式设置 (Set & Show Rules)
#

这是 Typst 实现自动化排版的核心。

Set 规则 (全局状态设置)
#

用于修改元素的默认属性:

1#set page(paper: "a4", margin: 2cm, numbering: "1") // 纸张、边距、页码
2#set text(font: "SimSun", size: 12pt, lang: "zh")   // 字体、字号、语言
3#set heading(numbering: "1.1 ")                    // 标题自动编号
4#set par(justify: true, first-line-indent: 2em)    // 段落两端对齐、首行缩进

Show 规则 (元素重定义)
#

用于改变特定元素的表现方式(类似于 CSS):

1// 将所有一级标题设为红色
2#show heading.where(level: 1): set text(red)
3
4// 在每个表格前自动加个加粗的提示
5#show table: it => [
6  *数据表:*
7  #it
8]

变量、编程与逻辑
#

Typst 是一门完整的编程语言。

  • 定义变量: #let name = "Typst"
  • 定义函数:
    1#let notice(body) = rect(fill: gray.lighten(80%))[
    2  *注意:* #body
    3]
    4#notice[这是一个自定义提醒框]
  • 条件判断: #if 1 < 2 [Yes] else [No]
  • 循环: #for i in range(3) [ #i ]

标签与引用
#

  • 定义标签: 在元素后面紧跟 <label_name>
  • 引用标签: 使用 @label_name
1= 核心介绍 <intro>
2如章节 @intro 所述...
3
4#figure(
5  image("graph.png"),
6  caption: [实验结果],
7) <fig_result>
8见图 @fig_result 

常用快捷键/对比 (vs LaTeX)
#

需求TypstLaTeX
字体颜色#text(fill: blue)[文本]\textcolor{blue}{文本}
环境包裹[...]\begin{...} ... \end{...}
加粗*text*\textbf{text}
导入模块#import "template.typ": *\usepackage{...}

个人使用经验
#

最近在完善毕设论文,亲自使用了这个模板,一边发现问题一边修改问题hhhhh,感觉效率非常高。同样也总结了一些工作的经验。

流程图
#

任何的论文都一定一定会有流程图,不管说是方法的流程,还是模型的结构,都需要流程图来让表达更加具体,让读者更容易,引用一个著名的理论论文就是要讲好一个故事,衡量论文,不光要从学术的角度看,也许要关注他的表述,怎么让读者看懂。 对于typst的流程图,我在我本人的毕设里面采用了两种方案,一种是通过drawio(现在叫digrams)来绘制,并导出pdf后再导入论文中。 typst插入论文图片使用以下的语法

1#figure(
2  image("image_path",width=80%),
3  caution:[图片的描述]
4)

第二种方案是使用typst嵌入的流程图绘制方式,typst作为开源项目,虽然原生的功能可能存在缺乏现象,但是会有很多开源社区的贡献来弥补,流程图的绘制,就会用到cetz这个库,首先在typ文件开头引用cetz

1#import "@preview/cetz:0.3.4"

然后就可以通过函数调用的方式来绘制具体的流程图,下面可以看到,真的非常之函数。😁

 1#figure(
 2  align(center)[
 3    #cetz.canvas({
 4      import cetz.draw: *
 5
 6      let box-stroke = (paint: rgb("#3E4A61"), thickness: 0.9pt)
 7      let box-fill = rgb("#F5F7FA")
 8      let arrow-stroke = (paint: rgb("#4B5563"), thickness: 0.9pt)
 9
10      let x1 = 0
11      let x2 = 6.4
12      let ys = (8.4, 7.2, 6.0, 4.8, 3.6, 2.4, 1.2)
13      let labels = (
14        [加载 FP32 权重],
15        [构建 RAFT-Stereo 网络],
16        [模型参数转换为 FP16],
17        [输入图像预处理并转换为 FP16],
18        [执行半精度前向推理],
19        [输出视差图],
20        [统计指标并与 FP32 基线对比],
21      )
22
23      for i in range(0, 7) {
24        let y = ys.at(i)
25        rect((x1, y - 0.35), (x2, y + 0.35), radius: 0.08, fill: box-fill, stroke: box-stroke)
26        content(((x1 + x2) / 2, y), labels.at(i))
27        if i < 6 {
28          line(((x1 + x2) / 2, y - 0.35), ((x1 + x2) / 2, ys.at(i + 1) + 0.35), mark: (end: ">"), stroke: arrow-stroke)
29        }
30      }
31    })
32  ],
33  caption: [基于 FP16 的半精度推理流程图],
34)

编译出来的效果如下:

专业绘图
#

同样的专业绘图也可以交给cetz,其实他的专业绘图这么强,我也是没想到的。这个双目摄像头成像示意图就是完全由Chatgpt 5.4生成的,没想到效果这么惊艳。

代码如下

 1#figure(
 2  align(center)[
 3    #cetz.canvas({
 4      import cetz.draw: *
 5
 6      // 定义核心几何参数
 7      let b = 5        // 基线长度 b
 8      let f = 1.5      // 相机焦距 f
 9      let Z = 6        // 目标点深度 Z
10
11      let Ol = (0, 0)  // 左相机光心
12      let Or = (b, 0)  // 右相机光心
13      let P = (2, Z)   // 空间目标点 P(X, Y, Z)
14
15      // 绘制基线与光心
16      line((-1, 0), (b + 1, 0), stroke: (dash: "dashed", paint: luma(150)), name: "baseline")
17      circle(Ol, radius: 0.06, fill: black)
18      content((0, -0.4), [$O_L$])
19      circle(Or, radius: 0.06, fill: black)
20      content((b, -0.4), [$O_R$])
21
22      // 标注基线 b
23      line((0, -0.8), (b, -0.8), mark: (start: ">", end: ">"), stroke: 0.8pt)
24      content((b / 2, -1.2), [$b$])
25
26      // 绘制图像平面 (高度为 f)
27      line((-1.5, f), (1.5, f), stroke: 1.2pt)
28      line((b - 1.5, f), (b + 1.5, f), stroke: 1.2pt)
29
30      // 标注焦距 f
31      line((-2, 0), (-2, f), mark: (start: ">", end: ">"), stroke: 0.8pt)
32      content((-2.4, f / 2), [$f$])
33
34      // 绘制空间点 P 及其到基线的垂线
35      circle(P, radius: 0.06, fill: black)
36      content((P.at(0), P.at(1) + 0.4), [$P(X,Y,Z)$])
37      line((P.at(0), 0), P, stroke: (dash: "dotted", paint: luma(100)))
38
39      // 标注深度 Z
40      line((b + 2, 0), (b + 2, Z), mark: (start: ">", end: ">"), stroke: 0.8pt)
41      content((b + 2.4, Z / 2), [$Z$])
42
43      // 绘制光线 (P 到光心)
44      line(P, Ol, stroke: (paint: blue.darken(20%), thickness: 0.8pt))
45      line(P, Or, stroke: (paint: blue.darken(20%), thickness: 0.8pt))
46
47      // 计算投影点位置 (利用相似三角形严谨计算)
48      let xL_pos = P.at(0) * (f / Z)
49      let xR_pos = b + (P.at(0) - b) * (f / Z)
50
51      let pL = (xL_pos, f)
52      let pR = (xR_pos, f)
53
54      // 绘制并标注左侧投影点 x_L
55      circle(pL, radius: 0.05, fill: red)
56      content((xL_pos - 0.4, f + 0.3), text(fill: red)[$x_L$])
57
58      // 绘制并标注右侧投影点 x_R
59      circle(pR, radius: 0.05, fill: red)
60      content((xR_pos + 0.4, f + 0.3), text(fill: red)[$x_R$])
61    })
62  ],
63  caption: [双目视觉立体测距原理示意图],
64)

我将永远拥护Chatgpt和typst(如果可以的话),听说那些专业期刊会要求提供tex格式的文件,这不就炸了,不过本科毕设肯定不会要求的,我感觉我同学百分之九十五都不会使用latex来写毕业论文,大部分估计都是word系。

同样对于柱状图,我们也可以直接使用cetz来绘制。 代码如下:

 1#figure(
 2  align(center)[
 3    #cetz.canvas({
 4      import cetz.draw: *
 5
 6      let axis-stroke = (paint: rgb("#4B5563"), thickness: 0.9pt)
 7      let grid-stroke = (paint: luma(220), thickness: 0.5pt)
 8      let fp32-fill = rgb("#8FB7E8")
 9      let fp16-fill = rgb("#F2A978")
10
11      // 坐标轴与网格
12      line((1.2, 0.8), (1.2, 6.0), stroke: axis-stroke)
13      line((1.2, 0.8), (8.6, 0.8), stroke: axis-stroke)
14      line((1.2, 1.8), (8.6, 1.8), stroke: grid-stroke)
15      line((1.2, 2.8), (8.6, 2.8), stroke: grid-stroke)
16      line((1.2, 3.8), (8.6, 3.8), stroke: grid-stroke)
17      line((1.2, 4.8), (8.6, 4.8), stroke: grid-stroke)
18      line((1.2, 5.8), (8.6, 5.8), stroke: grid-stroke)
19
20      // 纵轴刻度
21      content((0.7, 0.8), [0])
22      content((0.55, 1.8), [50])
23      content((0.45, 2.8), [100])
24      content((0.45, 3.8), [150])
25      content((0.45, 4.8), [200])
26      content((0.45, 5.8), [250])
27      content((0.6, 6.35), [时间/ms])
28
29      // 柱状图
30      rect((2.3, 0.8), (4.2, 5.0), fill: fp32-fill, stroke: none)
31      rect((5.4, 0.8), (7.3, 4.27), fill: fp16-fill, stroke: none)
32
33      // 数值与标签
34      content((3.25, 5.28), [209.80])
35      content((6.35, 4.55), [173.59])
36      content((3.25, 0.35), [FP32])
37      content((6.35, 0.35), [FP16])
38    })
39  ],
40  caption: [FP32  FP16 速度对比柱状图],
41)

效果如图所示

表格
#

typst原生支持表格,通过使用table可以来创建表格。 代码如下:

 1#figure(
 2  table(
 3    columns: (22%, 28%, 50%),
 4    stroke: none,
 5    align: (x, y) => (
 6      if y == 0 { center + horizon }
 7      else if x <= 1 { center + horizon }
 8      else { left + horizon }
 9    ),
10
11    table.hline(y: 0, stroke: 1.5pt),
12    table.header([*指标类别*], [*统计指标*], [*说明*]),
13    table.hline(y: 1, stroke: 0.5pt),
14
15    [精度指标], [EPE、D1], [衡量预测视差与真实视差之间的误差,用于判断半精度转换是否影响匹配精度。],
16    table.hline(y: 2, stroke: 0.5pt),
17
18    [速度指标], [AvgTime、FPS], [衡量单张图像推理耗时和连续处理能力,用于评价推理效率。],
19    table.hline(y: 3, stroke: 0.5pt),
20
21    [资源指标], [Max CUDA Memory], [统计推理过程中的 GPU 显存峰值,用于分析显存占用变化。],
22    table.hline(y: 4, stroke: 0.5pt),
23
24    [存储指标], [Model Size], [统计模型权重文件大小,用于评价模型部署和分发成本。],
25    table.hline(y: 5, stroke: 1.5pt),
26  ),
27  caption: [FP16 轻量化实验评价指标说明],
28  kind: table
29)

引用文献
#

只要是论文,必然会用引用文献,我们每个人都是站在巨人肩上修修补补,所以typst必然需要有引用文献的功能。😀 在thesis.typ的同目录下会有ref.bib,在其中可以添加参考文献目录。然后再文章中可以通过@文献名来引用,注意,不光要创建这样的ref.bib文件,并按照指定格式输入,还需要在thesis.typ中显式的使用它,具体可以看以下:

文献参考格式
#

大概可以按照这样的格式来写

 1@article{ref02_Hirschmuller2008SGM, //文章中引用就@这个名字
 2  author  = {Hirschmuller, Heiko},
 3  title   = {Stereo Processing by Semi-Global Matching and Mutual Information},
 4  journal = {IEEE Transactions on Pattern Analysis and Machine Intelligence},
 5  year    = {2008},
 6  volume  = {30},
 7  number  = {2},
 8  pages   = {328-341}
 9}
10
11@inproceedings{ref03_Scharstein2014Middlebury,
12  author    = {Scharstein, Daniel and Hirschmuller, Heiko and Kitajima, Yukihiro and Krathwohl, Greg and Ne{\v{s}}i{\'c}, Nera and Wang, Xi and Westling, Porter},
13  title     = {High-Resolution Stereo Datasets with Subpixel-Accurate Ground Truth},
14  booktitle = {German Conference on Pattern Recognition (GCPR)},
15  year      = {2014}
16}

全局定义
#

 1#let (
 2  functions
 3) = documentclass(
 4
 5fonts: (
 6  font-settings
 7),
 8  info: (
 9    info....
10  ),
11  bibliography: bibliography.with("ref.bib"),
12)

要在bibliography中改成自己的文献参考集的名字。

文章整体引用
#

只在前面的全局定义中写完了还不够,还需要在文章主体中使用文献参考集。

1#bilingual-bibliography(full: true)

要把这一行放在文末。 之后就可以直接通过@来引用了。


发发发 😠,学校毕业终稿不支持pdf提交,发发发,气死了,最后还要调格式。只有最迂腐最笨的学校才会有这样的规定。