Quantcast
Channel: C++博客-eryar
Viewing all 519 articles
Browse latest View live

解析几何求交之圆与二次曲面

$
0
0

解析几何求交之圆与二次曲面

eryar@163.com

 

Abstract. OpenCASCADE provides the analytic intersection between a conic and a quadric in the package IntAna.

Key Words. Analytic geometry, intersection, coninc, quadric

1. Introduction

OpenCASCADE中的包IntAna提供了解析几何曲线(二次曲线)与解析曲面(二次曲面)求交、解析曲面与解析曲面求交的功能。其中IntAna分别是Intersection Analytic的前三个字母缩写,表示解析几何求交。

我们学过了《线性代数》中的二次型,其中二次型在解析几何中的一个应用就是二次曲线和二次曲面方程的化简及其性质的分析。

本文主要结合源码分析圆与二次曲面求交的实现,其他二次曲线与二次曲面求交的可以依此类推。

2. Quadratic form

在解析几何中,为了便于研究二次曲线

的几何性质,我们可以选择适当的坐标旋转变换

把方程化成标准形式:

根据标准形式中的系数来分析这个二次曲线是什么曲线,即圆、椭圆、抛物线、双曲线。

OpenCASCADE中解析几何的二次曲面有以下几种类型:

l gp_Pln:平面可看作二次曲面的特例

l gp_Sphere:解析球面

l gp_Cylinder:解析柱面

l gp_Cone:解析锥面

这些解析曲面都可以统一使用二次多项式来表示,即这个二次多项式的系数确定了一个二次曲面。OpenCASCADE中相应的类是IntAna_Quaric

3. Math Trigonometric Function Roots

OpenCASCADE中类IntAna_IntConicQuad中提供了二次曲线与二次曲面求交功能,其中计算圆与二次曲面相交的函数是:

  //! Creates the intersection between a circle and a quadric.

  Standard_EXPORT IntAna_IntConicQuad(const gp_Circ& C, const IntAna_Quadric& Q);

其实现原码中注释如下:

 

根据其注释,可知其计算过程如下:将圆的方程在其局部坐标系中用参数形式表示,

将二次曲面方程的系数也变换到圆的坐标系中,因为圆的参数方程中为0,所以将圆的参数方程代入二次曲面的方程后,和z相关的项的系数都可以简化了。

得到一个三角函数的方程后,这时就要引入基础模块中的数学工具集来解决问题了。

math_TrigonometricFunctionRoots主要用于对如下形式的三角函数方程进行求解:

4. Conclusion

对于圆与二次曲面求交的实现来看,也可以不用变换二次曲面的坐标系,直接将圆的参数方程代入曲面的二次方程中:

最终化简也可得到一个三角函数方程,但是计算量与变换曲面坐标系对比来看会更大。

从上面的源码分析可知,在OpenCASCADE中对于解析曲线与曲面求交使用二次多项式系数来表示的。通过将二次曲线用参数形式的方程来表示,并代入二次曲面的方程,化简后直接使用math包中的工具(多项式方程求解和三角函数求解工具)对方程进行求解。

5. References

1. 同济大学应用数学系. 线性代数(第四版). 高等教育出版社

2. 丘维声. 解析几何. 北京大学出版社

 


为了方便大家在移动端也能看到我的博文和讨论交流,现已注册微信公众号,欢迎大家扫描下方二维码关注。
Shing Liu(eryar@163.com)




eryar 2019-02-21 22:05 发表评论

[转]GLTF-3D图形界的JPEG

$
0
0

GLTF简介

1.glTF(GL TransmissionFormat),即图形语言交换格式,它是一种3D内容的格式标准,由Khronos Group管理(Khronos Group还管理着OpenGL系列、OpenCL等重要的行业标准);

2.glTF的设计是面向实时渲染应用的,尽量提供可以直接传输给图形API的数据形式,不再需要二次转换;

3.glTF对OpenGL ES、WebGL非常友好;

4.glTF的目标是:3D领域的JPEG;

5.作为一个标准,自2015年10月发布(glTF 1.0)以来,已经得到了业界广泛的认可,你可以相信它的水平;

6.glTF目前最新版本为2.0已于2017年6月正式发布。

GLTF具体的数据存储格式可以去官方网站上看:https://www.khronos.org/gltf/,大概就是相对于XML的JSON存储方式。

 

文章关注的是GLTF给我们带来的便利——节约存储空间,减少带宽压力。

那么如何获得GLTF格式文件?

目前已经有了很多的转换工具:

 

