Sách HDSD Typst
Dùng Được Từ Đề Thi Đến Sách
Bộ hướng dẫn công khai, chi tiết cho hệ thống soạn thảo tài liệu toán học: đề thi THPT, sách giáo khoa, chuyên đề, ID, ngân hàng câu hỏi, BBT và hình vẽ Cetz.
Hệ thống được thiết kế bởi GV Nguyễn Văn Sang, tương thích hoàn toàn với Typst ≥ 0.12. Tất cả file template nằm trong thư mục typst/. Lệnh biên dịch chuẩn luôn dùng --root typst.
👨🏫 Thầy Nguyễn Văn Sang
Giáo viên môn Toán — THPT Nguyễn Hữu Cảnh, TP. HCM
Dự án ConicTypst và thư viện sang-math được phát triển với niềm đam mê mang lại một công cụ soạn thảo đề thi, tài liệu Toán học và trình chiếu bài giảng thực sự mạnh mẽ, đẹp mắt và tối ưu cho giáo viên Việt Nam.
☕ Ủng hộ dự án (Donate)
VPBank: 10389821115
⚙️ Cài đặt & Biên dịch
Yêu cầu hệ thống
@preview/cetz:0.5.2▶ Chạy dùng Typst compiler dạng WASM chạy ngay trong trình duyệt, vì vậy người dùng chỉ cần Chrome / Edge / Safari / Firefox hiện đại và mạng internet để tải compiler, Cetz, font/package lần đầu.
Cách dùng không cần dòng lệnh
BẢNG ĐIỀU KHIỂN, bấm nút chạy/xem trước trong web hoặc extension Typst ở trình soạn thảo.
// BẢNG ĐIỀU KHIỂN trong file mẫu
#let theme = "teal-pro"
#let profile = "dethi"
// Đổi nhanh khi cần:
// #let profile = "loigiai"
// #let profile = "compact"
// #let profile = "draft"
Cấu trúc thư mục
typst/
├── template.typ ← Import cổng chính (dùng cái này)
├── sang-exam.typ ← Engine đề thi (tn, ds, tln, tl...)
├── bbt.typ ← Bảng biến thiên, xét dấu
├── geometry.typ ← Hình học phẳng & không gian
├── modules/
│ ├── book.typ ← Sách, SGK, chuyên đề
│ ├── exam.typ ← Wrapper cho engine thi
│ ├── exam-set.typ ← Bộ đề nhiều đề
│ └── research.typ ← Bài nghiên cứu
└── exams/ ← Đề thi mẫu
└── de-01.typ
Import chuẩn
// Cách 1: Import tất cả (khuyến nghị)
#import "template.typ": *
// Cách 2: Chỉ dùng engine thi
#import "sang-exam.typ": *
#import "@preview/cetz:0.5.2"
// Cách 3: Chỉ dùng module sách
#import "modules/book.typ": *
🚀 Khởi Động Nhanh
File đề thi cơ bản (5 phút)
// ① Import
#import "@preview/sang-math:1.0.0": *
#import "@preview/cetz:0.5.2"
// ② Preset giao diện và đầu ra
// profile: "dethi" | "loigiai" | "compact" | "draft" | "beamer"
#let preset = exam-preset(theme: "royal", profile: "dethi")
// ③ Tạo macro câu hỏi theo preset
#let (tn, ds, tln, tl) = exam-mode(..preset.question)
// ④ Show rules chuẩn
#show: sang-setup.with(math-color: preset.accent)
// ⑤ Tiêu đề đề thi
#show: exam-theme.with(
theme: preset.theme,
department: "SỞ GIÁO DỤC VÀ ĐÀO TẠO",
school: "TỈNH ĐỒNG THÁP",
exam-title: "KỲ THI THỬ TỐT NGHIỆP THPT NĂM 2025",
subject: "TOÁN",
duration: "90 phút, không kể thời gian phát đề",
code: "001",
..preset.template,
)
// ⑥ Phần I — Trắc nghiệm
#exam-part([PHẦN I. Câu trắc nghiệm nhiều phương án.
Thí sinh chọn 1 phương án đúng.], count: 12)
#tn(
[Đạo hàm của $f(x) = x^3 - 3x + 1$ tại $x = 2$ là],
([$3$], [$6$], True([$9$]), [$-3$]),
loigiai: [$f'(x) = 3x^2 - 3 => f'(2) = 9$],
)
// ⑦ Phần II — Đúng-Sai
#exam-part([PHẦN II. Câu đúng sai.], count: 4)
#ds(
[Cho $f(x) = x^2 - 2x + 3$. Xét tính đúng/sai:],
(
True([Hàm đạt cực tiểu tại $x = 1$]),
True([Giá trị nhỏ nhất trên $[-1;3]$ bằng $2$]),
[Hàm nghịch biến trên $(1;3)$],
True([PT $f(x) = 3$ có nghiệm trên $[-1;3]$]),
),
loigiai: [Phân tích từng ý...],
)
// ⑧ Phần III — Tự luận
#exam-part([PHẦN III. Câu tự luận.])
#tl(
[Cho $f(x) = x^3 - 3x^2 + 4$.
a) Khảo sát sự biến thiên.
b) Tìm $m$ để $f(x) = m$ có 3 nghiệm phân biệt.],
lines: 8,
loigiai: [Lời giải mẫu...],
)
File sách / SGK cơ bản
#import "template.typ": *
#show: stexgv-doc.with(
doc-type: "sgk", // "sgk" | "book" | "chuyende"
title: "CHUYÊN ĐỀ TOÁN 12",
subtitle: "Giải tích & Hình học",
author: "GV Nguyễn Văn Sang",
institution: "Trường THPT...",
subject: "Toán",
grade: "Lớp 12",
academic-year: "2025–2026",
)
#chapter([Hàm Số & Đạo Hàm])
#bai([Khảo sát hàm bậc ba])
#muctieu[
- Lập được BBT của hàm bậc ba.
- Tìm được cực trị và khoảng đơn điệu.
]
#khoidong[
Quan sát đồ thị sau và trả lời: hàm có bao nhiêu cực trị?
]
#khampha[
Phương pháp: tính $f'(x)$, giải $f'(x) = 0$, lập BBT.
]
#vd(
[Khảo sát $f(x) = x^3 - 3x^2 + 4$.],
loigiai: [
$f'(x) = 3x^2 - 6x = 3x(x-2)$.
Lập BBT: cực đại $f(0) = 4$, cực tiểu $f(2) = 0$.
],
)
#bt(
[Tìm cực trị của $f(x) = -x^3 + 3x - 2$.],
lines: 4,
)
Bộ đề nhiều đề trong một file
#import "template.typ": *
#show: stexgv-doc.with(
doc-type: "bo-de",
title: "BỘ ĐỀ THI THỬ TOÁN 12",
author: "GV Nguyễn Văn Sang",
academic-year: "2025–2026",
)
#let (tn, ds, tln, tl) = exam-mode()
// ──── ĐỀ SỐ 1 ────
#de(
title: [Đề số 1],
code: "101",
subject: "TOÁN",
duration: "90 phút",
show-answer-key: true, // In đáp án ngay sau đề
)[\
#exam-part([PHẦN I. Trắc nghiệm.], count: 12)
#tn(
[Hàm $y = x^2$ có đạo hàm là],
([$x$], [$2$], True([$2x$]), [$x^2$]),
)
// ... thêm câu hỏi ...
]
// ──── ĐỀ SỐ 2 ────
#de(
title: [Đề số 2],
code: "102",
show-answer-key: true,
)[\
#exam-part([PHẦN I. Trắc nghiệm.], count: 12)
// ... câu hỏi đề 2 ...
]
📋 Tổng Quan Module Đề Thi
Module đề thi (sang-exam.typ) cung cấp đầy đủ công cụ soạn đề thi THPT theo cấu trúc Bộ GD&ĐT từ 2025.
Luồng làm việc
sang-exam.typ + cetz
dethi / loigiai / solcolor
Tạo macro tn/ds/tln/tl
thpt-school-exam
#tn #ds #tln #tl
Ba chế độ hiển thị
| mode | Hiển thị | Dùng khi |
|---|---|---|
"dethi" | Câu hỏi thuần, không đáp án, ô trống trắng | In đề cho học sinh |
"loigiai" | Câu hỏi + lời giải + đáp án đúng tô màu | Tài liệu ôn tập, đáp án |
"solcolor" | Như loigiai nhưng đáp án đúng nổi bật hơn | Đáp án chiếu trình chiếu |
4 loại câu hỏi
#tnTự chọn 1/2/4 cột
#dsBảng Đ/S tự động
#tln4 ô điền
#tlDòng kẻ tự động
① #tn — Câu Trắc Nghiệm Nhiều Phương Án
#tn (alias của #mcq) soạn câu trắc nghiệm 4 phương án, tự động chọn số cột 1/2/4 tuỳ độ rộng phương án.
Cú pháp đầy đủ
#tn(
[Nội dung đề bài],
( [$A$], True([$B$]), [$C$], [$D$] ),
loigiai: [Lời giải chi tiết],
// Các tham số tuỳ chọn:
fig: none,
fig-pos: "right",
fig-width: 35%,
cols: 0,
lines: 0,
num: auto,
prefix: "Câu",
boxed: false
)
Tham số chi tiết
[...].( ... ). Đáp án đúng phải được bọc trong hàm True(...).mode là "loigiai" hoặc "solcolor".cetz.canvas hoặc image)."right" (phải), "left" (trái), hoặc "center" (dưới đề).35%.0 (tự động chia 1, 2 hoặc 4 cột). Nhập 2 hoặc 4 để ép buộc.5 -> Câu 5). Mặc định tự tăng.Ví dụ 1 — Câu đơn không hình
#tn(
[Đạo hàm của hàm số $f(x) = x^3 - 3x + 1$ tại $x = 2$ bằng],
([$3$], [$6$], True([$9$]), [$-3$]),
loigiai: [
$f'(x) = 3x^2 - 3 => f'(2) = 12 - 3 = 9$.
],
)
Ví dụ 2 — Câu có hình bên phải
#tn(
[Đồ thị bên có bao nhiêu cực trị?],
([$0$], [$1$], True([$2$]), [$3$]),
fig: cetz.canvas(length: 0.7cm, {
import cetz.draw: *
line((-2.2, 0), (2.2, 0), mark: (end: ">"))
line((0, -1.5), (0, 2), mark: (end: ">"))
content((2.2, 0.3), $x$)
content((0.3, 2), $y$)
let pts = ()
for i in range(-21, 21) {
let x = i * 0.1
let y = -x * x * x / 3 + x
if y >= -1.5 and y <= 2 { pts.push((x, y)) }
}
line(..pts, stroke: 1.5pt + blue)
circle((-1, 2/3), radius: 2.5pt, fill: red)
circle((1, -2/3), radius: 2.5pt, fill: red)
}),
fig-pos: "right",
fig-width: 38%,
loigiai: [Quan sát đồ thị: hàm có 2 cực trị (1 cực đại, 1 cực tiểu).],
)
Ví dụ 3 — Phương án là hình vẽ
// Khi 4 phương án là hình vẽ (opt-fig: true)
#tn(
[Đồ thị nào sau đây là của hàm số $y = x^3$?],
(
True(cetz.canvas(length:0.5cm, { /* vẽ parabol */ })),
cetz.canvas(length:0.5cm, { /* vẽ ... */ }),
cetz.canvas(length:0.5cm, { /* vẽ ... */ }),
cetz.canvas(length:0.5cm, { /* vẽ ... */ }),
),
opt-fig: true, // BẮT BUỘC khi option là canvas
opt-fig-cols: 2, // 2×2 grid
)
cetz.canvas() và chuyển sang chế độ hình. Vẫn nên đặt opt-fig: true để rõ ràng.
Tuỳ biến nhãn câu hỏi (opt-style)
Bạn có thể đổi kiểu nhãn A, B, C, D hiển thị cho các phương án. Thiết lập bằng biến toàn cục #let opt-style = "..." ở đầu file hoặc truyền trực tiếp vào tuỳ chọn opt-style: "..." của câu hỏi.
"plain"(Mặc định): Hiển thị nhãn kèm dấu chấm, VD: A., B., C., D."circle"hoặc"circled": Vẽ hình tròn bao quanh nhãn, VD: Ⓐ, Ⓑ, Ⓒ, Ⓓ"hexagon": Vẽ hình lục giác bao quanh nhãn.
② #ds — Câu Đúng / Sai
Câu hỏi gồm một thân chung và 4 phát biểu a/b/c/d, học sinh chọn Đúng/Sai cho từng ý. Tự động sinh bảng Đ/S có header màu.
Cú pháp đầy đủ
#ds(
[Thân câu chung — nêu tình huống / bài toán],
(
[Phát biểu a — SAI],
True([Phát biểu b — ĐÚNG]),
True([Phát biểu c — ĐÚNG]),
[Phát biểu d — SAI],
),
loigiai: [Phân tích từng ý.],
// Các tham số tuỳ chọn:
fig: none,
fig-pos: "right",
fig-width: 30%,
lines: 0,
num: auto,
prefix: "Câu",
)
Ví dụ đầy đủ
#ds(
[Cho $f(x) = x^2 - 2x + 3$ trên $[-1; 3]$. Xét tính đúng/sai:],
(
True([Hàm số đạt cực tiểu tại $x = 1$]),
True([Giá trị nhỏ nhất của hàm số trên đoạn là $2$]),
[Hàm số nghịch biến trên $(1; 3)$],
True([Phương trình $f(x) = 3$ có nghiệm trên $[-1; 3]$]),
),
loigiai: [
- *Ý a) Đúng.* $f'(x)=2x-2=0 => x=1$, $f''(1)=2>0$ → cực tiểu.
- *Ý b) Đúng.* $f(1) = 1 - 2 + 3 = 2$.
- *Ý c) Sai.* Hàm đồng biến trên $(1;3)$ vì $f'(x)>0$.
- *Ý d) Đúng.* $f(0) = 3$ là nghiệm.
],
)
True([...]), phát biểu sai để nguyên [...]. Trong chế độ loigiai, hệ thống tự tô màu ô Đ/S.
Tham số tuỳ biến bảng Đúng/Sai (ds-style)
Có 4 kiểu hiển thị danh sách các phát biểu a, b, c, d. Điều khiển qua tham số ds-style:
ds-style: "plain"(Mặc định): Dạng văn bản chuẩn a), b), c), d).ds-style: "circled": Khoanh tròn ⓐ, ⓑ, ⓒ, ⓓ.ds-style: "box": Hộp vuông hiện đại.ds-style: "none": Ẩn hoàn toàn nhãn, chỉ thụt lề đầu dòng.
③ #tln — Câu Trả Lời Ngắn
Học sinh điền vào 4 ô trống (chế độ đề thi). Đáp án hiển thị trong ô màu xanh (chế độ lời giải).
Cú pháp đầy đủ
#tln(
[Nội dung đề bài],
[$6$],
loigiai: [Lời giải.],
// Các tham số tuỳ chọn:
fig: none,
fig-pos: "right",
fig-width: 30%,
show-boxes: true,
box-count: 4,
lines: 0,
num: auto,
prefix: "Câu",
)
Ví dụ 1 — Câu đơn
#tln(
[Giải phương trình $2x - 5 = 7$, tìm $x$.],
[$6$],
loigiai: [$2x = 12 => x = 6$.],
)
Ví dụ 2 — Có hình và dòng tính
#tln(
[Tính diện tích tam giác OAB với $A(3;0)$, $B(0;4)$.],
[$6$],
fig: cetz.canvas(length: 0.8cm, {
import cetz.draw: *
line((0,0),(3,0),(0,4),(0,0), stroke: 1pt)
content((3.3,0), $A$)
content((-0.3,4.3), $B$)
content((-0.3,-0.3), $O$)
}),
fig-pos: "center", // hình dưới đề bài, căn giữa
lines: 2, // 2 dòng kẻ để tính
loigiai: [$S = frac(1, 2) dot 3 dot 4 = 6$],
)
④ #tl — Câu Tự Luận
Cú pháp đầy đủ
#tl(
[Nội dung đề bài tự luận],
loigiai: [Lời giải mẫu.],
// Các tham số tuỳ chọn:
fig: none,
fig-pos: "right",
fig-width: 30%,
lines: 6,
num: auto,
prefix: "Câu",
boxed: false,
)
Ví dụ — Lời giải từng bước với #step
#tl(
[Cho hàm số $f(x) = x^3 - 3x^2 + 4$.
a) Khảo sát sự biến thiên và vẽ đồ thị.
b) Tìm $m$ để phương trình $f(x) = m$ có 3 nghiệm phân biệt.],
lines: 8,
loigiai: [
#ppgiai[Dùng đạo hàm và BBT để trả lời cả hai phần.]
#step[$f'(x) = 3x^2 - 6x = 3x(x-2)$. Cho $f'(x)=0 => x=0$ hoặc $x=2$.]
#step[Cực đại: $f(0) = 4$. Cực tiểu: $f(2) = 0$.]
#step[Phương trình $f(x)=m$ có 3 nghiệm phân biệt khi $0 < m < 4$.]
#reset-step()
],
)
Macro #step — Tô màu từng bước
loigiai: [
// Bước tự đếm, tự chọn màu xoay qua 6 màu
#step[Đặt ẩn phụ $t = e^x > 0$...]
#step[Giải: $t = 1$ hoặc $t = 2$.]
#step[Trả lại: $x = 0$ hoặc $x = \ln 2$.]
#step(color: red)[Lưu ý điều kiện $t > 0$.]
#reset-step() // Reset bộ đếm cho câu tiếp theo
]
Chia điểm từng bước bằng lệnh #step
Bên trong khối loigiai của câu trả lời ngắn và tự luận, giáo viên có thể dùng lệnh #step(điểm) để đánh dấu barem điểm chấm bài.
loigiai: [
Ta có $f(x) = x^2 - 4x + 3$. #step(0.25)
$f'(x) = 2x - 4$. #step(0.25)
Hàm số đạt cực tiểu tại $x = 2$. #step(0.5)
]
Lệnh này sẽ render ra ô điểm màu đỏ nổi bật ở bên lề phải, rất chuyên nghiệp cho tài liệu đáp án chi tiết.
🏷️ Tiêu Đề Đề Thi — #thpt-school-exam
#show: thpt-school-exam.with(
department: "SỞ GIÁO DỤC VÀ ĐÀO TẠO", // dòng 1 tiêu đề trái
school: "TỈNH ĐỒNG THÁP", // dòng 2 tiêu đề trái
exam-title: "KỲ THI THỬ TỐT NGHIỆP THPT", // tên kỳ thi, căn giữa
subject: "TOÁN",
duration: "90 phút, không kể thời gian phát đề",
code: "001", // mã đề
structure: auto, // tự sinh "Đề gồm X câu"
footer-left: [ĐỀ CHÍNH THỨC \ (Đề có 4 trang)],
accent: accent, // màu chủ đạo
show-topbar: true, // thanh màu phía trên
header-border: true, // đường kẻ dưới tiêu đề
)
#show: thpt-school-exam.with(...) phải đặt sau #let (tn,ds,tln,tl) = exam-mode(...) và sau #show: sang-setup, nhưng trước câu hỏi đầu tiên.
Macro #exam-part — Tiêu đề phần thi
// Phần trắc nghiệm — tự sinh "Câu 1 đến Câu 12"
#exam-part([PHẦN I. Câu trắc nghiệm nhiều phương án.
Thí sinh chọn 1 phương án đúng.], count: 12)
// Phần đúng-sai
#exam-part([PHẦN II. Câu đúng sai.
Thí sinh chọn Đúng hoặc Sai.], count: 4)
// Phần tự luận (không cần count)
#exam-part([PHẦN III. Câu tự luận.])
// Reset số câu giữa các phần nếu cần
#exam-part([PHẦN II.], reset-counter: true)
#exam-part đặc biệt được tối ưu để tạo các phần I, II, III theo quy định mới của Bộ GD&ĐT.
- Dùng
count: Nđể xác định số lượng câu hỏi trong phần, hệ thống sẽ in ra câu phụ (Câu 1 đến Câu N) ở tiêu đề. - Dùng
reset-counter: truenếu muốn đánh số thứ tự câu lại từ đầu ở phần mới.
🔀 Chế Độ Hiển Thị
exam-mode() — Tạo bộ macro câu hỏi
// Bảng điều khiển ngay trong file
#let theme = "teal-pro"
#let profile = "dethi"
#let opt-style = auto
// Bật nhanh bằng cách đổi dòng trên:
// #let profile = "loigiai" // lời giải + bảng đáp án
// #let profile = "compact" // đề 2 cột
// #let profile = "draft" // có nháp bên phải
// #let theme = "aurora" // đổi mẫu màu
// #let opt-style = "plain" // A. B. C. D. nguyên thuỷ
// #let opt-style = "theme" // tự theo theme
// #let opt-style = "circle" // khoanh tròn
// #let opt-style = "hexagon" // A/B/C/D trong lục giác đều
#let preset = exam-preset(theme: theme, profile: profile, opt-style: opt-style)
#let (tn, ds, tln, tl) = exam-mode(..preset.question)
#let profile = "dethi" thành #let profile = "loigiai" ngay trong file — không cần sửa nội dung câu hỏi.
exam-preset() — Công tắc nhanh cho đề, lời giải, beamer
exam-preset() gom sẵn màu sắc, kiểu nhãn A/B/C/D, kiểu nhãn Câu, chế độ in bảng đáp án, dàn 2 cột và cột nháp. Người dùng mới nên bắt đầu bằng preset thay vì tự viết nhiều biến boolean.
Hiểu về tham số profile (chế độ hiển thị)
"dethi": Chế độ đề thi gốc cho học sinh làm."loigiai": Hiện đáp án đúng và toàn bộ phần lời giải chi tiết."solcolor": (Rất hay dùng) Khoanh đỏ đáp án đúng nhưng vẫn ẩn lời giải chi tiết. Dùng để GV in đề cương / chữa bài nhanh trên lớp."compact": Đề thi chia 2 cột tiết kiệm giấy."draft": Đề thi có phần nháp trống bên phải.
#import "@preview/sang-math:1.0.0": *
// BẢNG ĐIỀU KHIỂN: người dùng chỉ sửa các dòng này.
#let theme = "aurora"
#let profile = "dethi"
#let opt-style = auto
#let q-label-style = auto
// Các dòng mẫu để bật nhanh:
// #let profile = "loigiai" // lời giải + bảng đáp án
// #let profile = "compact" // đề 2 cột
// #let profile = "draft" // có nháp bên phải
// #let opt-style = "plain" // A. B. C. D. nguyên thuỷ
// #let opt-style = "theme" // tự theo theme
// #let opt-style = "circle" // khoanh tròn
// #let opt-style = "hexagon" // lục giác đều
// #let opt-style = "solid-hexagon"
#let preset = exam-preset(
theme: theme,
profile: profile,
opt-style: opt-style,
q-label-style: q-label-style,
)
#let (tn, ds, tln, tl) = exam-mode(..preset.question)
#show: sang-setup.with(math-color: preset.accent)
#show: exam-theme.with(
theme: preset.theme,
school: "TRƯỜNG THPT SANG-MATH",
exam-title: "ĐỀ THI THỬ THPT",
subject: "TOÁN 12",
duration: "90 phút",
code: "101",
..preset.template,
)
answer-key: true để có bảng đáp án cuối đề.two-columns: true để dàn đề 2 cột.sang-beamer.typ.Kiểu nhãn đáp án và nhãn Câu
#let preset = exam-preset(
theme: "jade",
profile: "dethi",
opt-style: "hexagon", // lục giác đều
q-label-style: "badge",
)
#let preset2 = exam-preset(
theme: "navy-gold",
profile: "loigiai",
opt-style: "solid-hexagon", // lục giác đặc
q-label-style: "solid-pill",
)
plain là A. B. C. D. nguyên thuỷ; theme là tự theo mẫu màu; ngoài ra có circle, double-circle, solid-circle, square, rounded-square, diamond, triangle, pentagon, hexagon, solid-hexagon, badge.
Công tắc bật/tắt ngay trong file
// Giữ một dòng profile đang dùng, comment các dòng còn lại.
#let profile = "dethi"
// #let profile = "loigiai"
// #let profile = "compact"
// #let profile = "draft"
// Chọn một theme đang dùng.
#let theme = "teal-pro"
// #let theme = "aurora"
// #let theme = "navy-gold"
// #let theme = "lotus"
// Chọn kiểu nhãn đáp án.
#let opt-style = auto
// #let opt-style = "plain"
// #let opt-style = "theme"
// #let opt-style = "circle"
// #let opt-style = "hexagon"
// #let opt-style = "solid-hexagon"
// #let opt-style = "pentagon"
Chuyển cùng nội dung sang sang-beamer.typ
#import "sang-beamer.typ": *
#import "@preview/sang-math:1.0.0": exam-preset
#let preset = exam-preset(theme: "aurora", profile: "beamer")
#let (tn, ds, tln, tl) = exam-mode(mode: "loigiai", accent: preset.accent)
#show: sang-beamer-theme.with(
title: "CHỮA ĐỀ THI THỬ",
subtitle: "Toán 12",
author: "GV Nguyễn Văn Sang",
code: "101",
..preset.beamer-theme,
)
// Giữ nguyên các #exam-part, #tn, #ds, #tln, #tl như file đề.
Điều khiển bộ đếm câu
// Đặt câu kế tiếp là Câu 13
#setcau(13)
// Reset về Câu 1
#resetcau()
// Đặt phần kế tiếp là PHẦN III
#setphan(3)
// Reset trạng thái đề thi mới (dùng trong bộ đề)
#resetexamstate(question-start: 1, part-start: 1)
🆔 ID, Tags & Ngân Hàng Câu Hỏi
Hệ thống dùng ID để quản lý câu hỏi bền vững. ID trong câu hỏi giúp đối soát khi trộn đề; ID trong bank.json là bản đồ phân loại để lọc theo lớp, chương, bài, dạng và mức độ.
Quy ước ID khuyến nghị
0D1N1-1
│││││ └─ số thứ tự trong cụm
││││└── bài/dạng
│││└── mức độ: N=NB, H=TH, V=VD, C=VDC
││└── chương
│└── phân môn: D=Đại số/Giải tích, H=Hình học
└── khối: 0=Lớp 10, 1=Lớp 11, 2=Lớp 12
npm run bank:gen-id để sinh ID mới, npm run bank:stat để thống kê, npm run sync:bank nếu vừa chỉnh bank.json ở root.
Gắn ID và tags trực tiếp trong câu
#tn(
[Hàm số $y = x^3 - 3x$ nghịch biến trên khoảng nào?],
([$(-oo;-1)$], True([$(-1;1)$]), [$(1;+oo)$], [$RR$]),
id: "2D1H1-7",
tags: ("lop12", "dao-ham", "don-dieu", "bbt"),
loigiai: [
$y' = 3x^2 - 3 = 3(x-1)(x+1)$.
Hàm nghịch biến khi $-1 < x < 1$.
],
)
("lop12","bbt","vd"). #tn/#ds/#tln có thể render tags nhỏ cạnh stem.loigiai:; dùng được trong engine nếu loigiai để none.bank.json chỉ giữ metadata
Không nên nhét đề bài dài, công thức và hình Cetz vào JSON. Hãy để bank.json làm bản đồ mã, còn nội dung câu hỏi thật viết trong typst/questions.typ.
{
"0D1N1-1": "Lớp 10 | Đại số | Ch1: Mệnh đề | Bài 1: Mệnh đề | Nhận biết"
}
Khai báo câu hỏi thật trong Typst
#import "modules/question-bank.typ": (
ds-item, question-tn, question-ds, question-tln, question-tl,
render-question, render-selection,
)
#let question-bank = (
"0D1N1-1": question-tn(
[Phát biểu nào sau đây là một mệnh đề?],
([Bạn học tốt nhé!], [$2$ là số nguyên tố.], [Hôm nay đẹp quá!], [$x+1=3$]),
correct: 2,
solution: [Chỉ phát biểu "$2$ là số nguyên tố" xác định đúng/sai.],
tags: ("lop10", "menh-de"),
difficulty: "NB",
status: "ready",
source: "questions.typ",
),
)
API quản lý bank
| Lệnh | Ý nghĩa |
|---|---|
#load-bank() | Đọc metadata từ typst/bank.json. |
#bank-filter(...) | Lọc metadata theo lớp, phân môn, chương, bài, dạng, query hoặc danh sách ID. |
#bank-count(entries, by: "chapter") | Đếm số mã theo chương, bài, dạng, phân môn. |
#question-filter(...) | Lọc câu hỏi thật theo type, difficulty, status, source, tags, ids. |
#question-select(...) | Nối metadata trong bank với câu hỏi thật trong registry. |
#render-selection(..., show-id: true) | Render danh sách câu; bật ID khi biên tập nội bộ. |
Dựng câu theo bộ lọc
#import "template.typ": *
#import "questions.typ": question-bank
#let bank = load-bank()
#let selected = question-select(
bank,
question-bank,
chapter: "Mệnh đề",
status: "ready",
)
#render-selection(selected, mode: "loigiai", show-id: true)
🧪 Trộn Đề Bằng Typst
Phần này là bản Typst-native của ý tưởng trong stexgv.web.app: vẫn dùng ID ổn định, vẫn có ma trận slot, seed trộn, tránh trùng câu, trộn phương án trắc nghiệm và xuất bảng đáp án — nhưng render trực tiếp bằng #tn/#ds/#tln/#tl.
typst/exam-mix-demo.typ, sửa phần cấu hình ở đầu file rồi bấm xem trước. Demo sinh 2 mã đề từ cùng một ngân hàng và tự in bảng đáp án.
Nhập kho STEXGV sang Typst
Nếu máy có thư mục /Users/admin/stexgv, bridge mới sẽ đọc trực tiếp kho ThptExamBuilder/banks, giữ ID gốc, slot 1-22, topic, source, seed sinh câu, rồi xuất registry Typst để trộn đề.
npm run stexgv:import
npm run stexgv:mix -- --compile
| File | Vai trò |
|---|---|
scripts/stexgv-typst-bridge.mjs | Importer STEXGV: gọi generateLatex(seed), phân tích \choice, \choiceTF, \shortans, \loigiai. |
typst/stexgv-import/stexgv-bank.json | Snapshot quản lý ngân hàng: ID, slot, loại câu, topic, source, latex gốc, trạng thái. |
typst/stexgv-import/stexgv-bank.typ | Registry Typst dùng ngay với #build-exam-plan và #render-exam-plan. |
typst/stexgv-import/stexgv-mix-demo.typ | File trộn đề 22 slot chuẩn THPT, sinh 2 mã đề và bảng đáp án. |
output/stexgv-mix-demo.pdf | Đầu ra PDF khi chạy thêm --compile. |
ready, 810 câu review vì còn TikZ, tkz-tab hoặc lệnh LaTeX đặc thù cần chuyển sâu sang CeTZ/BBT. Tất cả câu vẫn có ID và trộn đề được khi dùng status: none.
Import lõi trộn đề
#import "sang-exam.typ": *
#import "questions.typ": question-bank
#import "modules/question-bank.typ": (
exam-slot,
build-exam-plan,
render-exam-plan,
render-answer-key,
thpt-2025-matrix,
)
Khai báo ma trận slot
Mỗi exam-slot(...) tương đương một ô trong ma trận đề. Có thể chọn theo ids cố định, hoặc lọc mềm bằng type, difficulty, tags, chapter, lesson, form.
#let matrix = (
// Chọn cố định theo ID
exam-slot(1, label: [TN — mệnh đề], type: "tn", ids: ("0D1N1-1",)),
// Chọn ngẫu nhiên trong nhóm ID
exam-slot(2, label: [TN — Oxyz], type: "tn", ids: ("12HH-MC-01", "12HH-DT-01", "12HH-MP-01")),
// Chọn mềm theo tag / mức độ
exam-slot(3, label: [TN — tích phân], type: "tn", tags: ("tich-phan",), difficulty: ("NB", "TH")),
exam-slot(4, label: [Đúng sai — xác suất], type: "ds", tags: ("xac-suat",), difficulty: ("VD",)),
exam-slot(5, label: [Trả lời ngắn], type: "tln", difficulty: ("VD", "VDC")),
)
Sinh đề từ seed
#let plan = build-exam-plan(
question-bank,
matrix,
seed: 2026,
strict: false, // false = nếu thiếu câu sẽ nới difficulty/tags
allow-duplicates: false // không lấy lại cùng một ID trong cùng đề
)
#render-exam-plan(
plan,
mode: "dethi",
seed: 2026,
shuffle-options: true, // trộn phương án A/B/C/D nhưng vẫn giữ đúng đáp án
show-id: true, // bật khi soạn nội bộ; tắt khi in cho học sinh
)
#pagebreak()
#render-answer-key(plan, seed: 2026, shuffle-options: true)
Sinh 2 mã đề
#let render-code(code, seed) = {
let plan = build-exam-plan(question-bank, matrix, seed: seed)
#show: thpt-school-exam.with(
school: "CONICTYPST",
exam-title: "ĐỀ TRỘN TỪ NGÂN HÀNG",
subject: "TOÁN",
duration: "90 phút",
code: code,
)
render-exam-plan(plan, seed: seed, shuffle-options: true)
pagebreak()
render-answer-key(plan, seed: seed, shuffle-options: true, title: [Bảng đáp án mã #code])
}
#render-code("101", 2026)
#pagebreak()
#render-code("102", 2027)
Ma trận 22 slot chuẩn THPT
Hệ thống đã có sẵn thpt-2025-matrix theo cấu trúc 12 câu trắc nghiệm, 4 câu đúng/sai, 6 câu trả lời ngắn. Khi ngân hàng đủ dữ liệu, dùng trực tiếp:
#let plan = build-exam-plan(
question-bank,
thpt-2025-matrix,
seed: 26072026,
strict: false,
)
#render-exam-plan(plan, seed: 26072026, shuffle-options: true)
#pagebreak()
#render-answer-key(plan, seed: 26072026, shuffle-options: true)
| Hàm | Vai trò |
|---|---|
#exam-slot(...) | Mô tả một ô ma trận: loại câu, ID, tags, mức độ, nguồn, trạng thái. |
#build-exam-plan(...) | Chọn câu theo seed, tránh trùng ID, trả về danh sách câu đã chọn. |
#render-exam-plan(...) | Render đề bằng macro #tn/#ds/#tln/#tl. |
#render-answer-key(...) | Xuất bảng đáp án sau khi đã trộn phương án. |
#thpt-2025-matrix | Ma trận mẫu 22 slot theo cấu trúc THPT hiện hành. |
⚙️ Tham Số Typst Thường Dùng
Đây là bảng đọc nhanh các lệnh Typst xuất hiện dày trong hệ thống. Hiểu nhóm này là đọc được phần lớn template.
Tham số nâng cao của câu hỏi
| Tham số | Macro | Ý nghĩa |
|---|---|---|
row-gutter: | #tn | Khoảng cách giữa các hàng phương án, hữu ích khi phương án có phân số/căn/log. |
opt-fig: | #tn | Đặt true khi phương án là hình vẽ để render dạng card. |
opt-fig-cols: | #tn | Số cột khi phương án là hình; thường dùng 2. |
box-count: | #tln | Số ô đáp số trong chế độ đề thi. |
show-boxes: | #tln | Tắt/bật cụm ô đáp số. |
boxed: | Tất cả | Đóng khung toàn câu, kèm box-fill, box-stroke, box-inset, box-radius. |
num: | Tất cả | Ép số câu thủ công; mặc định auto. |
prefix: | Tất cả | Đổi nhãn câu, ví dụ "Bài" hoặc "Question". |
Layout cốt lõi
| Lệnh | Cách hiểu nhanh |
|---|---|
#set page(...) | Khổ giấy, lề, header/footer, watermark/nền trang. |
#set text(...) | Font, cỡ chữ, ngôn ngữ, màu, độ đậm. |
#set par(...) | Căn đều đoạn văn và khoảng cách dòng. |
block(...)[...] | Khối lớn có nền, viền, padding, bo góc, khoảng cách trên/dưới. |
box(...)[...] | Hộp nhỏ/inline, hay dùng cho badge, ô đáp án, nhãn. |
grid(columns: (...), ...) | Bố cục nhiều cột. Ví dụ (1fr, 35%) là text co giãn + hình 35%. |
table(columns: (...), ...) | Bảng dữ liệu; có thể tô header hoặc zebra row bằng hàm fill. |
align(center)[...] | Căn nội dung trái/phải/giữa/trên/dưới. |
pad(x:, y:)[...] | Thêm khoảng đệm quanh nội dung. |
v(...) / h(...) | Khoảng trắng dọc/ngang. |
Kiểu dữ liệu hay gặp
| Kiểu | Ví dụ |
|---|---|
content | [Nội dung], [$x^2$], dùng cho stem/lời giải. |
string | "dethi", "right", "0D1N1-1". |
tuple | ([$A$], True([$B$]), [$C$], [$D$]); tuple 1 phần tử cần dấu phẩy như ("AC",). |
dictionary | (body: [text], correct: true); True([...]) là dictionary. |
color | rgb("#0057b8"), classic.blue, red. |
length | 12pt, 0.8em, 1cm. |
relative | 35%, 100%. |
auto | Để hệ thống tự tính: num: auto, structure: auto, h1: auto. |
none | Không có giá trị: tắt hình, lời giải, watermark, subtitle. |
Hình dài: dùng grid trong stem
#tln(
[
Đoạn mở đầu full-width.
#grid(
columns: (1fr, 38%),
column-gutter: 14pt,
align: (left + top, center + top),
[Đoạn văn song song với hình.],
cetz.canvas(length: 1cm, { /* hình */ }),
)
Đoạn kết vẫn full-width.
],
[$42$],
)
📦 Các Hộp Sư Phạm
Tất cả hộp dưới đây đều dùng được trong loigiai: của mọi loại câu hỏi.
Nhóm hộp lời giải
#ppgiai[...]#luuy[...]#meo[...]#nhanxet[...]#lythuyet[...]#note[...]Nhóm hộp học thuật (Theorem style)
#dn[...]#dl[...]#tc[...]#bode[...]Ví dụ sử dụng trong loigiai
#tl(
[Tìm min của $f(x) = x^2 - 4x + 5$.],
loigiai: [
#ppgiai[Dùng hoàn phương hoặc đạo hàm.]
#lythuyet[$f(x) = (x-a)^2 + b$ đạt min khi $x = a$, $\min = b$.]
#step[$f(x) = (x-2)^2 + 1$.]
#step[Min $= 1$ khi $x = 2$.]
#luuy[Không nhầm cực tiểu với giá trị nhỏ nhất trên đoạn.]
#reset-step()
],
)
📄 Quy trình 3 bước & File Mẫu Đề Thi
Để soạn một đề thi chuẩn theo định dạng mới, giáo viên nên làm theo quy trình chuẩn sau để đảm bảo không lỗi và tận dụng tối đa sức mạnh của hệ thống.
Bước 1: Khởi tạo file đề thi (Boilerplate)
Tạo một file mới (VD: de-giua-ky.typ) trong thư mục exams/ và copy đoạn code khung dưới đây vào. Đoạn code này chứa Bảng điều khiển để bật/tắt đáp án và cấu hình màu sắc.
// ═══════════════════════════════════════════
// ĐỀ THI MẪU — CHỈNH SỬA BẢNG ĐIỀU KHIỂN BÊN DƯỚI
// ═══════════════════════════════════════════
#import "../typst/sang-exam.typ": *
#import "@preview/cetz:0.5.2"
// ── 1. BẢNG ĐIỀU KHIỂN (Bật/tắt chế độ) ───
#let mode = "dethi" // ◄ Đổi thành "loigiai" hoặc "solcolor"
#let accent = classic.blue // ◄ classic.blue | .emerald | .crimson
// ── 2. KHỞI TẠO MACRO CÂU HỎI ─────────────
#let (tn, ds, tln, tl) = exam-mode(mode: mode, accent: accent)
#show: sang-setup
// ── 3. THIẾT LẬP TRANG & TIÊU ĐỀ ──────────
#set page(paper: "a4", margin: (x: 2cm, y: 2.5cm))
#show: thpt-school-exam.with(
school: "SỞ GD&ĐT TP. HỒ CHÍ MINH",
title: "ĐỀ KIỂM TRA GIỮA HỌC KỲ I - TOÁN 12",
time: "Thời gian làm bài: 90 phút",
exam-code: "135",
accent: accent,
)
// ── 4. NỘI DUNG ĐỀ THI BẮT ĐẦU TỪ ĐÂY ─────
Bước 2: Viết nội dung câu hỏi
Dán các câu hỏi của thầy/cô ngay dưới phần tiêu đề, phân chia cấu trúc bằng lệnh #exam-part.
#exam-part([PHẦN I. Câu trắc nghiệm nhiều phương án.], count: 12)
#tn(
[Nghiệm của phương trình $2x - 4 = 0$ là],
([$x = -2$], [$x = 4$], True([$x = 2$]), [$x = -4$]),
)
#exam-part([PHẦN II. Câu trắc nghiệm đúng sai.], count: 4, reset-counter: true)
#ds(
[Cho hàm số $y=f(x)$. Xét tính đúng sai:],
(
[Hàm số đồng biến trên $R$.],
True([Hàm số có cực đại.]),
True([Đồ thị cắt trục hoành tại 2 điểm.]),
[Giá trị lớn nhất bằng 5.],
)
)
Bước 3: Biên dịch (Compile)
Sau khi soạn xong, để xem kết quả PDF, có thể dùng 1 trong các cách sau:
- VS Code: Dùng extension
Tinymist Typst, mở file ấn Ctrl + K V để Preview. - Dòng lệnh: Mở terminal gõ:
typst compile --root . exams/de-giua-ky.typ - Web: Chép toàn bộ code vào trang web
typst.app.
📚 Tổng Quan Module Sách / SGK
Module sách (modules/book.typ) cung cấp hệ thống soạn thảo đầy đủ cho SGK, chuyên đề và bộ đề thi nhiều đề.
Các loại tài liệu hỗ trợ
| doc-type | Mô tả | File mẫu |
|---|---|---|
"book" | Sách / chuyên đề cơ bản | typst/book.typ |
"sgk" | Sách giáo khoa đầy đủ mạch dạy học | typst/sgk.typ |
"chuyende" | Chuyên đề học tập dài | typst/chuyende.typ |
"bo-de" | Bộ đề thi nhiều đề trong một file | typst/bo-de.typ |
"research" | Bài nghiên cứu khoa học 2 cột | modules/research.typ |
Metadata chuẩn
#show: stexgv-doc.with(
doc-type: "sgk", // Loại tài liệu
title: "TÊN SÁCH",
subtitle: "Phụ đề nếu có",
author: "GV Nguyễn Văn Sang",
institution: "Trường THPT...",
subject: "Toán",
grade: "Lớp 12",
series: "Tên bộ tài liệu",
academic-year: "2025–2026",
publisher: "Đơn vị phát hành",
theme-color: classic.blue, // Màu chủ đạo
show-cover: true, // Bật/tắt trang bìa
show-outline: true, // Bật/tắt mục lục
outline-depth: 3, // Độ sâu mục lục
)
🗂️ Cấu Trúc Phân Cấp Sách
Các lệnh cấu trúc
#part([Tên phần]) alias: #unit#chapter([Tên chương])#bai([Tên bài]) alias: #lesson#dang([Tên dạng toán]) alias: #topic#muc([Tên mục]) alias: #section#appendix([Tên phụ lục]) alias: #phulucCác lệnh mở đầu tài liệu
#loinoidau[Lời nói đầu...] // alias: #preface
#gioithieu[Giới thiệu...] // alias: #introduction
#huongdansudung[Hướng dẫn...]
#muctieuchung[Mục tiêu chung...]
#bangthuatngu[...] // alias: #glossary
#tailieuthamkhao[...] // alias: #references
Ví dụ cấu trúc đầy đủ
#part([Phần 1: Giải Tích])
#chapter([Hàm Số Và Đạo Hàm])
#bai([Bài 1: Khảo sát hàm bậc ba])
#muctieu[
- Lập được BBT của hàm bậc ba.
- Vẽ đồ thị hàm số.
]
#khoidong[
Quan sát đồ thị và nhận xét số cực trị.
]
#khampha[
Phương pháp: tính $f'(x)$, giải $f'(x) = 0$, lập BBT.
]
#vd(
[Khảo sát $f(x) = x^3 - 3x^2 + 4$.],
loigiai: [Lời giải...],
)
#bt([Tìm cực trị của $f(x) = -x^3 + 3x$], lines: 4)
#luyentap[
#bt([Bài 1.], lines: 3)
#bt([Bài 2.], lines: 4)
]
#tomtat[
Hàm bậc ba $f(x) = ax^3 + bx^2 + cx + d$ ($a \ne 0$) có tối đa 2 cực trị.
]
#appendix([Phụ lục: Bảng đạo hàm])
📦 Các Hộp Nội Dung SGK
Hộp mạch dạy học SGK
#muctieu[...]#chuanbi[...]#khoidong[...]#khampha[...]#hoatdong[...]#luyentap[...]#vandung[...]#morang[...]#tomtat[...]#duan[...]Hộp lý thuyết
#phuongphap[...]#lythuyet[...]#ghinho[...]#nhanxet[...]#luuy[...]#note[...]#dn[...]#dl[...]#tc[...]#bode[...]Các khối nội dung đánh số
// Ví dụ có lời giải
#vd(
[Nội dung ví dụ.],
loigiai: [Lời giải chi tiết.],
)
// Bài tập với dòng trống
#bt(
[Bài tập luyện tập.],
lines: 4, // số dòng kẻ trống
)
// Câu hỏi củng cố
#cauhoi([Câu hỏi kiểm tra kiến thức.])
// Hoạt động nhóm
#hd([Thực hiện hoạt động sau...])
// Ôn luyện
#onluyen([Bài ôn luyện chương...])
🔢 Bộ Đếm Trong Sách
Điều khiển bộ đếm
// Đặt số chương / bài / mục / phụ lục
#setchuong(3) // Chương kế tiếp là Chương 3
#setbai(4) // Bài kế tiếp là Bài 4
#setmuc(2) // Mục kế tiếp là Mục 2
#setphuluc(2) // Phụ lục kế tiếp là Phụ lục 2
// Reset về đầu
#resetchuong()
#resetbai()
#resetmuc()
// Ví dụ và bài tập
#setvd(6) // Ví dụ kế tiếp là Ví dụ 6
#setbt(8) // Bài tập kế tiếp là Bài 8
#resetvd()
#resetbt()
// API tổng quát
#setbookcounter("chapter", 3) // = setchuong(3)
#resetbookcounter("vd")
setvd/setbt khi ghép nội dung từ nhiều nguồn.
📰 Bộ Đề Nhiều Đề — #de
Cú pháp #de
#de(
title: [Đề số 1], // tên trên mục lục bộ đề
exam-title: none, // tên trên đề (nếu khác title)
department: "SỞ GD&ĐT",
school: "TỈNH...",
subject: "TOÁN",
duration: "90 phút",
code: "101",
show-answer-key: true, // in đáp án sau đề
show-end: true, // in dòng HẾT
reset-state: true, // reset số câu/phần mỗi đề mới
question-start: 1,
)[
// Nội dung đề thi ở đây
#exam-part([PHẦN I.], count: 12)
#tn([...], (...))
// ...
]
Các hộp bổ sung trong bộ đề
// Đặt trước mỗi đề hoặc trước toàn tuyển tập
#matran[Ma trận đề thi...]
#dacta[Đặc tả câu hỏi...]
#huongdancham[Hướng dẫn chấm điểm...]
#luuybode[Lưu ý khi chấm...]
📄 Quy trình 3 bước & File Mẫu Làm Sách
Làm sách/chuyên đề thường tốn rất nhiều trang và tài nguyên. Vì vậy, hệ thống khuyên bạn dùng cấu trúc Thư mục chia nhỏ thay vì nhồi nhét tất cả vào 1 file.
Bước 1: Cấu trúc thư mục
chuyen-de-toan/
├── main.typ ← (File chính để chạy compile)
├── chuong-01/
│ ├── bai-01.typ ← (Chứa nội dung Bài 1)
│ └── bai-02.typ
└── chuong-02/
└── bai-01.typ
Bước 2: File chính (main.typ)
Đây là file bạn sẽ cấu hình tiêu đề sách, tác giả, màu sắc và gọi (include) các bài con vào. Không viết trực tiếp câu hỏi ở đây.
// ═══════════════════════════════════════════
// FILE CHÍNH (GOM NỘI DUNG)
// ═══════════════════════════════════════════
#import "../typst/template.typ": *
#import "@preview/cetz:0.5.2"
// ── CẤU HÌNH SÁCH ────────────────────────
#show: stexgv-doc.with(
doc-type: "chuyende", // "sgk" | "book" | "chuyende"
title: "CHUYÊN ĐỀ TOÁN 12",
subtitle: "Tập 1",
author: "GV Nguyễn Văn Sang",
theme-color: classic.teal,
)
// ── LỜI NÓI ĐẦU ──────────────────────────
#loinoidau[
Chuyên đề được biên soạn nhằm mục đích...
]
// ── GỌI CÁC BÀI VÀO (INCLUDE) ────────────
#part([Phần 1: Giải Tích])
#chapter([Chương 1: Ứng dụng đạo hàm])
#include "chuong-01/bai-01.typ"
#include "chuong-01/bai-02.typ"
Bước 3: Viết nội dung bài học (bai-01.typ)
Bên trong các file bài học, bạn chỉ cần gõ nội dung, hệ thống sẽ tự động ghép nối và đánh số trang, số bài.
#bai([Bài 1: Khảo sát hàm số])
#muctieu[
- Lập được bảng biến thiên.
- Tìm cực trị và khoảng đơn điệu.
]
#vd(
[Khảo sát $f(x) = 2x^3 - 3x^2 + 1$.],
loigiai: [
$f'(x) = 6x^2 - 6x$.
],
)
#bt([Khảo sát $f(x) = x^3 - 3x + 1$.], lines: 6)
📈 Tổng Quan Module Vẽ Hình
Module này gồm hai file: bbt.typ (bảng biến thiên, xét dấu) và geometry.typ (hình học phẳng, không gian, đồ thị).
Import
// Cần import cetz trước
#import "@preview/cetz:0.5.2"
// Bảng biến thiên & xét dấu
#import "bbt.typ": *
// Hình học
#import "geometry.typ": *
Tổng hợp các hàm
| Hàm | File | Mô tả |
|---|---|---|
bbtv2(...) | bbt.typ | BBT tiêu chuẩn, tự điều chỉnh chiều cao, hỗ trợ shade vùng gạch chéo |
bbbt(...) | bbt.typ | BBT đầy đủ, có ranks thủ công để chỉnh cao độ nhãn và mũi tên |
bxd(...) | bbt.typ | Bảng xét dấu một hoặc nhiều dòng |
bang-gia-tri(...) | bbt.typ | Bảng giá trị hàm số |
tri-xyz(...) | geometry.typ | Tam giác từ 3 điểm toạ độ |
tri-abc(...) | geometry.typ | Tam giác thường (đáy ngang) |
tri-right(...) | geometry.typ | Tam giác vuông tại A |
chop-sabc(...) | geometry.typ | Hình chóp S.ABC |
chop-sabcd(...) | geometry.typ | Hình chóp S.ABCD |
lang-tru-abc(...) | geometry.typ | Lăng trụ ABC.A'B'C' |
axis-xy(...) | geometry.typ | Hệ trục Oxy |
parabola(...) | geometry.typ | Đồ thị parabol y=ax²+bx+c |
circle-desc(...) | geometry.typ | Đường tròn tâm O bán kính R |
#bbtv2 — Bảng Biến Thiên Tiêu Chuẩn
bbtv2 là macro BBT chính, tự động điều chỉnh chiều cao ô, hỗ trợ vùng gạch chéo (tiệm cận đứng), tự tính vị trí mũi tên.
Dựng bảng và lấy code ngay
Đổi mốc x, dấu đạo hàm, giá trị hàm hoặc vùng gạch chéo; kết quả bên phải được render chính thức bằng Typst, đoạn code bên dưới luôn đồng bộ.
Cú pháp đầy đủ
#bbtv2(
var: $x$, // nhãn hàng x (mặc định $x$)
der: $y'$, // nhãn hàng đạo hàm
func: $y$, // nhãn hàng hàm số
x-vals: (), // mảng giá trị x (gồm cả −∞, +∞)
d-signs: (), // mảng dấu đạo hàm
v-vals: (), // mảng giá trị hàm tương ứng
shade: (), // vùng gạch chéo: ((i, j),) cho i..j
w1: 1.5, // chiều rộng cột nhãn (cm)
w2: 10, // chiều rộng cột nội dung (cm)
h1: auto, // chiều cao hàng x (tự điều chỉnh)
h2: 0.8, // chiều cao hàng đạo hàm (cm)
h3: auto, // chiều cao hàng hàm số (tự điều chỉnh)
)
Quy tắc d-signs / f-signs
n mốc x-vals, mảng dấu thường có 2n−3 phần tử: xen kẽ dấu khoảng và dấu tại các mốc giữa.
VD: 4 mốc x → 5 dấu:
(khoảng₁, tại₂, khoảng₂, tại₃, khoảng₃).
x-vals: ($-oo$, $-1$, $2$, $+oo$)
d-signs: ($+$, $0$, $-$, $0$, $+$)
// khoảng1 tại-1 khoảng2 tại2 khoảng3
| Giá trị dấu | Ý nghĩa |
|---|---|
$+$ hoặc "+" | Dấu dương. |
$-$ hoặc "-" | Dấu âm. |
$0$ hoặc "0" | Điểm bằng 0. |
"||" | Vạch đôi, tiệm cận hoặc điểm không xác định. |
none, "", [] | Ô trống. |
Ví dụ 1 — Hàm bậc ba đơn giản
// f(x) = x³ - 3x, cực đại x=-1, cực tiểu x=1
#bbtv2(
x-vals: ($-oo$, $-1$, $1$, $+oo$),
d-signs: ($+$, $0$, $-$, $0$, $+$),
// ^khoảng1 ^tại-1 ^khoảng2 ^tại1 ^khoảng3
v-vals: ($-oo$, $2$, $-2$, $+oo$),
)
Ví dụ 2 — Có tiệm cận đứng (dấu ||)
// y = 1/(x-1): tiệm cận đứng x=1
#bbtv2(
x-vals: ($-oo$, $1$, $+oo$),
d-signs: ($-$, "||", $-$),
v-vals: ($0$, ($+oo$, $-oo$), $0$),
// v-vals tại tiệm cận: tuple (trái, phải)
shade: ((1, 2),), // gạch chéo vùng [index 1, index 2)
)
Tham số đầy đủ
x, gồm vô cực, điểm cực trị hoặc điểm không xác định."||" cho tiệm cận/không xác định.($+oo$, $-oo$).((1, 2),). Đồng thời ngắt mũi tên qua vùng này.w2 khi nhiều mốc hoặc nhãn dài.h1, h3 có thể để auto để tự tăng khi có phân số/cases.Ví dụ 3 — Hàm có 1 cực trị trên đoạn (bbt-opt)
// Dành cho bài toán tối ưu
#bbt-opt(
x-vals: ($0$, $x_0$, $pi$),
d-signs: ($-$, $0$, $+$),
v-vals: ($f(0)$, $f(x_0)$, $f(\pi)$),
is-min: true, // true = \/ ; false = /\
)
#bbbt — Bảng Biến Thiên Đầy Đủ
bbbt là phiên bản đầy đủ hơn, cho phép truyền ranks thủ công để can thiệp vị trí mũi tên.
Ví dụ — Hàm có 2 cực trị, ranks thủ công
#bbbt(
var: $x$,
der: $f'(x)$,
func: $f(x)$,
x-vals: ($-oo$, $-1$, $2$, $+oo$),
d-signs: ($+$, $0$, $-$, $0$, $+$),
v-vals: ($-oo$, $8$, $-19$, $+oo$),
// ranks thủ công: điều chỉnh vị trí nhãn nếu cần
ranks: ((0,), (2,), (0,), (2,)),
w2: 12, // độ rộng cột nội dung
)
Cách hiểu ranks
Mỗi mốc x nhận một rank tương đối. Rank càng lớn thì nhãn càng cao. Với 4 mốc, truyền 4 tuple rank; tại tiệm cận có thể dùng hai rank cho hai phía.
// 4 mốc x nên có 4 phần tử ranks
ranks: ((0,), (2,), (0,), (2,))
// Mốc giữa là tiệm cận, có hai phía trái/phải
ranks: ((0,), (2, -2), (0,))
#bxd — Bảng Xét Dấu
Dùng cho bảng xét dấu $f'(x)$, bất phương trình. Hỗ trợ nhiều hàng dấu cùng lúc.
Ví dụ — 1 hàng dấu
#bxd(
var: $x$,
func: $f'(x)$,
x-vals: ($-oo$, $-1$, $2$, $+oo$),
f-signs: ($+$, $0$, $-$, $0$, $+$),
)
Ví dụ — Nhiều hàng dấu
// Xét dấu f'(x) = (x+1)(x-2)
#bxd(
var: $x$,
func: ($x+1$, $x-2$, $f'(x)$), // 3 hàng
x-vals: ($-oo$, $-1$, $2$, $+oo$),
f-signs: (
($-$, $0$, $+$, $+$, $+$), // dấu (x+1)
($-$, $-$, $-$, $0$, $+$), // dấu (x-2)
($+$, $0$, $-$, $0$, $+$), // dấu f'(x)
),
)
Lỗi BBT / BXD thường gặp
| Triệu chứng | Cách sửa |
|---|---|
| Lỗi index hoặc bảng không render | Kiểm tra số mốc x-vals và số phần tử d-signs/f-signs. |
| Nhãn đè mũi tên | Tăng w2, tăng h3, hoặc dùng #bbbt(ranks: ...). |
| Phân số làm hàng quá thấp | Để h1: auto, h3: auto; nếu vẫn chật, đặt thủ công h3: 3.2. |
| Tiệm cận không có vạch đôi | Dùng "||" đúng vị trí dấu tại mốc và dùng v-vals dạng hai phía. |
| Vùng không xác định vẫn có mũi tên | Thêm shade: ((i, j),) để tô vùng và ngắt mũi tên. |
✏️ Cetz cơ bản — Typst tương đương TikZ
Nếu trong LaTeX bạn quen viết \begin{tikzpicture} ... \end{tikzpicture}, thì trong Typst/Cetz bạn viết một khối #cetz.canvas(...). Bên trong canvas là các lệnh vẽ như line, circle, content, vòng lặp for, biến let.
(x, y); trục x sang phải, trục y đi lên; length: 0.8cm quyết định 1 đơn vị tọa độ trên hình dài bao nhiêu.
So sánh trực tiếp TikZ và Cetz
LaTeX / TikZ
\begin{tikzpicture}[scale=.8]
\draw[->] (-2,0) -- (3,0) node[right] {$x$};
\draw[->] (0,-1) -- (0,3) node[above] {$y$};
\draw[blue, thick] (-1,1) -- (2,2);
\fill (2,2) circle (2pt) node[above] {$A$};
\end{tikzpicture}
Typst / Cetz
#import "@preview/cetz:0.5.2"
#cetz.canvas(length: 0.8cm, {
import cetz.draw: *
line((-2, 0), (3, 0), mark: (end: ">"))
line((0, -1), (0, 3), mark: (end: ">"))
content((2.85, 0.3), $x$)
content((0.3, 2.85), $y$)
line((-1, 1), (2, 2), stroke: 1.4pt + blue)
circle((2, 2), radius: 2.5pt, fill: black)
content((2.25, 2.25), $A$)
})
Khung lệnh chuẩn khi tự vẽ
#cetz.canvas(length: 0.8cm, {
import cetz.draw: *
let A = (0, 0)
let B = (4, 0)
let C = (1.2, 2.8)
line(A, B, C, A, stroke: 1.1pt + black)
circle(A, radius: 2.4pt, fill: black)
circle(B, radius: 2.4pt, fill: black)
circle(C, radius: 2.4pt, fill: black)
content((-0.35, -0.35), $A$)
content((4.25, -0.35), $B$)
content((1.2, 3.15), $C$)
})
Các lệnh hay dùng nhất
| Lệnh Cetz | Dùng để | Ví dụ ngắn |
|---|---|---|
line(A, B) | Vẽ đoạn, đường gấp khúc, đa giác | line((0,0), (3,1)) |
line(..., mark: (end: ">")) | Vẽ mũi tên | line((0,0), (2,0), mark: (end: ">")) |
circle(P, radius: ...) | Vẽ điểm hoặc đường tròn | circle((0,0), radius: 2pt, fill: black) |
content(P, ...) | Đặt nhãn, công thức, chữ lên hình | content((1,1), $A$) |
stroke: 1pt + blue | Chọn màu và độ dày nét | stroke: 1.2pt + rgb("#0057b8") |
dash: "dashed" | Vẽ cạnh khuất hoặc nét phụ | stroke: (paint: gray, thickness: .7pt, dash: "dashed") |
for ... in range(...) | Lấy nhiều điểm để vẽ đường cong | pts.push((x, x*x)) |
Đưa hình vào câu hỏi
Với đề thi, hình thường đặt vào tham số fig:. Nếu dùng hình ngắn, đặt fig-pos: "right"; nếu hình to, đặt fig-pos: "center".
#tn(
[Cho tam giác $A B C$ như hình. Khẳng định nào đúng?],
([$A B = A C$], True([$B C$ là cạnh đáy]), [$A$ là trung điểm], [$B C = 0$]),
fig: [
#cetz.canvas(length: 0.75cm, {
import cetz.draw: *
let A = (0, 2.8)
let B = (-2, 0)
let C = (2, 0)
line(A, B, C, A, stroke: 1.1pt + black)
line(A, (0, 0), stroke: (paint: gray, thickness: 0.7pt, dash: "dashed"))
content((0, 3.15), $A$)
content((-2.25, -0.3), $B$)
content((2.25, -0.3), $C$)
})
],
fig-pos: "right",
fig-width: 32%,
loigiai: [Quan sát hình, cạnh $B C$ là đáy của tam giác cân.],
)
📐 Hình Học Phẳng
Tất cả hàm trong geometry.typ trả về một cetz.canvas — dùng trực tiếp làm fig: trong câu hỏi.
Tam giác
// Tam giác từ 3 điểm toạ độ thật
#tri-xyz(
(0, 0), (5, 0), (2, 4),
labels: ("A", "B", "C"),
right-angle: none, // "A" | "B" | "C" để đánh dấu góc vuông
)
// Tam giác thường (đáy ngang, đỉnh trên)
#tri-abc(base: 6, height: 4, labels: ("A", "B", "C"))
// Tam giác vuông tại A
#tri-right(leg1: 4, leg2: 3, labels: ("A", "B", "C"))
Hình chữ nhật & Vuông
// Hình chữ nhật ABCD
#rect-abc(width: 6, height: 4, labels: ("A", "B", "C", "D"))
// Hình vuông
#square(a: 4, labels: ("A", "B", "C", "D"))
Đường tròn
// Đường tròn tâm O bán kính R
#circle-desc(center: (0, 0), radius: 2.5, label: "O")
// Elip ngang
#ellipse-h(center: (0, 0), a: 3, b: 2)
Dùng trực tiếp trong câu hỏi
#tn(
[Tam giác ABC có $A B = 5$, $B C = 4$, góc B bằng $90 degree$. Tính $A C$.],
([$3$], [$sqrt(41)$], True([$sqrt(41)$]), [$9$]),
fig: [#tri-right(leg1: 5, leg2: 4, labels: ("B", "A", "C"))],
fig-pos: "right",
fig-width: 30%,
loigiai: [$A C = sqrt(A B^2 + B C^2) = sqrt(41)$],
)
🗜️ Hình Học Không Gian
Hình chóp
// Hình chóp tam giác S.ABC
#chop-sabc(
S: (0, 8),
A: (-4, 0), B: (4, 0), C: (0, -2),
labels: ("S", "A", "B", "C"),
hidden: ("AC",), // cạnh AC vẽ nét đứt (bị che)
)
// Hình chóp tứ giác S.ABCD
#chop-sabcd(
S: (0, 7),
A: (-3, 0), B: (3, 0), C: (2, -2), D: (-2, -2),
labels: ("S", "A", "B", "C", "D"),
hidden: ("AD", "DC"), // 2 cạnh đáy bị che
)
Lăng trụ
// Lăng trụ tam giác đứng ABC.A'B'C'
#lang-tru-abc(
A: (-3, 0), B: (3, 0), C: (0, -2),
A2: (-3, 5), B2: (3, 5), C2: (0, 3),
labels: ("A", "B", "C", "A'", "B'", "C'"),
hidden: ("AA'", "AB"), // cạnh bị che → nét đứt
)
hidden nhận tuple các nhãn cạnh (VD: "SA", "BC"). Cạnh trong danh sách sẽ vẽ nét đứt thay vì nét liền.
📊 Đồ Thị Hàm Số
Hệ trục Oxy
#axis-xy(
xmin: -3, xmax: 3,
ymin: -2, ymax: 4,
xlabel: "x",
ylabel: "y",
xstep: 1, ystep: 1,
scale: 1cm,
ticks: true, // hiện số trên trục
)
Parabol y = ax² + bx + c
#parabola(
a: 1, b: -2, c: -3, // y = x² - 2x - 3
xmin: -2, xmax: 4,
ymin: -5, ymax: 2,
npts: 80, // số điểm vẽ
show-vertex: true, // hiện đỉnh
show-roots: true, // hiện nghiệm
accent: rgb("#0057b8"),
)
Vẽ đồ thị tuỳ ý với cetz
// Dùng trực tiếp cetz để vẽ hàm số bất kỳ
#cetz.canvas(length: 0.8cm, {
import cetz.draw: *
// Trục
line((-3.2, 0), (3.2, 0), mark: (end: ">"))
line((0, -2.2), (0, 3.2), mark: (end: ">"))
content((3.2, 0.3), $x$)
content((0.3, 3.2), $y$)
// Vẽ đồ thị f(x) = x³ - 3x + 1
let pts = ()
for i in range(-30, 31) {
let x = i * 0.1
let y = x * x * x - 3 * x + 1
if y >= -2 and y <= 3 { pts.push((x, y)) }
}
line(..pts, stroke: 1.5pt + blue)
// Đánh dấu cực trị
circle((-1, 3), radius: 3pt, fill: red)
circle((1, -1), radius: 3pt, fill: red)
content((-1.5, 3.2), $(-1;\ 3)$)
content((1.5, -1.3), $(1;\ -1)$)
})
🧩 Kho Hình Mẫu
Kho này gom các mẫu hình thường gặp khi soạn đề Toán. Mỗi mẫu đều là Typst/Cetz thật: bấm ▶ Chạy, sửa tọa độ hoặc tham số, rồi copy vào fig: hoặc nội dung bài.
Nhóm A — Hình phẳng dùng macro nhanh
Mẫu 01 — Tam giác thường theo tọa độ
#tri-xyz(
(0, 0), (5, 0), (1.4, 3.2),
labels: ("A", "B", "C"),
scale: 0.75cm,
)
Mẫu 02 — Tam giác vuông có dấu góc vuông
#tri-right(
leg1: 5,
leg2: 3,
labels: ("A", "B", "C"),
right-angle-vertex: "A",
scale: 0.75cm,
)
Mẫu 03 — Hình chữ nhật có kích thước đẹp
#rect-abc(
width: 6,
height: 3.5,
labels: ("A", "B", "C", "D"),
scale: 0.65cm,
)
Mẫu 04 — Đường tròn tâm O
#circle-desc(
center: (0, 0),
radius: 2.4,
label: "O",
scale: 0.75cm,
)
Mẫu 05 — Elip ngang
#ellipse-h(
center: (0, 0),
a: 3,
b: 1.6,
accent: rgb("#0057b8"),
scale: 0.75cm,
)
Nhóm B — Hình phẳng Cetz thủ công
Mẫu 06 — Tam giác có đường cao và trung tuyến
#cetz.canvas(length: 0.8cm, {
import cetz.draw: *
let A = (0, 3)
let B = (-2.6, 0)
let C = (3.2, 0)
let H = (0, 0)
let M = ((B.at(0) + C.at(0)) / 2, 0)
line(A, B, C, A, stroke: 1.1pt + black)
line(A, H, stroke: (paint: gray, thickness: 0.7pt, dash: "dashed"))
line(A, M, stroke: 0.8pt + rgb("#cc2200"))
line((0, 0), (0.35, 0), (0.35, 0.35), (0, 0.35), stroke: 0.6pt + black)
content((0, 3.35), $A$)
content((-2.9, -0.3), $B$)
content((3.45, -0.3), $C$)
content((-0.35, -0.35), $H$)
content((M.at(0), -0.35), $M$)
})
Mẫu 07 — Hình thang cân
#cetz.canvas(length: 0.75cm, {
import cetz.draw: *
let A = (-1.8, 2.4)
let B = (1.8, 2.4)
let C = (3, 0)
let D = (-3, 0)
line(A, B, C, D, A, stroke: 1.1pt + black)
line(A, C, stroke: (paint: gray, thickness: 0.6pt, dash: "dashed"))
line(B, D, stroke: (paint: gray, thickness: 0.6pt, dash: "dashed"))
content((-2.05, 2.7), $A$)
content((2.05, 2.7), $B$)
content((3.25, -0.25), $C$)
content((-3.3, -0.25), $D$)
})
Mẫu 08 — Hình bình hành có đường chéo
#cetz.canvas(length: 0.75cm, {
import cetz.draw: *
let A = (0, 0)
let B = (4.2, 0)
let D = (1.4, 2.4)
let C = (5.6, 2.4)
line(A, B, C, D, A, stroke: 1.1pt + black)
line(A, C, stroke: 0.8pt + rgb("#0057b8"))
line(B, D, stroke: (paint: rgb("#cc2200"), thickness: 0.8pt, dash: "dashed"))
content((-0.3, -0.3), $A$)
content((4.45, -0.3), $B$)
content((5.85, 2.55), $C$)
content((1.15, 2.55), $D$)
})
Mẫu 09 — Đường tròn, bán kính và tiếp tuyến
#cetz.canvas(length: 0.8cm, {
import cetz.draw: *
let O = (0, 0)
let A = (2, 0)
circle(O, radius: 2, stroke: 1.1pt + black)
line(O, A, stroke: 1pt + rgb("#0057b8"))
line((2, -1.7), (2, 1.7), stroke: 1pt + rgb("#cc2200"))
line((2, 0), (1.7, 0), (1.7, 0.3), (2, 0.3), stroke: 0.6pt + black)
circle(O, radius: 2.5pt, fill: black)
circle(A, radius: 2.5pt, fill: black)
content((-0.35, -0.35), $O$)
content((2.2, -0.25), $A$)
content((0.95, 0.3), $R$)
})
Mẫu 10 — Nửa đường tròn đường kính AB
#cetz.canvas(length: 0.8cm, {
import cetz.draw: *
let pts = ()
for i in range(0, 61) {
let t = calc.pi * i / 60
pts.push((2.5 * calc.cos(t), 2.5 * calc.sin(t)))
}
line(..pts, stroke: 1.2pt + rgb("#0057b8"))
line((-2.5, 0), (2.5, 0), stroke: 1pt + black)
circle((0, 0), radius: 2.5pt, fill: black)
content((-2.75, -0.3), $A$)
content((2.75, -0.3), $B$)
content((-0.25, -0.35), $O$)
})
Nhóm C — Hình không gian
Mẫu 11 — Hình chóp tam giác S.ABC
#chop-sabc(
S: (0, 7),
A: (-3.5, 0),
B: (3.5, 0),
C: (0, -1.8),
hidden: ("AC",),
scale: 0.65cm,
)
Mẫu 12 — Hình chóp tứ giác S.ABCD
#chop-sabcd(
S: (0, 6.5),
A: (-3, 0),
B: (3, 0),
C: (2, -2),
D: (-2, -2),
hidden: ("AD", "DC"),
scale: 0.68cm,
)
Mẫu 13 — Lăng trụ tam giác đứng
#lang-tru-abc(
A: (-3, 0), B: (3, 0), C: (0, -1.8),
A2: (-3, 4.8), B2: (3, 4.8), C2: (0, 3),
hidden: ("AB", "AA'"),
scale: 0.65cm,
)
Mẫu 14 — Hình hộp chữ nhật thủ công
#cetz.canvas(length: 0.62cm, {
import cetz.draw: *
let A = (0, 0); let B = (4, 0); let C = (5.3, 1.1); let D = (1.3, 1.1)
let A2 = (0, 3); let B2 = (4, 3); let C2 = (5.3, 4.1); let D2 = (1.3, 4.1)
line(A, B, C, D, A, stroke: 1pt + black)
line(A2, B2, C2, D2, A2, stroke: 1pt + black)
line(A, A2, stroke: 1pt + black)
line(B, B2, stroke: 1pt + black)
line(C, C2, stroke: 1pt + black)
line(D, D2, stroke: (paint: gray, thickness: 0.8pt, dash: "dashed"))
content((-0.3, -0.3), $A$)
content((4.25, -0.3), $B$)
content((5.55, 1.0), $C$)
content((1.0, 1.25), $D$)
})
Mẫu 15 — Hình nón
#cetz.canvas(length: 0.75cm, {
import cetz.draw: *
let S = (0, 3.5)
let front = ()
let back = ()
for i in range(0, 41) {
let t = calc.pi + calc.pi * i / 40
front.push((2.2 * calc.cos(t), 0.45 * calc.sin(t)))
}
for i in range(0, 41) {
let t = calc.pi * i / 40
back.push((2.2 * calc.cos(t), 0.45 * calc.sin(t)))
}
line(..back, stroke: (paint: gray, thickness: 0.7pt, dash: "dashed"))
line(..front, stroke: 1pt + black)
line(S, (-2.2, 0), stroke: 1pt + black)
line(S, (2.2, 0), stroke: 1pt + black)
line(S, (0, 0), stroke: (paint: gray, thickness: 0.7pt, dash: "dashed"))
content((0, 3.85), $S$)
content((0.25, -0.35), $O$)
})
Mẫu 16 — Hình trụ
#cetz.canvas(length: 0.75cm, {
import cetz.draw: *
let top = ()
let bottom = ()
for i in range(0, 81) {
let t = 2 * calc.pi * i / 80
top.push((2 * calc.cos(t), 3 + 0.42 * calc.sin(t)))
bottom.push((2 * calc.cos(t), 0.42 * calc.sin(t)))
}
line(..top, stroke: 1pt + black)
line(..bottom, stroke: 1pt + black)
line((-2, 3), (-2, 0), stroke: 1pt + black)
line((2, 3), (2, 0), stroke: 1pt + black)
line((0, 3), (0, 0), stroke: (paint: gray, thickness: 0.7pt, dash: "dashed"))
content((0.25, 1.5), $h$)
})
Nhóm D — Đồ thị hàm số
Mẫu 17 — Hệ trục Oxy có tick
#axis-xy(
xmin: -4, xmax: 4,
ymin: -3, ymax: 4,
xstep: 1,
ystep: 1,
ticks: true,
scale: 0.8cm,
)
Mẫu 18 — Parabol có đỉnh và nghiệm
#parabola(
a: 1,
b: -2,
c: -3,
xmin: -2,
xmax: 4,
ymin: -5,
ymax: 2,
show-vertex: true,
show-roots: true,
scale: 0.8cm,
)
Mẫu 19 — Đồ thị bậc ba tự lấy điểm
#cetz.canvas(length: 0.8cm, {
import cetz.draw: *
line((-3.2, 0), (3.2, 0), mark: (end: ">"))
line((0, -3.2), (0, 4.2), mark: (end: ">"))
content((3.1, 0.32), $x$)
content((0.32, 4.0), $y$)
let pts = ()
for i in range(-30, 31) {
let x = i * 0.1
let y = x * x * x - 3 * x + 1
if y >= -3 and y <= 4 {
pts.push((x, y))
}
}
line(..pts, stroke: 1.3pt + rgb("#0057b8"))
circle((-1, 3), radius: 2.8pt, fill: rgb("#cc2200"))
circle((1, -1), radius: 2.8pt, fill: rgb("#cc2200"))
})
Mẫu 20 — Đồ thị trị tuyệt đối
#cetz.canvas(length: 0.8cm, {
import cetz.draw: *
line((-3.2, 0), (3.2, 0), mark: (end: ">"))
line((0, -0.5), (0, 3.2), mark: (end: ">"))
let left = ()
let right = ()
for i in range(-30, 1) {
let x = i * 0.1
left.push((x, -x))
}
for i in range(0, 31) {
let x = i * 0.1
right.push((x, x))
}
line(..left, stroke: 1.3pt + rgb("#0057b8"))
line(..right, stroke: 1.3pt + rgb("#0057b8"))
circle((0, 0), radius: 2.8pt, fill: rgb("#cc2200"))
content((1.2, 1.45), $y = abs(x)$)
})
Mẫu 21 — Diện tích xấp xỉ bằng các lát đứng
#cetz.canvas(length: 0.8cm, {
import cetz.draw: *
line((-0.4, 0), (4.2, 0), mark: (end: ">"))
line((0, -0.2), (0, 3.5), mark: (end: ">"))
let pts = ()
for i in range(0, 41) {
let x = i * 0.1
let y = 0.25 * x * x + 0.4
pts.push((x, y))
}
for i in range(4, 37, step: 4) {
let x = i * 0.1
let y = 0.25 * x * x + 0.4
line((x, 0), (x, y), stroke: 0.45pt + rgb("#9aa0a6"))
}
line(..pts, stroke: 1.3pt + rgb("#0057b8"))
content((2.6, 2.45), $y = 1/4 x^2 + 0.4$)
})
Mẫu 22 — Đường thẳng cắt parabol
#cetz.canvas(length: 0.8cm, {
import cetz.draw: *
line((-3.2, 0), (3.2, 0), mark: (end: ">"))
line((0, -2.2), (0, 4.2), mark: (end: ">"))
let p = ()
for i in range(-30, 31) {
let x = i * 0.1
let y = x * x - 1
if y <= 4 {
p.push((x, y))
}
}
line(..p, stroke: 1.3pt + rgb("#0057b8"))
line((-3, -1), (3, 2), stroke: 1.1pt + rgb("#cc2200"))
circle((-1, 0), radius: 2.7pt, fill: black)
circle((2, 3), radius: 2.7pt, fill: black)
})
Nhóm E — Mẫu câu hỏi có hình
Mẫu 23 — Trắc nghiệm có hình tam giác bên phải
#tn(
[Tam giác vuông $A B C$ vuông tại $A$, $A B = 5$, $A C = 3$. Tính $B C$.],
([$4$], [$8$], True([$sqrt(34)$]), [$2 sqrt(10)$]),
fig: [#tri-right(leg1: 5, leg2: 3, labels: ("A", "B", "C"))],
fig-pos: "right",
fig-width: 30%,
loigiai: [$B C = sqrt(A B^2 + A C^2) = sqrt(34)$.],
)
Mẫu 24 — Đúng sai có đồ thị parabol
#ds(
[Cho parabol $y = x^2 - 2x - 3$ như hình. Xét các phát biểu sau:],
(
True([Trục đối xứng là $x = 1$.]),
False([Parabol quay bề lõm xuống.]),
True([Đồ thị cắt trục hoành tại hai điểm.]),
False([Giá trị nhỏ nhất bằng $0$.]),
),
fig: [#parabola(a: 1, b: -2, c: -3, xmin: -2, xmax: 4, ymin: -5, ymax: 2, show-vertex: true, show-roots: true)],
fig-pos: "right",
fig-width: 36%,
loigiai: [Đỉnh là $(1; -4)$, hệ số $a = 1 > 0$, nên parabol quay lên.],
)
Mẫu 25 — Trả lời ngắn có hình tọa độ
#tln(
[Điểm $A(3;4)$ cách gốc tọa độ $O$ một khoảng bằng bao nhiêu?],
[$5$],
fig: [
#cetz.canvas(length: 0.7cm, {
import cetz.draw: *
line((-0.5, 0), (4, 0), mark: (end: ">"))
line((0, -0.5), (0, 4.6), mark: (end: ">"))
line((0, 0), (3, 4), stroke: 1.2pt + rgb("#0057b8"))
circle((3, 4), radius: 2.8pt, fill: rgb("#cc2200"))
content((3.2, 4.2), $A(3;4)$)
content((-0.3, -0.3), $O$)
})
],
fig-pos: "right",
fig-width: 34%,
loigiai: [$O A = sqrt(3^2 + 4^2) = 5$.],
)
Mẫu 26 — Tự luận có hình chóp
#tl(
[Cho hình chóp $S.A B C$ có $S A$ vuông góc với đáy. Hãy xác định đường cao của hình chóp.],
fig: [#chop-sabc(S: (0, 6), A: (-3, 0), B: (3, 0), C: (0, -1.6), hidden: ("AC",))],
fig-pos: "right",
fig-width: 34%,
loigiai: [Vì $S A$ vuông góc với mặt phẳng đáy $(A B C)$ nên $S A$ là đường cao của hình chóp.],
)
🖥️ Trình Chiếu Beamer (Giảng dạy trên lớp)
Tính năng mạnh mẽ nhất của sang-math là khả năng tái sử dụng nguyên vẹn nội dung Đề thi / Chuyên đề và biến chúng thành Slide trình chiếu (Beamer) siêu đẹp chỉ với vài thao tác.
Nguyên lý hoạt động
- Bạn có một file chứa câu hỏi gốc (ví dụ:
de-30.typ). - Khi muốn in PDF cho học sinh: Bạn chạy file gốc.
- Khi muốn chiếu lên tivi sửa bài: Bạn chạy file
Beamer.typ(file này đã#importfile gốc và áp dụng giao diện slide).
Mã nguồn chuẩn Beamer.typ
// ═══════════════════════════════════════════
// MAIN-BEAMER.TYP — TRÌNH CHIẾU CHỮA ĐỀ
// ═══════════════════════════════════════════
#import "../typst/sang-beamer.typ": *
#import "@preview/cetz:0.5.2"
// ── Thông tin bài giảng ──────────────────
#let bm-title = "CHỮA ĐỀ THI THỬ THPT QG 2026"
#let bm-subtitle = "TOÁN - LỚP 12"
#let bm-author = "GV Nguyễn Văn Sang"
// ── Cấu hình màu sắc & cỡ chữ ────────────
#let bm-bg-color = rgb("#0f172a") // Màu nền slide (Tối)
#let bm-math-color = rgb("#f59e0b") // Màu chữ Toán (Vàng)
#let bm-base-size = 24pt // Cỡ chữ gốc (rất to dễ nhìn)
#let bm-auto-step = true // Tự động pause tại lệnh #step
#show: sang-beamer-theme.with(
title: bm-title,
subtitle: bm-subtitle,
author: bm-author,
bg_color: bm-bg-color,
text_size: bm-base-size * 1.15,
math_color: bm-math-color,
question_size: bm-base-size,
auto_step_pause: bm-auto-step,
)
// ── Gọi dữ liệu câu hỏi ──────────────────
#let mode = "loigiai"
#let _m = exam-mode(mode: mode)
#let (mcq, tf, short) = (_m.mcq, _m.tf, _m.short)
// Kéo file đề thi gốc vào đây (thay dethi/de-30.typ)
#import "dethi/de-30.typ": make-questions as _de
#_de(tn: _m.tn, ds: _m.ds, tln: _m.tln, tl: _m.tl, exam-part: exam-part)
Cách Biên dịch (Compile) Slide
beamer=1 vào quá trình biên dịch để chuyển đổi giao diện từ giấy sang slide.
- Dùng Terminal:
typst compile --root . --input beamer=1 typst/Beamer.typ - Dùng NPM (khuyên dùng): Gõ
npm run compile:Beamer
Sử dụng #step để trình chiếu từng dòng
Khi biến bm-auto-step = true, mọi lệnh #step bạn viết trong phần lời giải của tự luận (hoặc của Đ/S) sẽ tự động biến thành 1 "click chuột" chuyển slide (pause). Bạn không cần phải mất công làm hiệu ứng hoạt hình thủ công!
🎨 Kho Bìa (Cover Gallery)
Hệ thống cung cấp các macro tạo trang bìa chuyên nghiệp, màu sắc bắt mắt và rất dễ sử dụng cho nhiều mục đích khác nhau: Bìa đề thi, Bìa sách, Bìa chuyên đề.
1. Bìa Đề Thi (#cover-exam)
Dùng cho các đề thi thử, đề kiểm tra. Hiển thị trang trọng, có chỗ cho Logo trường.
#cover-exam(
title: "ĐỀ THI THỬ TỐT NGHIỆP THPT NĂM 2025",
subtitle: "Môn thi: TOÁN",
time: "Thời gian làm bài: 90 phút (không kể thời gian phát đề)",
school: "SỞ GD&ĐT TP. HỒ CHÍ MINH\nTRƯỜNG THPT NGUYỄN HỮU CẢNH",
year: "Năm học 2024 - 2025",
logo: "images/logo-truong.png",
accent: c-blue,
)
2. Bìa Sách / Giáo trình (#cover-book)
Một thiết kế tràn viền, đồ hoạ mạnh mẽ, chia lưới vuông hiện đại dành cho sách giáo khoa hoặc giáo trình nội bộ.
#cover-book(
title: "TÀI LIỆU DẠY HỌC\nTOÁN 12",
subtitle: "TẬP 1",
author: "Thầy NGUYỄN VĂN SANG",
version: "Năm học 2024-2025",
description: "Biên soạn theo chương trình GDPT 2018.\nĐầy đủ lý thuyết, bài tập trắc nghiệm và tự luận.",
accent: c-teal,
)
3. Bìa Chuyên đề (#cover-cd)
Thiết kế tinh gọn, góc cạnh, làm nổi bật tiêu đề chuyên đề, dùng cho tài liệu phát nhanh trên lớp.
#cover-cd(
title: "CHUYÊN ĐỀ HÀM SỐ & ĐỒ THỊ",
author: "Biên soạn: Thầy Nguyễn Văn Sang",
accent: c-purple,
)
⬇️ Tải Về Gói Template
Tải về các gói template để bắt đầu soạn thảo ngay mà không cần cấu hình thêm.
Cách dùng sau khi tải về
1. Giải nén gói mẫu.
2. Mở file đề thi hoặc sách trong trình soạn thảo.
3. Sửa phần BẢNG ĐIỀU KHIỂN ở đầu file.
4. Bấm xem trước hoặc xuất PDF bằng extension Typst.