简述思路
借助CGAL几何库,分为以下步骤:
1. 将mesh做成DCEl结构,
2. 构建AABB tree用于快速射线检测
3. 针对每个segment,求出segment端点所在的两个facet,从端点开始,求出两个端点构成的二维线段与对应三角面三个边求交,利用半边反转转到下一个待求交面
问题
- 为什么不用segment投影到三角面的三维平面与之求交,即Plane_3与segment_3求交?三维平面投影求交z值有浮点误差,所以采用先二维求一次交,再射线检测求第二次交
- dirVector_3是投影方向,想分段投影或者复杂投影自行修改
- 算法没有涉及对退化,非流形,自相交,孔的处理,对于三维有凹形的也需要自行修改
- 其他问题自行优化
接口原型
#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;
}
效果
有需要讨论的发邮箱geometricmodeling@aliyun.com
声明:
本文采用
BY-NC-SA
协议进行授权,如无注明均为原创,转载请注明转自
几何代码及文字整理
本文地址: 如何投影多段线到曲面上?
本文地址: 如何投影多段线到曲面上?
您必须 登录 才能发表评论