OpenCASCADE 参数曲线曲面面积

eryar@163.com

Abstract. 本文介绍了参数曲面的第一基本公式,并应用曲面的第一基本公式,结合OpenCASCADE中计算多重积分的类,对任意参数曲面的面积进行计算。

Key Words. Parametric Curve, Parametric Surface, Gauss Integration, Global Properties

1.Introduction

我们知道一元函数y=f(x)的图像是一条曲线,二元函数z=f(x,y)的图像是一张曲面。但是,把曲线曲面表示成参数方程则更加便利于研究,这种表示方法首先是由欧洲瑞士数学家Euler引进的。例如,在空间中的一条曲线可以表示为三个一元函数:

X=x(t), Y=y(t), Z=z(t)

在向量的概念出现后,空间中的一条曲线可以自然地表示为一个一元向量函数:

r=r(t)=(x(t), y(t), z(t))

用向量函数来表示曲线和曲面后,使曲线曲面一些量的计算方式比较统一。如曲线可以表示为一元向量函数,曲面可以表示为二元向量函数。

本文结合OpenCASCADE来介绍参数曲线曲面积分分别计算曲线弧长和曲面的面积。结合《微分几何》来更好地理解曲线曲面相关知识。

2.Curve Natural Parametric Equations

设曲线C的参数方程是r=r(t),命:

则s是该曲线的一个不变量,即它与空间中的坐标系的选择无关,也与该曲线的参数变换无关。前者是因为在笛卡尔直角坐标变换下,切向量的长度|r’(t)|是不变的,故s不变。关于后者可以通过积分的变量的替换来证明,设参数变换是:

并且

因此

根据积分的变量替换公式有:

不变量s的几何意义就是曲线段的弧长。这说明曲线参数t可以是任意的,但选择不同的参数得到的参数方程会有不同,但是曲线段的弧长是不变的。以曲线弧长作为曲线方程的参数,这样的方程称为曲线的自然参数方程Natural Parametric Equations。

由曲线的参数方程可知,曲线弧长的计算公式为:

几何意义就是在每个微元处的切向量的长度求和。

3.Gauss Integration for Arc Length

曲线弧长的计算就是一元函数的积分。OpenCASCADE中是如何计算任意曲线弧长的呢?直接找到相关的源码列举如下:(在类CPnts_AbscissaPoint中)

// auxiliary functions to compute the length of the derivative

static Standard_Real f3d(const Standard_Real X, const Standard_Address C)

{

gp_Pnt P;

gp_Vec V;

((Adaptor3d_Curve*)C)->D1(X,P,V);

return V.Magnitude();

}

static Standard_Real f2d(const Standard_Real X, const Standard_Address C)

{

gp_Pnt2d P;

gp_Vec2d V;

((Adaptor2d_Curve2d*)C)->D1(X,P,V);

return V.Magnitude();

}

//==================================================================

//function : Length

//purpose  : 3d with parameters

//==================================================================

Standard_Real CPnts_AbscissaPoint::Length(const Adaptor3d_Curve& C,

const Standard_Real U1,

const Standard_Real U2)

{

CPnts_MyGaussFunction FG;

//POP pout WNT

CPnts_RealFunction rf = f3d;

FG.Init(rf,(Standard_Address)&C);

//  FG.Init(f3d,(Standard_Address)&C);

math_GaussSingleIntegration TheLength(FG, U1, U2, order(C));

if (!TheLength.IsDone()) {

throw Standard_ConstructionError();

}

return Abs(TheLength.Value());

}

//==================================================================

//function : Length

//purpose  : 2d with parameters

//==================================================================

Standard_Real CPnts_AbscissaPoint::Length(const Adaptor2d_Curve2d& C,

const Standard_Real U1,

const Standard_Real U2)

{

CPnts_MyGaussFunction FG;

//POP pout WNT

CPnts_RealFunction rf = f2d;

FG.Init(rf,(Standard_Address)&C);

//  FG.Init(f2d,(Standard_Address)&C);

math_GaussSingleIntegration TheLength(FG, U1, U2, order(C));

if (!TheLength.IsDone()) {

throw Standard_ConstructionError();

}

return Abs(TheLength.Value());

}

上述代码的意思是直接对曲线的一阶导数的长度求积分,即是弧长。OpenCASCADE的代码写得有点难懂,根据意思把对三维曲线求弧长的代码改写下,更便于理解:

