OpenCascade BRep Format Description
摘要Abstract:本文結合OpenCascade的BRep格式描述文檔和源程序,對BRep格式進行分析,詳細說明BRep的數據組織形式。結合源程序,可以對OpenCascade中Modeling Data模塊中的模型數據結構進行理解。?
關鍵字Key Words:OpenCascade, BRep Format, ModelingData?
??
一、引言 Introduction
OpenCascade中的BRep格式主要用來存儲3D模型,也可用來存儲由下列元素組成的模型:vertices, edges, wires, faces, shells, solids, compsolids, compounds, edge triangulations, face triangulations, polylines on triangulations, space location and orientation.?
本格式的目的就是為了便于理解,也使用了類似BNF的定義方式。以下章節都是按下面的格式組織的:?
l 該部分的示例;?
l 該部分的類BNF定義;?
l 該部分的詳細說明;?
l 該部分的源程序片段。?
??
二、通用結構 Format Common Structure
BRep格式文件的讀寫采用了ASCII的編碼方式,該格式的數據都是文本形式存儲。?
BRep格式使用了下面的BNF術語:?
1) <\n>:換行;?
2) <_\n>:?
3) <_>:空格;?
4) <flag>:標志位:0和1;?
5) <int>:整數,范圍-231到231-1;?
6) <real>:實數,范圍-1.7976931348623159X10308到1.7976931348623158X10308;?
7) <2D point>:二維點,兩個實數;?
8) <3D point>:三維點,三個實數;?
9) <2D direction>:二維方向矢量,兩個實數,平方和為1,即為單位方向矢量;?
10) <3D direction>:三維方向矢量,三個實數,平方和為1,即為單位方向矢量;?
11) <+>?
BRep格式包含以下部分:?
1) <content type>?
2) <version>?
3) <locations>?
4) <geometry>?
5) <shapes>?
<content type>部分:?
<content type>也可以有其它的值。?
<version>部分:?
不同版本之間的區別將會在本文檔中說明。?
??
三、<locations>部分 Section <locations>
示例:?
BNF 定義:?
詳細說明:?
<location data 1>定義了3X4的矩陣Q,描述了三維空間的線性變換,并滿足如下約定:?
矩陣Q是線性變換矩陣,它可以通過矩陣乘法將一個點(x, y, z)變換成另外一點(u, v, w):?
Q也可能是以下基本變換矩陣的組合:?
1) 平移變換矩陣:?
2) 繞任意軸旋轉的變換矩陣,軸的方向為D(Dx, Dy, Dz),旋轉角度ψ:?
3) 縮放變換矩陣:?
4) 中心對稱變換矩陣:?
5) 軸對稱變換矩陣:?
6) 平面對稱變換矩陣:?
<location data 2>解釋為組合變換的冪。<location data 2>是整數對li, pi的序列。這個序列將被解釋為:?
Lli是<location record>部分的變換矩陣。?
讀取<locations>部分的類為TopTools_LocationSet,程序代碼如下所示:
1 // ======================================================================= 2 // function : Read 3 // purpose : 4 // ======================================================================= 5 void TopTools_LocationSet::Read(Standard_IStream& IS) 6 { 7 myMap.Clear(); 8 9 char buffer[ 255 ]; 10 Standard_Integer l1,p; 11 12 IS >> buffer; 13 if (strcmp(buffer, " Locations " )) { 14 cout << " Not a location table " << endl; 15 return ; 16 } 17 18 Standard_Integer i, nbLoc; 19 IS >> nbLoc; 20 21 TopLoc_Location L; 22 gp_Trsf T; 23 24 // OCC19559 25 Message_ProgressSentry PS(GetProgress(), " Locations " , 0 , nbLoc, 1 ); 26 for (i = 1 ; i <= nbLoc&& PS.More(); i++ , PS.Next()) { 27 if ( ! GetProgress().IsNull() ) 28 GetProgress()-> Show(); 29 30 Standard_Integer typLoc; 31 IS >> typLoc; 32 33 if (typLoc == 1 ) { 34 ReadTrsf(T,IS); 35 L = T; 36 } 37 38 else if (typLoc == 2 ) { 39 L = TopLoc_Location(); 40 IS >> l1; 41 while (l1 != 0 ) { 42 IS >> p; 43 TopLoc_Location L1 = myMap(l1); 44 L = L1.Powered(p) * L; 45 IS >> l1; 46 } 47 } 48 49 if (! L.IsIdentity()) myMap.Add(L); 50 } 51 }
雖然代碼風格不好,縮進、括號什么的都不工整,看起來很吃力,但是結合源程序,對上面的詳細說明的理解還是很有幫助的。?
其中變量nbLoc是<location record count>的值,成員變量myMap是TopLoc_Location的一個map。當是<location record 1>時把<location data 1>都放到TopLoc_Location的map中。當是<location record 2>時將li的變換矩陣TopLoc_Location乘pi次方。<flag>0表示<location data 2>的結束。?
??
四、<geometry>部分
<geometry>包含以下子部分:?
1.<2D curves>?
2.<3D curves>?
3.<3D polygons>?
4.<polygons on triangulations>?
5.<surfaces>?
6.<triangulations>?
讀取<geometry>部分的類為BRepTools_ShapeSet,程序代碼如下所示:
1 // ======================================================================= 2 // function : ReadGeometry 3 // purpose : 4 // ======================================================================= 5 void BRepTools_ShapeSet::ReadGeometry(Standard_IStream& IS) 6 { 7 // OCC19559 8 myCurves2d.SetProgress(GetProgress()); 9 myCurves.SetProgress(GetProgress()); 10 mySurfaces.SetProgress(GetProgress()); 11 12 if ( ! GetProgress().IsNull()) { 13 if ( GetProgress()->UserBreak() ) return ; 14 GetProgress()->NewScope ( 15 , " 2D Curves " ); 15 } 16 myCurves2d.Read(IS); 17 18 if ( ! GetProgress().IsNull()) { 19 if ( GetProgress()->UserBreak() ) return ; 20 GetProgress()-> EndScope(); 21 GetProgress()-> Show(); 22 23 GetProgress()->NewScope ( 15 , " 3D Curves " ); 24 } 25 myCurves.Read(IS); 26 27 if ( ! GetProgress().IsNull()) { 28 if ( GetProgress()->UserBreak() ) return ; 29 GetProgress()-> EndScope(); 30 GetProgress()-> Show(); 31 32 GetProgress()->NewScope ( 10 , " 3D Polygons " ); 33 } 34 ReadPolygon3D(IS); 35 if ( ! GetProgress().IsNull() ) { 36 if ( GetProgress()->UserBreak() ) return ; 37 GetProgress()-> EndScope(); 38 GetProgress()-> Show(); 39 40 GetProgress()->NewScope ( 10 , " Polygons On Triangulation " ); 41 } 42 ReadPolygonOnTriangulation(IS); 43 if ( ! GetProgress().IsNull()) { 44 if ( GetProgress()->UserBreak() ) return ; 45 GetProgress()-> EndScope(); 46 GetProgress()-> Show(); 47 48 GetProgress()->NewScope ( 10 , " Surfaces " ); 49 } 50 mySurfaces.Read(IS); 51 if ( ! GetProgress().IsNull() ) { 52 if ( GetProgress()->UserBreak() ) return ; 53 GetProgress()-> EndScope(); 54 GetProgress()-> Show(); 55 56 GetProgress()->NewScope ( 15 , " Triangulations " ); 57 } 58 ReadTriangulation(IS); 59 if ( ! GetProgress().IsNull()) { 60 if ( GetProgress()->UserBreak() ) return ; 61 GetProgress()-> EndScope(); 62 GetProgress()-> Show(); 63 } 64 }
?
4.1 子部分<3D curves>
示例:?
BNF定義:?
詳細說明:?
由Curves開始,后面是曲線的數量,再下面是每條曲線的具體數據。?
讀取<curves>部分的類為GeomTools_CurveSet,程序代碼如下所示:
1 #define LINE 1 2 #define CIRCLE 2 3 #define ELLIPSE 3 4 #define PARABOLA 4 5 #define HYPERBOLA 5 6 #define BEZIER 6 7 #define BSPLINE 7 8 #define TRIMMED 8 9 #define OFFSET 9 10 // ======================================================================= 11 // function : ReadCurve 12 // purpose : 13 // ======================================================================= 14 Standard_IStream& GeomTools_CurveSet::ReadCurve(Standard_IStream& IS, 15 Handle(Geom_Curve)& C) 16 { 17 Standard_Integer ctype; 18 19 try { 20 OCC_CATCH_SIGNALS 21 IS >> ctype; 22 switch (ctype) { 23 24 case LINE : 25 { 26 Handle(Geom_Line) CC; 27 IS >> CC; 28 C = CC; 29 } 30 break ; 31 32 case CIRCLE : 33 { 34 Handle(Geom_Circle) CC; 35 IS >> CC; 36 C = CC; 37 } 38 break ; 39 40 case ELLIPSE : 41 { 42 Handle(Geom_Ellipse) CC; 43 IS >> CC; 44 C = CC; 45 } 46 break ; 47 48 case PARABOLA : 49 { 50 Handle(Geom_Parabola) CC; 51 IS >> CC; 52 C = CC; 53 } 54 break ; 55 56 case HYPERBOLA : 57 { 58 Handle(Geom_Hyperbola) CC; 59 IS >> CC; 60 C = CC; 61 } 62 break ; 63 64 case BEZIER : 65 { 66 Handle(Geom_BezierCurve) CC; 67 IS >> CC; 68 C = CC; 69 } 70 break ; 71 72 case BSPLINE : 73 { 74 Handle(Geom_BSplineCurve) CC; 75 IS >> CC; 76 C = CC; 77 } 78 break ; 79 80 case TRIMMED : 81 { 82 Handle(Geom_TrimmedCurve) CC; 83 IS >> CC; 84 C = CC; 85 } 86 break ; 87 88 case OFFSET : 89 { 90 Handle(Geom_OffsetCurve) CC; 91 IS >> CC; 92 C = CC; 93 } 94 break ; 95 96 default : 97 { 98 Handle(Geom_Curve) CC; 99 GeomTools::GetUndefinedTypeHandler()-> ReadCurve(ctype,IS,CC); 100 C = CC; 101 } 102 } 103 } 104 catch (Standard_Failure) { 105 #ifdef DEB 106 Handle(Standard_Failure) anExc = Standard_Failure::Caught(); 107 cout << " EXCEPTION in GeomTools_CurveSet::ReadCurve(..)!!! " << endl; 108 cout << anExc << endl; 109 #endif 110 C = NULL; 111 } 112 return IS; 113 }
?
因為重載了操作符>>,使不同的類調用了不同的處理函數。?
因為讀取點和方向用得很頻繁,所以將讀取點和方向的函數程序先列出如下所示:
1 // ======================================================================= 2 // function : ReadPnt 3 // purpose : 4 // ======================================================================= 5 static Standard_IStream& operator >>(Standard_IStream& IS, gp_Pnt& P) 6 { 7 Standard_Real X= 0 .,Y= 0 .,Z= 0 .; 8 IS >> X >> Y >> Z; 9 P.SetCoord(X,Y,Z); 10 return IS; 11 } 12 13 // ======================================================================= 14 // function : ReadDir 15 // purpose : 16 // ======================================================================= 17 static Standard_IStream& operator >>(Standard_IStream& IS, gp_Dir& D) 18 { 19 Standard_Real X= 0 .,Y= 0 .,Z= 0 .; 20 IS >> X >> Y >> Z; 21 D.SetCoord(X,Y,Z); 22 return IS; 23 }
?
4.1.1 <3D curve record 1>-Line
示例:?
BNF定義:?
詳細說明:?
<3D curve record 1>定義了直線。直線數據由一個三維點P和一個三維方向矢量D組成。通過點P且方向為D的直線由下面的參數方程來定義:?
示例數據表示的直線為通過點P(1,0,3),方向D(0,1,0),得到的參數方程為:?
讀取直線部分的程序代碼如下所示:
1 // ======================================================================= 2 // function : ReadCurve 3 // purpose : 4 // ======================================================================= 5 static Standard_IStream& operator >>(Standard_IStream& IS, 6 Handle(Geom_Line)& L) 7 { 8 gp_Pnt P( 0 ., 0 ., 0 .); 9 gp_Dir AX( 1 ., 0 ., 0 .); 10 IS >> P >> AX; 11 L = new Geom_Line(P,AX); 12 return IS; 13 }
?
4.1.2 <3D curve record 2>-Circle
示例:?
BNF定義:?
詳細說明:?
<3D curve record 2>定義了圓。圓的數據包含一個三維點P,一個正交坐標系的三個軸的方向N,Dx,Dy,還有一個非負的實數r。其中點P為圓心坐標,圓位于平面的法向量為N的平面上,圓的半徑為r。圓的參數方程如下所示:?
示例數據表示的圓為:圓心P(1,2,3),位于平面的法向量N(0,0,1),圓的方向Dx=(1,0,-0),Dy=(-0,1,0),半徑r=4,其參數方向為:?
讀取圓部分的程序代碼如下所示:
1 // ======================================================================= 2 // function : ReadCurve 3 // purpose : 4 // ======================================================================= 5 static Standard_IStream& operator >>(Standard_IStream& IS, 6 Handle(Geom_Circle)& C) 7 { 8 gp_Pnt P( 0 ., 0 ., 0 .); 9 gp_Dir A( 1 ., 0 ., 0 .),AX( 1 ., 0 ., 0 .),AY( 1 ., 0 ., 0 .); 10 Standard_Real R= 0 .; 11 IS >> P >> A >> AX >> AY >> R; 12 C = new Geom_Circle(gp_Ax2(P,A,AX),R); 13 return IS; 14 }
?
4.1.3 <3D curve record 3>-Ellipse
示例:?
BNF定義:?
詳細說明:?
<3D curve record 3>定義了橢圓。橢圓的數據包含三維點P,三維正交坐標系N、Dmaj、Dmin和兩個非負實數rmaj和rmin,且rmin<=rmaj。橢圓位于中心點P,法向量為N的平面上,且長軸、短軸的方向分別為Dmaj, Dmin,長軸、短軸上的半徑分別為rmaj, rmin。橢圓的參數方程定義如下所示:?
示例數據表示的橢圓的中心點P=(1,2,3),平面的法向量N=(0,0,1),長軸方向Dmaj=(1,0,-0),短軸方向Dmin=(-0,1,0),長軸半徑為5,短軸半徑為4,?
讀取橢圓部分的程序代碼如下所示:
1 // ======================================================================= 2 // function : ReadCurve 3 // purpose : 4 // ======================================================================= 5 static Standard_IStream& operator >>(Standard_IStream& IS, 6 Handle(Geom_Ellipse)& E) 7 { 8 gp_Pnt P( 0 ., 0 ., 0 .); 9 gp_Dir A( 1 ., 0 ., 0 .),AX( 1 ., 0 ., 0 .),AY( 1 ., 0 ., 0 .); 10 Standard_Real R1= 0 .,R2= 0 .; 11 IS >> P >> A >> AX >> AY >> R1 >> R2; 12 E = new Geom_Ellipse(gp_Ax2(P,A,AX),R1,R2); 13 return IS; 14 }
?
?
4.1.4 <3D curve record 4>-Parabola
示例:?
BNF定義:?
詳細說明:?
<3D curve record 4>定義了拋物線。拋物線數據包含三維點P,三維正交坐標系坐標軸方向N,Dx,Dy和一個非負的實數f。拋物線通過點P,且位于法向量為N的平面上,焦點長度為f,其參數方程如下所示:?
示例數據表示的拋物線過點P=(1,2,3),位于平面的法向N=(0,0,1),拋物線的另兩個軸方向Dx=(1,0,-0),Dy=(-0,1,0),焦點長度f=16。參數方程為:?
讀取拋物線部分的程序代碼如下所示:
1 // ======================================================================= 2 // function : ReadCurve 3 // purpose : 4 // ======================================================================= 5 static Standard_IStream& operator >>(Standard_IStream& IS, 6 Handle(Geom_Parabola)& C) 7 { 8 gp_Pnt P( 0 ., 0 ., 0 .); 9 gp_Dir A( 1 ., 0 ., 0 .),AX( 1 ., 0 ., 0 .),AY( 1 ., 0 ., 0 .); 10 Standard_Real R1= 0 .; 11 IS >> P >> A >> AX >> AY >> R1; 12 C = new Geom_Parabola(gp_Ax2(P,A,AX),R1); 13 return IS; 14 }
?
?
4.1.5 <3D curve record 5>-Hyperbola
示例:?
BNF定義:?
詳細說明:?
<3D curve record 5>定義了雙曲線。雙曲線定義數據有三維點P,三維正交坐標系坐標軸方向為N,Dx,Dy和兩個非負實數Kx,Ky。雙曲線過P點且法向量為N的平面上,其參數方程如下所示:?
示例數據表示的雙曲線過點P=(1,2,3)且位于的平面的法向N=(0,0,1),其它的數據Dx=(1,0,-0),Dy=(-0,1,0),Kx=5和Ky=4。其參數方程為:?
讀取雙曲線部分的程序代碼如下所示:
1 // ======================================================================= 2 // function : ReadCurve 3 // purpose : 4 // ======================================================================= 5 static Standard_IStream& operator >>(Standard_IStream& IS, 6 Handle(Geom_Hyperbola)& H) 7 { 8 gp_Pnt P( 0 ., 0 ., 0 .); 9 gp_Dir A( 1 ., 0 ., 0 .),AX( 1 ., 0 ., 0 .),AY( 1 ., 0 ., 0 .); 10 Standard_Real R1= 0 .,R2= 0 .; 11 IS >> P >> A >> AX >> AY >> R1 >> R2; 12 H = new Geom_Hyperbola(gp_Ax2(P,A,AX),R1,R2); 13 return IS; 14 }
?
?
4.1.6 <3D curve record 6>-Bezier Curve
示例:?
BNF定義:?
詳細說明:?
<3D curve record 6>定義了Bezier曲線。Bezier曲線數據包含有理標志r,曲線的次數m(degree m <= 25查看源代碼可知OpenCascade可處理的B樣條次數不超過25)和帶權的控制點(weight poles)。當有理標志位r=0時,weight poles就是m+1個三維點:B0,B1...Bn;當有理標志位r=1時,weight poles就是帶權的控制點B0 h0... Bm hm。Bi是三維點,hi是[0,m]正實數,即權因子。當有理標志位r=0時,即不是有理Bezier曲線時,hi=1。Bezier曲線參數方程如下所示:?
示例數據表示的Bezier曲線是有理Bezier曲線,因其有理標志位r=1,次數m=2,帶權控制點及權因子分別為:B0=(0,1,0),h0=4,B1=(1,-2,0),h1=5,B2=(2,3,0),h2=6。Bezier曲線的參數方程如下所示:?
讀取Bezier曲線部分的程序代碼如下所示:
1 // ======================================================================= 2 // function : ReadCurve 3 // purpose : 4 // ======================================================================= 5 static Standard_IStream& operator >>(Standard_IStream& IS, 6 Handle(Geom_BezierCurve)& B) 7 { 8 Standard_Boolean rational= Standard_False; 9 IS >> rational; 10 11 // poles and weights 12 Standard_Integer i= 0 ,degree= 0 ; 13 IS >> degree; 14 15 TColgp_Array1OfPnt poles( 1 ,degree+ 1 ); 16 TColStd_Array1OfReal weights( 1 ,degree+ 1 ); 17 18 for (i = 1 ; i <= degree+ 1 ; i++ ) { 19 IS >> poles(i); 20 if (rational) 21 IS >> weights(i); 22 } 23 24 if (rational) 25 B = new Geom_BezierCurve(poles,weights); 26 else 27 B = new Geom_BezierCurve(poles); 28 29 return IS; 30 }
?
4.1.7 <3D curve record 7>-B-Spline curve
示例:?
BNF定義:?
詳細說明:?
<3D curve record 7>定義了B-Spline曲線。B-Spline曲線包含了有理標志位r,曲線次數m<=25,控制點數n>=2,重節點數k,帶權控制點wieght poles和重節點multiplicity knots。?
當有理標志位r=0時,是非有理B樣條曲線,weight poles有n個三維點B1,...,Bn;當有理標志位r=1時,是有理B樣條曲線,weight poles是n個帶權控制點對:B1, h1, .... Bn, hn。這里Bi表示一個三維點,hi表示一個[0,1]正實數。當有理標志位r=0時,hi=1。?
重節點有k對u1, q1, ... uk, qk。這里ui是重復度為qi>=1的節點。?
B-Spline曲線的參數方程如下所示:?
其中Ni,j有如下的遞歸定義:?
示例數據表示的B樣條曲線為:有理標志位r=1,次數m=1,控制點數n=3,重節點數k=5,帶權控制點:B1=(0,1,0),h1=4,B2=(1,-2,0),h2=5,B3=(2,3,0),h3=6;重節點u1=0,q1=1,u2=0.25,q2=1,u3=0.5,q3=1,u4=0.75,q4=1,u5=1,q5=1。B-Spline曲線的參數方程如下所示:?
讀取B-Spline曲線部分的程序代碼如下所示:?
?
1 // ======================================================================= 2 // function : ReadCurve 3 // purpose : 4 // ======================================================================= 5 static Standard_IStream& operator >>(Standard_IStream& IS, 6 Handle(Geom_BSplineCurve)& B) 7 { 8 9 Standard_Boolean rational=Standard_False,periodic= Standard_False; 10 IS >> rational >> periodic; 11 12 // poles and weights 13 Standard_Integer i= 0 ,degree= 0 ,nbpoles= 0 ,nbknots= 0 ; 14 IS >> degree >> nbpoles >> nbknots; 15 16 TColgp_Array1OfPnt poles( 1 ,nbpoles); 17 TColStd_Array1OfReal weights( 1 ,nbpoles); 18 19 for (i = 1 ; i <= nbpoles; i++ ) { 20 IS >> poles(i); 21 if (rational) 22 IS >> weights(i); 23 } 24 25 TColStd_Array1OfReal knots( 1 ,nbknots); 26 TColStd_Array1OfInteger mults( 1 ,nbknots); 27 28 for (i = 1 ; i <= nbknots; i++ ) { 29 IS >> knots(i) >> mults(i); 30 } 31 32 if (rational) 33 B = new Geom_BSplineCurve(poles,weights,knots,mults,degree,periodic); 34 else 35 B = new Geom_BSplineCurve(poles,knots,mults,degree,periodic); 36 37 return IS; 38 }
?
4.1.8 <3D curve record 8>-Trimmed Curve
示例:?
BNF定義:?
詳細說明:?
<3D curve record 8>定義了裁剪曲線(trimmed curve)。裁剪曲線數據包含:兩個實數umin,umax和<3D curve record>,且umin<umax。裁剪曲線是將<3D curve record>描述的曲線B限制在[umin,umax]。裁剪曲線的參數方程如下所示:?
示例數據表示的裁剪曲線為:umin=-4,umax=5,曲線B(u)=(1,2,3)+u(1,0,0)。裁剪曲線的參數方程如下所示:?
讀取裁剪曲線部分的程序代碼如下所示:?
?
1 // ======================================================================= 2 // function : ReadCurve 3 // purpose : 4 // ======================================================================= 5 6 static Standard_IStream& operator >>(Standard_IStream& IS, 7 Handle(Geom_TrimmedCurve)& C) 8 { 9 Standard_Real p1= 0 .,p2= 0 .; 10 IS >> p1 >> p2; 11 Handle(Geom_Curve) BC; 12 GeomTools_CurveSet::ReadCurve(IS,BC); 13 C = new Geom_TrimmedCurve(BC,p1,p2); 14 return IS; 15 }
?
4.1.9 <3D curve record 9>-Offset Curve
示例:?
BNF定義:?
詳細說明:?
<3D curve record 9>定義了偏移曲線(offset curve)。偏移曲線的數據包含偏移距離d,偏移方向D和曲線數據<3D curve record>。偏移曲線是將<3D curve record>描述的曲線沿矢量
偏移距離d后的結果。偏移曲線的參數方程如下所示:?
示例數據表示的偏移曲線為偏移距離d=2,方向D=(0,1,0),基曲線B(u)=(1,2,3)+u(1,0,0),其參數方程如下所示:?
讀取偏移曲線部分程序代碼如下所示:
1 // ======================================================================= 2 // function : ReadCurve 3 // purpose : 4 // ======================================================================= 5 static Standard_IStream& operator >>(Standard_IStream& IS, 6 Handle(Geom_OffsetCurve)& C) 7 { 8 Standard_Real p= 0 .; 9 IS >> p; 10 gp_Dir D( 1 ., 0 ., 0 .); 11 IS >> D; 12 Handle(Geom_Curve) BC; 13 GeomTools_CurveSet::ReadCurve(IS,BC); 14 C = new Geom_OffsetCurve(BC,p,D); 15 return IS; 16 }
?
未完,待續……
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
