TikZ从零开始(二)A Petri Net for Hagen

0、前言

本教程翻译自TikZ官方文档,在这里我们学习节点(node)的绘制

1、问题陈述

要画一个这样的图
在这里插入图片描述

2、创建环境

\documentclass{article} 
\usepackage{tikz}
\usetikzlibrary{arrows} 
\usetikzlibrary{decorations.pathmorphing} % 用于绘制图中心的蛇形线
\usetikzlibrary{backgrounds} % 用于绘制灰色背景
\usetikzlibrary{positioning} % 用于通过相对位置摆放节点
\usetikzlibrary{fit} % 可以容易地计算背景矩形的尺寸
\usetikzlibrary{petri}
\begin{document}
\begin{tikzpicture}
	\draw (0,0) -- (1,1);
\end{tikzpicture}
\end{document}

3、节点引论

原则上来说,除了蛇形线,我们已经可以用上节知识画出这幅图了。但是那种方法有严重的弊端,就是如果之后要添加位置(places,指Petri Net中的圆圈),会很麻烦,所有坐标都要重新计算;另外,代码可读性会差,因为全是一些坐标,从而遗漏了Petri Net的一些内在结构信息。

幸运的是,TikZ可以用node来改善这种情况。我们在前节已经提到了node,现在我们深入学习它。node创建时,我们要提供位置信息与形状参数,node里还可以放一些文本。此外,node还可以被命名,以便后续引用。

在这里我们用node来实现places和transition(指图中的小矩形),就是说,这儿我们不用上节学到的circle和rectangle,而是用node来画他们。

\path ( 0,2) node [shape=circle,draw] {}
( 0,1) node [shape=circle,draw] {}
( 0,0) node [shape=circle,draw] {}
( 1,1) node [shape=rectangle,draw] {}
(-1,1) node [shape=rectangle,draw] {};

这代码是怎么运作的呢?\path创建了一个路径,先move到(0,2),然后画一个节点,这个节点形状是圆圈,并且指定了draw就是要画出来,{}说明不添加任何文本;然后move到(0,1),再画节点……注意在move时没有进行任何操作,如果要把连线画出来,一方面在{}后面加上--表示连线,另一方面要把命令改成\path[draw]或者\draw。(\drawnode[draw]两个draw是不一样的,前者是绘制路径,后者是绘制节点)。
在这里插入图片描述

4、用At语句放置节点

上面这么略显繁琐,因为加入了移动操作。实际上可以通过at来简化这个过程。at直接指定了放置的节点,这样就回避了节点定位“node紧跟在哪个后面,就以哪个为参考系”(详见上一节)这个规则了。

\path node at ( 0,2) [shape=circle,draw] {}
node at ( 0,1) [shape=circle,draw] {}
node at ( 0,0) [shape=circle,draw] {}
node at ( 1,1) [shape=rectangle,draw] {}
node at (-1,1) [shape=rectangle,draw] {};

这样就省略了move操作了,但还是有个路径path,其实可以更简洁,path也省了:

\node at ( 0,2) [circle,draw] {};
\node at ( 0,1) [circle,draw] {};
\node at ( 0,0) [circle,draw] {};
\node at ( 1,1) [rectangle,draw] {};
\node at (-1,1) [rectangle,draw] {};

与之前代码的不同:每一个画节点操作后面都加了分号、shape参数名省略了(像color shape这种不会引起歧义的参数名就可以省略)、\path命令省了。
在这里插入图片描述
P.S. 我发现这种语法画出的node时自带轮廓的,如果不想画轮廓,只想标注文字,则可以用\draw (1,1) node {2}这种语法,参见此篇博客第20节。

5、使用样式(Styles)

下面我们把这个图上色,让它更好看。

\node at ( 0,2) [circle,draw=blue!50,fill=blue!20] {};
\node at ( 0,1) [circle,draw=blue!50,fill=blue!20] {};
\node at ( 0,0) [circle,draw=blue!50,fill=blue!20] {};
\node at ( 1,1) [rectangle,draw=black!50,fill=black!20] {};
\node at (-1,1) [rectangle,draw=black!50,fill=black!20] {};