其中Input代表输入模型的格式,输出为gltf格式。由于之前使用过Dae格式的文件(https://www.khronos.org/collada/),因此文章选取COLLADA2GLTF工具转换文件:

 

千万别纠结于工具源代码的编译,因为KhronosGroup已经给出了Release版本。这里说一些使用方法:

1.解压zip文件,在根目录新建dae文件夹——用于存放原始的dae文件

2.在根目录新建gltf文件夹——用于存放转换后的gltf文件

3.在根目录按住Ctrl+Shift+Alt+鼠标右键,打开PowerShell窗口

4.在命令行中输入:./collada2gltf-bin.exe input.dae output.gltf –i dae/input.dae –o gltf/output.gltf

其中input为dae文件的名称,output为输出gltf文件的名称。

5.Enter开始转换。

PS C:\Users\Ruby\Desktop\COLLADA2GLTF-v2.1.2-windows-Release-x64>./COLLADA2GLTF-bin.exe elf.dae elf.gltf -i dae/elf/elf.

dae -o gltf/elf.gltf

Convertingdae\elf\elf.dae -> gltf\elf.gltf

Time: 320 ms

原始dae文件包含4个jpg贴图一共2.46M,转换后为一个单独的gltf文件约1.38M(含贴图)。其实我很好奇那些贴图文件去哪儿了,于是打开了gltf文件查看,发现在image数组下已经把这些贴图文件用base 64编码,变成一堆机器码直接插在gltf文件中。

完成模型转换以后,利用ThreeJS的LoadGLTF API导入网页中浏览效果如下: 

如何在GLTF格式中捕获动画?

上述过程展示了从Collada到gltf的转换以及让gltf模型展示在网页上。下面将继续研究如何获取gltf模型的动画("Talk is cheap,show me the code"——哈哈哈) 

效果如图所示:

至此,gltf从模型到动画都run了一遍。

 

总结

GLTF格式号称3D图形界的JPEG,能够实现快速的模型数据交换。在2017年中旬更新的2.0版本克服了一些低版本的功能缺陷,使得自身功能得到发展;同时,gltf解析及转换Tool的快速发展,为GLTF的进一步推广做出了很多的贡献。文中也应证了,作为GLTF格式的用户,能够利用现有的工具对模型处理,减少模型的数据量,在WebGL的应用中在成倍地节省带宽的同时能够获取同质量的模型以及动画。

 

原文:https://cloud.tencent.com/developer/news/204942

 



eryar 2019-03-30 21:47 发表评论

AVEVA PDMS to DIALux

$
0
0

AVEVA PDMS to DIALux

eryar@163.com

  Abstract. DIAL develops DIALux - the world's leading software for planning, calculation and visualisation of indoor and outdoor lighting. With a community of over 700,000 users, the light planning tool is available in 25 languages and is free of charge for users. All renowned luminaire manufacturers offer digital product data for planning in DIALux. The software makes professional lighting design easier and accessible to everyone. As a platform and tool, it connects planners and manufacturers all over the world. DIALux is available as a desktop version and as a basic mobile app.

DIALux can import model by IFC, so use RvmTranslator to convert PDMS model to IFC format and imported by DIALux for lighting calculation.

Key Words. DIAlux, IFC, RvmTranslator

1. Introduction

首席灯光环境模拟与计算软件DIALux德国DIAL研发,能满足照明设计的所有需求:从标准化的室内、户外或街道的照明规划与计算,到专业的灯光设计、视觉立体化、能量评估,模拟效果接近真实,方便设计师验证各种特殊用法,导出报告,全方位协助设计师高效完成创作。

DIALux完全免费开放下载,有含中文在内的26种语言版本,在全球有100多万用户,在中国大陆有10万多名用户,用户主要包含照明设计师、建筑师、工程公司等,是全球领先的照明规划与计算软件。

 

PDMS主要用于工厂辅助设计,其中也会涉及到仪表、电气专业。通过使用RvmTranslator可以快速地将PDMS模型导入DIALux软件进行灯光照明设计。

2. PDMS to IFC

RvmTranslator7.0可以将PDMS模型转换成IFC格式,IFC主要用于BIM的数据交换,所以灯光设计软件DIALux为了方便导入其他软件创建的建筑模型,增加了IFC导入的功能。使用IFC导入功能,就可以导入PDMS的模型了。

上图为RvmTranslator转换的示例模型成IFC文件。

 

上图所示为导入RvmTranslator转换的IFC文件到DIALux.

3. Showcase

DIALux软件生成的光照效果很逼真,下面给出几个实际工厂的光照效果的例子:

 

 

4. Download

直接在搜索引擎中输入RvmTranslator7.0,即可以找到下载地址。



eryar 2019-04-02 16:19 发表评论

武汉欧凯德信息科技有限公司

$
0
0

武汉欧凯德信息科技有限公司

武汉欧凯德信息科技有限公司取名来源于OpenCASCADE的音译,主要提供开源几何造型库OpenCASCADE相关的咨询、培训和定制开发服务。Open意为开放分享,正是因为分享,为他人创造价值才会有更多的回报。将会继续分享OpenCASCADE的一些技巧和实现原理,共同进步。

 

目前公司的产品有:

l RvmTranslator

RvmTranslator可以将AVEVA PDMS/Plant/Marine中导出的RVM文件进行可视化,以及将RVM转换成常见的三维文件格式。如STEPIGESSTLDXF, OBJ, 3DPDF, 3DXML, IFC等,便于与其他CAD系统进行数据交换,如Autodesk AutoCAD, Plant3d, 3ds Max, CATIA, Solidworks, Pro/E, Unity3d, Bentley等。

1 RvmTranslator程序界面

 

2 RvmTranslator转换的3DXML到达索系统

3 RvmTranslator转换的STEPCATIA

4 RvmTranslator 转换的IFC

 

l IsoAlgo

IsoAlgopiping Isometric drawing generation Algorithm的简称,意为管道轴测图出图算法。国际主流的管道轴测图出图程序ISOGEN是英国一家公司开发的,现被Intergraph收购,而Intergraph也在2010年被美国公司Hexagon收购。ISOGEN作为相对成熟的软件,也存在一些问题,如环管出图成折线,以及客户的一些定制化要求不能及时反馈等。IsoAlgo具有自主知识产权,全面兼容ISOGEN的输入,如管道数据文件PCFIDF,以及SKEY定义文件。IsoAlgo生成管道轴测图如下:






通过程序IsoAlgo3d方便地将PCF/IDF可视化,更便捷的显示出管道信息。以及基于IsoAlgo3d定制开发相关的焊点管理程序。

定制开发

随着国内版权意识的提高以及本地化的一些标准规范要求,考虑成本和效率,有越来越多的定制化开发服务。成功案例有:

v GIM建模

随着国家电网数字化三维设计的推广,提出了GIM三维设计标准,为了满足国网GIM工程数字化移交,为某公司定制开发了GIM建模功能:

v FORAN定制

FORAN是一款3D造船软件,FORAN软件由西班牙SENER集团开发,该公司以船舶设计起家,已有50年的历史,具有40多年的造船CAD软件开发和应用经验。目前在进行软件开发和应用的同时,仍然承接船舶设计项目。FORAN软件是世界上应用最为广泛的大型造船专业软件之一,全球用户包括了120家以上的设计公司和造船厂,近年来更以较快的速度在全球推广。

定制开发了全面支持FORAN几何宏及NORM参数化模型,方便直接访问FORAN的数据库,脱离FORAN环境进行三维可视化。

 

船舶管子零件图

船舶管子零件图也称小票图,零件图上除了图形及其标注外,还应包括管材规格、弯管数据、校管数据、表面处理、安装位置等信息。

 



eryar 2019-04-08 11:34 发表评论

Compile OpenCASCADE7.3 with VS2008

$
0
0

Compile OpenCASCADE7.3 with VS2008

eryar@163.com

 

1. 概述

OpenCASCADE的源码文件夹中有个adm文件夹,里面提供了各个平台中编译源码的项目文件。其中在Windows操作系统中使用微软的Visual Studiomsvc文件夹中提供了VS相应版本的项目文件。所以在使用批处理对OpenCASCADE源码进行编译的时候,会根据vc的版本来启动相应版本的visual studio。如果文件夹中没有对应的VS版本,就打不开相应的VS

 

在最新版本的OpenCASCADE7.3.0中,没有提供VS2008的项目文件。所以对于想将OpenCASCADE用于老的系统,如XP系统中时,或者主程序是用VS2008编译的,这时就需要将OpenCASCADEVS2008来编译了。

2. 生成VS项目

其实OpenCASCADEVS项目都是采用Tcl脚本生成的,而且生成的脚本里面还保留了对VS2008的支持。下面就介绍一下如何生成VS项目。在OpenCASCADE源码文件夹中有个批处理文件genproj.bat

 

由于这个批处理会调用Tcl文件中的命令,所以需要将Tcl加入到环境变量PATH中以便于运行Tcl脚本文件中的命令。如上图中

SET "PATH=%PATH%;D:\OpenCASCADE-7.3.0\tcltk-86-64\bin;"

就是将Tcl加入到PATH的一种方法,配置好这个就可以直接双击genproj.bat,运行截图如下:

 

根据genproj.bat中的注释可知genproj还可以带上参数,第一个参数是VS编译器的版本,第二个参数是操作系统。我们要生成Windows操作系统中VS2008的项目,所以输入参数:

genproj vc9 wnt

这样就生成了VS2008的项目了!

 


为了方便大家在移动端也能看到我的博文和讨论交流,现已注册微信公众号,欢迎大家扫描下方二维码关注。
Shing Liu(eryar@163.com)


eryar 2019-04-18 07:17 发表评论

OpenCASCADE点向平面投影

$
0
0

OpenCASCADE点向平面投影

 

OpenCASCADEProjLib类提供了解析曲线(直线、圆、椭圆、抛物线、双曲线)向解析曲面(平面、圆柱面、圆锥面、球面、圆环面)投影的功能,主要用来计算三维曲线在二维参数空间的参数。

 

其中点向平面投影是最简单的情况,本文主要介绍点向平面投影的注意事项。ProjLib类是个工具类,因为其函数都是静态函数。点向平面投影很简单,直接用ProjLib::Project(aPlane, aPoint)即可。

 

其实现代码如下:

gp_Pnt2d  ProjLib::Project(const gp_Pln& Pl, const gp_Pnt& P)
{
  Standard_Real U, V;
  ElSLib::Parameters(Pl, P, U, V);
  return gp_Pnt2d(U,V);
}
inline void ElSLib::Parameters(const gp_Pln& Pl,
          const gp_Pnt& P,
          Standard_Real& U,
          Standard_Real& V) {
  ElSLib::PlaneParameters(Pl.Position(),P,U,V);
}
void ElSLib::PlaneParameters (const gp_Ax3& Pos,
         const gp_Pnt& P,
         Standard_Real& U,
         Standard_Real& V)
{
  gp_Trsf T;
  T.SetTransformation (Pos);
  gp_Pnt Ploc = P.Transformed (T);
  U = Ploc.X();
  V = Ploc.Y();
}

从上面的代码可以看出,点向平面投影实现就是将点变换到平面所在的坐标系中。使用这个类向平面投影要注意的事项是平面的构造。平面gp_Pln有如下构造函数:

 

默认构造函数:构造了一个XOY平面

基于一个坐标系gp_Ax3构造平面

基于一个点和一个方向构造平面

基于平面的系数方程,即AX+BY+CZ+D=0

前两个构造函数很清晰,而第三个构造函数即基于一个点和一个方向构造平面的方式没有明确,注释不清晰。这里的方向指定了平面的法向,但是还缺少一个方向来确定一个坐标系,所以使用这个构造函数来生成平面的时候,需要理解其生成另外一个方向的算法是不是自己需要的。

gp_Pln::gp_Pln (const gp_Pnt& P,
  const gp_Dir& V)
{
  Standard_Real A = V.X();
  Standard_Real B = V.Y();
  Standard_Real C = V.Z();
  Standard_Real Aabs = A;
  if (Aabs < 0) Aabs = - Aabs;
  Standard_Real Babs = B;
  if (Babs < 0) Babs = - Babs;
  Standard_Real Cabs = C;
  if (Cabs < 0) Cabs = - Cabs;
  //  pour determiner l'axe X :
  //  on dit que le produit scalaire Vx.V = 0. 
  //  et on recherche le max(A,B,C) pour faire la division.
  //  l'une des coordonnees du vecteur est nulle. 
  if( Babs <= Aabs && Babs <= Cabs) {
    if (Aabs > Cabs)  pos = gp_Ax3 (P, V, gp_Dir (-C,0., A));
    else              pos = gp_Ax3 (P, V, gp_Dir ( C,0.,-A));
  }
  else if( Aabs <= Babs && Aabs <= Cabs) {
    if (Babs > Cabs)  pos = gp_Ax3 (P, V, gp_Dir (0.,-C, B));
    else              pos = gp_Ax3 (P, V, gp_Dir (0., C,-B));
  }
  else {
    if (Aabs > Babs)  pos = gp_Ax3 (P, V, gp_Dir (-B, A,0.));
    else              pos = gp_Ax3 (P, V, gp_Dir ( B,-A,0.));
  }
}

当这里确定平面坐标系的方式与需要的不一致时,在使用投影算法的时候就会产生问题。



eryar 2019-05-29 21:02 发表评论

OpenCASCADE License FAQs

$
0
0

OpenCASCADE License FAQs

 

经常用人问我使用OpenCASCADE开发商业软件是否需要付费,下面从OpenCASCADE的官方网站上截取其回答翻译成中文,官方网址:https://www.opencascade.com/content/faqs 

 

翻译统一将Open CASCADE Technology简称为 OCCT

 

OCCT许可与GPL兼容吗?

是的,从版本6.7.0开始,OCCT是根据GNU LGPL版本2.1的条款(以及一些小的附加权限)发布的,因此它与GNU GPL版本2及更高版本完全兼容。

但是,OCCT版本6.6.0及更早版本是根据自定义许可证(OCCT Public License)发布的。 该许可在目的和意图方面类似于GNU LGPL,但与GNU GPL不兼容。

 

我可以使用OCCT根据GPLLGPL或其他许可(包括专有)分发我的软件产品吗?

是的你可以。 但是,您应始终确保最终产品许可证符合产品中使用的库的许可证要求。 从版本6.7.0开始,OCCT根据GNU LGPL版本2.1的条款(以及一些小的附加权限)发布,使用该库的软件产品的要求在LGPL文本的第6部分中描述。 如果您的最终产品的许可与其中使用的库的许可有任何冲突,您需要采取措施(例如,对此类最终产品许可进行适当的修改)以解决任何矛盾。

如果有疑问并避免可能的误解,请联系我们获取建议,我们很乐意回答您对此事可能有的任何疑问。

 

我可以使用Open CASCADE技术制作商业产品并进行销售吗? 如果是,是否有任何限制?

是的,您可以在商业应用程序中使用Open CASCADE技术(OCCT)库,而无需支付任何开发许可费或运行时费或版税。您的义务是要明确注意您的软件使用Open CASCADE技术(OPEN CASCADE公司的商标),在您的产品中提供OCCT许可证的副本,并遵循许可证本身的其他要求。 从版本6.7.0开始,此许可证是GNU LGPL版本2.1(具有一些小的附加权限)。另请注意,OCCT使用“系统要求”页面上列出的第三方组件。 第三方组件的使用受其相应许可证的约束。

 

 

如果我想发布自己使用OCCT的软件,我是否有义务重新分发整个安装包,或者我只能分发我需要的部分代码?

这样是允许的并期望您仅分发你的程序使用到的库和资源。

 

 

我对OCCT的使用是否会侵犯任何其他许可或专利?

您不需要任何特定的安排来使用OCCT开源分发中包含的任何算法。多年来,我们一直没有听说过客户或用户使用Open CASCADE技术的任何专利问题。

 

 

我是否有义务使用OCCT的时候开源应用程序的完整源代码?

不,你不是。 披露您的应用程序源代码的决定取决于您。



eryar 2019-05-29 21:03 发表评论

RvmTranslator7.0-OBJ

$
0
0

RvmTranslator7.0-OBJ

eryar@163.com

 

RvmTranslator can translate the RVM file exported by AVEVA Plant(PDMS)/AVEVA Marine to STEP, IGES, STL, DXF, 3D PDF, OBJ, 3DXML, IFC,.etc. So it can be used for exchanging model data between other CAD software, such as Autodesk AutoCAD, Plant3d, 3ds Max, CATIA, Solidworks, Pro/E, Unity3d, .etc.

 

RvmTranslator可以将AVEVA PDMS/Plant/Marine中导出的RVM文件进行可视化,以及将RVM转换成常见的三维文件格式。如STEPIGESSTLDXF, OBJ, 3DPDF, 3DXML, IFC等,便于与其他CAD系统进行数据交换,如Autodesk AutoCAD, Plant3d, 3ds Max, CATIA, Solidworks, Pro/E, Unity3d, Bentley等。

OBJ文件是Alias|Wavefront公司为它的一套基于工作站的3D建模和动画软件"Advanced Visualizer"开发的一种标准3D模型文件格式,很适合用于3D软件模型之间的互导,也可以通过Maya读写。比如你在3dsMaxLightWave中建了一个模型,想把它调到Maya里面渲染或动画,导出OBJ文件就是一种很好的选择。目前几乎所有知名的3D软件都支持OBJ文件的读写,不过其中很多需要通过插件才能实现。RvmTranslator可以将PDMS模型转换成OBJ格式。

 

上图为RvmTranslator转换的工厂模型。

上图为RvmTranslator转换的船体模型。

 

Download RvmTranslator

 https://share.weiyun.com/5fcHSBC



eryar 2019-05-29 21:04 发表评论

OpenCASCADE直线与平面求交

$
0
0

OpenCASCADE直线与平面求交

在《解析几何》相关的书中都给出了直线和平面的一般方程和参数方程。其中直线的一般方程有点向式形式的。

由于过空间一点可作且只能作一条直线平行于已知直线,所以当直线上一点(x0, y0, z0)和它的一方向向量(m,n,p)为已知时,直线就完全确定了。所以在OpenCASCADE中直线类gp_Lin有一个构造函数:

gp_Lin (const gp_Pnt &P, const gp_Dir &V) 即通过点和方向来构造直线。由直线的点向式方程容易导出直线的参数方程:

其中OpenCASCADE的直线是用参数方程来表示的。

同理对于平面而言,过空间一点可以作而且只能作一平面垂直于一已知直线,所以平面的一点(x0,y0,z0)和它的一个法线方向(A, B, C)为已知时,平面就完全确定了。所以平面方程也有点向式的:

从一个点和两个不共线的向量确定一个平面作为讨论的出发点,可以得出平面的参数方程:

 

如上图所示,已知一个点M0(x0,y0,z0),向量v1(x1,y1,z1)和向量v2(x2,y2,z2),我们来求点M0和向量V1V2确定的平面方程。点M(x,y,z)在平面上的充要条件是向量M0MV1, V2共面。因为向量V1, V2不平行,所以共面的充要条件是存在唯一的一对实数u, v使:

向量M0MV1V2共面的充要条件是:

根据平面的参数方程可知,要确定一个平面从参数方程的角度来看需要一个点和两个方向。从参数方程推导出一般方程的过程也是计算平面一般方程系数的方法。

根据直线的参数方程及平面的一般方程可以推导出直线与平面交点的计算公式,推导过程如下:

从上面的推导过程可以看出,计算直线与平面的交点主要就是计算参数t,当t求出后代入直线参数方程即可得到交点坐标。从参数t的计算公式可知,有个特殊情况就是分母为零的情况,此时是直线与平面平行共面需要特别处理。

OpenCASCADE中提供了直线与平面求交的计算类IntAna_IntConicQuad,其实现源码如下:

 

void IntAna_IntConicQuad::Perform (const gp_Lin& L, const gp_Pln& P,
                                   const Standard_Real Tolang,
                                   const Standard_Real Tol,
                                   const Standard_Real Len) {
  // Tolang represente la tolerance angulaire a partir de laquelle on considere
  // que l angle entre 2 vecteurs est nul. On raisonnera sur le cosinus de cet
  // angle, (on a Cos(t) equivalent a t au voisinage de Pi/2).
  
  done=Standard_False;
  Standard_Real A,B,C,D;
  Standard_Real Al,Bl,Cl;
  Standard_Real Dis,Direc;
  P.Coefficients(A,B,C,D);
  gp_Pnt Orig(L.Location());
  L.Direction().Coord(Al,Bl,Cl);
  Direc=A*Al+B*Bl+C*Cl;
  Dis = A*Orig.X() + B*Orig.Y() + C*Orig.Z() + D;
  //
  parallel=Standard_False;
  if (Abs(Direc) < Tolang) {
    parallel=Standard_True;
    if (Len!=0 && Direc!=0) {
      //check the distance from bounding point of the line to the plane
      gp_Pnt aP1, aP2;
      //
      aP1.SetCoord(Orig.X()-Dis*A, Orig.Y()-Dis*B, Orig.Z()-Dis*C);
      aP2.SetCoord(aP1.X()+Len*Al, aP1.Y()+Len*Bl, aP1.Z()+Len*Cl);
      if (P.Distance(aP2) > Tol) {
        parallel=Standard_False;
      } 
    }
  }
  if (parallel) {
    if (Abs(Dis) < Tolang) {
      inquadric=Standard_True;
    }
    else {
      inquadric=Standard_False;
    }
  }
  else {
    parallel=Standard_False;
    inquadric=Standard_False;
    nbpts = 1;
    paramonc [0] = - Dis/Direc;
    pnts[0].SetCoord(Orig.X()+paramonc[0]*Al,
                     Orig.Y()+paramonc[0]*Bl,
                     Orig.Z()+paramonc[0]*Cl);
  }
  done=Standard_True;
}

 

从上述代码中可以看出其计算思路也是先计算参数t,还加了一个特殊用法,即当参数Len!=0且参数t的分母!=0时重新判断直线与平面的平行状态。这个用法虽然有平行状态的重新判断,但是如果不平行没有计算交点的处理。所以使用这个函数时,参数Len可以用默认值0,即不用这段处理逻辑。还有个不严谨的地方是这里的实数判断没有用区间判断法。



eryar 2019-06-03 16:40 发表评论

RvmTranslator7.1

$
0
0

RvmTranslator7.1

eryar@163.com

 

RvmTranslator can translate the RVM file exported by AVEVA Plant(PDMS)/AVEVA Marine to STEP, IGES, STL, DXF, 3D PDF, OBJ, 3DXML, IFC,.etc. So it can be used for exchanging model data between other CAD software, such as Autodesk AutoCAD, Plant3d, 3ds Max, CATIA, Solidworks, Pro/E, Unity3d, .etc.

RvmTranslator可以将AVEVA PDMS/Plant/Marine中导出的RVM文件进行可视化,以及将RVM转换成常见的三维文件格式。如STEPIGESSTLDXF, OBJ, 3DPDF, 3DXML, IFC等,便于与其他CAD系统进行数据交换,如Autodesk AutoCAD, Plant3d, 3ds Max, CATIA, Solidworks, Pro/E, Unity3d, Bentley等。

RvmTranslator7.1修复了设计树上中文显示问题:

下载RvmTranslator7.1: https://share.weiyun.com/5I8biQw

 


为了方便大家在移动端也能看到我的博文和讨论交流,现已注册微信公众号,欢迎大家扫描下方二维码关注。
Shing Liu(eryar@163.com)


eryar 2019-06-15 10:04 发表评论

RvmTranslator for Linux

$
0
0

RvmTranslator for Linux
eryar@163.com

RvmTranslator can translate the RVM file exported by AVEVA Plant(PDMS)/AVEVA Marine to STEP, IGES, STL, DXF, 3D PDF, OBJ, 3DXML, IFC,.etc. So it can be used for exchanging model data between other CAD software, such as Autodesk AutoCAD, Plant3d, 3ds Max, CATIA, Solidworks, Pro/E, Unity3d, .etc.
RvmTranslator可以将AVEVA PDMS/Plant/Marine中导出的RVM文件进行可视化,以及将RVM转换成常见的三维文件格式。如STEP,IGES,STL,DXF, OBJ, 3DPDF, 3DXML, IFC等,便于与其他CAD系统进行数据交换,如Autodesk AutoCAD, Plant3d, 3ds Max, CATIA, Solidworks, Pro/E, Unity3d, Bentley等。

由于使用的是跨平台的Qt等开源库,现在将RvmTranslator移植到Linux系统中,如下图所示为RvmTranslator在Ubuntu系统中的界面:

支持RVM文件中的中文字符:


RVM是AVEVA的一个统一模型格式,包括其收购的船舶系统Tribon也可以导出RVM格式的文件。

上图为船体结构,


上图为船舶管路,因为船体空间相对狭小,船舶管路布置的密集些。

在Ubuntu系统上试了下开发环境,觉得还比较顺手。开发的IDE可以用Qt Creator,基本满足编码,调试的要求。使用Qt Creator还有一个好处就是方便代码的跨平台移植,在Ubuntu上的代码以Qt的*.pro形式保存,可以直接在Visual Studio中用Qt VS Addin打开编译。


为了方便大家在移动端也能看到我的博文和讨论交流,现已注册微信公众号,欢迎大家扫描下方二维码关注。
Shing Liu(eryar@163.com)


eryar 2019-06-24 19:27 发表评论

最小二乘法拟合直线

$
0
0

最小二乘法拟合直线

在科学实验和生产实践中,经常需要从一组实验数据出发寻求函数y=f(x)的一个近似表达式,也称为经验公式。从几何上看,就是希望根据给定的m个点,求曲线y=f(x)的一条近似曲线。因此这是个曲线拟合问题。

当我们要求近似曲线严格通过给定的每个点时,这是插值算法。对于本文所述的直线拟合来说,如果用插值算法,则只需要两个点就够了。实际直线拟合数据可能满足不了这个条件,为了便于计算,分析与应用,我们较多地根据“使测量点到直线距离的平方和最小”的原则来拟合。按最小二乘原则选择拟合曲线的方法,称为最小二乘法(Method of Least Squares)。

利用最小二乘法拟合曲线的一般步骤是:

  • 将实验数据显示出来,分析曲线的形式;

  • 确定拟合曲线的形式。对于本文来说,曲线形式是直线,y=a+bx;

  • 建立法方程组并对其进行求解;

 

因为OpenCASCADE中数据结构及算法丰富,所以用OpenCASCADE可以快速实现直线的最小二乘法拟合算法。下面给出具体实现代码:

#include <iostream>
#include <gp_Lin2d.hxx>
#include <gp_Pnt2d.hxx>
#include <TColgp_Array1OfPnt2d.hxx>
#include <math_Vector.hxx>
#include <math_SVD.hxx>
#include <math_Gauss.hxx>
#include <math_GaussLeastSquare.hxx>
#include <OSD_Chronometer.hxx>
void fitLine(const TColgp_Array1OfPnt2d& thePoints,
             const std::string& theFileName,
             gp_Lin2d& theLine)
{
    math_Vector aB(1, 2, 0.0);
    math_Vector aX(1, 2, 0.0);
    math_Matrix aM(1, 2, 1, 2);
    Standard_Real aSxi = 0.0;
    Standard_Real aSyi = 0.0;
    Standard_Real aSxx = 0.0;
    Standard_Real aSxy = 0.0;
    std::ofstream aDrawFile(theFileName);
    for (Standard_Integer i = thePoints.Lower(); i <= thePoints.Upper(); ++i)
    {
        const gp_Pnt2d& aPoint = thePoints.Value(i);
        aSxi += aPoint.X();
        aSyi += aPoint.Y();
        aSxx += aPoint.X() * aPoint.X();
        aSxy += aPoint.X() * aPoint.Y();
        aDrawFile << "vpoint p" << i << " " <<
                     aPoint.X() << " " << aPoint.Y() << " 0" << std::endl;
    }
    aM(1, 1) = thePoints.Size();
    aM(1, 2) = aSxi;
    aM(2, 1) = aSxi;
    aM(2, 2) = aSxx;
    aB(1) = aSyi;
    aB(2) = aSxy;
    OSD_Chronometer aChronometer;
    aChronometer.Start();
    math_Gauss aSolver(aM);
    //math_GaussLeastSquare aSolver(aM);
    //math_SVD aSolver(aM);
    aSolver.Solve(aB, aX);
    if (aSolver.IsDone())
    {
        Standard_Real aA = aX(1);
        Standard_Real aB = aX(2);
        gp_Pnt2d aP1(0.0, aA);
        gp_Pnt2d aP2(-aA/aB, 0.0);
        theLine.SetLocation(aP1);
        theLine.SetDirection(gp_Vec2d(aP1, aP2).XY());
        aDrawFile << "vaxis l "
                  << aP1.X() << " " << aP1.Y() << " 0 "
                  << aP2.X() << " " << aP2.Y() << " 0 " << std::endl;
        std::cout << "===================" << std::endl;
        aX.Dump(std::cout);
    }
    aChronometer.Stop();
    aChronometer.Show();
}
int main()
{
    gp_Lin2d aLine;
    // Test data 1
    TColgp_Array1OfPnt2d aPoints1(1, 6);
    aPoints1.SetValue(1, gp_Pnt2d(36.9, 181.0));
    aPoints1.SetValue(2, gp_Pnt2d(46.7, 197.0));
    aPoints1.SetValue(3, gp_Pnt2d(63.7, 235.0));
    aPoints1.SetValue(4, gp_Pnt2d(77.8, 270.0));
    aPoints1.SetValue(5, gp_Pnt2d(84.0, 283.0));
    aPoints1.SetValue(6, gp_Pnt2d(87.5, 292.0));
    fitLine(aPoints1, "fit1.tcl", aLine);
    // Test data 2
    TColgp_Array1OfPnt2d aPoints2(0, 7);
    aPoints2.SetValue(0, gp_Pnt2d(0.0, 27.0));
    aPoints2.SetValue(1, gp_Pnt2d(1.0, 26.8));
    aPoints2.SetValue(2, gp_Pnt2d(2.0, 26.5));
    aPoints2.SetValue(3, gp_Pnt2d(3.0, 26.3));
    aPoints2.SetValue(4, gp_Pnt2d(4.0, 26.1));
    aPoints2.SetValue(5, gp_Pnt2d(5.0, 25.7));
    aPoints2.SetValue(6, gp_Pnt2d(6.0, 25.3));
    aPoints2.SetValue(7, gp_Pnt2d(7.0, 24.8));
    fitLine(aPoints2, "fit2.tcl", aLine);
    return 0;
}

在函数fitLine()中,根据拟合点建立法方程组,并使用math_Gauss来对法方程组进行求解。其实也可以使用math_GaussLeastSquare或者math_SVD等求解法方程组。在主函数main()中测试了两组数据。测试数据1来自易大义等《计算方法》,测试数据2来自《高等数学》。程序运行结果如下图所示:

与书中计算结果吻合。


由于需要将计算结果显示出来,所以在fitLine()函数中增加了输出Draw脚本文件的代码,实际运用时可将这部分代码去掉。将程序生成的脚本文件加载到Draw中,即可得到下面两个图:

测试数据1拟合直线


测试数据2拟合直线


综上所述,对于二维直线的最小二乘法拟合算法的关键是对建立的法方程组进行求解。OpenCASCADEmath包中提供了一些解方程组的类可以直接使用。对于没有使用OpenCASCADE的开发环境的情况,也可以使用其他矩阵库,如Eigen等用得很广泛。Eigen官方网站:http://eigen.tuxfamily.org/index.php?title=Main_Page


将计算结果导出Draw脚本可视化,可以方便直观地查看拟合结果。如果熟悉其他脚本库如Pythonmatplotlib,也可以类似处理来将结果可视化。



eryar 2019-07-04 16:26 发表评论

Boost test vs2013 fatal error C1001

$
0
0

Boost test vs2013 fatal error C1001

Visual Studio 2013中使用boosttest模块时会报致命错误,如下图所示:

输出报错信息如下:

1>------ Build started: Project: Test, Configuration: Debug Win32 ------

1>  main.cpp

1>d:\boost_1_70_0\boost\type_traits\common_type.hpp(47): fatal error C1001: An internal error has occurred in the compiler.

1>  (compiler file 'msc1.cpp', line 1325)

1>   To work around this problem, try simplifying or changing the program near the locations listed above.

1>  Please choose the Technical Support command on the Visual C++ 

1>   Help menu, or open the Technical Support help file for more information

========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

 

定位到报错文件是common_type.hpp

 

可以看出是因为CXX11的原因,因为VS2013不支持C++11,所以boost提供了一个宏定义来设置是否支持CXX11

BOOST_NO_CXX11_TEMPLATE_ALIASES

在Project->Properties->C/C++->Preprocessor中设置即可:

 

 


为了方便大家在移动端也能看到我的博文和讨论交流,现已注册微信公众号,欢迎大家扫描下方二维码关注。
Shing Liu(eryar@163.com)


eryar 2019-07-06 12:54 发表评论

Installing Node.js and Express on Ubuntu

$
0
0

Installing Node.js and Express on Ubuntu

1. 在nodejs官网上下载Linux Binaries(已经包含了npm):

2. 安装Node.js
下载后解压,并在解压的文件夹中启动Terminal后,输入命令:
    sudo cp * /usr/local/ -r
再输入命令:
    node -v
    npm -v
查看程序版本,来检测是否成功安装:


3. 安装express
    npm install -g express-generator

4. 测试Node和Express
用Cesium来测试一下安装是否成功,进入到Cesium源码文件夹,输入命令:
Node server.js


表示启动成功,在浏览器中输入网址:http://localhost:8080/


点击链接Hello World出现下面的这个地球:





eryar 2019-07-08 12:10 发表评论

PipeCAD之管道标准库PipeStd(3)

$
0
0

PipeCAD之管道标准库PipeStd(3)

Key Words: PipeCAD, PipeStd, Pipe Design 3D, Linux

1. Introduction
    管道标准部件库程序PipeStd的思路与PDMS的Paragon类似,主要也是采用参数化的方式来表示管件。在PipeStd中定义好管件的参数化模型及管道等级数据后,就可以为后面管道的建模打好基础。
    程序主要用C++开发,所以可以跨平台运行,即可以在Linux系统中运行,也可以在Windows系统中运行。如下图所示为程序在Ubuntu系统中运行的截图:


程序界面主要分为两部分:数据导航树和三维视图区。数据导航树中显示定义的管件数据和管道等级信息。三维视图区用来显示参数化的管件模型。上图三维显示区显示的是一个法兰型。

2. Catalogue
    管件参数化模型的定义显示在Catalogue中,考虑到管件标准有限且便于自定义,即可以定义任何三维模型,所以提供了参数化建模的方式:


通过参数化的方式由用户自定义管件的模型,理论上可以定义任意三维模型。

上图为定义的阀门参数化模型。

3. Specification
    管道等级Specification是用来控制管道建模时的材料,只有加入到管道等级中的管件才能在建模的时候使用,这样就避免管道三维建模的时候选择了错误的管材。管道等级是由材料控制工程师或管理员来控制。


上图的设计导航树上有一个SPEC World,显示定义的所有管道等级数据。

4. Conclusion
    有了管件参数化模型及管道等级数据,下一步可以在此基础上实现一个管道建模出图的程序。程序自主开发,可以跨平台运行。



eryar 2019-07-15 19:04 发表评论

PipeCAD 简介

$
0
0

PipeCAD 简介

 

 

PipeCAD的定位是中小型项目的管道设计软件,主要有管道建模、设备建模以及管道ISO图及平面图功能。程序的操作方式尽量参考PDMS,考虑灵活性、易于使用。如果用来和国内其他管道设计软件来对比,主要优势就是基于独立图形平台,不依赖第三方图形平台如AutoCAD,程序安装好即可运行。

已经搭建好程序框架,也是尽量与PDMS的设计树保持一致,支持Undo/Redo

因为有PipeStd模块来定义管件图形,所以可以基于此来开发导入管道数据交换常见的PCF/IDF文件。通过定义管道Specification,可以方便地将PCF/IDF中的管件数据进行匹配,从而真实还原其三维模型。下图所示为一个法兰的参数化模型。

先开发导入IDFISOGEN Data File,来自Intergraph PDS and AVEVA PDMS)、PCFPiping Component File,来自Intergraph SmartPlant 3D等使用ISOGEN的设计软件),实现与设计院/工程设计公司的设计数据无缝对接。导入管道IDF/PCF文件后,可以方便地查看三维模型,并且通过点击相应管件的三维模型,在属性窗口中会显示出管件相关的信息,如管道等级、管件类型、材料代号、材料描述等。

