想用CGAL做一些关于mesh的操作,我的上游输出是一个用PLY文件存储的surface mesh,我希望将其转换为CGAL的格式。经过Google发现,较新的CGAL版本带有read_ply()
函数,而当前系统配套的CGAL版本并不支持,于是需要安装新的CGAL。How hard can it be?! 但是事实是,这花去了几乎一下午的时间。
Ubuntu 18.04
cmake 3.16
Qt 5.9.5
从CGAL的Github clone 到本地。
当前CGAL的版本是5.0.2,通过git checkout releases/CGAL-5.0.2切换到对应的tag上。
在CGAL Github的页面上有INSTALL.md文件。内有如何将CGAL添加到本地project的方法。基本原理就是CGAL本身已经是header only的library了,所以只需要让cmake找到我们clone CGAL的位置即可。INSTALL.md内描述了如何运用cgal_create_cmake_script
脚本生成一个CMakeLists.txt,并且指出在执行cmake时需要指定CGAL的具体位置。在cgal_create_cmake_script
所生成的CMakeLists.txt最后,使用了一个名为create_single_source_cgal_program
的cmake函数,该函数貌似只能创建具有一个源文件的target。在简单Google之后,发现其实create_single_source_cgal_program
也可以指定多个源文件,只不过target名称会根据第一个指定的源文件进行配置。请参考这里。
把create_single_source_cgal_program
生成的CMakeLists.txt作为模板,编写一个新的CMakeLists.txt文件如下
cmake_minimum_required(VERSION 3.16)
project( TestCGAL5 )
set(CMAKE_CXX_STANDARD 14)
set(CGAL_DIR /path/to/cgal)
find_package(CGAL REQUIRED QUIET OPTIONAL_COMPONENTS Core )
add_executable(TestCGAL5 main.cpp)
target_link_libraries(TestCGAL5 ${CGAL_LIBRARIES})
此时我们调用CMake时不再需要指定CGAL的位置。
用于测试的程序(附录)需要用到CGAL提供的draw()
函数来绘制一个mesh,未对CMakeLists.txt做任何更改时,编译无问题,但运行时将会报出“Impossible to draw, CGAL_USE_BASIC_VIEWER is not defined.” 的错误。
于是在首个cpp文件内加入了如下代码(参考这里)
#define CGAL_USE_BASIC_VIEWER
编译报错,报错内容与Qt相关。
这里真的要开始吐槽一下CGAL,文档真的是没有写如何正确的对Qt进行支持。
从文档里能找到的线索很有限,这里描述了CGAL需要使用到的Qt component。对于CMake,Qt有推荐的方式,参考这里。为了满足这些需求,除了Qt本身,某些系统库似乎也需要安装(参考这里和这里)
sudo apt-get install qt5-default
sudo apt-get install libqt5svg5-dev qtscript5-dev
但是当真的按照Qt推荐的方式去做时,却不能正常编译,会遇到有关autouic的错误,根据报错信息Google,根本得不到有效的解决方案(例如这个方案是不好使的)。于是想到了CGAL会不会有例程代码。还真有。
就在CGAL的INSTALL.md里,有描述如何编译例程代码。而用于举例说明编译流程的Triangulation_2/examples/Triangulation_2
文件夹内,就有一个需要调用CGAL::Draw()
的例程,文件名为draw_triangulation_2.cpp
。唯一用于编译所有保存在Triangulation_2/examples/Triangulation_2
内的cpp文件的CMakeLists.txt的内容如下
# Created by the script cgal_create_cmake_script
# This is the CMake script for compiling a CGAL application.
cmake_minimum_required(VERSION 3.1...3.15)
project( Triangulation_2_Examples )
if(NOT POLICY CMP0070 AND POLICY CMP0053)
# Only set CMP0053 to OLD with CMake<3.10, otherwise there is a warning.
cmake_policy(SET CMP0053 OLD)
endif()
if(POLICY CMP0071)
cmake_policy(SET CMP0071 NEW)
endif()
find_package(CGAL COMPONENTS Qt5)
if(CGAL_Qt5_FOUND)
add_definitions(-DCGAL_USE_BASIC_VIEWER -DQT_NO_KEYWORDS)
endif()
if ( CGAL_FOUND )
# create a target per cppfile
file(GLOB cppfiles RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
foreach(cppfile ${cppfiles})
create_single_source_cgal_program( "${cppfile}" )
endforeach()
if(CGAL_Qt5_FOUND)
target_link_libraries(draw_triangulation_2 PUBLIC CGAL::CGAL_Qt5)
else()
message(STATUS "NOTICE: The example draw_triangulation_2 requires Qt and will not be compiled.")
endif()
else()
message(STATUS "This program requires the CGAL library, and will not be compiled.")
endif()
首先一个关键点是,CGAL的示例CMakeLists.txt中,并未按照Qt5推荐的方式添加对Qt的依赖。另外有三点需要注意的地方是(1)find_package时需要指定Qt component;(2)使用add_definitions();(3)增加对CGAL::CGAL_Qt5的链接。
根据以上的观察,将我们一开始的CMakeLists.txt修改成如下形式
cmake_minimum_required(VERSION 3.16)
project( TestCGAL5 )
set(CMAKE_CXX_STANDARD 14)
set(CGAL_DIR /path/to/cgal)
find_package(CGAL REQUIRED COMPONENTS Qt5 OPTIONAL_COMPONENTS Core )
if(CGAL_Qt5_FOUND)
add_definitions(-DCGAL_USE_BASIC_VIEWER -DQT_NO_KEYWORDS)
endif()
add_executable(TestCGAL5 main.cpp)
target_link_libraries(TestCGAL5 ${CGAL_LIBRARIES} CGAL::CGAL_Qt5)
编译成功。
简单的测试程序,读入一个PLY文件作为surface mesh,在屏幕上绘制这个mesh。源码参考CGAL Surface Mesh官方文档。
#include
#include
#include
#include
#include
#include
#include
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Surface_mesh<Point> Mesh;
int main( int argc, char **argv ) {
std::cout << "Hello, TestCGAL5! " << std::endl;
if ( argc <= 1 ) {
throw std::runtime_error("Must use arguments. ");
}
std::ifstream ifs { argv[1] };
if ( !ifs ) {
std::stringstream ss;
ss << "Open " << argv[1] << " failed. ";
throw std::runtime_error(ss.str());
}
Mesh surfaceMesh;
if ( !CGAL::read_ply( ifs, surfaceMesh ) ) {
throw std::runtime_error("Failed to real the file. ");
}
ifs.close();
CGAL::draw(surfaceMesh);
return 0;
}