第12节 实例-挖方填方量的计算

感谢

感谢网友热心提出这个问题,大家有问题也可以在评论区提出。

资源下载

本文集包括本节所有资源包括模型代码都在此下载,按节的序号有文件或文件夹:

【击此打开网盘资源链接】

功能

1. 我们先画一个盒子,长宽高是10,8,7,再随便旋转个30度,盒子的体积是560。这样我们就好看看我们自己的方式算的准不准。
2. 点击w键,会采样变密,点击s键会采样变粗,命令行中打印出采样的间隔以及算出的体积和盒子本身的体积560的对比来看看误差。
image.png

具体实现

思想: 我寻思填方是把一个坑填平喽,挖方是再把填的挖出来,因此应该是一个算法。我们找到要填的几何体,这里是一个盒子然后算出包围球,这样就求出来全覆盖这个包围球的xyz,然后以xy为平面,固定间隔连一条直线,然后与盒子求交,求出来的交点以采样间隔为长宽,以交点间的距离为高,然后算出来体积,这么多体积相加,就是总体积。有点微积分的味道。

实现: 具体实现也没有什么好说的,直线和物体求交也就是个函数的API,没有什么好讲的。看代码吧。

结果: 有一点是肯定的,当采样越密的时候,精度就越高,这个是可以显见收敛的。有些人可能说我采样往粗了变,怎么有时候误差大有时候误差小呢?这就是采样过粗是不行的。因此以采样点密为要。如果要非常精确,不考虑实时性,可以增加采样点,肯定会越来越精确。

扩展思考: 如果有些几何体特别复杂,比如你溶洞似的,那么怎么办呢?这就牵扯到一条线可能有多个交点的问题,一般情况下交点必为偶数个则才成为一个封闭的体,则两两认为是在一个空间,是要求的。自己想象一下。

具体代码如下:

#include <osgViewer/viewer>
#include <osgDB/ReadFile>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/ShapeDrawable>
#include <osg/MatrixTransform>
#include <osgUtil/IntersectionVisitor>

//定义全局变量用着方便
osg::Group* _root = nullptr;
osg::Node* _box = nullptr;
osg::Group* _shiyi = nullptr;

osg::Geode* CreateBox(osg::Vec3 from, osg::Vec3 to, float stepSize)
{
    osg::Geode* gnode = new osg::Geode;
    gnode->addDrawable(new osg::ShapeDrawable(new osg::Box((from+to)/2, stepSize, stepSize, std::abs(to.z()-from.z()))));
    return gnode;
}

//画一个盒子
osg::Node* CreateScene()
{
    osg::Group* root = new osg::Group;
    _root = root;

    osg::MatrixTransform* mt = new osg::MatrixTransform;
    _box = mt;
    root->addChild(mt);
    mt->setMatrix(osg::Matrix::rotate(osg::inDegrees(30.0), osg::Vec3(1.0, 1.0, 1.0)));

    osg::Geode* gnode = new osg::Geode;
    osg::ShapeDrawable* sd = new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0, 0.0, 0.0), 10, 8, 7));
    gnode->addDrawable(sd);

    mt->addChild(gnode);

    return root;
}

class MyEventHandler : public osgGA::GUIEventHandler
{
public:
    MyEventHandler()
    {
        _jiange = 0.5;
    }

    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
    {
        if (ea.getEventType() == ea.KEYDOWN)
        {
            bool _push = false;
            if ((ea.getKey() == 'w') || (ea.getKey() == 'W'))
            {
                _push = true;
                _jiange /= 1.5;
            }
            
            if ((ea.getKey() == 's') || (ea.getKey() == 'S'))
            {
                _push = true;
                _jiange *= 1.5;
            }

            if (_push)
            {
                //清空上一次采样结果
                if (_shiyi != nullptr)
                {
                    _root->removeChild(_shiyi);
                    _shiyi = nullptr;
                }

                _shiyi = new osg::Group();
                _root->addChild(_shiyi);

                std::cout << "采样间隔:"<<_jiange << std::endl;
                //求要求的场景的boudingbox
                osg::BoundingSphere bs = _box->getBound();
                //求出xyz的最小值和最大值,然后根据间隔来求
                osg::Vec3 center = bs.center();
                float r = bs.radius();
                float areas = 0.0;
                for (float fromx = center.x() - r; fromx <= center.x() + r; fromx += _jiange)
                {
                    for (float fromy = center.y() - r; fromy <= center.y() + r; fromy += _jiange)
                    {
                        osg::Vec3 from = osg::Vec3(fromx, fromy, center.z() - r);
                        osg::Vec3 to = osg::Vec3(fromx, fromy, center.z() + r);
                        osgUtil::LineSegmentIntersector::Intersections intersections;
                        osg::ref_ptr<osgUtil::LineSegmentIntersector> ls = new osgUtil::LineSegmentIntersector(from, to);
                        osg::ref_ptr<osgUtil::IntersectionVisitor> iv = new osgUtil::IntersectionVisitor(ls);
                        _box->accept(*iv.get());

                        if (ls->containsIntersections())
                        {
                            intersections = ls->getIntersections();
                            //判断当前交点数
                            if (intersections.size() == 2)
                            {
                                osgUtil::LineSegmentIntersector::Intersections::iterator iter = intersections.begin();
                                osg::Vec3 firstInter = iter->getWorldIntersectPoint();
                                iter++;
                                osg::Vec3 secondInter = iter->getWorldIntersectPoint();
                                _shiyi->addChild(CreateBox(firstInter, secondInter, _jiange));
                                areas += (_jiange*_jiange*std::abs(firstInter.z() - secondInter.z()));
                            }
                        }
                    }
                }

                std::cout << "实际面积:560 计算面积:" << areas << std::endl;
            }
        }

        return false;
    }

    float _jiange;
};


int main()
{
    osgViewer::Viewer viewer;
    viewer.addEventHandler(new MyEventHandler());
    viewer.setSceneData(CreateScene());
    return viewer.run();
}

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片