在这里插入图片描述
图倒是变好看了,但代码段变丑了,我们在这儿想清楚地表达由三个places两个transitions,不要看那么多颜色规定的语句。为此,我们可以预设样式。

[place/.style={circle,draw=blue!50,fill=blue!20},
transition/.style={rectangle,draw=black!50,fill=black!20}]
\node at ( 0,2) [place] {};
\node at ( 0,1) [place] {};
\node at ( 0,0) [place] {};
\node at ( 1,1) [transition] {};
\node at (-1,1) [transition] {};

6、节点大小

如果文本为空,TikZ自动给其设置一些空白,如果要改变大小,我们可以把空白设置大一些,通过inner sep参数。

[inner sep=2mm,
place/.style={circle,draw=blue!50,fill=blue!20,thick},
transition/.style={rectangle,draw=black!50,fill=black!20,thick}]
\node at ( 0,2) [place] {};
\node at ( 0,1) [place] {};
\node at ( 0,0) [place] {};
\node at ( 1,1) [transition] {};
\node at (-1,1) [transition] {};

在这里插入图片描述
但一个更好的方式是设定最小尺寸minimum size,有了最小尺寸,如果添加一些文本,节点会自动变大,如文本为空,则它为最小尺寸。通过minimum heightminimum width我们还可以设置最小高度和最小宽度。

[place/.style={circle,draw=blue!50,fill=blue!20,inner sep=0pt,minimum size=6mm},
transition/.style={rectangle,draw=black!50,fill=black!20,inner sep=0pt,minimum size=4mm}]
\node at ( 0,2) [place] {};
\node at ( 0,1) [place] {};
\node at ( 0,0) [place] {};
\node at ( 1,1) [transition] {fffffffff};
\node at (-1,1) [transition] {};

在这里插入图片描述
文字有些太挤,可以通过调大transition的inner sep解决

[place/.style={circle,draw=blue!50,fill=blue!20,inner sep=0pt,minimum size=6mm},
transition/.style={rectangle,draw=black!50,fill=black!20,inner sep=1pt,minimum size=4mm}]
\node at ( 0,2) [place] {};
\node at ( 0,1) [place] {};
\node at ( 0,0) [place] {};
\node at ( 1,1) [transition] {fffffffff};
\node at (-1,1) [transition] {};

在这里插入图片描述

7、命名节点

下面要添加箭头,但似乎很麻烦,因为箭头肯定不是从图形的中心开始画的,而是从圆圈和矩形边缘开始画的,要是还得计算坐标的话(比如圆心向右平移一个半径是哪个点),那可就麻烦了。
好在PGF可以自动帮我们做这些事,不过那样的话,我们得先命名节点。

命名节点有两种方式,一种是通过name=的选项,另一种是把命名放在圆括号里。这里我们介绍第二种。

\node[place] (waiting 1) at ( 0,2) {};
\node[place] (critical 1) at ( 0,1) {};
\node[place] (semaphore) at ( 0,0) {};
\node[transition] (leave critical) at ( 1,1) {};
\node[transition] (enter critical) at (-1,1) {};

为了更易读,把选项放在了紧跟\node的位置。名字随便定,可以包括下划线、连字符,只要没有逗号、句点、圆括号、冒号和其他一些特殊字符就行。

8、使用相对位置放置节点

在连接节点之前, 我们还要解决一个问题,就是节点的摆放问题。at语句很有用,但没法体现节点的相互关联,比如critical 1要在waiting 1 的下方,有不少方法来实现,比较好的是下面的below选项,通过positioning库来实现。

\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical] {};
\node[transition] (leave critical) [right=of critical] {};
\node[transition] (enter critical) [left=of critical] {};

在这里插入图片描述

9、在节点旁添加标注

连接节点前,我们先给底部的semaphore places添加 s ≤ 3 s\le 3 s3的capacity。
有两种方式。

  • 在semaphore节点的北部添加新节点(任何情况下均适用)。