//! Function for curve length evaluation.

class math_LengthFunction : public math_Function

{

public:

math_LengthFunction(const Handle(Geom_Curve)& theCurve)

: myCurve(theCurve)

{

}

virtual Standard_Boolean Value(const Standard_Real X, Standard_Real& F)

{

gp_Pnt aP;

gp_Vec aV;

myCurve->D1(X, aP, aV);

F = aV.Magnitude();

return Standard_True;

}

private:

Handle(Geom_Curve) myCurve;

};

4.First Fundamental Form of a Surface

曲面参数方程是个二元向量函数。根据《微分几何》中曲面的第一基本公式(First Fundamental Form of a Surface)可知,曲面上曲线的表达式为:

r=r(u(t), v(t)) = (x(t), y(t), z(t))

若以s表示曲面上曲线的弧长,则由复合函数求导公式可得弧长微分公式:

在古典微分几何中,上式称为曲面的第一基本公式,E、F、G称为第一基本量。在曲面上,每一点的第一基本量与参数化无关。

利用曲面第一基本公式可以用于计算曲面的面积。参数曲面上与u,v参数空间的元素dudv对应的面积元为:

由参数曲面法向的计算可知,曲面的面积元素即为u,v方向上的偏导数的乘积的模。

其几何意义可以理解为参数曲面的面积微元是由u,v方向的偏导数的向量围成的一个四边形的面积,则整个曲面的面积即是对面积元素求积分。由于参数曲面有两个参数,所以若要计算曲面的面积,只需要对面积元素计算二重积分即可。

5.Gauss Integration for Area

OpenCASCADE的math包中提供了多重积分的计算类math_GaussMultipleIntegration,由类名可知积分算法采用了Gauss积分算法。下面通过具体代码来说明OpenCASCADE中计算曲面积分的过程。要计算积分,先要定义被积函数。因为参数曲面与参数曲线不同,参数曲线只有一个参数,而参数曲面有两个参数,所以是一个多元函数。

//! 2D variable function for surface area evaluation.

class math_AreaFunction : public math_MultipleVarFunction

{

public:

math_AreaFunction(const Handle(Geom_Surface)& theSurface)

: mySurface(theSurface)

{

}

virtual Standard_Integer NbVariables() const

{

return 2;

}

virtual Standard_Boolean Value(const math_Vector& X, Standard_Real& Y)

{

gp_Pnt aP;

gp_Vec aDu;

gp_Vec aDv;

mySurface->D1(X(1), X(2), aP, aDu, aDv);

Y = aDu.Crossed(aDv).Magnitude();

return Standard_True;

}

private:

Handle(Geom_Surface) mySurface;

};

由于参数曲面是多元函数,所以从类math_MultipleVarFunction派生,并在虚函数NbVariables()中说明有两个变量。在虚函数Value()中计算面积元素的值,即根据曲面第一基本公式中面积元素的定义,对参数曲面求一阶导数,计算两个偏导数向量的叉乘的模。

有了被积函数,只需要在定义域对其计算二重积分,相应代码如下所示:

void evalArea(const Handle(Geom_Surface)& theSurface, const math_Vector& theLower, const math_Vector& theUpper)

{

math_IntegerVector aOrder(1, 2, math::GaussPointsMax());

math_AreaFunction aFunction(theSurface);

math_GaussMultipleIntegration anIntegral(aFunction, theLower, theUpper, aOrder);

if (anIntegral.IsDone())

{

anIntegral.Dump(std::cout);

}

}

通过theLower和theUpper指定定义域,由于采用了Gauss-Legendre算法计算二重积分,所以需要指定阶数,且阶数越高积分结果精度越高,这里使用了OpenCASCADE中最高的阶数。

下面通过对基本曲面的面积计算来验证结果的正确性,并将计算结果和OpenCASCADE中计算面积的类BRepGProp::SurfaceProperties()结果进行对比。

6.Elementary Surface Area Test

下面通过对OpenCASCADE中几个初等曲面的面积进行计算,代码如下所示:

