Một package Typst
Từ Đề Thi Đến Sách Toán
Hướng dẫn công khai theo đúng API đã merge của sang-math:1.0.0: một import duy nhất cho đề thi THPT, sách/chuyên đề, 18 theme đề, 12 theme sách, BBT/BXD, hình học và CeTZ nâng cao.
Thiết kế bởi GV Nguyễn Văn Sang, yêu cầu Typst ≥ 0.12 và phát hành theo MIT License. Người dùng cộng đồng chỉ import @preview/sang-math:1.0.0; không cần tải hay nhớ đường dẫn các file nội bộ.
⚙️ Cài đặt & Biên dịch
Yêu cầu hệ thống
winget install Typst.Typst@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. Chỉ khi muốn biên dịch file PDF trên máy cá nhân bằng terminal thì mới cần cài Typst CLI.
Lệnh biên dịch chuẩn
--root. Typst tự tải sang-math:1.0.0 và phụ thuộc CeTZ trong lần biên dịch đầu tiên.typst compile main.typ main.pdf
typst watch main.typ main.pdf
# Chọn giao diện và đầu ra từ CLI với exam-input-preset
typst compile main.typ de-thi.pdf --input theme=teal-pro --input profile=dethi
typst compile main.typ loi-giai.pdf --input theme=royal --input profile=loigiai --input answer-key=1
Cấu trúc dự án tối thiểu
du-an-toan/
├── main.typ ← import package và nội dung
├── images/ ← hình ảnh riêng (nếu có)
└── main.pdf ← kết quả biên dịch
Không sao chép sang-exam.typ, bbt.typ hay modules/book.typ vào dự án.
Import chuẩn
// Dùng nhiều nhóm API
#import "@preview/sang-math:1.0.0": *
// Hoặc import tường minh để source dễ đọc
#import "@preview/sang-math:1.0.0": tn, ds, tln, tl, True, exam-mode, sang-setup
// Chỉ cần import CeTZ trực tiếp khi tự tạo canvas hoặc dùng draw-*
#import "@preview/cetz:0.5.2"
template.typ, sang-exam.typ, modules/book.typ và các đường dẫn con là cấu trúc repository cũ, không phải API của package.🚀 Khởi Động Nhanh
File đề thi cơ bản (5 phút)
// ① Import package public
#import "@preview/sang-math:1.0.0": *
// ② Chọn theme và profile
#let preset = exam-preset(theme: "teal-pro", profile: "dethi")
// ③ Tạo bộ câu hỏi đồng nhất
#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 "@preview/sang-math:1.0.0": *
#let theme = "sgk-modern"
#show: book-theme.with(
theme: theme,
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",
year: "2026–2027",
)
#book-chapter([Hàm Số & Đạo Hàm], number: "01", theme: theme)
#book-lesson([Khảo sát hàm bậc ba], number: "1", theme: theme)
#goal-box(theme: theme)[
- Lập được BBT của hàm bậc ba.
- Tìm được cực trị và khoảng đơn điệu.
]
#warmup-box(theme: theme)[
Quan sát đồ thị sau và trả lời: hàm có bao nhiêu cực trị?
]
#explore-box(theme: theme)[
Phương pháp: tính $f'(x)$, giải $f'(x) = 0$, lập BBT.
]
#example-box(theme: theme)[
*Ví dụ.* Khảo sát $f(x) = x^3 - 3x^2 + 4$.
*Lời giải.*
$f'(x) = 3x^2 - 6x = 3x(x-2)$.
Lập BBT: cực đại $f(0) = 4$, cực tiểu $f(2) = 0$.
]
#exercise-box(theme: theme, lines: 4)[
Tìm cực trị của $f(x) = -x^3 + 3x - 2$.
]
Bộ đề nhiều đề trong một file
#import "@preview/sang-math:1.0.0": *
#let preset = exam-preset(theme: "classic", profile: "dethi")
#let (tn, ds, tln, tl) = exam-mode(..preset.question)
#let render-exam(body, code) = exam-theme(
body,
theme: preset.theme,
exam-title: "BỘ ĐỀ THI THỬ TOÁN 12",
subject: "TOÁN 12",
duration: "90 phút",
code: code,
)
// ──── ĐỀ SỐ 1 ────
#render-exam(code: "101")[
#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 ...
#het
]
#pagebreak()
#resetexamstate()
// ──── ĐỀ SỐ 2 ────
#render-exam(code: "102")[
#exam-part([PHẦN I. Trắc nghiệm.], count: 12)
// ... câu hỏi đề 2 ...
]
📦 Gói sang-math 1.0.0
lib.typ là cổng API công khai duy nhất. Chỉ một lệnh import đã mở được engine đề thi, giao diện đề/sách, BBT và hình học CeTZ nâng cao; không cần nhớ đường dẫn file nội bộ.
: * khi cần nhiều nhóm macro trong cùng tài liệu.
// Toàn bộ API
#import "@preview/sang-math:1.0.0": *
// Hoặc import tường minh
#import "@preview/sang-math:1.0.0": tn, ds, tln, tl, True, sang-setup
#import "@preview/sang-math:1.0.0": exam-preset, exam-theme
#import "@preview/sang-math:1.0.0": draw-ellipse, draw-cylinder
Bản đồ API
| Nhóm | Macro tiêu biểu | Dùng cho |
|---|---|---|
| Đề thi | tn, ds, tln, tl, exam-mode | Đề 4 dạng câu, lời giải, đáp án |
| Giao diện | exam-preset, exam-theme, book-theme | 18 theme đề và 12 theme sách |
| Bảng Toán | bbtv2, bbbt, bxd, auto-bbt | BBT, xét dấu, bảng giá trị |
| Hình học | tri-abc, chop-sabc, axis-xy | Hình THPT và đồ thị cơ bản |
| CeTZ nâng cao | draw-ellipse, draw-cylinder, draw-helix | Conic, khối tròn xoay, đường cong 3D |
Quy ước ổn định
lib.typ không đổi trong cùng major version.sang-math, cùng URL và cùng phiên bản.Theme và profile chính thức
| Nhóm | Giá trị hỗ trợ |
|---|---|
| Profile đề | dethi, loigiai, compact, draft, beamer |
| 18 theme đề | classic, ocean, emerald, royal, violet, crimson, graphite, amber, teal-pro, sky, indigo-minimal, print-economy, aurora, lotus, navy-gold, jade, coral, plum |
| 12 theme sách | sgk-modern, vdc-elite, workbook-jade, solution-crimson, lesson-amber, olympiad-indigo, magazine-coral, blackboard-green, minimal-graphite, handout-sky, research-slate, lotus-study |
Mẫu package-first chuẩn
#import "@preview/sang-math:1.0.0": *
#let preset = exam-preset(theme: "teal-pro", profile: "dethi")
#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Ử TỐT NGHIỆP THPT",
subject: "TOÁN 12",
duration: "90 phút",
code: "101",
..preset.template,
)
Di trú từ template cũ
| Cú pháp cũ | API package 1.0.0 |
|---|---|
#import "sang-exam.typ": * | #import "@preview/sang-math:1.0.0": * |
#import "bbt.typ": * | Không cần; BBT đã export từ package root. |
stexgv-doc | book-theme |
chapter / bai / muc | book-chapter / book-lesson / book-section |
muctieu / phuongphap / vd / bt | goal-box / method-box / example-box / exercise-box |
Đường dẫn nội bộ package như geometry-3d/* | Không import trực tiếp; dùng tên đã export từ lib.typ. |
📋 Tổng Quan Module Đề Thi
Nhóm API đề thi của sang-math:1.0.0 cung cấp đầy đủ công cụ soạn đề THPT theo cấu trúc Bộ GD&ĐT từ 2025.
Luồng làm việc
sang-math:1.0.0
theme + profile
Tạo macro tn/ds/tln/tl
thpt-school-exam
#tn #ds #tln #tl
Profile 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 |
"compact" | Dàn đề hai cột | Đề ngắn, tiết kiệm giấy |
"draft" | Chừa vùng nháp cạnh câu | Phiếu luyện tập |
"beamer" | Bỏ dàn trang in | Chuẩn bị nội dung cho engine slide riêng |
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.
② #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.
③ #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
]
🏷️ 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)
🔀 Chế Độ Hiển Thị
exam-mode() — Tạo bộ macro câu hỏi
// Tạo macro theo mode và màu sắc
#let (tn, ds, tln, tl) = exam-mode(
mode: "dethi", // "dethi" | "loigiai" | "solcolor"
accent: classic.blue // classic.blue | .emerald | .crimson
)
// Bảng màu có sẵn
classic.blue // = rgb("#0057b8") — xanh dương
classic.emerald // = rgb("#1a7a2e") — xanh lá
classic.crimson // = rgb("#cc2200") — đỏ đậm
mode = "loigiai" và biên dịch lại — không cần sửa nội dung câu hỏi. Đây là điểm mạnh nhất của hệ thống.
Đ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 độ.
id: và tags: có sẵn trong sang-math:1.0.0. Các script bank:* và modules/question-bank.typ bên dưới là tiện ích mở rộng của repository ConicTypst, không nằm trong package Typst Universe.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 "@preview/sang-math:1.0.0": *
#import "modules/question-bank.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 hoặc chạy typst compile --root typst typst/exam-mix-demo.typ output/exam-mix-demo.pdf. 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
Đây là quy trình dành cho maintainer repository ConicTypst. Bridge đọc kho ThptExamBuilder/banks từ dự án STEXGV đặt cạnh repository, giữ ID gốc, slot 1–22, topic, source và seed, 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. |
tkz-tab hoặc lệnh LaTeX đặc thù được đánh dấu review. Chỉ nên public câu ready sau khi đã kiểm tra PDF; status: none dành cho rà soát nội bộ.Import lõi trộn đề
#import "@preview/sang-math:1.0.0": *
#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()
],
)
📄 File Mẫu Đề Thi Hoàn Chỉnh
Copy file mẫu này, đổi thông tin tiêu đề và thêm câu hỏi vào đúng phần.
// ═══════════════════════════════════════════
// ĐỀ THI MẪU — Chỉnh sửa phần có dấu ◄
// Biên dịch: typst compile de-thi-mau.typ de-thi-mau.pdf
// ═══════════════════════════════════════════
#import "@preview/sang-math:1.0.0": *
// ── Chế độ ◄ ─────────────────────────────
#let mode = "dethi" // ◄ đổi thành "loigiai" để xem đáp án
#let accent = classic.blue // ◄ classic.blue | .emerald | .crimson
// ── Tạo macro ────────────────────────────
#let (tn, ds, tln, tl) = exam-mode(mode: mode, accent: accent)
#show: sang-setup
// ── Trang ────────────────────────────────
#set page(paper: "a4", margin: (x: 2cm, y: 2.5cm))
#set text(font: "New Computer Modern", size: 11pt, lang: "vi")
// ── Tiêu đề ◄ ────────────────────────────
#show: thpt-school-exam.with(
department: "SỞ GIÁO DỤC VÀ ĐÀO TẠO", // ◄ Đổi tên sở
school: "TỈNH ĐỒNG THÁP", // ◄ Đổi tên tỉnh
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", // ◄ Mã đề
accent: accent,
)
// ═══════════════════════════════════════
// PHẦN I — TRẮC NGHIỆM (12 câu)
// ═══════════════════════════════════════
#exam-part([PHẦN I. Câu trắc nghiệm nhiều phương án. _(Câu 1 đến Câu 12)_
Thí sinh chọn *một phương án đúng* cho mỗi câu.], count: 12)
#tn(
[Đạo hàm của $f(x) = 2x^3 - 3x^2 + 1$ là],
([$6x^2 - 3$], True([$6x^2 - 6x$]), [$6x - 6$], [$2x^2 - 6x$]),
loigiai: [$f'(x) = 6x^2 - 6x$],
)
// ◄ Thêm câu 2–12 ở đây...
// ═══════════════════════════════════════
// PHẦN II — ĐÚNG SAI (4 câu)
// ═══════════════════════════════════════
#exam-part([PHẦN II. Câu đúng sai. _(Câu 13 đến Câu 16)_
Thí sinh chọn *Đúng hoặc Sai* cho mỗi mệnh đề.], count: 4)
#ds(
[Cho hàm số $f(x) = x^3 - 3x + 2$. Xét tính đúng/sai:],
(
[Hàm số có 2 cực trị.], // SAI
True([$f(1) = 0$]), // ĐÚNG
True([Cực đại tại $x = -1$]), // ĐÚNG
[Hàm đồng biến trên $\mathbb{R}$], // SAI
),
loigiai: [Phân tích từng ý...],
)
// ◄ Thêm câu 14–16 ở đây...
// ═══════════════════════════════════════
// PHẦN III — TRẢ LỜI NGẮN (4 câu)
// ═══════════════════════════════════════
#exam-part([PHẦN III. Câu trả lời ngắn. _(Câu 17 đến Câu 20)_
Thí sinh điền đáp án vào ô trống.], count: 4)
#tln(
[Tính $lim_(x -> 2) frac(x^2 - 4, x - 2)$.],
[$4$],
loigiai: [Rút gọn: $frac((x-2)(x+2), x-2) = x+2 -> 4$.],
)
// ◄ Thêm câu 18–20 ở đây...
// ═══════════════════════════════════════
// PHẦN IV — TỰ LUẬN (2 câu)
// ═══════════════════════════════════════
#exam-part([PHẦN IV. Câu tự luận. _(Câu 21 đến Câu 22)_])
#tl(
[Khảo sát sự biến thiên của $f(x) = x^3 - 3x^2 + 4$.],
lines: 10,
loigiai: [
#ppgiai[Tính $f'$, giải $f' = 0$, lập BBT.]
#step[$f'(x) = 3x^2 - 6x = 3x(x-2)$.]
#step[CT: $f(0) = 4$; CT': $f(2) = 0$.]
#reset-step()
],
)
📚 Tổng Quan Sách / SGK
sang-math:1.0.0 cung cấp một API ổn định để soạn SGK, chuyên đề, workbook, sách lời giải, handout và tài liệu nghiên cứu. Không cần sao chép module nội bộ.
12 giao diện sách chính thức
| Nhóm | Theme | Phù hợp |
|---|---|---|
| Sách học | sgk-modern, lotus-study, blackboard-green | SGK, chuyên đề, bài học |
| Luyện tập | workbook-jade, handout-sky, lesson-amber | Workbook, phiếu học tập |
| Nâng cao | vdc-elite, olympiad-indigo, solution-crimson | VDC, Olympic, sách lời giải |
| Xuất bản | magazine-coral, minimal-graphite, research-slate | Tạp chí, tối giản, nghiên cứu |
Khung sách tối thiểu
#import "@preview/sang-math:1.0.0": *
#let theme = "sgk-modern"
#show: book-theme.with(
theme: theme,
title: "CHUYÊN ĐỀ HÀM SỐ",
subtitle: "Lý thuyết · Ví dụ · Luyện tập",
author: "Tổ Toán",
institution: "Trường THPT Sang-Math",
grade: "Lớp 12",
subject: "Toán",
year: "2026–2027",
cover-note: [Tài liệu học tập dùng trong nhà trường.],
toc: true,
)
#book-chapter([Ứng dụng đạo hàm], number: "01", theme: theme)
#book-lesson([Tính đơn điệu], number: "1", theme: theme)
🗂️ Cấu Trúc Phân Cấp Sách
Ba cấp cấu trúc ổn định
#book-chapter([Tên chương], number: "01", theme: theme)#book-lesson([Tên bài], number: "1", theme: theme)#book-section([Tên mục], number: "1.1", theme: theme)Quy tắc đồng bộ giao diện
// Dùng cùng một biến theme cho bìa, tiêu đề và mọi hộp.
#let theme = "workbook-jade"
#show: book-theme.with(theme: theme, title: "BÀI TẬP TOÁN 12")
#book-chapter([Nguyên hàm], number: "03", theme: theme)
#book-lesson([Bảng nguyên hàm], number: "1", theme: theme)
#book-section([Bài tập cơ bản], number: "3.1", theme: theme)
Ví dụ cấu trúc đầy đủ
#book-chapter([Hàm số và đạo hàm], number: "01", theme: theme)
#book-lesson([Khảo sát hàm bậc ba], number: "1", theme: theme)
#goal-box(theme: theme)[
- Lập được BBT của hàm bậc ba.
- Vẽ đồ thị hàm số.
]
#warmup-box(theme: theme)[
Quan sát đồ thị và nhận xét số cực trị.
]
#explore-box(theme: theme)[
Phương pháp: tính $f'(x)$, giải $f'(x) = 0$, lập BBT.
]
#example-box(theme: theme)[Khảo sát $f(x) = x^3 - 3x^2 + 4$.]
#exercise-box(theme: theme, lines: 4)[Tìm cực trị của $f(x) = -x^3 + 3x$.]
#practice-box(theme: theme)[
Thực hiện thêm hai bài toán cùng dạng.
]
#summary-box(theme: theme)[
Hàm bậc ba $f(x) = ax^3 + bx^2 + cx + d$ ($a \ne 0$) có tối đa 2 cực trị.
]
📦 Các Hộp Nội Dung SGK
Phiên bản 1.0.0 xuất chính thức 13 hộp nội dung. Mỗi hộp nhận theme:; riêng exercise-box nhận thêm lines:.
Mạch tổ chức bài học
#goal-box(theme: theme)[...]#warmup-box(theme: theme)[...]#explore-box(theme: theme)[...]#activity-box(theme: theme)[...]#practice-box(theme: theme)[...]#summary-box(theme: theme)[...]Kiến thức và bài tập
#theory-box(theme: theme)[...]#definition-box(theme: theme)[...]#theorem-box(theme: theme)[...]#method-box(theme: theme)[...]#example-box(theme: theme)[...]#warning-box(theme: theme)[...]#exercise-box(theme: theme, lines: 4)[...]Các khối nội dung đánh số
#theory-box(theme: theme)[
Hàm số đồng biến trên $I$ nếu $f'(x) > 0$ với mọi $x in I$.
]
#method-box(theme: theme)[
Tính đạo hàm, tìm điểm tới hạn, xét dấu rồi kết luận.
]
#example-box(theme: theme)[
Xét tính đơn điệu của $f(x) = x^3 - 3x$.
]
#exercise-box(theme: theme, lines: 5)[
Xét tính đơn điệu của $g(x) = x^3 - 6x^2 + 9x$.
]
🔢 Đánh Số Trong Sách
API sách 1.0.0 chủ động nhận chuỗi number:. Cách này ổn định khi tách chương thành nhiều file, ghép sách hoặc dùng số dạng 01, 1.2.
Đặt số rõ ràng ở từng cấp
#book-chapter(
[Ứng dụng đạo hàm], number: "01", theme: theme,
)
#book-lesson(
[Tính đơn điệu], number: "1", theme: theme,
)
#book-section(
[Bài tập vận dụng], number: "1.3", theme: theme,
)
01, 02; bài 1, 2; mục 1.1, 1.2. Hộp ví dụ và bài tập được phân biệt bằng kiểu trình bày, không cần bộ đếm riêng.
📰 Ghép Nhiều Đề Trong Một PDF
Phiên bản public không dùng wrapper #de. Mỗi đề là một lần gọi exam-theme; trước đề tiếp theo dùng resetexamstate() và pagebreak().
Mẫu hai mã đề
#import "@preview/sang-math:1.0.0": *
#let preset = exam-preset(theme: "classic", profile: "dethi")
#let (tn, ds, tln, tl) = exam-mode(..preset.question)
#exam-theme(..preset.template, code: "101")[
#exam-part([PHẦN I. Trắc nghiệm], count: 12)
#tn([Câu của mã 101...], (True([$A$]), [$B$], [$C$], [$D$]))
#het
]
#pagebreak()
#resetexamstate()
#exam-theme(..preset.template, code: "102")[
#exam-part([PHẦN I. Trắc nghiệm], count: 12)
#tn([Câu của mã 102...], ([$A$], True([$B$]), [$C$], [$D$]))
#het
]
resetexamstate() giữa hai đề để số câu, số phần và dữ liệu bảng đáp án không bị nối tiếp.📄 File Mẫu Sách / Chuyên Đề
// Biên dịch: typst compile chuyen-de-mau.typ chuyen-de-mau.pdf
#import "@preview/sang-math:1.0.0": *
#let theme = "sgk-modern" // đổi sang một trong 12 book theme
#show: book-theme.with(
theme: theme,
title: "CHUYÊN ĐỀ TOÁN 12",
subtitle: "Giải tích — Hình học — Xác suất",
author: "Tổ Toán",
institution: "Trường THPT Nguyễn Du",
subject: "Toán",
grade: "Lớp 12",
year: "2026–2027",
cover-note: [Tài liệu học tập dùng trong nhà trường.],
toc: true,
)
// ── Cấu trúc nội dung ────────────────────
#book-chapter([Hàm Số Đơn Điệu], number: "01", theme: theme)
#book-lesson([Khảo sát hàm bậc ba], number: "1", theme: theme)
#book-section([Phương pháp], number: "1.1", theme: theme)
#goal-box(theme: theme)[
- Lập được bảng biến thiên.
- Tìm cực trị và khoảng đơn điệu.
]
#method-box(theme: theme)[
*Phương pháp tổng quát:*
+ Tính $f'(x)$.
+ Giải $f'(x) = 0$, xác định dấu $f'$.
+ Lập BBT và kết luận.
]
#example-box(theme: theme)[
*Ví dụ.* Khảo sát $f(x) = 2x^3 - 3x^2 - 12x + 1$.
#ppgiai[
#step[$f'(x) = 6x^2 - 6x - 12 = 6(x-2)(x+1)$.]
#step[$f'(x) = 0$ khi $x = -1$ hoặc $x = 2$.]
#step[CT: $f(-1) = 8$; ct: $f(2) = -19$.]
#reset-step()
]
]
#exercise-box(theme: theme, lines: 6)[
Khảo sát $f(x) = x^3 - 6x^2 + 9x - 4$.
]
#practice-box(theme: theme)[
Tìm cực trị của $f(x) = x^4 - 8x^2 + 3$.
]
#summary-box(theme: theme)[
Hàm bậc ba $y = ax^3+bx^2+cx+d$ ($a \ne 0$):
- Có $\Delta' = b^2-3ac$: 2 cực trị khi $\Delta' > 0$.
]
📈 Bảng Biến Thiên & Hình Học
Tất cả API BBT, BXD, bảng số liệu và hình học đều được xuất từ package gốc. Chỉ cần một lệnh import; cetz được package quản lý như dependency.
Import
#import "@preview/sang-math:1.0.0": *
// Dùng trực tiếp:
#bbtv2(...)
#bxd(...)
#tri-abc(...)
#parabola(...)
Tổng hợp các hàm
| Hàm | Nhóm | Mô tả |
|---|---|---|
bbtv2(...), bbbt(...) | Biến thiên | BBT tự động hoặc điều chỉnh cao độ thủ công |
bxd(...), bbt-opt(...) | Dấu / tối ưu | Bảng xét dấu và cấu hình BBT |
bang-gia-tri(...), bang-phan-phoi(...) | Bảng | Bảng giá trị và bảng phân phối |
auto-bbt(...) | Tự động | Sinh BBT từ dữ liệu đầu vào |
tri-xyz(...), tri-abc(...), tri-right(...) | 2D | Tam giác tổng quát, chuẩn và vuông |
rect-xyz(...), rect-abc(...), square(...) | 2D | Tứ giác và hình vuông |
chop-sabc(...), chop-sabcd(...), lang-tru-abc(...) | 3D | Hình chóp và lăng trụ |
axis-xy(...), plot(...), parabola(...) | Đồ thị | Hệ trục và đồ thị hàm số |
circle-desc(...), ellipse-h(...) | Đường cong | Đường tròn và ellipse cơ bản |
#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.
🧭 Hình Học Nâng Cao 1.0.0
Các hàm bắt đầu bằng draw-... dùng trực tiếp ngữ cảnh vẽ, vì vậy phải đặt bên trong cetz.canvas. Đây là điểm khác với các macro cơ bản như tri-abc vốn tự tạo canvas.
Conic: parabol, ellipse, hyperbol
#import "@preview/cetz:0.5.2"
#import "@preview/sang-math:1.0.0": draw-parabola, draw-ellipse, draw-hyperbola
#cetz.canvas(length: 1cm, {
draw-ellipse(a: 2.5, b: 1.5, show-axes: true, show-foci: true)
})
#cetz.canvas(length: 1cm, {
draw-parabola(a: 0.5, h: 0, k: -1,
x-range: (-3.0, 3.0), label-vertex: [$I$])
})
#cetz.canvas(length: 1cm, {
draw-hyperbola(a: 1.2, b: 0.8,
show-asymptotes: true, show-foci: true)
})
Hàm dữ liệu tương ứng: parabola-points, ellipse-points, hyperbola-branch-points.
Khối tròn xoay
#cetz.canvas(length: 1cm, {
draw-cylinder(radius: 1.3, height: 3.5, show-hidden: true)
})
#cetz.canvas(length: 1cm, {
draw-cone(radius: 1.5, height: 3.8,
label-apex: [$S$], label-center: [$O$])
})
#cetz.canvas(length: 1cm, {
draw-sphere(radius: 1.7, show-equator: true, show-meridian: true)
})
Helix và lò xo
#cetz.canvas(length: 1cm, {
draw-helix(radius: 1, height: 4, loops: 2.5, auto-dashed: true)
})
#cetz.canvas(length: 1cm, {
draw-spring(radius: 0.4, height: 4, coils: 7)
})
API dữ liệu gồm helix-points và helix-split-visible. Các khối và đường cong hỗ trợ anchor để nối thêm nhãn, kích thước hoặc đoạn thẳng CeTZ.
📊 Đồ 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.],
)
⬇️ Tài Liệu & Cài Package
Cách chính thức là import package từ Typst Universe. Bạn không cần tải và chép các file nguồn vào dự án.
Biên dịch tài liệu của bạn
# Biên dịch một lần
typst compile main.typ main.pdf
# Tự biên dịch khi lưu
typst watch main.typ main.pdf
# Chọn theme/profile từ dòng lệnh nếu dùng exam-input-preset
typst compile --input theme=navy-gold --input profile=loigiai main.typ dap-an.pdf
- Windows:
winget install Typst.Typst - macOS:
brew install typst - Linux:
snap install typst - Hoặc tải tại github.com/typst/typst/releases