[place/.style={circle,draw=blue!50,fill=blue!20,inner sep=0pt,minimum size=6mm},
transition/.style={rectangle,draw=black!50,fill=black!20,inner sep=1pt,minimum size=4mm}]
\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical] {};
\node[transition] (leave critical) [right=of critical] {};
\node[transition] (enter critical) [left=of critical] {};

\node[red,above] at (semaphore.north) {$s\le 3$};

在这里插入图片描述

  • 使用标注选项,它是node的一个可选参数。
[place/.style={circle,draw=blue!50,fill=blue!20,inner sep=0pt,minimum size=6mm},
transition/.style={rectangle,draw=black!50,fill=black!20,inner sep=1pt,minimum size=4mm}]
\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical,
label=above:\textcolor{red}{$ s\le 3 $}] {};
\node[transition] (leave critical) [right=of critical] {};
\node[transition] (enter critical) [left=of critical] {};

除了above,我们还可以设置位置为low left、60(代表极坐标里的60度)。

\node[circle,draw,label=below:$ -90^\circ $,label=60:$ 60^\circ $] {my circle};

在这里插入图片描述
为了把标注颜色变成红色,可以通过label=above:\textcolor{red}{text}来实现,也可以通过label={[red]above:text}来实现,还可以预设样式every label/.style={red},以下为预设样式的完整代码

[place/.style={circle,draw=blue!50,fill=blue!20,inner sep=0pt,minimum size=6mm},
transition/.style={rectangle,draw=black!50,fill=black!20,inner sep=1pt,minimum size=4mm},
every label/.style={red}]
\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical,
label=above:$ s\le 3 $] {};
\node[transition] (leave critical) [right=of critical] {};
\node[transition] (enter critical) [left=of critical] {};

10、连接节点

终于要开始连接节点了,我们先从简单的开始,连接从enter critical到critical的线。为此,我们调用节点的锚(anchor),通过<node's name>.<anchor's name>来调用。

... % define styles
\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical,
label=above:$ s\le 3 $] {};
\node[transition] (leave critical) [right=of critical] {};
\node[transition] (enter critical) [left=of critical] {};

\draw[->](enter critical.east)--(critical.west);

现在,让我们画从waiting到enter critical的曲线。通过<a> .. controls <b> and <c> .. <d>画弧来实现。

\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical,
label=above:$ s\le 3 $] {};
\node[transition] (leave critical) [right=of critical] {};
\node[transition] (enter critical) [left=of critical] {};

\draw[->] (enter critical.east)--(critical.west);
\draw[->] (waiting.west)..controls +(left:5mm) and +(up:5mm)..(enter critical.north);

这边+(left:5mm)+(up:5mm)都描述相对位置(从而定位切线),表示waiting.west左边5mm处,enter critical.north上方5mm处。在这里插入图片描述

这个代码可以优化,因为TikZ可以智能地帮助我们判断锚会在节点的哪一侧,从而我们可以把anchor略去。

\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical,
label=above:$ s\le 3 $] {};
\node[transition] (leave critical) [right=of critical] {};
\node[transition] (enter critical) [left=of critical] {};

\draw[->](enter critical)--(critical);
\draw[->](waiting)..controls+(left:5mm)and+(up:5mm)..(enter critical);

在这里插入图片描述
似乎弧线有点像直线了,可能TikZ还没那么智能,所以我选择给弧线代码保留anchor,直线省略之。

\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical,
label=above:$ s\le 3 $] {};
\node[transition] (leave critical) [right=of critical] {};
\node[transition] (enter critical) [left=of critical] {};

\draw[->](enter critical)--(critical);
\draw[->](waiting.west)..controls+(left:5mm)and+(up:5mm)..(enter critical.north);

在这里插入图片描述
弧线代码还可以做进一步优化,我们可以用to语句来代替control-and语句,并且通过指定出节点的切线角度和入节点的切线角度(均指与极轴的夹角,你可以想象一个单位圆,逆时针方向为正角)。