下面录一个简单的视频来介绍一下程序的用法及现在已经实现的功能:

 

 

 

 

 

 

 

 



eryar 2019-07-24 23:28 发表评论

OpenCASCADE动画功能

$
0
0

OpenCASCADE动画功能

 

eryar@163.com


1.Introduction

OpenCASCADE提供了类AIS_Animation等来实现简单的动画功能。

 

从其类图可以看出,动画功能有两种:一种是相机的动画AnimationCamera,一种是模型的动画AnimationObject。因为OpenCASCADE不像OpenSceneGraph那样是专门做仿真动画的,内置多线程,OpenCASCADE中的动画可以理解为一个while(true) {update();},即在动画时间内对相机或模型的起始位置和终止位置进行插值并更新来实现动画。对于简单的动画效果,这种方式是够用了。

 

2. Examples

OpenCASCADE的源码文件夹中提供了动画用法的示例,分别演示了模型变换、视图变换及将动画保存成视频文件,当然这个功能需要引入第三方库FFmpeg

OpenCASCADE中动画中间位置的生成采用了类gp_TrsfNLerp进行插值。

 根据其注释是对一个变换的三个部分分别进行线性插值,旋转部分采用四元数类进行插值。这种插值方式不可能满足复杂动画的要求,不过可以把这个类当成一个插值的例子。