/*

Copyright(C) 2017 Shing Liu(eryar@163.com)

Permission is hereby granted, free of charge, to any person obtaining a copy

of this software and associated documentation files(the "Software"), to deal

in the Software without restriction, including without limitation the rights

to use, copy, modify, merge, publish, distribute, sublicense, and / or sell

copies of the Software, and to permit persons to whom the Software is

furnished to do so, subject to the following conditions :

The above copyright notice and this permission notice shall be included in all

copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE

AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER

LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,

OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE

SOFTWARE.

*/

#include <gp_Pnt.hxx>

#include <gp_Vec.hxx>

#include <math.hxx>

#include <math_Function.hxx>

#include <math_MultipleVarFunction.hxx>

#include <math_GaussMultipleIntegration.hxx>

#include <Geom_Plane.hxx>

#include <Geom_ConicalSurface.hxx>

#include <Geom_CylindricalSurface.hxx>

#include <Geom_SphericalSurface.hxx>

#include <Geom_ToroidalSurface.hxx>

#include <Geom_BSplineSurface.hxx>

#include <Geom_RectangularTrimmedSurface.hxx>

#include <GeomConvert.hxx>

#include <GProp_GProps.hxx>

#include <TopoDS_Face.hxx>

#include <BRepGProp.hxx>

#include <BRepBuilderAPI_MakeFace.hxx>

#pragma comment(lib, "TKernel.lib")

#pragma comment(lib, "TKMath.lib")

#pragma comment(lib, "TKG2d.lib")

#pragma comment(lib, "TKG3d.lib")

#pragma comment(lib, "TKGeomBase.lib")

#pragma comment(lib, "TKGeomAlgo.lib")

#pragma comment(lib, "TKBRep.lib")

#pragma comment(lib, "TKTopAlgo.lib")

//! 2D variable function for surface area evaluation.

class math_AreaFunction : public math_MultipleVarFunction

{

public:

math_AreaFunction(const Handle(Geom_Surface)& theSurface)

: mySurface(theSurface)

{

}

virtual Standard_Integer NbVariables() const

{

return 2;

}

virtual Standard_Boolean Value(const math_Vector& X, Standard_Real& Y)

{

gp_Pnt aP;

gp_Vec aDu;

gp_Vec aDv;

Standard_Real E = 0.0;

Standard_Real F = 0.0;

Standard_Real G = 0.0;

mySurface->D1(X(1), X(2), aP, aDu, aDv);

E = aDu.Dot(aDu);

F = aDu.Dot(aDv);

G = aDv.Dot(aDv);

Y = Sqrt(E * G - F * F);

//Y = aDu.Crossed(aDv).Magnitude();

return Standard_True;

}

private:

Handle(Geom_Surface) mySurface;

};

void evalArea(const Handle(Geom_Surface)& theSurface, const math_Vector& theLower, const math_Vector& theUpper)

{

math_IntegerVector aOrder(1, 2, math::GaussPointsMax());

math_AreaFunction aFunction(theSurface);

math_GaussMultipleIntegration anIntegral(aFunction, theLower, theUpper, aOrder);

if (anIntegral.IsDone())

{

anIntegral.Dump(std::cout);

}

}

void evalArea(const Handle(Geom_BoundedSurface)& theSurface)

{

math_IntegerVector aOrder(1, 2, math::GaussPointsMax());

math_Vector aLower(1, 2, 0.0);

math_Vector aUpper(1, 2, 0.0);

theSurface->Bounds(aLower(1), aUpper(1), aLower(2), aUpper(2));

math_AreaFunction aFunction(theSurface);

math_GaussMultipleIntegration anIntegral(aFunction, aLower, aUpper, aOrder);

if (anIntegral.IsDone())

{

anIntegral.Dump(std::cout);

}

}

void testFace(const TopoDS_Shape& theFace)

{

GProp_GProps aSurfaceProps;

BRepGProp::SurfaceProperties(theFace, aSurfaceProps);

std::cout << "Face area: " << aSurfaceProps.Mass() << std::endl;

}

void testPlane()

