diff --git a/README.md b/README.md index 05e6f26..f81f8f0 100644 --- a/README.md +++ b/README.md @@ -308,6 +308,65 @@ Tex文件的开头,是基本信息部分,可以根据需要添加`\title`{ `\l_ouc_pbg_vgap`(纵向,默认 6pt)。 4. 单个要点框仍然可以像以前那样手写 `columns` + `pointbox`,两种用法并存。 +## 图示组件(扁平风格) + +模板自带 9 个**扁平简约**图示组件:圆角色块 + 实心纯色 + 白字 + 细箭头, +**无渐变、无阴影、无立体**。全部用纯 TikZ 绘制,不依赖任何外部绘图宏包。 + +![图示组件](/figs/fig16.png) + +### 颜色:一个或多个都可以 + +所有组件的**颜色列表写在方括号里**(可选参数)。写一个就是全部同色, +写多个就**按顺序取色、不够时自动循环**: + +``` +\flatflow[oucblue]{数据采集, 数据清洗, 模型训练, 结果评估} % 全部海大蓝 +\flatflow[oucblue,oucred,oucpine,oucamber]{数据采集, 数据清洗, 模型训练, 结果评估} + % ← 4 个块 4 种颜色 +\flatflow[oucred,oucblue]{块一, 块二, 块三, 块四, 块五, 块六} + % ← 2 色循环:红蓝红蓝红蓝 +``` + +> ⚠️ 颜色**必须写成方括号 `[ ]`**。若误写成花括号 `{ }`,颜色会被当成第一个条目, +> 编译时会报 `图示组件的颜色要写在方括号里` 的明确错误(不会静默出错)。 + +### 九个组件速查 + +| 命令 | 说明 | +|------|------| +| `\flatflow[颜色]{条目1, 条目2, ...}` | **横向箭头流程**,自动等分满宽 | +| `\flatstack[颜色]{条目1, 条目2, ...}` | 纵向流程 | +| `\flatcycle[颜色]{条目1, ...}` | 循环图(弧线箭头首尾相接) | +| `\flathub[颜色]{中心文字}{环绕1, 环绕2, ...}` | **中心辐射**(中心圆 + 环绕卡片) | +| `\flatsteps[颜色]{条目1, ...}` | 阶梯递进 | +| `\flattime[颜色]{年份列表}{事件列表}` | 时间轴(节点上下交替) | +| `\flatquad[颜色]{左上}{右上}{左下}{右下}` | 四象限 | +| `\flatpyramid[颜色]{顶层, ..., 底层}` | 金字塔(上窄下宽) | +| `\flatcompare[左色][右色]{左标题}{左内容}{右标题}{右内容}` | 左右对比 | + +示例(`slides.tex` 里有完整的 5 页演示): + +``` +\flatflow[oucblue,oucred,oucpine,oucamber]{数据采集, 数据清洗, 模型训练, 结果评估} + +\flathub[oucblue,oucred,oucpine,oucamber,oucdusk]{核心}{可行性, 必要性, 创新性, 实用性} + +\flattime[oucblue,oucred,oucpine,oucdusk]{2020, 2021, 2023, 2025}{立项, 方案设计, 中期检查, 结题验收} + +\flatcompare[oucpine][oucred]{优势}{响应快、成本低、易于维护}{不足}{精度受限、依赖数据质量} +``` + +`\flatquad` 与 `\flatcompare` 的颜色是独立的固定参数(分别 1 个和 2 个), +不参与循环取色。 + +### 几点说明 + +1. 颜色可以直接用模板的 10 个颜色(`oucblue`、`oucred`、`oucpine`、`oucdusk`、`oucamber` 等)。 +2. 横向流程会自动把 N 个块加间隙**摊满整行**,不需要手算宽度。 +3. 纵向对比、四象限这类元素较宽,建议用 `\begin{center}...\end{center}` 居中。 +4. 这套组件已用 TeX Live 2022~2026 逐一编译验证:0 错误、0 溢出,且跨版本渲染逐像素一致。 + ## 代码 模板可以插入代码块,使用`lstlisting`即可。 diff --git a/figs/fig16.png b/figs/fig16.png new file mode 100644 index 0000000..171f5c0 Binary files /dev/null and b/figs/fig16.png differ diff --git a/slides.pdf b/slides.pdf index b714016..73c1d22 100644 Binary files a/slides.pdf and b/slides.pdf differ diff --git a/slides.tex b/slides.tex index 1d4fc5c..f3c119a 100644 --- a/slides.tex +++ b/slides.tex @@ -308,6 +308,76 @@ \end{frame} +\subsection{图示组件} + +\begin{frame} + \frametitle{横向流程} + 流程类图示用 \texttt{\textbackslash flatflow},条目用逗号分隔,自动等分排布。\\ + 颜色写在方括号里:写一个则全部同色,写多个则按顺序取色。\\ + + \vspace{4pt} + \textbf{单色:} + \begin{center} + \flatflow[oucblue]{数据采集, 数据清洗, 模型训练, 结果评估} + \end{center} + + \vspace{4pt} + \textbf{每个块不同颜色:} + \begin{center} + \flatflow[oucblue,oucred,oucpine,oucamber]{数据采集, 数据清洗, 模型训练, 结果评估} + \end{center} +\end{frame} + +\begin{frame} + \frametitle{纵向流程与循环} + \begin{columns} + \begin{column}{0.42\textwidth} + \centering + \flatstack[oucblue,oucred,oucpine,oucdusk]{提出问题, 建立模型, 求解验证} + \end{column} + \begin{column}{0.42\textwidth} + \centering + \flatcycle[oucblue,oucred,oucpine,oucamber]{计划, 执行, 检查, 改进} + \end{column} + \end{columns} +\end{frame} + +\begin{frame} + \frametitle{中心辐射} + \begin{center} + \flathub[oucblue,oucred,oucpine,oucamber,oucdusk]{核心}{可行性, 必要性, 创新性, 实用性} + \end{center} +\end{frame} + +\begin{frame} + \frametitle{阶梯递进} + \begin{center} + \flatsteps[oucblue,oucred,oucpine,oucamber]{基础研究, 技术开发, 集成验证, 示范应用} + \end{center} +\end{frame} + +\begin{frame} + \frametitle{时间轴与四象限} + \begin{center} + \flattime[oucblue,oucred,oucpine,oucdusk]{2020, 2021, 2023, 2025}{立项, 方案设计, 中期检查, 结题验收} + \end{center} + \vspace{8pt} + \begin{center} + \flatquad[oucblue,oucred,oucpine,oucamber]{重要且紧急}{重要不紧急}{紧急不重要}{不重要不紧急} + \end{center} +\end{frame} + +\begin{frame} + \frametitle{金字塔与对比} + \begin{center} + \flatpyramid[oucblue,oucred,oucpine,oucamber]{总体目标, 实施策略, 具体措施, 落地行动} + \end{center} + \vspace{6pt} + \begin{center} + \flatcompare[oucpine][oucred]{优势}{响应快、成本低、易于维护}{不足}{精度受限、依赖数据质量} + \end{center} +\end{frame} + \subsection{块} \begin{frame} diff --git a/theme/beamerinnerthemeOUCSlide.sty b/theme/beamerinnerthemeOUCSlide.sty index 3f3ee11..889bd3e 100644 --- a/theme/beamerinnerthemeOUCSlide.sty +++ b/theme/beamerinnerthemeOUCSlide.sty @@ -299,6 +299,9 @@ { \ouc_pbg_typeset: } \ExplSyntaxOff + + +%% ====================================================================== %文本框textbox \newtcolorbox{textbox}[1]{ colback=#1!5!white, @@ -323,4 +326,269 @@ \renewcommand{\figurename}{图} \renewcommand{\tablename}{表} \renewcommand{\lstlistingname}{代码} -\renewcommand{\abstractname}{摘要} \ No newline at end of file +\renewcommand{\abstractname}{摘要} + +%% ====================================================================== +%% 扁平简约图示组件 (flat diagrams) +%% --------------------------------------------------------------------- +%% 设计基调:圆角块 + 实心纯色 + 白字 + 细箭头 +%% **无渐变、无阴影、无立体** +%% +%% 所有组件都支持「颜色列表」:写一个颜色则全部同色; +%% 写多个颜色(逗号分隔)则按顺序循环取色。 +%% \flatflow{oucblue} % 4 个块全为海大蓝 +%% \flatflow{oucblue,oucred,oucpine} % 4 个块循环取这 3 色 +%% \flatflow{oucblue,oucred,oucpine,oucdusk,oucamber} % 5 色各不同 +%% ====================================================================== + +\RequirePackage{tikz} +\usetikzlibrary{positioning,arrows.meta,calc,shapes.geometric} + +%% ---------------- 防错:颜色参数必须写在 [] 里 ---------------- +%% \flatflow 这类命令的「颜色列表」是可选参数,必须写成方括号 [颜色]。 +%% 若误写成花括号 {颜色},颜色会被当成第一个条目(静默出错、极难排查), +%% 因此这里检查每个条目是不是模板的颜色名,是则明确报错。 +\newcommand{\flcheckitems}[1]{% + \foreach \flci in {#1}{% + %% 注意:\foreach 每轮迭代会开组,内层的 \def 出组即失效, + %% 因此这里必须用 \global\def(否则检测永远不触发)。 + \global\def\flhit{0}% + \foreach \flck in {oucblue,ouclightblue,oucred,ouccherry,oucbrown,% + oucgolden,oucsilver,oucpine,oucdusk,oucamber}{% + %% 注意:此处 \ifstrequal 在本机不生效(恒为假),必须用 \ifx + \ifx\flci\flck\global\def\flhit{1}\fi + }% + \ifnum\flhit=1 + \PackageError{oucslide}{图示组件的颜色要写在方括号里}{% + 命令 \string\flatflow[颜色列表]{条目1, 条目2} 中的颜色是可选参数,% + 必须用方括号 [ ],不能用花括号 { }。\MessageBreak + 现在「\flci」被当成了条目内容(它看起来是颜色名)。}% + \fi + }% +} + +%% ---------------- 辅助:长度 / 计数 / 取色 ---------------- +\newlength\flgap +\newlength\flbw +\newcount\flcnt +\def\flcntval{0} +%% 数条目个数,并存成**纯数字宏**(本机 pgfmath 的算式里不能出现计数器) +\newcommand{\flcount}[1]{% + \flcnt=0 + \foreach \flzz in {#1}{\global\advance\flcnt by 1}% + \xdef\flcntval{\the\flcnt}% +} + +\newcount\flcolcnt +\def\flcoln{0} +\newcommand{\flcolcount}[1]{% + \flcolcnt=0 + \foreach \flzz in {#1}{\global\advance\flcolcnt by 1}% + \xdef\flcoln{\the\flcolcnt}% +} + +\def\flcur{oucblue} +\def\flmain{oucblue} +%% 第 #2 个块取颜色列表 #1 中的颜色(超出则循环),结果存进 \flcur +\newcommand{\flpick}[2]{% + \pgfmathtruncatemacro{\flidx}{mod(#2-1,\flcoln)+1}% + \foreach \flzz [count=\fljj] in {#1}{% + \ifnum\fljj=\flidx \xdef\flcur{\flzz}\fi + }% +} +%% 取列表第一项作为「主色」(箭头、轴线等用) +\newcommand{\flsetmain}[1]{\flpick{#1}{1}\xdef\flmain{\flcur}} + +%% ---------------- 公共样式 ---------------- +\tikzset{ + flblk/.style={rectangle, rounded corners=2pt, draw=none, + text=white, align=center, inner sep=4pt, font=\small}, + flblkl/.style={rectangle, rounded corners=2pt, draw=none, + align=center, inner sep=4pt, font=\footnotesize}, + flarr/.style={line width=1pt, -{Stealth[length=1.7mm,width=1.4mm]}} +} + +%% ================== 1. 横向箭头流程 ================== +%% \flatflow[颜色列表]{条目1, 条目2, ...} +\NewDocumentCommand \flatflow { O{oucblue} m }{{% + \flcheckitems{#2}\flcount{#2}\flcolcount{#1}\flsetmain{#1}% + \setlength{\flgap}{16pt}% + \pgfmathsetlength{\flbw}{(0.99\textwidth-(\flcntval-1)*\flgap)/\flcntval}% + \begin{tikzpicture} + \foreach \flx [count=\fli] in {#2} { + \flpick{#1}{\fli}% + \pgfmathtruncatemacro{\flprev}{\fli-1} + \ifnum\fli=1 + \node[flblk, fill=\flcur, minimum height=20pt, + text width=\dimexpr\flbw-8pt\relax] (fn1) at (0,0) {\flx}; + \else + \node[flblk, fill=\flcur, minimum height=20pt, + text width=\dimexpr\flbw-8pt\relax, + right=\flgap of fn\flprev] (fn\fli) {\flx}; + \fi + }% + \foreach \flx [count=\fli] in {#2} { + \ifnum\fli>1 + \pgfmathtruncatemacro{\flprev}{\fli-1} + \draw[flarr, draw=\flmain!70!black] (fn\flprev.east) -- (fn\fli.west); + \fi + }% + \end{tikzpicture}% +}} + +%% ================== 2. 纵向流程 ================== +\NewDocumentCommand \flatstack { O{oucblue} m }{{% + \flcheckitems{#2}\flcount{#2}\flcolcount{#1}\flsetmain{#1}% + \begin{tikzpicture} + \foreach \flx [count=\fli] in {#2} { + \flpick{#1}{\fli}% + \pgfmathtruncatemacro{\flprev}{\fli-1} + \ifnum\fli=1 + \node[flblk, fill=\flcur, minimum height=18pt, + minimum width=0.7\textwidth] (fn1) at (0,0) {\flx}; + \else + \node[flblk, fill=\flcur, minimum height=18pt, + minimum width=0.7\textwidth, below=8pt of fn\flprev] (fn\fli) {\flx}; + \fi + }% + \foreach \flx [count=\fli] in {#2} { + \ifnum\fli>1 + \pgfmathtruncatemacro{\flprev}{\fli-1} + \draw[flarr, draw=\flmain!70!black] (fn\flprev.south) -- (fn\fli.north); + \fi + }% + \end{tikzpicture}% +}} + +%% ================== 3. 循环图 ================== +\NewDocumentCommand \flatcycle { O{oucblue} m }{{% + \flcheckitems{#2}\flcount{#2}\flcolcount{#1}\flsetmain{#1}% + \begin{tikzpicture} + \foreach \flx [count=\fli] in {#2} { + \flpick{#1}{\fli}% + \pgfmathsetmacro{\flang}{90-360/\flcntval*(\fli-1)} + \node[circle, fill=\flcur, text=white, align=center, inner sep=1pt, + minimum size=32pt, font=\footnotesize] (cn\fli) at (\flang:34pt) {\flx}; + }% + \foreach \flx [count=\fli] in {#2} { + \pgfmathtruncatemacro{\flnext}{mod(\fli,\flcntval)+1} + \draw[flarr, draw=\flmain!50!black] (cn\fli) to[bend left=20] (cn\flnext); + }% + \end{tikzpicture}% +}} + +%% ================== 4. 中心辐射 ================== +%% \flathub[颜色列表]{中心文字}{环绕1, 环绕2, ...} +\NewDocumentCommand \flathub { O{oucblue} m m }{{% + \flcheckitems{#3}\flcount{#3}\flcolcount{#1}\flsetmain{#1}% + \begin{tikzpicture} + \foreach \flx [count=\fli] in {#3} { + \flpick{#1}{\fli+1}% % 环绕从颜色列表第 2 项起取色 + \pgfmathsetmacro{\flang}{90-360/\flcntval*(\fli-1)} + \node[rectangle, rounded corners=2pt, draw=\flcur!30!white, line width=0.6pt, + fill=\flcur!7!white, text=\flcur!55!black, align=center, inner sep=3pt, + font=\footnotesize, minimum height=17pt] (ln\fli) at (\flang:3.1cm) {\flx}; + \draw[draw=\flcur!40!white, line width=0.7pt] (0,0) -- (ln\fli); + }% + \flsetmain{#1}% + \node[circle, fill=\flmain, text=white, align=center, inner sep=2pt, + minimum size=40pt, font=\small\bfseries] at (0,0) {#2}; + \end{tikzpicture}% +}} + +%% ================== 5. 阶梯递进 ================== +\NewDocumentCommand \flatsteps { O{oucblue} m }{{% + \flcheckitems{#2}\flcolcount{#1}% + \begin{tikzpicture} + \foreach \flx [count=\fli] in {#2} { + \flpick{#1}{\fli}% + \pgfmathsetlengthmacro{\flsx}{(\fli-1)*2.6cm} + \pgfmathsetlengthmacro{\flsy}{(\fli-1)*13pt} + \node[flblk, fill=\flcur, minimum height=16pt, minimum width=2.4cm, + anchor=south west] at (\flsx,\flsy) {\flx}; + }% + \end{tikzpicture}% +}} + +%% ================== 6. 时间轴 ================== +%% \flattime[颜色列表]{年份列表}{事件列表} +\NewDocumentCommand \flattime { O{oucblue} m m }{{% + \flcheckitems{#2}\flcheckitems{#3}\flcount{#2}\flcolcount{#1}\flsetmain{#1}% + \begin{tikzpicture} + \pgfmathsetlengthmacro{\flstep}{0.9\textwidth/\flcntval}% + \draw[line width=1.8pt, draw=\flmain, + -{Stealth[length=2mm,width=1.6mm]}] (0,0) -- (0.9\textwidth,0); + \foreach \fly [count=\fli] in {#2} { + \flpick{#1}{\fli}% + \pgfmathsetlengthmacro{\flxx}{(\fli-0.5)*\flstep} + \node[circle, fill=\flcur, minimum size=6pt, inner sep=0pt] at (\flxx,0) {}; + \node[rectangle, fill=\flcur, text=white, inner sep=2.5pt, + font=\footnotesize\bfseries, rounded corners=1pt] + at (\flxx,0.22cm) {\fly}; + }% + \foreach \fle [count=\fli] in {#3} { + \flpick{#1}{\fli}% + \pgfmathsetlengthmacro{\flxx}{(\fli-0.5)*\flstep} + \ifodd\fli + \node[flblkl, fill=\flcur!7!white, draw=\flcur!25!white, line width=0.5pt, + text=\flcur!55!black, text width=2.3cm, anchor=south] + at (\flxx,0.45cm) {\fle}; + \else + \node[flblkl, fill=\flcur!7!white, draw=\flcur!25!white, line width=0.5pt, + text=\flcur!55!black, text width=2.3cm, anchor=north] + at (\flxx,-0.22cm) {\fle}; + \fi + }% + \end{tikzpicture}% +}} + +%% ================== 7. 四象限 ================== +%% \flatquad[颜色列表]{左上}{右上}{左下}{右下} +\NewDocumentCommand \flatquad { O{oucblue} m m m m }{{% + \flcolcount{#1}% + \flpick{#1}{1}\xdef\flcA{\flcur}% + \flpick{#1}{2}\xdef\flcB{\flcur}% + \flpick{#1}{3}\xdef\flcC{\flcur}% + \flpick{#1}{4}\xdef\flcD{\flcur}% + \begin{tikzpicture} + \node[flblkl, fill=\flcA!8!white, text=\flcA!55!black, text width=4.5cm, + minimum height=34pt] (q1) at (0,0) {#2}; + \node[flblkl, fill=\flcB!14!white, text=\flcB!55!black, text width=4.5cm, + minimum height=34pt, right=3pt of q1] (q2) {#3}; + \node[flblkl, fill=\flcC!14!white, text=\flcC!55!black, text width=4.5cm, + minimum height=34pt, below=3pt of q1] (q3) {#4}; + \node[flblkl, fill=\flcD!8!white, text=\flcD!55!black, text width=4.5cm, + minimum height=34pt, right=3pt of q3] (q4) {#5}; + \end{tikzpicture}% +}} + +%% ================== 8. 金字塔 ================== +%% \flatpyramid[颜色列表]{顶层, 第二层, ...} (按书写顺序自上而下) +\NewDocumentCommand \flatpyramid { O{oucblue} m }{{% + \flcheckitems{#2}\flcolcount{#1}% + \begin{tikzpicture} + \foreach \flx [count=\fli] in {#2} { + \flpick{#1}{\fli}% + \pgfmathsetlengthmacro{\flw}{(1.6+(\fli-1)*1.6)*1cm} + \pgfmathsetlengthmacro{\flyy}{-(\fli-1)*15.5pt} + \node[trapezium, trapezium left angle=76, trapezium right angle=104, + draw=none, fill=\flcur, text=white, align=center, + inner sep=1pt, minimum height=15pt, minimum width=\flw] + at (0,\flyy) {\flx}; + }% + \end{tikzpicture}% +}} + +%% ================== 9. 左右对比 ================== +%% \flatcompare[左色][右色]{左标题}{左内容}{右标题}{右内容} +\NewDocumentCommand \flatcompare { O{oucblue} O{oucamber} m m m m }{{% + \begin{tikzpicture} + \node[flblk, fill=#1, minimum width=4.7cm, minimum height=19pt] (h1) at (0,0) {#3}; + \node[flblk, fill=#2, minimum width=4.7cm, minimum height=19pt, + right=8pt of h1] (h2) {#5}; + \node[flblkl, fill=#1!7!white, text=#1!55!black, text width=4.5cm, + minimum height=54pt, below=3pt of h1] {#4}; + \node[flblkl, fill=#2!7!white, text=#2!55!black, text width=4.5cm, + minimum height=54pt, below=3pt of h2] {#6}; + \end{tikzpicture}% +}}