\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical,
label=above:$ s\le 3 $] {};
\node[transition] (leave critical) [right=of critical] {};
\node[transition] (enter critical) [left=of critical] {};

\draw[->](enter critical)--(critical);
\draw[->](waiting.west) to[out=180,in=90] (enter critical.north);

在这里插入图片描述
这比control-and语句画出的更接近1/4圆弧,爱了。
还有一个bend rightbend left选项也可以用,但是我觉得它比较费解,我觉得right和left反了吧,明明是往左转,它却是bend right,明明是转90度,却是bend right=45。。

还有一种指定边的方式,就是edge语句,一般是紧跟在定义节点之后,随之给出它到其他节点的连线。
为了真正搞懂bend rightbend left参数,我用edge语句试了四个例子,我发现他画出来的弧线型状跟[->]还是[<-]没有任何关系

\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical,
label=above:$ s\le 3 $] {};
\node[transition] (leave critical) [right=of critical] {};
\node[transition] (enter critical) [left=of critical] {}
edge [->] (critical)
edge [bend left=45,red] (waiting)
edge [bend right=45,blue] (waiting)
edge [bend left=45,red] (semaphore)
edge [bend right=45,blue](semaphore);

在这里插入图片描述
我给出的解释是bend right还是bend left取决于从定义的节点和要达到的节点之间的连线段,从出节点的方向看,要是把线往左边掰就是bend left,反之就是bend right。但是对于to语句,我是真的没法解释,,,

\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical,
label=above:$ s\le 3 $] {};
\node[transition] (leave critical) [right=of critical] {}
edge [<-] (critical)
edge [<-,in=0,out=270] (semaphore)
edge [->,bend right=45] (waiting);
\node[transition] (enter critical) [left=of critical] {}
edge [->] (critical)
edge [<-,bend left=45] (waiting)
edge [->,bend right=45](semaphore);

注意:edge 语句里,[<-][->]不影响方向,只影响箭头(arrow tip)的位置,用in-out选项也不太正常,感觉要自己多试几个才能试出来,我不是很懂他的原理是什么了。
在这里插入图片描述
我们可以预设箭头连线的样式,预设掰弯角度为45度,通过prepost,注意,所有样式的代码要放在同一个方括号里。

[place/.style={circle,draw=blue!50,fill=blue!20,inner sep=0pt,minimum size=6mm},
transition/.style={rectangle,draw=black!50,fill=black!20,inner sep=1pt,minimum size=4mm},every label/.style={red},
bend angle=45,
pre/.style={<-,shorten <=1pt,>={Stealth[round]},semithick},
post/.style={->,shorten >=1pt,>={Stealth[round]},semithick}]
\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical,
label=above:$ s\le 3 $] {};
\node[transition] (leave critical) [right=of critical] {}
edge [pre] (critical)
edge [pre,bend left] (semaphore)
edge [post,bend right] (waiting);
\node[transition] (enter critical) [left=of critical] {}
edge [post] (critical)
edge [pre,bend left] (waiting)
edge [post,bend right](semaphore);

还有一种比较好看的连线方法,不完全是弧线,是直线与弧线的结合。
\draw[post,rounded corners=5mm] (e1)|-(s);
在这里插入图片描述
还记得|-记号吗?我们在第一节指定坐标里学过,这里表示的是我先竖直画一条线,再水平画一条线。如果我不设置rounded corners就是方角,调大圆角半径可以使得转弯更smooth。当然了,也可以用-|那就是先水平画线再竖直画线。

11、给连线添加标注

在这里插入图片描述
在9种我们给节点旁边添加了标注,这里我们学习如何给连线添加标注。
当然我们可以手动添加节点,但是通过auto参数我们可以自动地把文本添加在连线旁边的位置而不是连线上;通过swap我们进行了镜像操作,因为默认是在内侧添加的。