{

std::cout << "====== Test Plane Area =====" << std::endl;

Handle(Geom_Plane) aPlaneSurface = new Geom_Plane(gp::XOY());

math_Vector aLower(1, 2);

math_Vector aUpper(1, 2);

// Parameter U range.

aLower(1) = 0.0;

aUpper(1) = 2.0;

// Parameter V range.

aLower(2) = 0.0;

aUpper(2) = 3.0;

evalArea(aPlaneSurface, aLower, aUpper);

// Convert to BSpline Surface.

Handle(Geom_RectangularTrimmedSurface) aTrimmedSurface =

new Geom_RectangularTrimmedSurface(aPlaneSurface, aLower(1), aUpper(1), aLower(2), aUpper(2));

Handle(Geom_BSplineSurface) aBSplineSurface = GeomConvert::SurfaceToBSplineSurface(aTrimmedSurface);

evalArea(aBSplineSurface);

// Test Face.

TopoDS_Face aFace = BRepBuilderAPI_MakeFace(aTrimmedSurface, Precision::Confusion()).Face();

testFace(aFace);

aFace = BRepBuilderAPI_MakeFace(aBSplineSurface, Precision::Confusion()).Face();

testFace(aFace);

}

void testCylinder()

{

std::cout << "====== Test Cylinder Area =====" << std::endl;

Handle(Geom_CylindricalSurface) aCylindrialSurface = new Geom_CylindricalSurface(gp::XOY(), 1.0);

math_Vector aLower(1, 2);

math_Vector aUpper(1, 2);

aLower(1) = 0.0;

aUpper(1) = M_PI * 2.0;

aLower(2) = 0.0;

aUpper(2) = 3.0;

evalArea(aCylindrialSurface, aLower, aUpper);

// Convert to BSpline Surface.

Handle(Geom_RectangularTrimmedSurface) aTrimmedSurface =

new Geom_RectangularTrimmedSurface(aCylindrialSurface, aLower(1), aUpper(1), aLower(2), aUpper(2));

Handle(Geom_BSplineSurface) aBSplineSurface = GeomConvert::SurfaceToBSplineSurface(aTrimmedSurface);

evalArea(aBSplineSurface);

// Test Face.

TopoDS_Face aFace = BRepBuilderAPI_MakeFace(aTrimmedSurface, Precision::Confusion()).Face();

testFace(aFace);

aFace = BRepBuilderAPI_MakeFace(aBSplineSurface, Precision::Confusion()).Face();

testFace(aFace);

}

void testSphere()

{

std::cout << "====== Test Sphere Area =====" << std::endl;

Handle(Geom_SphericalSurface) aSphericalSurface = new Geom_SphericalSurface(gp::XOY(), 1.0);

math_Vector aLower(1, 2);

math_Vector aUpper(1, 2);

aSphericalSurface->Bounds(aLower(1), aUpper(1), aLower(2), aUpper(2));

evalArea(aSphericalSurface, aLower, aUpper);

// Convert to BSpline Surface.

Handle(Geom_BSplineSurface) aBSplineSurface = GeomConvert::SurfaceToBSplineSurface(aSphericalSurface);

evalArea(aBSplineSurface);

// Test Face.

TopoDS_Face aFace = BRepBuilderAPI_MakeFace(aSphericalSurface, Precision::Confusion()).Face();

testFace(aFace);

aFace = BRepBuilderAPI_MakeFace(aBSplineSurface, Precision::Confusion()).Face();

testFace(aFace);

}

void test()

{

testPlane();

testSphere();

testCylinder();

}

int main(int argc, char* argv[])

{

test();

return 0;

}

计算结果如下图所示:

上述代码计算了曲面的面积,再将曲面转换成B样条曲面,再使用算法计算面积。再将曲面和转换的B样条曲面生成拓朴面,利用OpenCASCADE中计算曲面面积功能进行对比。使用自定义函数math_AreaFunction利用多重积分类计算的结果与OpenCASCADE中计算曲面面积的值是一致的。当把曲面转换成B样条曲面后,OpenCASCADE计算的曲面面积偏大。

7.Conclusion

在学习《高等数学》的积分时,其主要的一个应用就是计算弧长、面积和体积等。学习高数抽象概念时,总会问学了高数有什么用?就从计算机图形方面来看,可以利用数学工具对任意曲线求弧长,对任意曲面计算面积等,更具一般性。

通过自定义被积函数再利用积分算法来计算任意曲面的面积,将理论与实践结合起来了。即将曲面的第一基本公式与具体的代码甚至可以利用OpenCASCADE生成对应的图形,这样抽象的理论就直观了,更便于理解相应的概念。

8.References

1.朱心雄. 自由曲线曲面造型技术. 科学出版社. 2000

2.陈维桓. 微分几何. 北京大学出版社. 2006

3.同济大学数学教研室. 高等数学. 高等教育出版社. 1996