Draw Test Harness中输入以下命令:

source tests/v3d/anim/propeller

vanim anim -play -playSpeed 0.1

即可以让一个螺旋桨动起来了:

其实这个螺旋桨动画是个复杂的例子,包含了视图动画和模型动画。对于简单的示例,可以参考另外几个文件,里面的例子是相对简单的。

 

3. Conclusion

OpenCASCADE7.3版本中引入了简单动画的功能,结合其测试案例,找到其实现源码,可以方便地在程序中实现简单的动画功能。其中动画支持两种方式:一种是视图支画,一种是模型动画。视图动画一般用于两个视图之间的动画过渡,如从主视图切换到仰视图中间加一个动画过渡;模型动画可用于简单的动画仿真。

 

 

 

 

 

 

 

 



eryar 2019-08-06 11:07 发表评论

PipeCAD Import IDF

$
0
0

PipeCAD Import IDF

eryar@163.com


IDF/PCF文件是国际标准管道数据交换文件,主要用于isogen生成ISO图。目前主流设计软件都可以生成这些格式的文件,如PDMSIntergraph SmartPlant3d等。目前PipeCAD实现导入IDF/PCF管道数据文件后,生成三维模型及可以查询管件属性(材料描述、管段长度等)信息。

要导入IDF/PCF,先要创建一个项目文件,再通过文件菜单->导入->IDF/PCF即可。如果三维模型没有正确显示,那么是需要定义管道等级spec数据文件。关于等级spec及管件的参数化定义将会稍后给出说明文档。

