安装测试CGAL和read_ply()

起因

想用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

clone repo

从CGAL的Github clone 到本地。
当前CGAL的版本是5.0.2,通过git checkout releases/CGAL-5.0.2切换到对应的tag上。

与本地project结合

在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的位置。

增加Qt支持

用于测试的程序(附录)需要用到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;
}

你可能感兴趣的:(CGAL)