OpenCASCADE 参数曲线曲面面积的更多相关文章

  1. OpenCASCADE 参数曲面面积

    OpenCASCADE 参数曲面面积 eryar@163.com Abstract. 本文介绍了参数曲面的第一基本公式,并应用曲面的第一基本公式,结合OpenCASCADE中计算多重积分的类,对任意参 ...

  2. Make Helix Curve in OpenCASCADE

    Make Helix Curve in OpenCASCADE eryar@163.com Abstract. OpenCASCADE does not provide helix curve dir ...

  3. OpenCASCADE PCurve of Topological Face

    OpenCASCADE PCurve of Topological Face eryar@163.com Abstract. OpenCASCADE provides a class BRepBuil ...

  4. OpenCASCADE Make Primitives-Sphere

    OpenCASCADE Make Primitives-Sphere eryar@163.com Abstract. The sphere is the simplest topology shape ...

  5. OpenCASCADE Make Primitives-Box

    OpenCASCADE Make Primitives-Box eryar@163.com Abstract. By making a simple box to demonstrate the BR ...

  6. OpenCASCADE BRep vs. OpenNURBS BRep

    OpenCASCADE BRep vs. OpenNURBS BRep eryar@163.com Abstract. BRep short for Boundary Representation. ...

  7. OpenCASCADE Gauss Integration

    OpenCASCADE Gauss Integration eryar@163.com Abstract. Numerical integration is the approximate compu ...

  8. OpenCascade Primitives BRep-Cone

    OpenCascade Primitives BRep-Cone eryar@163.com Abstract. BRep is short for Boundary Representation. ...

  9. OpenCascade Primitives BRep-Torus

    OpenCascade Primitives BRep-Torus eryar@163.com Abstract. BRep is short for Boundary Representation. ...

随机推荐

  1. Unity3D手机斗地主游戏开发实战(01)_发牌功能实现

    园子荒废多年,闲来无事,用Unity3D来尝试做一个简单的小游戏,一方面是对最近研究的Unity3D有点总结,一方面跟广大的园友相互学习和提高.话不多说,进入正题~ 一.创建项目 1.创建Unity2 ...

  2. 异步API

    整合到一起,作为参考: //2 5 3 4 1 //2 5 3 1 4 var img = new Image(); img.src = '.'; img.onerror = function() { ...

  3. 微信小程序之页面跳转路径问题

    错误如下: 业务需求:在movie页面点击进入detail页面. 在遍历跳转路径的时候,写绝对路径了 只需改一下就好了 教程参考地址:http://blog.csdn.net/reylen/artic ...

  4. Codeforces 376C. Socks

    C. Socks time limit per test 2 seconds memory limit per test 256 megabytes input standard input outp ...

  5. 兼容IE、火狐、谷歌的页面关闭事件

    <html> <head> <script language="javascript"> var blnCheckUnload = true; ...

  6. PHP实现前台页面与MySQL的数据绑定、同步更新

    今天我来给大家介绍一个PHP-MySQL的小项目. 使用 PHP和前台Ajax 实现在前台对MySQL数据库中数据的增.删等操作语句功能. 如果有问题,欢迎拍砖~ 首先,我们先做好前台HTML.CSS ...

  7. 【Win 10 应用开发】UI Composition 札记(五):灯光

    UI Composition 除了能够为 UI 元素建立三维空间外,还有相当重要的一个部件——灯光.宇宙万物的精彩缤纷,皆源于光明,光,使我们看到各种东西,除了黑洞之外的世界都是五彩斑谰的.故而,真要 ...

  8. Java 核心内容相关面试题【1】

    1.什么是 transient 变量? transient 变量是指不会被序列化的变量. 2.什么是同步(synchronization)? 在多线程环境中,同步是指控制多个线程访问共享资源的方式.没 ...

  9. Spring面试题目

    问题清单: 1. 什么是Spring框架?Spring框架有哪些主要模块? 2. 使用Spring框架有什么好处? 3. 什么是控制反转(IOC)?什么是依赖注入? 4. 请解释下Spring中的IO ...

  10. ASP.NET Core 与 Vue.js 服务端渲染

    http://mgyongyosi.com/2016/Vuejs-server-side-rendering-with-aspnet-core/ 原作者:Mihály Gyöngyösi 译者:oop ...