今天发布一个PipeCAD版本,主要功能是可以导入IDF/PCF文件后进行三维显示。当然三维显示是第一步,后面可以基于此生成一些有用文件,如生成管段焊接信息表,管道材料表等。感兴趣的同学可以从下面链接下载:

https://share.weiyun.com/5z5DoZo

 

若有任何意见、建议,可以直接留言或者可以发送邮件:eryar@163.com

 

 

 


为了方便大家在移动端也能看到我的博文和讨论交流,现已注册微信公众号,欢迎大家扫描下方二维码关注。
Shing Liu(eryar@163.com)

 

 

 

 



eryar 2019-08-11 22:45 发表评论

OpenCASCADE圆与平面求交

$
0
0

OpenCASCADE圆与平面求交

eryar@163.com

 

在 解析几何求交之圆与二次曲面中分析了OpenCASCADE提供的类IntAna_IntConicQuad可以用来计算圆与二次曲面之间的交点,这个算法是将平面Plane作为二次曲面的一个特例来处理,最后主要是对三角函数方程进行求解。

 

当直接使用圆和平面作为参数时,IntAna_IntConicQuad重载了函数Perform来对圆和平面进行求交计算,这时的算法与前面解三角函数不同,代码如下:

void IntAna_IntConicQuad::Perform (const gp_Circ& C, const gp_Pln& P,
                  const Standard_Real Tolang,
                  const Standard_Real Tol)
{
  
  done=Standard_False;
  
  gp_Pln Plconic(gp_Ax3(C.Position()));
  IntAna_QuadQuadGeo IntP(Plconic,P,Tolang,Tol);
  if (!IntP.IsDone()) {return;}
  if (IntP.TypeInter() == IntAna_Empty) {
    parallel=Standard_True;
    Standard_Real distmax = P.Distance(C.Location()) + C.Radius()*Tolang;
    if (distmax < Tol) {
      inquadric = Standard_True;
    }
    else {
      inquadric = Standard_False;
    }
    done=Standard_True;
  }
  else     if(IntP.TypeInter() == IntAna_Same) { 
    inquadric = Standard_True;
    done = Standard_True;
  }
  else {
    inquadric=Standard_False;
    parallel=Standard_False;
    gp_Lin Ligsol(IntP.Line(1));
    
    gp_Vec V0(Plconic.Location(),Ligsol.Location());
    gp_Vec Axex(Plconic.Position().XDirection());
    gp_Vec Axey(Plconic.Position().YDirection());
    
    gp_Pnt2d Orig(Axex.Dot(V0),Axey.Dot(V0));
    gp_Vec2d Dire(Axex.Dot(Ligsol.Direction()),
          Axey.Dot(Ligsol.Direction()));
    
    gp_Lin2d Ligs(Orig,Dire);
    gp_Pnt2d Pnt2dBid(0.0,0.0);
    gp_Dir2d Dir2dBid(1.0,0.0);
    gp_Ax2d Ax2dBid(Pnt2dBid,Dir2dBid);
    gp_Circ2d Cir(Ax2dBid,C.Radius());
    
    IntAna2d_AnaIntersection Int2d(Ligs,Cir);
    
    if (!Int2d.IsDone()) {return;}
    
    nbpts=Int2d.NbPoints();
    for (Standard_Integer i=1; i<=nbpts; i++) {
      
      gp_Pnt2d resul(Int2d.Point(i).Value());
      Standard_Real X= resul.X();
      Standard_Real Y= resul.Y();
      pnts[i-1].SetCoord(Plconic.Location().X() + X*Axex.X() + Y*Axey.X(),
             Plconic.Location().Y() + X*Axex.Y() + Y*Axey.Y(),
             Plconic.Location().Z() + X*Axex.Z() + Y*Axey.Z());
      paramonc[i-1]=Int2d.Point(i).ParamOnSecond();
    }
    done=Standard_True;
  }
}