\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical,
label=above:$ s\le 3 $] {};
\node[transition] (leave critical) [right=of critical] {}
edge [pre] (critical)
edge [pre,bend left] (semaphore)
edge [post,bend right] node[auto,swap]{2} (waiting);
\node[transition] (enter critical) [left=of critical] {}
edge [post] (critical)
edge [pre,bend left] (waiting)
edge [post,bend right](semaphore);

在这里插入图片描述

12、添加蛇形线与多行文本

为此,我们需要在可选参数里面添加decoratedecoration=snake。这需要导入TikZ的decorations.pathmorphing库。
在这里插入图片描述这么画可不对劲,因为箭头看起来很糟糕,其实我们可以设定更多的参数。

\draw[->,decorate,decoration={snake,amplitude=.4mm,segment length=10mm,post length=1mm}] (0,0)--(2,0);
\draw[->,decorate,decoration={snake,amplitude=1mm,segment length=4mm,post length=1mm}] (0,-1)--(2,-1);
\draw[->,decorate,decoration={snake,amplitude=1mm,segment length=10mm,post length=1mm}] (0,-2)--(2,-2);
\draw[->,decorate,decoration={snake,amplitude=.4mm,segment length=10mm,post length=5mm}] (3,0)--(5,0);
\draw[->,decorate,decoration={snake,amplitude=1mm,segment length=4mm,post length=5mm}] (3,-1)--(5,-1);
\draw[->,decorate,decoration={snake,amplitude=1mm,segment length=10mm,post length=5mm}] (3,-2)--(5,-2);

amplitue指定振幅,segment length类似于周期长度,post length大概是arrow tip一端的直线段长度
在这里插入图片描述
下面是添加多行文本,有两种方法,在这两种方法中,我们都需要首先指定align=center(或者left right都行,但一定要指定对齐方式,否则无法换行)

  • 然后用\\强制换行。
\draw[->,decorate,decoration={snake,amplitude=.4mm,segment length=2mm,post length=1mm}](0,0)--(3,0)node[above,midway,align=center]
{replacement of\\the capacity\\by two places};

因为我们已经制定了位置是midway,所以node放在语句的哪个位置无所谓。
在这里插入图片描述

  • 通过指定文本宽度来实现 TeX \TeX TEX自动换行。
\draw[->,decorate,decoration={snake,amplitude=.4mm,segment length=2mm,post length=1mm}](0,0)--(3,0)node[above,midway,align=center,text width=3cm]
{replacement of the \textcolor{red}{capacity} by \textcolor{red}{two places}};

在这里插入图片描述

13、使用图层:矩形背景

这有点棘手,我们必须在画完Petri Net之后再画矩形,因为画好之前我们并不知道我们画的Petri Net有多大。这可以通过TikZ的background库来实现。加载background库之后,我们就可以把图的一部分放在设置了on background layer选项的scope里。当tikzpicture环境结束时,图层依次往上叠放,首先叠放的就是背景层。

另一个棘手的问题是,如果确定背景矩形的尺寸呢?当然可以去手工计算横纵坐标,但更好的方式是让TikZ来算出一个适合(fit)所有节点的矩形。这就要导入fit库了,它定义了fit选项,当给到一个节点的时候,它会重新调整节点的尺寸、移动节点以使得所有节点都被背景所覆盖。

\node[place] (waiting) {};
\node[place] (critical) [below=of waiting] {};
\node[place] (semaphore) [below=of critical,
label=above:$ s\le 3 $] {};
\node[transition] (leave critical) [right=of critical] {}
edge [pre] (critical)
edge [pre,bend left] (semaphore)
edge [post,bend right] node[auto,swap]{2} (waiting);
\node[transition] (enter critical) [left=of critical] {}
edge [post] (critical)
edge [pre,bend left] (waiting)
edge [post,bend right](semaphore);

\begin{scope}[on background layer]
	\node [fill=black!30,fit=(waiting)(critical)(semaphore)(enter critical)(leave critical)] {};
\end{scope}

注意:node形状默认为矩形,所以可以不指定rectanglefit各节点放在用圆括号内,圆括号之间不能有除了空格以外的任何分隔符,记得在最后加上表示空文本的{}
在这里插入图片描述

