简述思路

借助CGAL几何库,分为以下步骤:
1. 将mesh做成DCEl结构,
2. 构建AABB tree用于快速射线检测
3. 针对每个segment,求出segment端点所在的两个facet,从端点开始,求出两个端点构成的二维线段与对应三角面三个边求交,利用半边反转转到下一个待求交面

问题

  1. 为什么不用segment投影到三角面的三维平面与之求交,即Plane_3与segment_3求交?三维平面投影求交z值有浮点误差,所以采用先二维求一次交,再射线检测求第二次交
  2. dirVector_3是投影方向,想分段投影或者复杂投影自行修改
  3. 算法没有涉及对退化,非流形,自相交,孔的处理,对于三维有凹形的也需要自行修改
  4. 其他问题自行优化

接口原型

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
#include <CGAL/Vector_3.h>
#include <CGAL/Surface_mesh.h>

#include <CGAL/AABB_face_graph_triangle_primitive.h>
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits.h>


using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
using Point_2 = Kernel::Point_2;
using Point_3 = Kernel::Point_3;
using Vector_3 = Kernel::Vector_3;
using Segment_2 = Kernel::Segment_2;
using Mesh = CGAL::Surface_mesh<Kernel::Point_3>;

using Polyline2D = std::vector<Point_2>;
using Polyline3D = std::vector<Point_3>;

void PolylineProjectToMesh(Mesh& mesh, Polyline2D& pline, const Vector_3& dir,Polyline3D& pline3d);

代码

void PolylineProjectToMesh(Mesh& mesh, Polyline2D& pline, const Vector_3& dir,Polyline3D& pline3d)
{
    typedef CGAL::AABB_face_graph_triangle_primitive<Mesh>                AABB_face_graph_primitive;
    typedef CGAL::AABB_traits<Kernel, AABB_face_graph_primitive>               AABB_face_graph_traits;
    typedef Kernel::FT                     FT;
    typedef PMP::Face_location<Mesh, FT>   Face_location;

    //计时器,测试使用
    CGAL::Timer timer;
    timer.start(); 
    //AABB_tree 加速数据结构,具体和octree,kdtree一个作用
    CGAL::AABB_tree<AABB_face_graph_traits> tree;
    PMP::build_AABB_tree(mesh, tree);
    int len = pline.size();

    for (int i = 0; i < len; i++)
    {
        //auto&只是偷懒

        auto& p = pline[i];
        newpline.push_back(p);
        auto p3d = Point_3(p.x(), p.y(), 0);
        const Ray_3 ray_3(p3d, dir);
        //AABB_tree便于快速射线检测
        Face_location ray_location = PMP::locate_with_AABB_tree(ray_3, tree, mesh);

        const auto& projectp = PMP::construct_point(ray_location, mesh);
        pline3d.push_back(projectp);
        if (ray_location.first.is_valid())
        {
            if (i != len -1)
            {
                Mesh::Halfedge_index nexthalfedge;
                auto& facet = ray_location.first;
                Segment_2 currsegment(p, pline[i+1]);
                const Ray_3 next_ray_3(Point_3(pline[i + 1].x(), pline[i + 1].y(), 0), dir);
                Face_location next_ray_location = PMP::locate_with_AABB_tree(next_ray_3, tree, mesh);

                while (true)
                {
                    if (next_ray_location.first.is_valid() && next_ray_location.first == facet)
                    {
                        ///如果到下一个顶点所在曲面退出循环
                        break;
                    }


                    auto halfedges = mesh.halfedges_around_face(mesh.halfedge(facet));
                    for (auto& halfedge : halfedges)
                    {
                        if (halfedge != nexthalfedge)
                        {
                            auto source = mesh.source(halfedge);
                            auto target = mesh.target(halfedge);

                            Segment_2 segment(Point_2(mesh.point(source).x(), mesh.point(source).y()), Point_2(mesh.point(target).x(), mesh.point(target).y()));


                            const auto result = CGAL::intersection(currsegment, segment);
                            if (result)
                            {
                                //segment相交一般是个点,这里没有考虑共线的情况,自行处理
                                //大面积曲面共线概率低,对于算法生成的比较差的曲面会有共线情况,
                                const Point_2* Intermediate_p = boost::get<Point_2 >(&*result);
                                newpline.push_back(*Intermediate_p);
                                //std::cout << std::setprecision(15) << *p << std::endl;


                                const Ray_3 Intermediate_ray_3(Point_3(Intermediate_p->x(), Intermediate_p->y(), 0), dir);
                                Face_location Intermediate_ray_location = PMP::locate_with_AABB_tree(Intermediate_ray_3, tree, mesh);
                                const auto& Intermediate_projectp = PMP::construct_point(Intermediate_ray_location, mesh);
                                pline3d.push_back(Intermediate_projectp);


                                //对边转换到下一个facet所在对应半边
                                nexthalfedge = mesh.opposite(halfedge);

                                auto nextfacet = mesh.face(nexthalfedge);

                                facet = mesh.face(nexthalfedge);
                                break;
                            }
                            else
                            {
                                //可以打印输出
                            }
                        }
                    }
                }
            }
        }
        else
        {
            //这里没有考虑mesh出现孔或者中断情况,直接break,处理情况类似都是考虑边界情况
            break;
        }
    }
    std::cout << "time cost:" << timer.time() << std::endl;
}

效果

图1
有需要讨论的发邮箱geometricmodeling@aliyun.com

您必须 登录 才能发表评论