从上述代码中可以看出,直接对圆和平面求交的算法步骤如下:

l 对圆所在平面与平面进行求交,来判断圆所在平面与平面的状态:平行或是圆在平面内部;

l 如果圆所在平面与平面不平行,则得出交线;

l 然后将交线和圆转换成二维空间进行求交计算;

 

我觉得在得出圆所在平面与平面的交线后,再转换到二维空间来计算交点的方法有点复杂。在得到两个平面的交线后,就可以直接将圆心P0坐标向交线投影得到垂点Pm,先判断圆心到Pm点距离等于半径时,圆和平面就只有一个交点,就是Pm。小于半径时有两个交点,将Pm沿着交线方向分别移动L和-L距离就可以得到交点了,其中:

 

这样处理只涉及到一个点向直线投影、一个开方及几个向量操作,代码简单容量理解。


为了方便大家在移动端也能看到我的博文和讨论交流,现已注册微信公众号,欢迎大家扫描下方二维码关注。
Shing Liu(eryar@163.com)


eryar 2019-09-19 13:19 发表评论

OpenCASCADE点向直线投影

$
0
0

OpenCASCADE点向直线投影

eryar@163.com

 

GeomLib_Tool类中提供了计算指定点在曲线、曲面上的参数,这个算法具有通用性,即对任意曲线、曲面来反求点的参数。

 

本文主要结合源码分析点向直线投影的算法。在类Extrema_ExtPElC中提供了点向基本的曲线距离极值计算的功能,基本曲线就是常见的直线、圆、椭圆、抛物线、双曲线等。其中点到直线的距离源码如下:

 

根据源码的意思画出一个图来说明会更直观,其中向量V1是直线的方向向量,V是直线的起点到点P的向量。

 

算法主要使用向量点乘得到点投影到直线上的距离Mydist,再将点OR沿着直线的方向V1移动Mydist距离就得到了点投影到直线上的点。结合源码及其注释还可以看出只有点在直线的投影点到直线原点OR的距离Mydist在指定的范围内才会保存计算结果。当有范围限制时,即是计算的点到线段的投影,无限制时就是点到直线的投影。

向量的计算有时可以简化程序而且算法性能高,充分理解向量的运算并熟练地运用是图形学编程中的基础。

 


为了方便大家在移动端也能看到我的博文和讨论交流,现已注册微信公众号,欢迎大家扫描下方二维码关注。
Shing Liu(eryar@163.com)


eryar 2019-09-20 13:15 发表评论
Viewing all 519 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>