14、完整代码

\documentclass{article} 
\usepackage{tikz}
\usetikzlibrary{arrows.meta} 
\usetikzlibrary{decorations.pathmorphing} % 用于绘制图中心的蛇形线
\usetikzlibrary{backgrounds} % 用于绘制灰色背景
\usetikzlibrary{positioning} % 用于方便的确定节点位置
\usetikzlibrary{fit} % 可以容易地计算矩形尺寸
\usetikzlibrary{petri}
\begin{document}
	\begin{tikzpicture}
		[
		node distance=1.3cm,on grid,>={Stealth[round]},bend angle=45,auto,
		every place/.style={minimum size=6mm,thick,draw=blue!75,fill=blue!20},
		every transition/.style={thick,draw=black!75,fill=black!20},
		red place/.style= {place,draw=red!75,fill=red!20},
		every label/.style={red}
		]
		\node [place,tokens=1] (w1) {};
		\node [place] (c1) [below=of w1] {};
		\node [place] (s) [below=of c1,label=above:$s\le 3$] {};
		\node [place] (c2) [below=of s] {};
		\node [place,tokens=1] (w2) [below=of c2] {};
		\node [transition] (e1) [left=of c1] {}
		edge [pre,bend left] (w1)
		edge [post,bend right] (s)
		edge [post] (c1);
		\node [transition] (e2) [left=of c2] {}
		edge [pre,bend right] (w2)
		edge [post,bend left] (s)
		edge [post] (c2);
		\node [transition] (l1) [right=of c1] {}
		edge [pre] (c1)
		edge [pre,bend left] (s)
		edge [post,bend right] node[swap] {2} (w1);
		\node [transition] (l2) [right=of c2] {}
		edge [pre] (c2)
		edge [pre,bend right] (s)
		edge [post,bend left] node {2} (w2);
		
		\begin{scope}[xshift=6cm]
			\node [place,tokens=1] (w1') {};
			\node [place] (c1') [below=of w1'] {};
			\node [red place] (s1') [below=of c1',xshift=-5mm]
			[label=left:$s$] {};
			\node [red place,tokens=3] (s2') [below=of c1',xshift=5mm]
			[label=right:$\bar s$] {};
			\node [place] (c2') [below=of s1',xshift=5mm] {};
			\node [place,tokens=1] (w2') [below=of c2'] {};
			\node [transition] (e1') [left=of c1'] {}
			edge [pre,bend left] (w1')
			edge [post] (s1')
			edge [pre] (s2')
			edge [post] (c1');
			\node [transition] (e2') [left=of c2'] {}
			edge [pre,bend right] (w2')
			edge [post] (s1')
			edge [pre] (s2')
			edge [post] (c2');
			\node [transition] (l1') [right=of c1'] {}
			edge [pre] (c1')
			edge [pre] (s1')
			edge [post] (s2')
			edge [post,bend right] node[swap] {2} (w1');
			\node [transition] (l2') [right=of c2'] {}
			edge [pre] (c2')
			edge [pre] (s1')
			edge [post] (s2')
			edge [post,bend left] node {2} (w2');
		\end{scope}
		\begin{scope}[on background layer]
			\node (r1) [fill=black!10,rounded corners,fit=(w1)(w2)(e1)(e2)(l1)(l2)] {};
			\node (r2) [fill=black!10,rounded corners,fit=(w1')(w2')(e1')(e2')(l1')(l2')] {};
		\end{scope}
		\draw [->,shorten >=1mm,
		-to, % 用于加载下面的pre选项
		thick,decorate,
		decoration={snake,amplitude=.4mm,segment length=2mm,
			pre=moveto,
			pre length=1mm, %箭头始端的直线段长度
			post length=2mm}]
		(r1) -- (r2) node [above=1mm,midway,text width=3cm,align=center]
		{replacement of the \textcolor{red}{capacity} by \textcolor{red}{two places}};
	\end{tikzpicture}
\end{document}

在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值