Merge branch 'Surface_modeling-integration_with_BGL-GF'

Introduces the Surface modeling package that allows
  to deform a triangulated surface mesh

Successfully tested in CGAL-4.5-Ic-90
Approved by the Release Manager

Conflicts:
	Installation/changes.html
This commit is contained in:
Sébastien Loriot 2014-07-08 19:53:36 +02:00
commit 16441b0f6d
502 changed files with 796083 additions and 375 deletions

View File

@ -2335,6 +2335,57 @@ ADDRESS = "Saarbr{\"u}cken, Germany"
series = {Texts and Monographs in Symbolic Computation} series = {Texts and Monographs in Symbolic Computation}
} }
@inproceedings{Sorkine2007AsRigidAs,
title={As-rigid-as-possible surface modeling},
author={Sorkine, Olga and Alexa, Marc},
booktitle={ACM International Conference Proceeding Series},
volume={257},
pages={109--116},
year={2007},
organization={Citeseer}
}
@article{Pinkall1993Cotangent,
title={Computing discrete minimal surfaces and their conjugates},
author={Pinkall, Ulrich and Polthier, Konrad},
journal={Experimental mathematics},
volume={2},
number={1},
pages={15--36},
year={1993},
publisher={Taylor \& Francis}
}
@article{Botsch2008OnLinearVariational,
title={On linear variational surface deformation methods},
author={Botsch, Mario and Sorkine, Olga},
journal={Visualization and Computer Graphics, IEEE Transactions on},
volume={14},
number={1},
pages={213--230},
year={2008},
publisher={IEEE}
}
@inproceedings{Chao2010SimpleGeomModel,
author = {Chao, Isaac and Pinkall, Ulrich and Sanan, Patrick and Schr\"{o}der, Peter},
title = {A simple geometric model for elastic deformations},
booktitle = {ACM SIGGRAPH 2010 papers},
series = {SIGGRAPH '10},
year = {2010},
pages = {38:1--38:6},
publisher = {ACM}
}
@misc{Sorkine2009LeastSquaresRigid,
title={Least-Squares Rigid Motion Using {SVD}},
journal={Technical notes},
author={Sorkine, Olga},
year={2009},
pages={1-6},
howpublished = {http://igl.ethz.ch/projects/ARAP/}
}
% ---------------------------------------------------------------------------- % ----------------------------------------------------------------------------
% END OF BIBFILE % END OF BIBFILE
% ---------------------------------------------------------------------------- % ----------------------------------------------------------------------------

View File

@ -151900,3 +151900,11 @@ pages = {179--189}
publisher = {Society for Industrial and Applied Mathematics}, publisher = {Society for Industrial and Applied Mathematics},
address = {Philadelphia, PA, USA}, address = {Philadelphia, PA, USA},
} }
@book{botsch2010polygon,
title={Polygon mesh processing},
author={Botsch, Mario and Kobbelt, Leif and Pauly, Mark and Alliez, Pierre and L{\'e}vy, Bruno and others},
publisher = "A K Peters, Ltd.",
year={2010}
}

View File

@ -112,3 +112,4 @@ IMAGE_PATH = ${CMAKE_SOURCE_DIR}/Documentation/doc/Documentation/fig \
${CMAKE_SOURCE_DIR}/Surface_reconstruction_points_3/doc/Surface_reconstruction_points_3/fig \ ${CMAKE_SOURCE_DIR}/Surface_reconstruction_points_3/doc/Surface_reconstruction_points_3/fig \
${CMAKE_SOURCE_DIR}/Stream_lines_2/doc/Stream_lines_2/fig \ ${CMAKE_SOURCE_DIR}/Stream_lines_2/doc/Stream_lines_2/fig \
${CMAKE_SOURCE_DIR}/Stream_support/doc/Stream_support/fig \ ${CMAKE_SOURCE_DIR}/Stream_support/doc/Stream_support/fig \
${CMAKE_SOURCE_DIR}/Surface_modeling/doc/Surface_modeling/fig \

View File

@ -79,3 +79,4 @@ Surface_reconstruction_points_3
Surface_mesh_segmentation Surface_mesh_segmentation
Stream_lines_2 Stream_lines_2
Stream_support Stream_support
Surface_modeling

View File

@ -102,6 +102,7 @@ h1 {
\package_listing{Ridges_3} \package_listing{Ridges_3}
\package_listing{Jet_fitting_3} \package_listing{Jet_fitting_3}
\package_listing{Point_set_processing_3} \package_listing{Point_set_processing_3}
\package_listing{Surface_modeling}
\section PartSearchStructures Spatial Searching and Sorting \section PartSearchStructures Spatial Searching and Sorting

View File

@ -208,7 +208,15 @@ and <code>src/</code> directories).
the macro <code>CGAL_SMS_EDGE_PROFILE_ALWAYS_NEED_UNIQUE_VERTEX_IN_LINK</code> the macro <code>CGAL_SMS_EDGE_PROFILE_ALWAYS_NEED_UNIQUE_VERTEX_IN_LINK</code>
will restore the former behavior. will restore the former behavior.
</li> </li>
</ul>
<h3>Triangulated Surface Mesh Deformation (new package)</h3>
<ul>
<li> This package allows to deform a triangulated surface mesh
under positional constraints of some of its vertices without
requiring any additional structure other than the surface mesh itself.
The methods provided implements an as-rigid-as-possible deformation.
</li>
</ul> </ul>
<h3>3D Convex Hulls</h3> <h3>3D Convex Hulls</h3>

View File

@ -0,0 +1,65 @@
// Copyright (c) 2010-2011 GeometryFactory Sarl (France)
//
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 3 of the License,
// or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// $URL$
// $Id$
//
//
// Author(s) : Laurent Rineau
// The main goal of FPU.h is to define functions or macros to modify the
// control word of the FPU, to:
// - set the precision to 53 bits of mantissa,
// - get/set the rounding mode.
//
// The goal of FPU_extension.h is to define inline functions similar to
// feclearexcept and fetestexcept of C99.
//
// For the moment, only i386 and x64 processors are supported, with MSVC,
// gcc, or the Intel compiler suite. Otherwise, the non-inline functions of
// C99 are used.
#ifndef CGAL_FPU_EXTENSION_H
#define CGAL_FPU_EXTENSION_H
#if __i386__ && !defined __PGI && !defined __SUNPRO_CC
# ifdef CGAL_SAFE_SSE2
# include <CGAL/FPU_gcc_i386_sse2.h>
# else
# include <CGAL/FPU_gcc_i386.h>
# endif
#elif defined _MSC_VER
# include <CGAL/FPU_msvc.h>
#else
// generic functions, using C99
extern "C" {
# include <fenv.h>
}
namespace CGAL {
inline int
feclearexcept(int exceptions) {
return ::feclearexcept(exceptions);
}
inline int
fetestexcept(int exceptions) {
return ::fetestexcept(exceptions);
}
} // end namespace CGAL
#endif // use fenv
#endif // CGAL_FPU_EXTENSION_H

View File

@ -0,0 +1,38 @@
// Copyright (c) 2010-2011 GeometryFactory Sarl (France)
//
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 3 of the License,
// or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// $URL$
// $Id$
//
//
// Author(s) : Laurent Rineau
extern "C" {
#include <fenv.h>
}
namespace CGAL {
// brute-force replacement for C99 (which does not require an inline-function)
inline int
feclearexcept(int exceptions) {
// TODO: clear only given exceptions
asm volatile("fnclex");
return 0;
}
inline int
fetestexcept(int exceptions) {
int status;
asm volatile("fnstsw %0" : "=m" (status));
return status & exceptions;
}
} // end namespace CGAL

View File

@ -0,0 +1,41 @@
// Copyright (c) 2010-2011 GeometryFactory Sarl (France)
//
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 3 of the License,
// or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// $URL$
// $Id$
//
//
// Author(s) : Laurent Rineau
extern "C" {
#include <fenv.h>
}
namespace CGAL {
// replacement for C99
inline int
feclearexcept(int exceptions) {
int mxcsr;
asm volatile("stmxcsr %0" : "=m" (mxcsr) );
mxcsr &= ~exceptions;
asm volatile("ldmxcsr %0" : : "m" (mxcsr) );
return 0;
}
inline int
fetestexcept(int exceptions) {
int status;
asm volatile("stmxcsr %0" : "=m" (status) );
return status & exceptions;
}
} // end namespace CGAL

View File

@ -0,0 +1,52 @@
// Copyright (c) 2010-2011 GeometryFactory Sarl (France)
//
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 3 of the License,
// or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// $URL$
// $Id$
//
//
// Author(s) : Laurent Rineau
#include <cfloat>
#ifndef FE_INVALID
# define FE_INEXACT _EM_INEXACT
# define FE_UNDERFLOW _EM_UNDERFLOW
# define FE_OVERFLOW _EM_OVERFLOW
# define FE_DIVBYZERO _EM_ZERODIVIDE
# define FE_INVALID _EM_INVALID
#endif
namespace CGAL {
// replacement for C99 functions
inline int
feclearexcept(int exceptions) {
_clearfp();
return 0;
}
inline int
fetestexcept(int exceptions)
{
#if defined(_M_IX86) && _M_IX86_FP > 0
// On x86/x64 processors, when SSE is used.
unsigned int i1;
unsigned int i2;
_statusfp2(&i1, &i2);
return (i1 & exceptions) | (i2 & exceptions);
#else
// On x86 processors without SSE, or on other processors supported by MSVC
return _statusfp() & exceptions;
#endif
}
} // end namespace CGAL

View File

@ -61,22 +61,12 @@ endif(QT4_FOUND)
# Eigen is now used by default # Eigen is required by default
find_package(Eigen3 3.1.0) #(requires 3.1.0 or greater) find_package(Eigen3 3.1.0) #(requires 3.1.0 or greater)
if (NOT EIGEN3_FOUND) if (EIGEN3_FOUND)
# Find LAPACK (optional), for curvatures estimation
find_package(LAPACK)
if(LAPACK_FOUND)
include( ${LAPACK_USE_FILE} )
endif(LAPACK_FOUND)
# Find TAUCS (optionnal), for parametrization
find_package(TAUCS)
if(TAUCS_FOUND)
include( ${TAUCS_USE_FILE} )
endif(TAUCS_FOUND)
else()
include( ${EIGEN3_USE_FILE} ) include( ${EIGEN3_USE_FILE} )
else()
message(STATUS "NOTICE: parametrization, poisson reconstruction and curvatures estimation plugins require Eigen 3.1 (or greater) and will not be available.")
endif() endif()
@ -87,16 +77,6 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
include_directories ( ${QGLVIEWER_INCLUDE_DIR} ) include_directories ( ${QGLVIEWER_INCLUDE_DIR} )
# Parameterization needs Eigen3 or TAUCS
if(NOT EIGEN3_FOUND AND NOT TAUCS_FOUND)
message(STATUS "NOTICE: Eigen 3.1 (or greater) and TAUCS is not found. parametrization will not be available.")
endif(NOT EIGEN3_FOUND AND NOT TAUCS_FOUND)
# Curvature estimation needs Eigen3 or LAPACK
if(NOT EIGEN3_FOUND AND NOT LAPACK_FOUND)
message(STATUS "NOTICE: Eigen 3.1 (or greater) and LAPACK is not found. curvatures estimation will not be available.")
endif(NOT EIGEN3_FOUND AND NOT LAPACK_FOUND)
qt4_wrap_ui( MainWindowUI_files MainWindow.ui ) qt4_wrap_ui( MainWindowUI_files MainWindow.ui )
qt4_wrap_ui( FileLoaderDialogUI_files FileLoaderDialog.ui ) qt4_wrap_ui( FileLoaderDialogUI_files FileLoaderDialog.ui )
qt4_wrap_ui( Show_point_dialogUI_FILES Show_point_dialog.ui ) qt4_wrap_ui( Show_point_dialogUI_FILES Show_point_dialog.ui )
@ -132,7 +112,6 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
Scene_polyhedron_item.cpp Scene_polyhedron_item.cpp
Scene_polyhedron_transform_item.cpp Scene_polyhedron_transform_item.cpp
Scene_polylines_item.cpp Scene_polylines_item.cpp
Scene_edit_polyhedron_item.cpp
Scene_textured_polyhedron_item.cpp Scene_textured_polyhedron_item.cpp
Scene_c2t3_item.cpp Scene_c2t3_item.cpp
Scene_nef_polyhedron_item.cpp Scene_nef_polyhedron_item.cpp
@ -199,10 +178,6 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
add_item(scene_polylines_item Scene_polylines_item.cpp Scene_polylines_item.moc) add_item(scene_polylines_item Scene_polylines_item.cpp Scene_polylines_item.moc)
add_item(scene_edit_polyhedron_item Scene_edit_polyhedron_item.cpp Scene_edit_polyhedron_item.moc)
# special
target_link_libraries(scene_edit_polyhedron_item scene_polyhedron_item)
add_item(scene_polyhedron_item_decorator Scene_polyhedron_item_decorator.cpp Scene_polyhedron_item_decorator.moc) add_item(scene_polyhedron_item_decorator Scene_polyhedron_item_decorator.cpp Scene_polyhedron_item_decorator.moc)
target_link_libraries(scene_polyhedron_item_decorator scene_polyhedron_item) target_link_libraries(scene_polyhedron_item_decorator scene_polyhedron_item)
@ -212,9 +187,9 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
add_item(scene_polyhedron_selection_item Scene_polyhedron_selection_item.cpp Scene_polyhedron_selection_item.moc) add_item(scene_polyhedron_selection_item Scene_polyhedron_selection_item.cpp Scene_polyhedron_selection_item.moc)
target_link_libraries(scene_polyhedron_selection_item scene_polyhedron_item_decorator scene_polyhedron_item_k_ring_selection) target_link_libraries(scene_polyhedron_selection_item scene_polyhedron_item_decorator scene_polyhedron_item_k_ring_selection)
if(EIGEN3_FOUND OR TAUCS_FOUND) if(EIGEN3_FOUND )
add_item(scene_textured_polyhedron_item Scene_textured_polyhedron_item.cpp texture.cpp Scene_textured_polyhedron_item.moc) add_item(scene_textured_polyhedron_item Scene_textured_polyhedron_item.cpp texture.cpp Scene_textured_polyhedron_item.moc)
endif(EIGEN3_FOUND OR TAUCS_FOUND) endif()
add_item(scene_implicit_function_item Scene_implicit_function_item.cpp Scene_implicit_function_item.moc Color_ramp.cpp) add_item(scene_implicit_function_item Scene_implicit_function_item.cpp Scene_implicit_function_item.moc Color_ramp.cpp)
@ -247,7 +222,7 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
# Viewer_moc.cpp # Viewer_moc.cpp
${FileLoaderDialogUI_files} ${MainWindowUI_files} ${PreferencesUI_FILES} ${RESOURCE_FILES} ) ${FileLoaderDialogUI_files} ${MainWindowUI_files} ${PreferencesUI_FILES} ${RESOURCE_FILES} )
add_to_cached_list( CGAL_EXECUTABLE_TARGETS Polyhedron_3 ) add_to_cached_list( CGAL_EXECUTABLE_TARGETS Polyhedron_3 )
if(EIGEN3_FOUND OR TAUCS_FOUND) if(EIGEN3_FOUND)
# add_executable( Polyhedron_3 Scene_tex_rendering.cpp Scene_tex_polyhedron_operations.cpp ) # add_executable( Polyhedron_3 Scene_tex_rendering.cpp Scene_tex_polyhedron_operations.cpp )
endif() endif()
# else(POLYHEDRON_DEMO_ENABLE_FORWARD_DECL) # else(POLYHEDRON_DEMO_ENABLE_FORWARD_DECL)
@ -352,20 +327,16 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
polyhedron_demo_plugin(pca_plugin Polyhedron_demo_pca_plugin) polyhedron_demo_plugin(pca_plugin Polyhedron_demo_pca_plugin)
target_link_libraries(pca_plugin scene_polyhedron_item scene_basic_objects) target_link_libraries(pca_plugin scene_polyhedron_item scene_basic_objects)
if(EIGEN3_FOUND OR TAUCS_FOUND) if(EIGEN3_FOUND)
polyhedron_demo_plugin(parameterization_plugin Polyhedron_demo_parameterization_plugin) polyhedron_demo_plugin(parameterization_plugin Polyhedron_demo_parameterization_plugin)
target_link_libraries(parameterization_plugin scene_polyhedron_item scene_textured_polyhedron_item ) target_link_libraries(parameterization_plugin scene_polyhedron_item scene_textured_polyhedron_item )
else(EIGEN3_FOUND OR TAUCS_FOUND) endif()
message(STATUS "NOTICE: Neither TAUCS nor Eigen 3.1 (or greater) libraries have been found. Parameterization will not be available.")
endif(EIGEN3_FOUND OR TAUCS_FOUND)
if(TAUCS_FOUND OR EIGEN3_FOUND) if(EIGEN3_FOUND)
qt4_wrap_ui( poissonUI_FILES Polyhedron_demo_poisson_plugin.ui) qt4_wrap_ui( poissonUI_FILES Polyhedron_demo_poisson_plugin.ui)
polyhedron_demo_plugin(poisson_plugin Polyhedron_demo_poisson_plugin Polyhedron_demo_poisson_plugin_impl.cpp ${poissonUI_FILES}) polyhedron_demo_plugin(poisson_plugin Polyhedron_demo_poisson_plugin Polyhedron_demo_poisson_plugin_impl.cpp ${poissonUI_FILES})
target_link_libraries(poisson_plugin scene_polyhedron_item scene_points_with_normal_item) target_link_libraries(poisson_plugin scene_polyhedron_item scene_points_with_normal_item)
else(TAUCS_FOUND OR EIGEN3_FOUND) endif()
message(STATUS "NOTICE: Neither TAUCS nor Eigen 3.1 (or greater) libraries have been found. Poisson reconstruction will not be available.")
endif(TAUCS_FOUND OR EIGEN3_FOUND)
# Link with BLAS and LAPACK only (optional), for Jet Fitting # Link with BLAS and LAPACK only (optional), for Jet Fitting
@ -388,7 +359,7 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
target_link_libraries(jet_fitting_plugin scene_polyhedron_item scene_polylines_item) target_link_libraries(jet_fitting_plugin scene_polyhedron_item scene_polylines_item)
else(EIGEN3_FOUND OR LAPACK_FOUND) else(EIGEN3_FOUND OR LAPACK_FOUND)
message(STATUS "NOTICE: Nor Eigen 3.1 (or greater) nor LAPACK library were found. Normal estimation and smoothing will not be available.") message(STATUS "NOTICE: Normal estimation and smoothing plugins require Eigen 3.1 (or greater) or LAPACK libraries and will not be available.")
endif(EIGEN3_FOUND OR LAPACK_FOUND) endif(EIGEN3_FOUND OR LAPACK_FOUND)
polyhedron_demo_plugin(self_intersection_plugin Polyhedron_demo_self_intersection_plugin) polyhedron_demo_plugin(self_intersection_plugin Polyhedron_demo_self_intersection_plugin)
@ -426,8 +397,19 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
polyhedron_demo_plugin(trivial_plugin Polyhedron_demo_trivial_plugin) polyhedron_demo_plugin(trivial_plugin Polyhedron_demo_trivial_plugin)
polyhedron_demo_plugin(edit_polyhedron_plugin Polyhedron_demo_edit_polyhedron_plugin) # Edit polyhedron scene item and plugin
target_link_libraries(edit_polyhedron_plugin scene_polyhedron_item scene_edit_polyhedron_item) if ( EIGEN3_FOUND AND "${EIGEN3_VERSION}" VERSION_GREATER "3.1.90" )
qt4_wrap_ui( editionUI_FILES Deform_mesh.ui )
qt4_automoc( Scene_edit_polyhedron_item.cpp )
add_item(scene_edit_polyhedron_item Scene_edit_polyhedron_item.cpp Scene_edit_polyhedron_item.moc ${editionUI_FILES})
target_link_libraries(scene_edit_polyhedron_item scene_polyhedron_item scene_polyhedron_item_k_ring_selection)
polyhedron_demo_plugin(edit_polyhedron_plugin Polyhedron_demo_edit_polyhedron_plugin ${editionUI_FILES})
target_link_libraries(edit_polyhedron_plugin scene_polyhedron_item scene_edit_polyhedron_item)
else()
message(STATUS "NOTICE: The polyhedron edit plugin require Eigen 3.2 (or higher) and will not be available.")
endif()
polyhedron_demo_plugin(cut_plugin Polyhedron_demo_cut_plugin) polyhedron_demo_plugin(cut_plugin Polyhedron_demo_cut_plugin)
target_link_libraries(cut_plugin scene_polyhedron_item scene_basic_objects) target_link_libraries(cut_plugin scene_polyhedron_item scene_basic_objects)

View File

@ -0,0 +1,353 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DeformMesh</class>
<widget class="QDockWidget" name="DeformMesh">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>357</width>
<height>491</height>
</rect>
</property>
<property name="windowTitle">
<string>Surface Mesh Deformation</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="SelectionGroupBox">
<property name="title">
<string>Selection</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="1" rowspan="2" colspan="3">
<layout class="QVBoxLayout" name="RingLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Brush Size ROI:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Control Vertices Brush Size:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QSpinBox" name="BrushSpinBoxRoi"/>
</item>
<item>
<widget class="QSpinBox" name="BrushSpinBoxCtrlVert"/>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item row="4" column="0" colspan="5">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="SelectAllVerticesPushButton">
<property name="text">
<string>Set All Vertices as ROI</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ClearROIPushButton">
<property name="text">
<string>Clear ROI</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0" rowspan="2">
<widget class="QGroupBox" name="ROICtrlVertGroupBox">
<layout class="QVBoxLayout" name="CtrlVertRoiLayout">
<item>
<widget class="QRadioButton" name="ROIRadioButton">
<property name="toolTip">
<string>Use Shift + Left Click to paint ROI vertices</string>
</property>
<property name="text">
<string>ROI</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="CtrlVertRadioButton">
<property name="toolTip">
<string>Use Shift + Left Click to paint control vertices</string>
</property>
<property name="text">
<string>Control vertices</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="4" rowspan="2">
<widget class="QGroupBox" name="InsertEraseGroupBox">
<layout class="QVBoxLayout" name="InsertRemoveLayout">
<item>
<widget class="QRadioButton" name="InsertRadioButton">
<property name="text">
<string>Insertion</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="EraseRadioButton">
<property name="text">
<string>Removal</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Isolated Component Size:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="Threshold_size_spin_box">
<property name="maximum">
<number>999999999</number>
</property>
<property name="value">
<number>8</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="Get_minimum_button">
<property name="text">
<string>Get Minimum</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="Select_isolated_components_button">
<property name="text">
<string>Select Isolated Components Below Threshold</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="CtrlVertGroupNavigationGroupBox">
<property name="title">
<string>Group of Control Vertices Navigation</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="PrevCtrlVertPushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;&lt;</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="NextCtrlVertPushButton">
<property name="text">
<string>&gt;&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="AddCtrlVertPushButton">
<property name="text">
<string>Create new</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="DeleteCtrlVertPushButton">
<property name="text">
<string>Delete </string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="ActivatePivotingCheckBox">
<property name="text">
<string>Activate Pivoting </string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="OverwritePushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Update Original Positions</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QCheckBox" name="ShowROICheckBox">
<property name="text">
<string>Show ROI</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ShowAsSphereCheckBox">
<property name="text">
<string>Show As Sphere</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QPushButton" name="SaveROIPushButton">
<property name="text">
<string>Save ROI / Control Vertices</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ReadROIPushButton">
<property name="text">
<string>Load ROI / Control Vertices</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="ApplyAndCloseLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="ApplyAndClosePushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Apply and Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,11 +1,14 @@
#include "Polyhedron_demo_plugin_helper.h"
#include "Scene_polyhedron_item.h" #include "Scene_polyhedron_item.h"
#include "Scene_edit_polyhedron_item.h" #include "Scene_edit_polyhedron_item.h"
#include <QAction> #include <QAction>
#include <QMainWindow> #include <QMainWindow>
#include <QMessageBox> #include <QFileDialog>
#include <QInputDialog>
#include "Polyhedron_demo_plugin_helper.h" #include <QGLViewer/qglviewer.h>
#include "ui_Deform_mesh.h"
class Polyhedron_demo_edit_polyhedron_plugin : class Polyhedron_demo_edit_polyhedron_plugin :
public QObject, public QObject,
@ -16,112 +19,378 @@ class Polyhedron_demo_edit_polyhedron_plugin :
public: public:
Polyhedron_demo_edit_polyhedron_plugin() Polyhedron_demo_edit_polyhedron_plugin()
: Polyhedron_demo_plugin_helper(), size(0) : Polyhedron_demo_plugin_helper(), dock_widget(NULL)
{} { }
~Polyhedron_demo_edit_polyhedron_plugin()
{ }
void init(QMainWindow* mainWindow, Scene_interface* scene_interface); void init(QMainWindow* mainWindow, Scene_interface* scene_interface);
QList<QAction*> actions() const { QList<QAction*> actions() const;
return QList<QAction*>() << actionToggleEdit; bool applicable() const;
}
//! Applicable if any of the currently selected items is either a Polyhedron or an Edit polyhedron
bool applicable() const {
Q_FOREACH(Scene_interface::Item_id i, scene->selectionIndices())
{
if(qobject_cast<Scene_polyhedron_item*>(scene->item(i))
|| qobject_cast<Scene_edit_polyhedron_item*>(scene->item(i)))
return true;
}
return false;
}
public slots: public slots:
void on_actionToggleEdit_triggered(); void on_actionDeformation_triggered();
void edition(); /////// Dock window signal handlers //////
// what they do is simply transmiting required 'action' to selected scene_edit_polyhedron_item object
void on_AddCtrlVertPushButton_clicked();
void on_PrevCtrlVertPushButton_clicked();
void on_NextCtrlVertPushButton_clicked();
void on_SelectAllVerticesPushButton_clicked();
void on_DeleteCtrlVertPushButton_clicked();
void on_ApplyAndClosePushButton_clicked();
void on_ClearROIPushButton_clicked();
void on_ShowROICheckBox_stateChanged(int state);
void on_ShowAsSphereCheckBox_stateChanged(int state);
void on_ActivatePivotingCheckBox_stateChanged(int state);
void on_OverwritePushButton_clicked();
void on_SaveROIPushButton_clicked();
void on_ReadROIPushButton_clicked();
void dock_widget_visibility_changed(bool visible);
void on_Select_isolated_components_button_clicked();
void on_Get_minimum_button_clicked();
void on_BrushSpinBoxCtrlVert_changed(int);
void on_BrushSpinBoxRoi_changed(int);
void on_ROIRadioButton_toggled(bool);
void new_item_created(int item_id);
private: private:
QAction* actionToggleEdit; typedef Scene_interface::Item_id Item_id;
int size;
Scene_edit_polyhedron_item* convert_to_edit_polyhedron(Item_id, Scene_polyhedron_item*);
Scene_polyhedron_item* convert_to_plain_polyhedron(Item_id, Scene_edit_polyhedron_item*);
Ui::DeformMesh ui_widget;
QDockWidget* dock_widget;
QAction* actionDeformation;
}; // end Polyhedron_demo_edit_polyhedron_plugin }; // end Polyhedron_demo_edit_polyhedron_plugin
void Polyhedron_demo_edit_polyhedron_plugin::init(QMainWindow* mainWindow, QList<QAction*> Polyhedron_demo_edit_polyhedron_plugin::actions() const {
Scene_interface* scene_interface) return QList<QAction*>() << actionDeformation;
{
actionToggleEdit = new QAction(tr("Toggle &edition of item(s)"), mainWindow);
actionToggleEdit->setObjectName("actionToggleEdit");
Polyhedron_demo_plugin_helper::init(mainWindow, scene_interface);
} }
bool Polyhedron_demo_edit_polyhedron_plugin::applicable() const {
void Polyhedron_demo_edit_polyhedron_plugin::on_actionToggleEdit_triggered() {
bool found_polyhedron = false;
bool edit_needed = false;
QList<Scene_item*> changed_items;
Q_FOREACH(Scene_interface::Item_id i, scene->selectionIndices()) Q_FOREACH(Scene_interface::Item_id i, scene->selectionIndices())
{ {
if(Scene_polyhedron_item* poly_item = if(qobject_cast<Scene_polyhedron_item*>(scene->item(i))
qobject_cast<Scene_polyhedron_item*>(scene->item(i))) || qobject_cast<Scene_edit_polyhedron_item*>(scene->item(i)))
{ return true;
found_polyhedron = true;
Scene_edit_polyhedron_item* edit_poly =
new Scene_edit_polyhedron_item(poly_item);
edit_poly->setColor(poly_item->color());
edit_poly->setName(QString("%1 (edit)").arg(poly_item->name()));
scene->replaceItem(i, edit_poly);
changed_items.push_back(scene->item(i));
edit_needed = true;
connect(edit_poly, SIGNAL(modified()),
this, SLOT(edition()));
} else if(Scene_edit_polyhedron_item* poly_item =
qobject_cast<Scene_edit_polyhedron_item*>(scene->item(i)))
{
found_polyhedron = true;
scene->replaceItem(i, poly_item->to_polyhedron_item());
delete poly_item;
}
} }
if(found_polyhedron == false) { return false;
QMessageBox::warning(mw, tr("Warning"), }
tr("No polyhedron was selected"));
void Polyhedron_demo_edit_polyhedron_plugin::init(QMainWindow* mainWindow, Scene_interface* scene_interface)
{
mw = mainWindow;
scene = scene_interface;
actionDeformation = new QAction("Surface Mesh Deformation", mw);
actionDeformation->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E));
connect(actionDeformation, SIGNAL(triggered()), this, SLOT(on_actionDeformation_triggered()));
// Connect Scene::newItem so that, if dock_widget is visible, convert
// automatically polyhedron items to "edit polyhedron" items.
QObject* scene = dynamic_cast<QObject*>(scene_interface);
if(scene) {
connect(scene, SIGNAL(newItem(int)), this, SLOT(new_item_created(int)));
} else {
std::cerr << "ERROR " << __FILE__ << ":" << __LINE__ << " :"
<< " cannot convert scene_interface to scene!\n";
} }
if(edit_needed) {
size = QInputDialog::getInt(mw, ////////////////// Construct widget /////////////////////////////
tr("Polyhedron edition zone"), // First time, construct docking window
tr("Size of edition zone:"), dock_widget = new QDockWidget("Mesh Deformation", mw);
size /* default value */ , dock_widget->setVisible(false); // do not show at the beginning
0 /* min */ );
std::cerr << "size = " << size << std::endl; ui_widget.setupUi(dock_widget);
Q_FOREACH(Scene_item* item, changed_items) mw->addDockWidget(Qt::LeftDockWidgetArea, dock_widget);
{
Scene_edit_polyhedron_item* poly_edit_item = connect(ui_widget.AddCtrlVertPushButton, SIGNAL(clicked()), this, SLOT(on_AddCtrlVertPushButton_clicked()));
qobject_cast<Scene_edit_polyhedron_item*>(item); connect(ui_widget.PrevCtrlVertPushButton, SIGNAL(clicked()), this, SLOT(on_PrevCtrlVertPushButton_clicked()));
if(poly_edit_item) poly_edit_item->setZoneSize(size); connect(ui_widget.NextCtrlVertPushButton, SIGNAL(clicked()), this, SLOT(on_NextCtrlVertPushButton_clicked()));
connect(ui_widget.SelectAllVerticesPushButton, SIGNAL(clicked()), this, SLOT(on_SelectAllVerticesPushButton_clicked()));
connect(ui_widget.DeleteCtrlVertPushButton, SIGNAL(clicked()), this, SLOT(on_DeleteCtrlVertPushButton_clicked()));
connect(ui_widget.ApplyAndClosePushButton, SIGNAL(clicked()), this, SLOT(on_ApplyAndClosePushButton_clicked()));
connect(ui_widget.ClearROIPushButton, SIGNAL(clicked()), this, SLOT(on_ClearROIPushButton_clicked()));
connect(ui_widget.ShowROICheckBox, SIGNAL(stateChanged(int)), this, SLOT(on_ShowROICheckBox_stateChanged(int)));
connect(ui_widget.ShowAsSphereCheckBox, SIGNAL(stateChanged(int)), this, SLOT(on_ShowAsSphereCheckBox_stateChanged(int)));
connect(ui_widget.ActivatePivotingCheckBox, SIGNAL(stateChanged(int)), this, SLOT(on_ActivatePivotingCheckBox_stateChanged(int)));
connect(ui_widget.OverwritePushButton, SIGNAL(clicked()), this, SLOT(on_OverwritePushButton_clicked()));
connect(ui_widget.Select_isolated_components_button, SIGNAL(clicked()), this, SLOT(on_Select_isolated_components_button_clicked()));
connect(ui_widget.Get_minimum_button, SIGNAL(clicked()), this, SLOT(on_Get_minimum_button_clicked()));
connect(ui_widget.SaveROIPushButton, SIGNAL(clicked()), this, SLOT(on_SaveROIPushButton_clicked()));
connect(ui_widget.ReadROIPushButton, SIGNAL(clicked()), this, SLOT(on_ReadROIPushButton_clicked()));
connect(dock_widget, SIGNAL(visibilityChanged(bool)), this, SLOT(dock_widget_visibility_changed(bool)) );
connect(ui_widget.BrushSpinBoxRoi, SIGNAL(valueChanged(int)), this, SLOT(on_BrushSpinBoxRoi_changed(int)));
connect(ui_widget.BrushSpinBoxCtrlVert, SIGNAL(valueChanged(int)), this, SLOT(on_BrushSpinBoxCtrlVert_changed(int)));
connect(ui_widget.ROIRadioButton, SIGNAL(toggled(bool)), this, SLOT(on_ROIRadioButton_toggled(bool)));
///////////////////////////////////////////////////////////////////
}
void Polyhedron_demo_edit_polyhedron_plugin::on_actionDeformation_triggered()
{
// dock widget should be constructed in init()
if(dock_widget->isVisible()) { dock_widget->hide(); }
else { dock_widget->show(); }
}
/////// Dock window signal handlers //////
// what they do is simply transmitting required 'action' to selected scene_edit_polyhedron_item object
void Polyhedron_demo_edit_polyhedron_plugin::on_AddCtrlVertPushButton_clicked()
{
int item_id = scene->mainSelectionIndex();
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(item_id));
if(!edit_item) return; // the selected item is not of the right type
edit_item->create_ctrl_vertices_group();
}
void Polyhedron_demo_edit_polyhedron_plugin::on_PrevCtrlVertPushButton_clicked()
{
int item_id = scene->mainSelectionIndex();
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(item_id));
if(!edit_item) return; // the selected item is not of the right type
edit_item->prev_ctrl_vertices_group();
scene->itemChanged(edit_item); // for repaint
}
void Polyhedron_demo_edit_polyhedron_plugin::on_NextCtrlVertPushButton_clicked()
{
int item_id = scene->mainSelectionIndex();
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(item_id));
if(!edit_item) return; // the selected item is not of the right type
edit_item->next_ctrl_vertices_group();
scene->itemChanged(edit_item); // for repaint
}
void Polyhedron_demo_edit_polyhedron_plugin::on_SelectAllVerticesPushButton_clicked()
{
int item_id = scene->mainSelectionIndex();
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(item_id));
if(!edit_item) return; // the selected item is not of the right type
edit_item->set_all_vertices_as_roi();
scene->itemChanged(edit_item); // for repaint
}
void Polyhedron_demo_edit_polyhedron_plugin::on_DeleteCtrlVertPushButton_clicked()
{
int item_id = scene->mainSelectionIndex();
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(item_id));
if(!edit_item) return; // the selected item is not of the right type
edit_item->delete_ctrl_vertices_group();
scene->itemChanged(edit_item); // for repaint
}
void Polyhedron_demo_edit_polyhedron_plugin::on_ClearROIPushButton_clicked()
{
int item_id = scene->mainSelectionIndex();
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(item_id));
if(!edit_item) return; // the selected item is not of the right type
edit_item->clear_roi();
scene->itemChanged(edit_item); // for repaint
}
void Polyhedron_demo_edit_polyhedron_plugin::on_ApplyAndClosePushButton_clicked()
{
dock_widget->setVisible(false);
}
void Polyhedron_demo_edit_polyhedron_plugin::on_ShowROICheckBox_stateChanged(int /*state*/)
{
for(Scene_interface::Item_id i = 0, end = scene->numberOfEntries(); i < end; ++i)
{
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(i));
if(!edit_item) { continue; }
scene->itemChanged(edit_item); // just for redraw
}
}
void Polyhedron_demo_edit_polyhedron_plugin::on_ShowAsSphereCheckBox_stateChanged(int /*state*/)
{
for(Scene_interface::Item_id i = 0, end = scene->numberOfEntries(); i < end; ++i)
{
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(i));
if(!edit_item) { continue; }
scene->itemChanged(edit_item); // just for redraw
}
}
void Polyhedron_demo_edit_polyhedron_plugin::on_ActivatePivotingCheckBox_stateChanged(int state)
{
for(Scene_interface::Item_id i = 0, end = scene->numberOfEntries(); i < end; ++i)
{
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(i));
if(!edit_item) { continue; }
if(state == Qt::Checked) {
edit_item->pivoting_begin();
} }
else {
edit_item->pivoting_end();
}
scene->itemChanged(edit_item);
}
}
void Polyhedron_demo_edit_polyhedron_plugin::on_OverwritePushButton_clicked()
{
int item_id = scene->mainSelectionIndex();
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(item_id));
if(!edit_item) return; // the selected item is not of the right type
edit_item->overwrite_deform_object();
}
void Polyhedron_demo_edit_polyhedron_plugin::on_Select_isolated_components_button_clicked() {
int item_id = scene->mainSelectionIndex();
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(item_id));
if(!edit_item) return; // the selected item is not of the right type
boost::optional<std::size_t> minimum =
edit_item->select_isolated_components(ui_widget.Threshold_size_spin_box->value());
if(minimum) {
ui_widget.Threshold_size_spin_box->setValue((int) *minimum);
} }
} }
void Polyhedron_demo_edit_polyhedron_plugin::edition() { void Polyhedron_demo_edit_polyhedron_plugin::on_Get_minimum_button_clicked() {
QObject* obj = sender(); int item_id = scene->mainSelectionIndex();
Scene_edit_polyhedron_item* edit_item = Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(item_id));
qobject_cast<Scene_edit_polyhedron_item*>(obj); if(!edit_item) return; // the selected item is not of the right type
if(!edit_item) {
std::cerr << "ERROR" << __FILE__ << ":" << __LINE__ boost::optional<std::size_t> minimum = edit_item->get_minimum_isolated_component();
<< " : " << "unknown object type" << std::endl; if(minimum) {
return; ui_widget.Threshold_size_spin_box->setValue((int) *minimum);
} }
}
typedef Kernel::Point_3 Point; void Polyhedron_demo_edit_polyhedron_plugin::on_SaveROIPushButton_clicked()
typedef Kernel::Vector_3 Vector; {
typedef Polyhedron::Vertex_handle Vertex_handle; int item_id = scene->mainSelectionIndex();
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(item_id));
if(!edit_item) return;
const Point& orig = edit_item->original_position(); QString fileName = QFileDialog::getSaveFileName(mw, "Save",
const Vector move_vector = edit_item->current_position() - orig; "roi.txt", "Text (*.txt)");
if(fileName.isNull()) { return; }
Q_FOREACH(Vertex_handle vh, edit_item->selected_vertices()) edit_item->save_roi(fileName.toLocal8Bit().data());
{ }
vh->point() = vh->point() + move_vector; void Polyhedron_demo_edit_polyhedron_plugin::on_ReadROIPushButton_clicked()
} {
edit_item->changed(); // that reset the original_position() int item_id = scene->mainSelectionIndex();
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(item_id));
if(!edit_item) return;
QString fileName = QFileDialog::getOpenFileName(mw, "Read",
"roi.txt", "Text (*.txt)");
if(fileName.isNull()) { return; }
edit_item->read_roi(fileName.toLocal8Bit().data());
scene->itemChanged(edit_item); scene->itemChanged(edit_item);
} }
void Polyhedron_demo_edit_polyhedron_plugin::dock_widget_visibility_changed(bool visible)
{
for(Scene_interface::Item_id i = 0, end = scene->numberOfEntries();
i < end; ++i)
{
Scene_polyhedron_item* poly_item = qobject_cast<Scene_polyhedron_item*>(scene->item(i));
if (poly_item)
{ poly_item->update_halfedge_indices(); }
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(i));
if(visible && poly_item) {
convert_to_edit_polyhedron(i, poly_item);
} else if(!visible && edit_item) {
convert_to_plain_polyhedron(i, edit_item);
}
}
//QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin();
//if(visible)
//{
// viewer->camera()->setType(qglviewer::Camera::ORTHOGRAPHIC);
//}else
//{
// viewer->camera()->setType(qglviewer::Camera::PERSPECTIVE);
//}
}
void Polyhedron_demo_edit_polyhedron_plugin::on_ROIRadioButton_toggled(bool value) {
int k_ring = value ? ui_widget.BrushSpinBoxRoi->value() :
ui_widget.BrushSpinBoxCtrlVert->value();
for(Scene_interface::Item_id i = 0, end = scene->numberOfEntries(); i < end; ++i)
{
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(i));
if(!edit_item) { continue; }
edit_item->set_k_ring(k_ring);
}
}
void Polyhedron_demo_edit_polyhedron_plugin::on_BrushSpinBoxCtrlVert_changed(int value) {
if(ui_widget.ROIRadioButton->isChecked()) { return; }
for(Scene_interface::Item_id i = 0, end = scene->numberOfEntries(); i < end; ++i)
{
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(i));
if(!edit_item) { continue; }
edit_item->set_k_ring(value);
}
}
void Polyhedron_demo_edit_polyhedron_plugin::on_BrushSpinBoxRoi_changed(int value) {
if(!ui_widget.ROIRadioButton->isChecked()) { return; }
for(Scene_interface::Item_id i = 0, end = scene->numberOfEntries(); i < end; ++i)
{
Scene_edit_polyhedron_item* edit_item = qobject_cast<Scene_edit_polyhedron_item*>(scene->item(i));
if(!edit_item) { continue; }
edit_item->set_k_ring(value);
}
}
void Polyhedron_demo_edit_polyhedron_plugin::new_item_created(int item_id)
{
if(dock_widget->isVisible()) {
Scene_polyhedron_item* poly_item =
qobject_cast<Scene_polyhedron_item*>(scene->item(item_id));
if(poly_item) {
convert_to_edit_polyhedron(item_id, poly_item);
}
}
}
Scene_edit_polyhedron_item*
Polyhedron_demo_edit_polyhedron_plugin::convert_to_edit_polyhedron(Item_id i,
Scene_polyhedron_item* poly_item)
{
QString poly_item_name = poly_item->name();
Scene_edit_polyhedron_item* edit_poly = new Scene_edit_polyhedron_item(poly_item, &ui_widget, mw);
edit_poly->setColor(poly_item->color());
edit_poly->setName(QString("%1 (edit)").arg(poly_item->name()));
edit_poly->setRenderingMode(Gouraud);
poly_item->setName(poly_item_name); // Because it is changed when the
// name of edit_poly is changed.
int k_ring = ui_widget.ROIRadioButton->isChecked() ? ui_widget.BrushSpinBoxRoi->value() :
ui_widget.BrushSpinBoxCtrlVert->value();
edit_poly->set_k_ring(k_ring);
scene->replaceItem(i, edit_poly);
return edit_poly;
}
Scene_polyhedron_item*
Polyhedron_demo_edit_polyhedron_plugin::convert_to_plain_polyhedron(Item_id i,
Scene_edit_polyhedron_item* edit_item)
{
Scene_polyhedron_item* poly_item = edit_item->to_polyhedron_item();
scene->replaceItem(i, poly_item);
delete edit_item;
return poly_item;
}
Q_EXPORT_PLUGIN2(Polyhedron_demo_edit_polyhedron_plugin, Polyhedron_demo_edit_polyhedron_plugin) Q_EXPORT_PLUGIN2(Polyhedron_demo_edit_polyhedron_plugin, Polyhedron_demo_edit_polyhedron_plugin)

View File

@ -1,60 +1,317 @@
#include "Scene_edit_polyhedron_item.h" #include "Scene_edit_polyhedron_item.h"
#include "Kernel_type.h"
#include "Polyhedron_type.h"
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <algorithm>
#include <QVariant>
#include <set>
#include <QObject>
#include <QMenu>
#include <QAction>
#include <CGAL/gl_render.h> #include <CGAL/gl_render.h>
#include <QGLViewer/manipulatedFrame.h> Scene_edit_polyhedron_item::Scene_edit_polyhedron_item
(Scene_polyhedron_item* poly_item,
typedef Polyhedron::Vertex_handle Vertex_handle; Ui::DeformMesh* ui_widget,
typedef std::set<Vertex_handle> Selected_vertices; QMainWindow* mw)
typedef Selected_vertices::iterator Selected_vertices_it; : ui_widget(ui_widget),
poly_item(poly_item),
struct Scene_edit_polyhedron_item_priv { deform_mesh(*(poly_item->polyhedron()), Deform_mesh::Vertex_index_map(), Deform_mesh::Hedge_index_map(), Array_based_vertex_point_map(&positions)),
Scene_polyhedron_item* poly_item; is_rot_free(true),
int zone_size; own_poly_item(true),
qglviewer::ManipulatedFrame* frame; k_ring_selector(poly_item, mw, Scene_polyhedron_item_k_ring_selection::Active_handle::VERTEX, true),
Selected_vertices selected_vertices; quadric(gluNewQuadric())
Vertex_handle selected_vertex;
Kernel::Point_3 orig_pos;
}; // end struct Scene_edit_polyhedron_item_priv
Scene_edit_polyhedron_item::Scene_edit_polyhedron_item(Scene_polyhedron_item* poly_item)
: d(new Scene_edit_polyhedron_item_priv)
{ {
d->poly_item = poly_item; mw->installEventFilter(this);
d->zone_size = 0; gluQuadricNormals(quadric, GLU_SMOOTH);
d->frame = new ManipulatedFrame(); // bind vertex picking
d->frame->setProperty("item", QVariant::fromValue<QObject*>(this)); connect(&k_ring_selector, SIGNAL(selected(const std::map<Polyhedron::Vertex_handle, int>&)), this,
if(!connect(poly_item, SIGNAL(selected_vertex(void*)), SLOT(selected(const std::map<Polyhedron::Vertex_handle, int>&)));
this, SLOT(vertex_has_been_selected(void*))))
std::cerr << __FILE__ << ": connection failed!\n"; poly_item->set_color_vector_read_only(true); // to prevent recomputation of color vector in changed()
poly_item->enable_facets_picking(true); poly_item->update_vertex_indices();
length_of_axis = bbox().diagonal_length() / 15.0;
// interleave events of viewer (there is only one viewer)
QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin();
viewer->installEventFilter(this);
// create an empty group of control vertices for starting
create_ctrl_vertices_group();
// start QObject's timer for continuous effects
// (deforming mesh while mouse not moving)
startTimer(0);
// Required for drawing functionality
positions.resize(num_vertices(*polyhedron())*3);
normals.resize(positions.size());
Polyhedron::Vertex_iterator vb, ve;
std::size_t counter = 0;
for(vb=polyhedron()->vertices_begin(), ve = polyhedron()->vertices_end();vb != ve; ++vb, ++counter) {
positions[counter*3] = vb->point().x();
positions[counter*3+1] = vb->point().y();
positions[counter*3+2] = vb->point().z();
const Polyhedron::Traits::Vector_3& n =
compute_vertex_normal<Polyhedron::Vertex, Polyhedron::Traits>(*vb);
normals[counter*3] = n.x();
normals[counter*3+1] = n.y();
normals[counter*3+2] = n.z();
}
tris.resize(polyhedron()->size_of_facets()*3);
counter = 0;
for(Polyhedron::Facet_handle fb = polyhedron()->facets_begin(); fb != polyhedron()->facets_end(); ++fb, ++counter) {
tris[counter*3] = fb->halfedge()->vertex()->id();
tris[counter*3+1] = fb->halfedge()->next()->vertex()->id();
tris[counter*3+2] = fb->halfedge()->prev()->vertex()->id();
}
edges.resize(polyhedron()->size_of_halfedges());
counter = 0;
for(Polyhedron::Edge_iterator eb = polyhedron()->edges_begin(); eb != polyhedron()->edges_end(); ++eb, ++counter) {
edges[counter*2] = eb->vertex()->id();
edges[counter*2+1] = eb->opposite()->vertex()->id();
}
} }
Scene_edit_polyhedron_item::~Scene_edit_polyhedron_item() Scene_edit_polyhedron_item::~Scene_edit_polyhedron_item()
{ {
delete d->frame; while(is_there_any_ctrl_vertices_group())
delete d; {
delete_ctrl_vertices_group(false);
}
gluDeleteQuadric(quadric);
if (own_poly_item) delete poly_item;
} }
Scene_edit_polyhedron_item* /////////////////////////////////////////////////////////
Scene_edit_polyhedron_item::clone() const { /////////// Most relevant functions lie here ///////////
return 0; void Scene_edit_polyhedron_item::deform()
}
QString
Scene_edit_polyhedron_item::toolTip() const
{ {
if(!d->poly_item->polyhedron()) if(!is_there_any_ctrl_vertices()) { return; }
for(Ctrl_vertices_group_data_list::iterator it = ctrl_vertex_frame_map.begin(); it != ctrl_vertex_frame_map.end(); ++it)
{ it->set_target_positions(); }
deform_mesh.deform();
poly_item->changed(); // now we need to call poly_item changed to delete AABB tree
emit itemChanged();
}
void Scene_edit_polyhedron_item::timerEvent(QTimerEvent* /*event*/)
{ // just handle deformation - paint like selection is handled in eventFilter()
if(state.ctrl_pressing && (state.left_button_pressing || state.right_button_pressing)) {
if(!ui_widget->ActivatePivotingCheckBox->isChecked()) {
deform();
}
else {
emit itemChanged(); // for redraw while Pivoting (since we close signals of manipulatedFrames while pivoting,
// for now redraw with timer)
}
}
}
bool Scene_edit_polyhedron_item::eventFilter(QObject* /*target*/, QEvent *event)
{
// This filter is both filtering events from 'viewer' and 'main window'
Mouse_keyboard_state_deformation old_state = state;
////////////////// TAKE EVENTS /////////////////////
// key events
if(event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
Qt::KeyboardModifiers modifiers = keyEvent->modifiers();
state.ctrl_pressing = modifiers.testFlag(Qt::ControlModifier);
state.shift_pressing = modifiers.testFlag(Qt::ShiftModifier);
}
// mouse events
if(event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease)
{
QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
if(mouse_event->button() == Qt::LeftButton) {
state.left_button_pressing = event->type() == QEvent::MouseButtonPress;
}
if(mouse_event->button() == Qt::RightButton) {
state.right_button_pressing = event->type() == QEvent::MouseButtonPress;
}
}
////////////////// //////////////// /////////////////////
if(!poly_item->visible()) { return false; } // if not visible just update event state but don't do any action
// check state changes between old and current state
bool ctrl_pressed_now = state.ctrl_pressing && !old_state.ctrl_pressing;
bool ctrl_released_now = !state.ctrl_pressing && old_state.ctrl_pressing;
if(ctrl_pressed_now || ctrl_released_now || event->type() == QEvent::HoverMove)
{// activate a handle manipulated frame
QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin();
const QPoint& p = viewer->mapFromGlobal(QCursor::pos());
bool need_repaint = activate_closest_manipulated_frame(p.x(), p.y());
if(need_repaint) { emit itemChanged(); }
}
return false;
}
#include "opengl_tools.h"
void Scene_edit_polyhedron_item::draw_edges() const {
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_DOUBLE, 0, positions.data());
glDrawElements(GL_LINES, (GLsizei) edges.size(), GL_UNSIGNED_INT, edges.data());
glDisableClientState(GL_VERTEX_ARRAY);
if(rendering_mode == Wireframe) {
draw_ROI_and_control_vertices();
}
}
void Scene_edit_polyhedron_item::draw() const {
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glVertexPointer(3, GL_DOUBLE, 0, positions.data());
glNormalPointer(GL_DOUBLE, 0, normals.data());
glDrawElements(GL_TRIANGLES, (GLsizei) tris.size(), GL_UNSIGNED_INT, tris.data());
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
CGAL::GL::Color color;
color.set_rgb_color(0, 0, 0);
draw_edges();
draw_ROI_and_control_vertices();
}
void Scene_edit_polyhedron_item::draw_ROI_and_control_vertices() const {
GLboolean enable_back_lighting = glIsEnabled(GL_LIGHTING);
glDisable(GL_LIGHTING);
CGAL::GL::Color color;
CGAL::GL::Point_size point_size; point_size.set_point_size(5);
color.set_rgb_color(0, 1.f, 0);
// draw ROI
if(ui_widget->ShowROICheckBox->isChecked()) {
BOOST_FOREACH(vertex_descriptor vd, deform_mesh.roi_vertices())
{
if(!deform_mesh.is_control_vertex(vd))
gl_draw_point( vd->point() );
}
}
// draw control vertices related things
QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin();
for(Ctrl_vertices_group_data_list::const_iterator hgb_data = ctrl_vertex_frame_map.begin(); hgb_data != ctrl_vertex_frame_map.end(); ++hgb_data)
{
if(hgb_data->frame == viewer->manipulatedFrame())
{
// draw axis
::glPushMatrix();
::glMultMatrixd(hgb_data->frame->matrix());
QGLViewer::drawAxis(length_of_axis);
::glPopMatrix();
// draw bbox
if(!ui_widget->ActivatePivotingCheckBox->isChecked())
{
color.set_rgb_color(1.0f, 0, 0);
::glPushMatrix();
::glTranslated(hgb_data->frame->position().x, hgb_data->frame->position().y, hgb_data->frame->position().z);
::glMultMatrixd(hgb_data->frame->orientation().matrix());
::glTranslated(-hgb_data->frame_initial_center.x, -hgb_data->frame_initial_center.y, -hgb_data->frame_initial_center.z);
draw_bbox(hgb_data->bbox);
::glPopMatrix();
}
}
// draw control vertices
if(hgb_data == active_group) { color.set_rgb_color(1.0f, 0, 0); }
else { color.set_rgb_color(0, 0, 1.0f); }
for(std::vector<vertex_descriptor>::const_iterator hb = hgb_data->ctrl_vertices_group.begin(); hb != hgb_data->ctrl_vertices_group.end(); ++hb)
{ gl_draw_point( (*hb)->point() );
}
}
if(enable_back_lighting) { glEnable(GL_LIGHTING); }
}
void Scene_edit_polyhedron_item::gl_draw_point(const Point& p) const
{
if(!ui_widget->ShowAsSphereCheckBox->isChecked()) {
::glBegin(GL_POINTS);
::glVertex3d(p.x(), p.y(), p.z());
::glEnd();
}
else {
GLint shading;
::glGetIntegerv(GL_SHADE_MODEL, &shading);
::glShadeModel(GL_SMOOTH);
::glPushMatrix();
::glTranslated(p.x(), p.y(), p.z());
::gluSphere(quadric, length_of_axis/15, 8, 8);
::glPopMatrix();
::glShadeModel(shading);
}
}
//////////////////////////////////////////////////////////
/////////////// from trivial_plugin //////////////////////
void Scene_edit_polyhedron_item::draw_bbox(const Scene_interface::Bbox& bb ) const {
::glBegin(GL_LINES);
gl_draw_edge(bb.xmin, bb.ymin, bb.zmin,
bb.xmax, bb.ymin, bb.zmin);
gl_draw_edge(bb.xmin, bb.ymin, bb.zmin,
bb.xmin, bb.ymax, bb.zmin);
gl_draw_edge(bb.xmin, bb.ymin, bb.zmin,
bb.xmin, bb.ymin, bb.zmax);
gl_draw_edge(bb.xmax, bb.ymin, bb.zmin,
bb.xmax, bb.ymax, bb.zmin);
gl_draw_edge(bb.xmax, bb.ymin, bb.zmin,
bb.xmax, bb.ymin, bb.zmax);
gl_draw_edge(bb.xmin, bb.ymax, bb.zmin,
bb.xmax, bb.ymax, bb.zmin);
gl_draw_edge(bb.xmin, bb.ymax, bb.zmin,
bb.xmin, bb.ymax, bb.zmax);
gl_draw_edge(bb.xmin, bb.ymin, bb.zmax,
bb.xmax, bb.ymin, bb.zmax);
gl_draw_edge(bb.xmin, bb.ymin, bb.zmax,
bb.xmin, bb.ymax, bb.zmax);
gl_draw_edge(bb.xmax, bb.ymax, bb.zmax,
bb.xmin, bb.ymax, bb.zmax);
gl_draw_edge(bb.xmax, bb.ymax, bb.zmax,
bb.xmax, bb.ymin, bb.zmax);
gl_draw_edge(bb.xmax, bb.ymax, bb.zmax,
bb.xmax, bb.ymax, bb.zmin);
::glEnd();
}
void Scene_edit_polyhedron_item::gl_draw_edge(double px, double py, double pz,
double qx, double qy, double qz) const
{
::glVertex3d(px,py,pz);
::glVertex3d(qx,qy,qz);
}
/////////////////////////////////////////////////////////////
void Scene_edit_polyhedron_item::changed()
{ update_normals(); }
Scene_polyhedron_item* Scene_edit_polyhedron_item::to_polyhedron_item() {
Scene_polyhedron_item* poly_item_tmp = poly_item;
poly_item->set_color_vector_read_only(false);
own_poly_item=false;
return poly_item_tmp;
}
Polyhedron* Scene_edit_polyhedron_item::polyhedron()
{ return poly_item->polyhedron(); }
const Polyhedron* Scene_edit_polyhedron_item::polyhedron() const
{ return poly_item->polyhedron(); }
QString Scene_edit_polyhedron_item::toolTip() const
{
if(!poly_item->polyhedron())
return QString(); return QString();
return QObject::tr("<p>Polyhedron <b>%1</b> (mode: %5, color: %6)</p>" return QObject::tr("<p>Polyhedron <b>%1</b> (mode: %5, color: %6)</p>"
@ -62,65 +319,49 @@ Scene_edit_polyhedron_item::toolTip() const
"Number of edges: %3<br />" "Number of edges: %3<br />"
"Number of facets: %4</p>") "Number of facets: %4</p>")
.arg(this->name()) .arg(this->name())
.arg(d->poly_item->polyhedron()->size_of_vertices()) .arg(poly_item->polyhedron()->size_of_vertices())
.arg(d->poly_item->polyhedron()->size_of_halfedges()/2) .arg(poly_item->polyhedron()->size_of_halfedges()/2)
.arg(d->poly_item->polyhedron()->size_of_facets()) .arg(poly_item->polyhedron()->size_of_facets())
.arg(this->renderingModeName()) .arg(this->renderingModeName())
.arg(this->color().name()); .arg(this->color().name());
} }
bool Scene_edit_polyhedron_item::isEmpty() const {
return poly_item->isEmpty();
}
Scene_edit_polyhedron_item::Bbox Scene_edit_polyhedron_item::bbox() const {
return poly_item->bbox();
}
#include "opengl_tools.h" void Scene_edit_polyhedron_item::setVisible(bool b) {
poly_item->setVisible(b);
void Scene_edit_polyhedron_item::draw() const { Scene_item::setVisible(b);
d->poly_item->direct_draw(); if(!b) {
if(!d->selected_vertices.empty()) { (*QGLViewer::QGLViewerPool().begin())->setManipulatedFrame(NULL);
CGAL::GL::Point_size point_size; point_size.set_point_size(5);
CGAL::GL::Color color; color.set_rgb_color(0, 0, 0);
::glBegin(GL_POINTS);
for(Selected_vertices_it
it = d->selected_vertices.begin(),
end = d->selected_vertices.end();
it != end; ++it)
{
const Kernel::Point_3& p = (*it)->point();
::glVertex3d(p.x(), p.y(), p.z());
}
::glEnd();
} }
} }
void Scene_edit_polyhedron_item::setColor(QColor c) {
Polyhedron* poly_item->setColor(c);
Scene_edit_polyhedron_item::polyhedron() { return d->poly_item->polyhedron(); } Scene_item::setColor(c);
const Polyhedron*
Scene_edit_polyhedron_item::polyhedron() const { return d->poly_item->polyhedron(); }
bool
Scene_edit_polyhedron_item::isEmpty() const {
return d->poly_item->isEmpty();
} }
void Scene_edit_polyhedron_item::setName(QString n) {
Scene_edit_polyhedron_item::Bbox Scene_item::setName(n);
Scene_edit_polyhedron_item::bbox() const { n.replace(" (edit)", "");
return d->poly_item->bbox(); poly_item->setName(n);
} }
void Scene_edit_polyhedron_item::setRenderingMode(RenderingMode m) {
poly_item->setRenderingMode(m);
void Scene_item::setRenderingMode(m);
Scene_edit_polyhedron_item::
changed()
{
d->poly_item->changed();
Scene_item::changed();
d->orig_pos = current_position();
} }
Scene_edit_polyhedron_item* Scene_edit_polyhedron_item::clone() const {
void return 0;
Scene_edit_polyhedron_item::select(double orig_x, }
double orig_y, void Scene_edit_polyhedron_item::select(
double orig_z, double orig_x,
double dir_x, double orig_y,
double dir_y, double orig_z,
double dir_z) double dir_x,
double dir_y,
double dir_z)
{ {
Scene_item::select(orig_x, Scene_item::select(orig_x,
orig_y, orig_y,
@ -128,7 +369,7 @@ Scene_edit_polyhedron_item::select(double orig_x,
dir_x, dir_x,
dir_y, dir_y,
dir_z); dir_z);
d->poly_item->select(orig_x, poly_item->select(orig_x,
orig_y, orig_y,
orig_z, orig_z,
dir_x, dir_x,
@ -136,104 +377,18 @@ Scene_edit_polyhedron_item::select(double orig_x,
dir_z); dir_z);
} }
Scene_polyhedron_item* Scene_edit_polyhedron_item::to_polyhedron_item() const { bool Scene_edit_polyhedron_item::keyPressEvent(QKeyEvent* e)
return d->poly_item;
}
void
Scene_edit_polyhedron_item::setZoneSize(int i) {
if(i >= 0) {
std::cerr << "item \"" << qPrintable(name())
<< "\".setZoneSize(" << i << ")\n";
d->zone_size = i;
}
}
qglviewer::ManipulatedFrame*
Scene_edit_polyhedron_item::manipulatedFrame() {
return d->frame;
}
struct Get_vertex_handle : public CGAL::Modifier_base<Polyhedron::HDS>
{ {
Polyhedron::Vertex* vertex_ptr; //setting/unsetting rotation constraints
Vertex_handle vh; if (e->key()==Qt::Key_R && !state.ctrl_pressing)
void operator()(Polyhedron::HDS& hds) { {
vh = hds.vertex_handle(vertex_ptr); is_rot_free = !is_rot_free;
rot_constraint.setRotationConstraintType( is_rot_free?
qglviewer::AxisPlaneConstraint::FREE:
qglviewer::AxisPlaneConstraint::AXIS);
return true;
} }
}; return false;
void Scene_edit_polyhedron_item::vertex_has_been_selected(void* void_ptr) {
Polyhedron* poly = d->poly_item->polyhedron();
// Need a modifier to get access to the HDS, to get the vertex handle
// from the vertex pointer.
Get_vertex_handle get_vertex_handle;
get_vertex_handle.vertex_ptr = static_cast<Polyhedron::Vertex*>(void_ptr);
poly->delegate(get_vertex_handle);
Vertex_handle vh = get_vertex_handle.vh;
std::cerr << "Selected vertex: " << void_ptr << " = " << vh->point()
<< std::endl;
d->selected_vertices.clear();
d->selected_vertices.insert(vh);
std::cerr << "d->zone_size = " << d->zone_size << std::endl;
// Naive way to compute the k-neighborhood of vh, with k==d->zone_size.
for(int i = 0; i < d->zone_size; ++i) {
std::set<Vertex_handle> selected_vertices;
for(Selected_vertices_it
it = d->selected_vertices.begin(),
end = d->selected_vertices.end();
it != end; ++it) {
selected_vertices.insert(*it);
}
BOOST_FOREACH(Vertex_handle v, selected_vertices) {
Polyhedron::Halfedge_around_vertex_circulator
he_it = v->vertex_begin(), he_it_end(he_it);
if(he_it != 0) {
do {
const Vertex_handle other_v = he_it->opposite()->vertex();
if( d->selected_vertices.find(other_v) == d->selected_vertices.end() )
{
d->selected_vertices.insert(other_v);
}
} while(++he_it != he_it_end);
}
}
}
const Kernel::Point_3& p = vh->point();
d->orig_pos = p;
d->frame->setPosition(qglviewer::Vec(p.x(), p.y(), p.z()));
connect(d->frame, SIGNAL(modified()),
this, SIGNAL(modified()));
emit begin_edit();
}
Vertex_handle
Scene_edit_polyhedron_item::selected_vertex() const {
return d->selected_vertex;
}
QList<Vertex_handle>
Scene_edit_polyhedron_item::selected_vertices() const {
QList<Vertex_handle> result;
BOOST_FOREACH(Vertex_handle vh, d->selected_vertices) {
result << vh;
}
return result;
}
Kernel::Point_3 Scene_edit_polyhedron_item::current_position() const {
const qglviewer::Vec vec = d->frame->position();
return Kernel::Point_3(vec.x, vec.y, vec.z);
}
Kernel::Point_3 Scene_edit_polyhedron_item::original_position() const {
return d->orig_pos;
} }
#include "Scene_edit_polyhedron_item.moc" #include "Scene_edit_polyhedron_item.moc"

View File

@ -1,18 +1,172 @@
#ifndef SCENE_EDIT_POLYHEDRON_ITEM_H #ifndef SCENE_EDIT_POLYHEDRON_ITEM_H
#define SCENE_EDIT_POLYHEDRON_ITEM_H #define SCENE_EDIT_POLYHEDRON_ITEM_H
//#define CGAL_PROFILE
#include "Scene_edit_polyhedron_item_config.h" #include "Scene_edit_polyhedron_item_config.h"
#include "Scene_polyhedron_item.h" #include "Scene_polyhedron_item.h"
#include "Polyhedron_type.h" #include "Scene_polyhedron_item_k_ring_selection.h"
#include "Travel_isolated_components.h"
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/properties_Polyhedron_3.h>
#include <CGAL/internal/Operations_on_polyhedra/compute_normal.h>
#include <iostream> #include <iostream>
#include <fstream>
#include <vector> #include <CGAL/glu.h>
#include <QGLViewer/manipulatedFrame.h>
#include <QGLViewer/qglviewer.h>
#include <QGLViewer/camera.h>
#include <QColor> #include "ui_Deform_mesh.h"
#include <QList> #include <CGAL/Deform_mesh.h>
#include <boost/function_output_iterator.hpp>
class QMenu;
struct Scene_edit_polyhedron_item_priv; typedef Polyhedron::Vertex_handle Vertex_handle;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::vertex_iterator vertex_iterator;
typedef boost::graph_traits<Polyhedron>::in_edge_iterator in_edge_iterator;
typedef boost::graph_traits<Polyhedron>::out_edge_iterator out_edge_iterator;
struct Array_based_vertex_point_map
{
public:
typedef vertex_descriptor key_type;
typedef Polyhedron::Traits::Point_3 value_type;
typedef value_type& reference;
typedef boost::read_write_property_map_tag category;
Array_based_vertex_point_map(std::vector<double>* positions) : positions(positions) {}
std::vector<double>* positions;
};
Array_based_vertex_point_map::value_type
get(Array_based_vertex_point_map,
Array_based_vertex_point_map::key_type key) {
return key->point();
}
void
put(Array_based_vertex_point_map pmap,
Array_based_vertex_point_map::key_type key,
Array_based_vertex_point_map::value_type val) {
key->point() = val; // to make things easy (ray selection after deformation, save to polyhedron after close etc),
// I also change point() of vertex together with positions list
// So that we do not need to pmap everywhere other than draw
std::size_t pos = key->id() * 3;
(*pmap.positions)[pos] = val.x();
(*pmap.positions)[pos+1] = val.y();
(*pmap.positions)[pos+2] = val.z();
}
typedef CGAL::Deform_mesh<Polyhedron, CGAL::Default, CGAL::Default, CGAL::ORIGINAL_ARAP
,CGAL::Default, CGAL::Default, CGAL::Default,
Array_based_vertex_point_map> Deform_mesh;
typedef Deform_mesh::Point Point;
/// For storing associated data with a group of control vertices
class Control_vertices_data
{
public:
std::vector<vertex_descriptor> ctrl_vertices_group;
qglviewer::ManipulatedFrame* frame; // manframe assoc with a group of control vertices
qglviewer::Vec frame_initial_center; // initial center of frame
Scene_interface::Bbox bbox; // bbox of control vertices inside group
qglviewer::Vec rot_direction; // vector for constraint rotation
private:
std::vector<qglviewer::Vec> initial_positions;
Deform_mesh* deform_mesh;
public:
Control_vertices_data(Deform_mesh* deform_mesh, qglviewer::ManipulatedFrame* frame = 0)
: frame(frame), bbox(0,0,0,0,0,0), rot_direction(0.,0.,1.), deform_mesh(deform_mesh)
{ }
void refresh()
{
for(std::vector<vertex_descriptor>::iterator it = ctrl_vertices_group.begin(); it != ctrl_vertices_group.end(); ) {
if(!deform_mesh->is_control_vertex(*it)) {
it = ctrl_vertices_group.erase(it);
}
else { ++it; }
}
reset_initial_positions();
frame_initial_center = calculate_initial_center();
bbox = calculate_initial_bbox();
bool oldState = frame->blockSignals(true); // do not let it emit modified, which will cause a deformation
// but we are just adjusting the center so it does not require a deformation
frame->setOrientation(qglviewer::Quaternion());
frame->setPosition(frame_initial_center);
frame->blockSignals(oldState);
}
void set_target_positions()
{
std::vector<vertex_descriptor>::iterator hb = ctrl_vertices_group.begin();
for(std::vector<qglviewer::Vec>::iterator it = initial_positions.begin(); it != initial_positions.end(); ++it, ++hb)
{
qglviewer::Vec dif_from_initial_center = (*it) - frame_initial_center;
qglviewer::Vec rotated = frame->orientation() * dif_from_initial_center;
qglviewer::Vec rotated_and_translated = rotated + frame->position();
deform_mesh->set_target_position(*hb, Point(rotated_and_translated.x, rotated_and_translated.y, rotated_and_translated.z) );
}
}
private:
void reset_initial_positions()
{
initial_positions.clear();
for(std::vector<vertex_descriptor>::iterator hb = ctrl_vertices_group.begin(); hb != ctrl_vertices_group.end(); ++hb)
{
qglviewer::Vec point((*hb)->point().x(), (*hb)->point().y(), (*hb)->point().z() );
initial_positions.push_back(point);
}
}
qglviewer::Vec calculate_initial_center()
{
qglviewer::Vec center_acc(0, 0, 0);
if(initial_positions.empty()) {return center_acc; }
for(std::vector<qglviewer::Vec>::iterator it = initial_positions.begin(); it != initial_positions.end(); ++it)
{
center_acc += (*it);
}
return center_acc / initial_positions.size();
}
Scene_interface::Bbox calculate_initial_bbox()
{
if(initial_positions.empty()) {return Scene_interface::Bbox(0,0,0,0,0,0); }
const qglviewer::Vec& p_i = *(initial_positions.begin());
Scene_interface::Bbox bbox(p_i.x, p_i.y, p_i.z, p_i.x, p_i.y, p_i.z);
for(std::vector<qglviewer::Vec>::iterator it = initial_positions.begin(); it != initial_positions.end(); ++it)
{
const qglviewer::Vec& p_i = (*it);
Scene_interface::Bbox bbox_it(p_i.x, p_i.y, p_i.z, p_i.x, p_i.y, p_i.z);
bbox = bbox + bbox_it;
}
return bbox;
}
};
// To hold pressing states together
struct Mouse_keyboard_state_deformation
{
bool ctrl_pressing;
bool shift_pressing;
bool left_button_pressing;
bool right_button_pressing;
Mouse_keyboard_state_deformation()
: ctrl_pressing(false), shift_pressing(false), left_button_pressing(false), right_button_pressing(false)
{ }
};
// This class represents a polyhedron in the OpenGL scene // This class represents a polyhedron in the OpenGL scene
class SCENE_EDIT_POLYHEDRON_ITEM_EXPORT Scene_edit_polyhedron_item class SCENE_EDIT_POLYHEDRON_ITEM_EXPORT Scene_edit_polyhedron_item
@ -22,68 +176,496 @@ public:
/// Create an Scene_edit_polyhedron_item from a Scene_polyhedron_item. /// Create an Scene_edit_polyhedron_item from a Scene_polyhedron_item.
/// The ownership of the polyhedron is moved to the new edit_polyhedron /// The ownership of the polyhedron is moved to the new edit_polyhedron
/// item. /// item.
Scene_edit_polyhedron_item(Scene_polyhedron_item* poly_item); Scene_edit_polyhedron_item(Scene_polyhedron_item* poly_item, Ui::DeformMesh* ui_widget, QMainWindow* mw);
~Scene_edit_polyhedron_item(); ~Scene_edit_polyhedron_item();
/// Returns 0, so that one cannot clone an "edit polyhedron" item. /// Returns 0, so that one cannot clone an "edit polyhedron" item.
Scene_edit_polyhedron_item* clone() const; Scene_edit_polyhedron_item* clone() const;
// // IO
// bool load(std::istream& in);
// bool save(std::ostream& out) const;
// Function for displaying meta-data of the item // Function for displaying meta-data of the item
QString toolTip() const; QString toolTip() const;
// // Function to override the context menu void setColor(QColor c);
// QMenu* contextMenu(); void setName(QString n);
void setVisible(bool b);
void setRenderingMode(RenderingMode m);
// Indicate if rendering mode is supported // Indicate if rendering mode is supported
bool supportsRenderingMode(RenderingMode m) const { return (m!=PointsPlusNormals); } bool supportsRenderingMode(RenderingMode m) const {
return m == Gouraud;
}
// Points/Wireframe/Flat/Gouraud OpenGL drawing in a display list // Points/Wireframe/Flat/Gouraud OpenGL drawing in a display list
void draw() const; void draw() const;
void draw_edges() const;
bool manipulatable() const { return true; } void draw_bbox(const Scene_interface::Bbox& bb ) const;
qglviewer::ManipulatedFrame* manipulatedFrame(); void gl_draw_edge(double px, double py, double pz,
double qx, double qy, double qz) const;
void gl_draw_point(const Point& p) const;
// Get wrapped polyhedron // Get wrapped polyhedron
Polyhedron* polyhedron(); Polyhedron* polyhedron();
const Polyhedron* polyhedron() const; const Polyhedron* polyhedron() const;
// Functions related to the edition
Kernel::Point_3 original_position() const;
Kernel::Point_3 current_position() const;
Polyhedron::Vertex_handle selected_vertex() const;
QList<Polyhedron::Vertex_handle> selected_vertices() const;
/// Returns a Scene_polyhedron_item from the edit polyhedron item, and /// Returns a Scene_polyhedron_item from the edit polyhedron item, and
/// transfer the ownership of the polyhedron to it. /// transfer the ownership of the polyhedron to it.
/// The item 'this' must be destroy just after a call to this function. /// The item 'this' must be destroy just after a call to this function.
Scene_polyhedron_item* to_polyhedron_item() const; Scene_polyhedron_item* to_polyhedron_item();
// Get dimensions // Get dimensions
bool isFinite() const { return true; } bool isFinite() const { return true; }
bool isEmpty() const; bool isEmpty() const;
Bbox bbox() const; Bbox bbox() const;
int get_k_ring() { return k_ring_selector.k_ring; }
void set_k_ring(int v) { k_ring_selector.k_ring = v; }
// take mouse events from viewer, main-window does not work
// take keyboard events from main-window, which is more stable
bool eventFilter(QObject *target, QEvent *event);
protected:
void timerEvent(QTimerEvent *event);
void draw_ROI_and_control_vertices() const;
public slots: public slots:
void changed(); void changed();
void selected(const std::map<Polyhedron::Vertex_handle, int>& m)
{
bool any_changes = false;
for(std::map<vertex_descriptor, int>::const_iterator it = m.begin(); it != m.end(); ++it)
{
vertex_descriptor vh = it->first;
bool changed = false;
if(ui_widget->ROIRadioButton->isChecked()) {
if(ui_widget->InsertRadioButton->isChecked()) { changed = insert_roi_vertex(vh); }
else { changed = erase_roi_vertex(vh); }
}
else {
if(ui_widget->InsertRadioButton->isChecked()) { changed = insert_control_vertex(vh); }
else { changed = erase_control_vertex(vh); }
}
any_changes |= changed;
}
if(any_changes) { emit itemChanged(); }
}
void select(double orig_x, void select(double orig_x,
double orig_y, double orig_y,
double orig_z, double orig_z,
double dir_x, double dir_x,
double dir_y, double dir_y,
double dir_z); double dir_z);
void setZoneSize(int i);
void vertex_has_been_selected(void* vertex_handle);
signals: void deform(); // deform the mesh
void begin_edit(); // members
void modified(); private:
Ui::DeformMesh* ui_widget;
Scene_polyhedron_item* poly_item;
// For drawing
std::vector<double> positions;
std::vector<std::size_t> tris;
std::vector<std::size_t> edges;
std::vector<double> normals;
Deform_mesh deform_mesh;
typedef std::list<Control_vertices_data> Ctrl_vertices_group_data_list;
Ctrl_vertices_group_data_list::iterator active_group;
Ctrl_vertices_group_data_list ctrl_vertex_frame_map; // keep list of group of control vertices with assoc data
double length_of_axis; // for drawing axis at a group of control vertices
// by interleaving 'viewer's events (check constructor), keep followings:
Mouse_keyboard_state_deformation state;
//For constraint rotation
qglviewer::LocalConstraint rot_constraint;
bool is_rot_free;
bool own_poly_item; //indicates if the poly_item should be deleted by the destructor
Scene_polyhedron_item_k_ring_selection k_ring_selector;
public:
// Deformation related functions //
bool insert_control_vertex(vertex_descriptor v)
{
if(!is_there_any_ctrl_vertices_group()) {
print_message("There is no group of control vertices, create one!");
return false;
} // no group of control vertices to insert
bool inserted = deform_mesh.insert_control_vertex(v);
if(inserted) {
active_group->ctrl_vertices_group.push_back(v);
active_group->refresh();
}
return inserted;
}
bool insert_roi_vertex(vertex_descriptor v)
{
return deform_mesh.insert_roi_vertex(v);
}
bool erase_control_vertex(vertex_descriptor v)
{
if(deform_mesh.erase_control_vertex(v)) // API should be safe enough to do that (without checking empty group of control vertices etc.)
{
refresh_all_group_centers(); // since we don't know which group of control vertices v is erased from, refresh all
return true;
}
print_message("Selected vertex is not a control vertex!");
return false;
}
bool erase_roi_vertex(vertex_descriptor v)
{
erase_control_vertex(v); // erase control vertex
return deform_mesh.erase_roi_vertex(v);
}
void set_all_vertices_as_roi()
{
vertex_iterator vb, ve;
for(boost::tie(vb, ve) = vertices(*polyhedron()); vb != ve; ++vb)
{
insert_roi_vertex(*vb);
}
}
void clear_roi()
{
for(Ctrl_vertices_group_data_list::iterator it = ctrl_vertex_frame_map.begin(); it != ctrl_vertex_frame_map.end(); ++it)
{
delete it->frame;
}
ctrl_vertex_frame_map.clear();
deform_mesh.clear_roi_vertices();
create_ctrl_vertices_group(); // create one new group of control vertices
}
void create_ctrl_vertices_group()
{
for(Ctrl_vertices_group_data_list::iterator it = ctrl_vertex_frame_map.begin(); it != ctrl_vertex_frame_map.end(); ++it) {
if(it->ctrl_vertices_group.empty()) {
active_group = it;
return;
}
}
// No empty group of control vertices
qglviewer::ManipulatedFrame* new_frame = new qglviewer::ManipulatedFrame();
new_frame->setRotationSensitivity(2.0f);
Control_vertices_data hgd(&deform_mesh, new_frame);
ctrl_vertex_frame_map.push_back(hgd);
hgd.refresh();
active_group = --ctrl_vertex_frame_map.end();
connect(new_frame, SIGNAL(modified()), this, SLOT(deform())); // OK we are deforming via timer,
// but it makes demo more responsive if we also add this signal
emit itemChanged();
print_message("A new empty group of control vertices is created.");
}
void delete_ctrl_vertices_group(bool create_new = true)
{
if(!is_there_any_ctrl_vertices_group()) {
print_message("There is no group of control vertices to be deleted!");
return;
} // no group of control vertices
// delete group representative
for(Ctrl_vertices_group_data_list::iterator it = ctrl_vertex_frame_map.begin(); it != ctrl_vertex_frame_map.end(); ++it)
{
if(it == active_group)
{
delete it->frame;
for(std::vector<vertex_descriptor>::iterator v_it = it->ctrl_vertices_group.begin(); v_it != it->ctrl_vertices_group.end(); ++v_it) {
deform_mesh.erase_control_vertex(*v_it);
}
ctrl_vertex_frame_map.erase(it);
break;
}
}
// assign another ctrl_vertices_group to active_group
Ctrl_vertices_group_data_list::iterator hgb, hge;
if( is_there_any_ctrl_vertices_group(hgb, hge) )
{
active_group = hgb;
} // no group of control vertices
else if(create_new)
{
create_ctrl_vertices_group();
}
}
void prev_ctrl_vertices_group()
{
Ctrl_vertices_group_data_list::iterator hgb, hge;
if( !is_there_any_ctrl_vertices_group(hgb, hge) ) {
print_message("There is no group of control vertices to iterate on!");
return;
}
// shift
if(hgb == active_group) { active_group = --hge; }
else {--active_group; }
}
void next_ctrl_vertices_group()
{
Ctrl_vertices_group_data_list::iterator hgb, hge;
if( !is_there_any_ctrl_vertices_group(hgb, hge) ) {
print_message("There is no group of control vertices to iterate on!");
return;
}
// shift
if(--hge == active_group) { active_group = hgb; }
else {++active_group; }
}
void pivoting_end()
{
for(Ctrl_vertices_group_data_list::iterator it = ctrl_vertex_frame_map.begin(); it != ctrl_vertex_frame_map.end(); ++it)
{
//update constraint rotation vector, set only for the last group
it->rot_direction = it->frame->rotation().rotate( qglviewer::Vec(0.,0.,1.) );
//translate center of the frame
qglviewer::Vec vec= it->frame->position();
it->refresh();
it->frame_initial_center = vec;
it->frame->setPosition(vec);
}
for(Ctrl_vertices_group_data_list::iterator it = ctrl_vertex_frame_map.begin(); it != ctrl_vertex_frame_map.end(); ++it)
{
it->frame->blockSignals(false);
}
}
void pivoting_begin()
{
is_rot_free=true;
rot_constraint.setRotationConstraintType(qglviewer::AxisPlaneConstraint::FREE);
// just block signals to prevent deformation
for(Ctrl_vertices_group_data_list::iterator it = ctrl_vertex_frame_map.begin(); it != ctrl_vertex_frame_map.end(); ++it)
{
it->frame->blockSignals(true);
}
}
void save_roi(const char* file_name) const
{
std::ofstream out(file_name);
// save roi
out << deform_mesh.roi_vertices().size() << std::endl;
BOOST_FOREACH(vertex_descriptor vd, deform_mesh.roi_vertices())
{
out << vd->id() << " ";
}
out << std::endl;
// save control vertices
out << ctrl_vertex_frame_map.size() << std::endl; // control vertices count
for(Ctrl_vertices_group_data_list::const_iterator hgb = ctrl_vertex_frame_map.begin(); hgb != ctrl_vertex_frame_map.end(); ++hgb) {
out << hgb->ctrl_vertices_group.size() << std::endl;
for(std::vector<vertex_descriptor>::const_iterator hb = hgb->ctrl_vertices_group.begin(); hb != hgb->ctrl_vertices_group.end(); ++hb)
{
out << (*hb)->id() << " ";
}
out << std::endl;
}
}
void read_roi(const char* file_name)
{
clear_roi();
delete_ctrl_vertices_group(false);
// put vertices to vector
std::vector<vertex_descriptor> all_vertices;
all_vertices.reserve(num_vertices(deform_mesh.halfedge_graph()));
vertex_iterator vb, ve;
for(boost::tie(vb, ve) = vertices(deform_mesh.halfedge_graph()); vb != ve; ++vb) {
all_vertices.push_back(*vb);
}
// read roi
std::ifstream in(file_name);
int roi_size;
in >> roi_size;
while(roi_size-- > 0)
{
std::size_t v_id;
in >> v_id;
insert_roi_vertex(all_vertices[v_id]);
}
// read control vertices
int ctrl_vertices_group_size;
in >> ctrl_vertices_group_size;
while(ctrl_vertices_group_size-- > 0)
{
create_ctrl_vertices_group();
int ctrl_size;
in >> ctrl_size;
while(ctrl_size-- > 0)
{
std::size_t v_id;
in >> v_id;
insert_control_vertex(all_vertices[v_id]);
}
}
}
void overwrite_deform_object()
{
deform_mesh.overwrite_initial_geometry();
refresh_all_group_centers();
}
struct Is_selected {
Deform_mesh& dm;
Is_selected(Deform_mesh& dm) : dm(dm) {}
bool is_selected(Vertex_handle vh) const {
return dm.is_roi_vertex(vh);
}
};
boost::optional<std::size_t> get_minimum_isolated_component() {
Travel_isolated_components::Minimum_visitor visitor;
Travel_isolated_components().travel<Vertex_handle>
(polyhedron()->vertices_begin(), polyhedron()->vertices_end(),
polyhedron()->size_of_vertices(), Is_selected(deform_mesh), visitor);
return visitor.minimum;
}
struct Select_roi_output {
Select_roi_output(Deform_mesh* dm) : dm(dm) { }
void operator()(Vertex_handle vh) {
dm->insert_roi_vertex(vh);
}
Deform_mesh* dm;
};
boost::optional<std::size_t> select_isolated_components(std::size_t threshold) {
typedef boost::function_output_iterator<Select_roi_output> Output_iterator;
Output_iterator out(&deform_mesh);
Travel_isolated_components::Selection_visitor<Output_iterator> visitor(threshold, out);
Travel_isolated_components().travel<Vertex_handle>
(polyhedron()->vertices_begin(), polyhedron()->vertices_end(),
polyhedron()->size_of_vertices(), Is_selected(deform_mesh), visitor);
if(visitor.any_inserted) { emit itemChanged(); }
return visitor.minimum_visitor.minimum;
}
protected: protected:
Scene_edit_polyhedron_item_priv* d; // Deformation related functions //
void print_message(const QString& /*message*/)
{
// std::cout << message.toStdString() << std::endl;
}
bool is_there_any_ctrl_vertices_group(Ctrl_vertices_group_data_list::iterator& hgb, Ctrl_vertices_group_data_list::iterator& hge)
{
hgb = ctrl_vertex_frame_map.begin(); hge = ctrl_vertex_frame_map.end();
return hgb != hge;
}
bool is_there_any_ctrl_vertices_group()
{
Ctrl_vertices_group_data_list::iterator hgb, hge;
return is_there_any_ctrl_vertices_group(hgb, hge);
}
bool is_there_any_ctrl_vertices()
{
Ctrl_vertices_group_data_list::iterator hgb, hge;
if(!is_there_any_ctrl_vertices_group(hgb, hge)) { return false; } // there isn't any group of control vertices
for(; hgb != hge; ++hgb) // check inside groups of control vertices
{
if(!hgb->ctrl_vertices_group.empty()) { return true; }
}
return false;
}
void refresh_all_group_centers()
{
for(Ctrl_vertices_group_data_list::iterator it = ctrl_vertex_frame_map.begin(); it != ctrl_vertex_frame_map.end(); ++it)
{ it->refresh(); }
}
bool activate_closest_manipulated_frame(int x, int y)
{
if(state.ctrl_pressing && (state.left_button_pressing || state.right_button_pressing) )
{ // user is deforming currently don't change the state
return false;
}
if(ctrl_vertex_frame_map.empty()) { return false; }
QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin();
qglviewer::Camera* camera = viewer->camera();
if(!state.ctrl_pressing)
{
if(viewer->manipulatedFrame() == NULL)
{ return false;}
viewer->setManipulatedFrame(NULL);
return true;
}
// now find closest frame and make it active manipulated frame
Ctrl_vertices_group_data_list::iterator min_it = ctrl_vertex_frame_map.begin();
const qglviewer::Vec& pos_it = camera->projectedCoordinatesOf(min_it->frame->position());
float min_dist = std::pow(pos_it.x - x, 2) + std::pow(pos_it.y - y, 2);
for(Ctrl_vertices_group_data_list::iterator it = ctrl_vertex_frame_map.begin(); it != ctrl_vertex_frame_map.end(); ++it)
{
const qglviewer::Vec& pos_it = camera->projectedCoordinatesOf(it->frame->position());
float dist = std::pow(pos_it.x - x, 2) + std::pow(pos_it.y - y, 2);
if(dist < min_dist) {
min_dist = dist;
min_it = it;
}
}
//set rotation constraint for the manipulated frame
if (!is_rot_free){
rot_constraint.setRotationConstraintDirection(min_it->rot_direction);
rot_constraint.setRotationConstraintType(qglviewer::AxisPlaneConstraint::AXIS);
min_it->frame->setConstraint(&rot_constraint);
}
else
rot_constraint.setRotationConstraintType(qglviewer::AxisPlaneConstraint::FREE);
if(viewer->manipulatedFrame() == min_it->frame)
{ return false; }
viewer->setManipulatedFrame(min_it->frame);
return true;
}
bool keyPressEvent(QKeyEvent* e);
void update_normals() {
BOOST_FOREACH(vertex_descriptor vd, deform_mesh.roi_vertices())
{
std::size_t id = vd->id();
const Polyhedron::Traits::Vector_3& n =
compute_vertex_normal<Polyhedron::Vertex, Polyhedron::Traits>(*vd);
normals[id*3] = n.x();
normals[id*3+1] = n.y();
normals[id*3+2] = n.z();
}
}
protected:
GLUquadric* quadric; // for drawing spheres
}; // end class Scene_edit_polyhedron_item }; // end class Scene_edit_polyhedron_item
#endif // SCENE_EDIT_POLYHEDRON_ITEM_H #endif // SCENE_EDIT_POLYHEDRON_ITEM_H

View File

@ -379,7 +379,7 @@ Scene_polyhedron_item::select(double orig_x,
nearest_v = v; nearest_v = v;
} }
} }
std::cerr << "Selected vertex: " << nearest_v->point() << std::endl;
emit selected_vertex((void*)(&*nearest_v)); emit selected_vertex((void*)(&*nearest_v));
} }
@ -415,8 +415,6 @@ Scene_polyhedron_item::select(double orig_x,
changed(); changed();
emit itemChanged(); emit itemChanged();
} }
std::cerr << "Facet selected. patch_id="
<< selected_fh->patch_id() << std::endl;
} }
} }
} }

View File

@ -0,0 +1,15 @@
OFF
7 6 0
-215.983 70.3187 137.883
-216.952 39.6826 144.676
-207.932 -24.8993 148.723
-247.708 69.538 123.733
-246.196 45.203 131.288
-246.937 14.9498 134.175
-255.988 45.6255 130.774
3 3 4 0
3 1 0 4
3 5 2 4
3 1 4 2
3 4 3 6
3 4 6 5

View File

@ -44,12 +44,24 @@ struct Eigen_sparse_matrix
// Public types // Public types
public: public:
typedef Eigen::SparseMatrix<T> EigenType; typedef Eigen::SparseMatrix<T> EigenType;
typedef T NT; typedef T NT;
// Public operations // Public operations
public: public:
/// Create a square matrix initialized with zeros.
Eigen_sparse_matrix(std::size_t dim, ///< Matrix dimension.
bool is_symmetric = false) ///< Symmetric/hermitian?
: m_is_already_built(false), m_matrix(static_cast<int>(dim),static_cast<int>(dim))
{
CGAL_precondition(dim > 0);
m_is_symmetric = is_symmetric;
// reserve memory for a regular 3D grid
m_triplets.reserve(dim);
}
/// Create a square matrix initialized with zeros. /// Create a square matrix initialized with zeros.
Eigen_sparse_matrix(int dim, ///< Matrix dimension. Eigen_sparse_matrix(int dim, ///< Matrix dimension.
bool is_symmetric = false) ///< Symmetric/hermitian? bool is_symmetric = false) ///< Symmetric/hermitian?
@ -62,6 +74,30 @@ public:
m_triplets.reserve(dim); m_triplets.reserve(dim);
} }
/// Create a rectangular matrix initialized with zeros.
///
/// @commentheading Precondition: rows == columns if is_symmetric is true.
Eigen_sparse_matrix(std::size_t rows, ///< Number of rows.
std::size_t columns, ///< Number of columns.
bool is_symmetric = false) ///< Symmetric/hermitian?
: m_is_already_built(false), m_matrix(static_cast<int>(rows),static_cast<int>(columns))
{
CGAL_precondition(rows > 0);
CGAL_precondition(columns > 0);
if (is_symmetric) {
CGAL_precondition(rows == columns);
}
m_is_symmetric = is_symmetric;
// reserve memory for a regular 3D grid
m_triplets.reserve(rows);
}
/// Delete this object and the wrapped TAUCS matrix.
~Eigen_sparse_matrix()
{
}
/// Create a rectangular matrix initialized with zeros. /// Create a rectangular matrix initialized with zeros.
/// ///
/// @commentheading Precondition: rows == columns if is_symmetric is true. /// @commentheading Precondition: rows == columns if is_symmetric is true.
@ -81,11 +117,6 @@ public:
m_triplets.reserve(rows); m_triplets.reserve(rows);
} }
/// Delete this object and the wrapped TAUCS matrix.
~Eigen_sparse_matrix()
{
}
/// Return the matrix number of rows /// Return the matrix number of rows
int row_dimension() const { return m_matrix.rows(); } int row_dimension() const { return m_matrix.rows(); }
/// Return the matrix number of columns /// Return the matrix number of columns
@ -103,8 +134,10 @@ public:
/// @commentheading Preconditions: /// @commentheading Preconditions:
/// - 0 <= i < row_dimension(). /// - 0 <= i < row_dimension().
/// - 0 <= j < column_dimension(). /// - 0 <= j < column_dimension().
void set_coef(int i, int j, T val, bool new_coef = false) void set_coef(std::size_t i_, std::size_t j_, T val, bool new_coef = false)
{ {
int i = static_cast<int>(i_);
int j = static_cast<int>(j_);
CGAL_precondition(i < row_dimension()); CGAL_precondition(i < row_dimension());
CGAL_precondition(j < column_dimension()); CGAL_precondition(j < column_dimension());

View File

@ -19,9 +19,22 @@
#ifndef CGAL_EIGEN_SOLVER_TRAITS_H #ifndef CGAL_EIGEN_SOLVER_TRAITS_H
#define CGAL_EIGEN_SOLVER_TRAITS_H #define CGAL_EIGEN_SOLVER_TRAITS_H
#include <CGAL/basic.h> // include basic.h before testing #defines #include <CGAL/config.h> // include basic.h before testing #defines
#if defined(BOOST_MSVC)
# pragma warning(push)
# pragma warning(disable:4244)
#endif
#include <Eigen/Sparse> #include <Eigen/Sparse>
#if EIGEN_VERSION_AT_LEAST(3, 1, 91)
#include <Eigen/SparseLU>
#endif
#if defined(BOOST_MSVC)
# pragma warning(pop)
#endif
#include <CGAL/Eigen_matrix.h> #include <CGAL/Eigen_matrix.h>
#include <CGAL/Eigen_vector.h> #include <CGAL/Eigen_vector.h>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
@ -44,6 +57,12 @@ namespace internal {
struct Get_eigen_matrix< ::Eigen::SimplicialCholesky<EigenMatrix>,FT>{ struct Get_eigen_matrix< ::Eigen::SimplicialCholesky<EigenMatrix>,FT>{
typedef Eigen_sparse_symmetric_matrix<FT> type; typedef Eigen_sparse_symmetric_matrix<FT> type;
}; };
#if EIGEN_VERSION_AT_LEAST(3, 1, 91)
template <class FT, class EigenMatrix, class EigenOrdering>
struct Get_eigen_matrix< ::Eigen::SparseLU<EigenMatrix, EigenOrdering >, FT> {
typedef Eigen_sparse_matrix<FT> type;
};
#endif
} //internal } //internal
/// The class Eigen_solver_traits /// The class Eigen_solver_traits
@ -52,7 +71,7 @@ namespace internal {
/// The default solver is the iterative bi-congugate gradient stabilized solver /// The default solver is the iterative bi-congugate gradient stabilized solver
/// Eigen::BiCGSTAB for double. /// Eigen::BiCGSTAB for double.
/// ///
/// @heading Is Model for the Concepts: Model of the SparseLinearAlgebraTraits_d concept. /// \cgalModels `SparseLinearAlgebraTraitsWithFactor_d`.
template<class EigenSolverT = Eigen::BiCGSTAB<Eigen_sparse_matrix<double>::EigenType> > template<class EigenSolverT = Eigen::BiCGSTAB<Eigen_sparse_matrix<double>::EigenType> >
class Eigen_solver_traits class Eigen_solver_traits
@ -68,7 +87,7 @@ public:
// Public operations // Public operations
public: public:
Eigen_solver_traits(): m_solver_sptr(new EigenSolverT) Eigen_solver_traits():m_mat(NULL), m_solver_sptr(new EigenSolverT)
{ {
} }
@ -93,7 +112,24 @@ public:
return m_solver_sptr->info() == Eigen::Success; return m_solver_sptr->info() == Eigen::Success;
} }
bool factor (const Matrix& A, NT& D)
{
D = 1;
m_mat = &A.eigen_object();
solver().compute(*m_mat);
return solver().info() == Eigen::Success;
}
bool linear_solver(const Vector& B, Vector& X)
{
CGAL_precondition(m_mat!=NULL); //factor should have been called first
X = solver().solve(B);
return solver().info() == Eigen::Success;
}
protected: protected:
const typename Matrix::EigenType* m_mat;
boost::shared_ptr<EigenSolverT> m_solver_sptr; boost::shared_ptr<EigenSolverT> m_solver_sptr;
}; };

View File

@ -54,8 +54,8 @@ public:
} }
/// Create a vector initialized with zeros. /// Create a vector initialized with zeros.
Eigen_vector(int dimension) Eigen_vector(std::size_t dimension)
: EigenType(dimension) : EigenType(static_cast<int>(dimension))
{ {
this->setZero(); this->setZero();
} }
@ -83,8 +83,8 @@ public:
return *this; return *this;
} }
void set(int i,NT value) { void set(std::size_t i,NT value) {
this->operator[](i)=value; this->operator[](static_cast<int>(i))=value;
} }
NT* vector() {return this->data();} NT* vector() {return this->data();}

View File

@ -7,7 +7,7 @@ namespace CGAL {
The class `Eigen_solver_traits` provides an interface to the sparse solvers of \ref thirdpartyEigen "Eigen". The class `Eigen_solver_traits` provides an interface to the sparse solvers of \ref thirdpartyEigen "Eigen".
The version 3.1 (or greater) of \ref thirdpartyEigen "Eigen" must be available on the system. The version 3.1 (or greater) of \ref thirdpartyEigen "Eigen" must be available on the system.
\cgalModels `SparseLinearAlgebraTraits_d` \cgalModels `SparseLinearAlgebraTraitsWithFactor_d`
Parameters Parameters
-------------- --------------

View File

@ -66,6 +66,10 @@ template<class T> // Tested with T = taucs_single or taucs_double
// May also work with T = taucs_dcomplex and taucs_scomplex // May also work with T = taucs_dcomplex and taucs_scomplex
class Taucs_symmetric_solver_traits class Taucs_symmetric_solver_traits
{ {
private:
boost::shared_ptr<taucs_io_handle> mtr;
// Public types // Public types
public: public:
@ -176,6 +180,104 @@ public:
} }
} }
bool factor (const Matrix& A, NT& D)
{
D = 1; // TAUCS does not support homogeneous coordinates
#ifdef DEBUG_TRACE
// Turn on TAUCS trace to stderr or to a log file
#if DEBUG_TRACE >= 2
std::cerr.flush();
taucs_logfile((char*)"stderr");
#else
taucs_logfile((char*)"taucs.log");
#endif
#endif
#ifdef WIN32
Win32_exception_handler eh; // catch Win32 structured exceptions
#endif
try
{
int success;
// ordering
int* perm_raw = NULL;
int* invperm_raw = NULL;
taucs_ccs_order((taucs_ccs_matrix*) A.get_taucs_matrix(),
&perm_raw,
&invperm_raw,
(char*)"colamd");
boost::shared_ptr<int> perm(perm_raw, free);
boost::shared_ptr<int> invperm(invperm_raw, free);
if ( perm == NULL || invperm == NULL)
throw std::runtime_error("Ordering Failed");
// Create multi-file for out-of-core swapping.
// Note: g++ complains that tempnam() is deprecated. You may safely ignore the warning.
#ifdef _MSC_VER
char template_name[13] = {'t', 'a', 'u', 'c', 's','.','X','X','X','X','X','X', '\0' };
char* matrixfile = _mktemp(template_name);
if (matrixfile == NULL)
throw std::runtime_error("Cannot Create Multifile");
boost::shared_ptr<taucs_io_handle> oocL(taucs_io_create_multifile(matrixfile), taucs_io_delete);
mtr = oocL;
#else
boost::shared_ptr<char> matrixfile(tempnam(NULL, "taucs.L"), free);
if (matrixfile == NULL)
throw std::runtime_error("Cannot Create Multifile");
boost::shared_ptr<taucs_io_handle> oocL(taucs_io_create_multifile(matrixfile.get()), taucs_io_delete);
mtr = oocL;
#endif
if (mtr == NULL)
throw std::runtime_error("Cannot Create Multifile");
// factor
int memory_mb = int(taucs_available_memory_size()/1048576.0);
success = taucs_ooc_factor_llt((taucs_ccs_matrix*) A.get_taucs_matrix(),
mtr.get(),
memory_mb*1048576.0);
if (success != TAUCS_SUCCESS)
throw std::runtime_error("Factorization Failed");
return true;
}
catch (std::exception& e)
{
taucs_printf((char*)"\t");
taucs_printf((char*)(e.what() != NULL ? e.what() : "Incorrect Matrix"));
taucs_printf((char*)"\n");
return false;
}
catch (...)
{
taucs_printf((char*)"\tIncorrect Matrix\n");
return false;
}
}
bool solve (const Vector& B, Vector& X)
{
int success;
success = taucs_ooc_solve_llt(mtr.get(),
X.get_taucs_vector(),
(T*) B.get_taucs_vector());
if (success != TAUCS_SUCCESS)
throw std::runtime_error("Solving Failed");
return true;
}
private: private:
// Test if a floating point number is (close to) 0.0. // Test if a floating point number is (close to) 0.0.
@ -204,6 +306,11 @@ template<class T> // Tested with T = taucs_single or taucs_double
class Taucs_solver_traits class Taucs_solver_traits
{ {
// Public types // Public types
private:
boost::shared_ptr<taucs_io_handle> mtr;
public: public:
typedef Taucs_matrix<T> Matrix; typedef Taucs_matrix<T> Matrix;
@ -338,6 +445,105 @@ public:
} }
} }
bool factor (const Matrix& A, NT& D)
{
D = 1; // TAUCS does not support homogeneous coordinates
#ifdef DEBUG_TRACE
// Turn on TAUCS trace to stderr or to a log file
#if DEBUG_TRACE >= 2
std::cerr.flush();
taucs_logfile((char*)"stderr");
#else
taucs_logfile((char*)"taucs.log");
#endif
#endif
#ifdef WIN32
Win32_exception_handler eh; // catch Win32 structured exceptions
#endif
try
{
int success;
// ordering
int* perm_raw = NULL;
int* invperm_raw = NULL;
taucs_ccs_order((taucs_ccs_matrix*) A.get_taucs_matrix(),
&perm_raw,
&invperm_raw,
(char*)"colamd");
boost::shared_ptr<int> perm(perm_raw, free);
boost::shared_ptr<int> invperm(invperm_raw, free);
if ( perm == NULL || invperm == NULL)
throw std::runtime_error("Ordering Failed");
// Create multi-file for out-of-core swapping.
// Note: g++ complains that tempnam() is deprecated. You may safely ignore the warning.
#ifdef _MSC_VER
char template_name[13] = {'t', 'a', 'u', 'c', 's','.','X','X','X','X','X','X', '\0' };
char* matrixfile = _mktemp(template_name);
if (matrixfile == NULL)
throw std::runtime_error("Cannot Create Multifile");
boost::shared_ptr<taucs_io_handle> oocL(taucs_io_create_multifile(matrixfile), taucs_io_delete);
mtr = oocL;
#else
boost::shared_ptr<char> matrixfile(tempnam(NULL, "taucs.L"), free);
if (matrixfile == NULL)
throw std::runtime_error("Cannot Create Multifile");
boost::shared_ptr<taucs_io_handle> oocL(taucs_io_create_multifile(matrixfile.get()), taucs_io_delete);
mtr = oocL;
#endif
if (mtr == NULL)
throw std::runtime_error("Cannot Create Multifile");
// factor
int memory_mb = int(taucs_available_memory_size()/1048576.0);
success = taucs_ooc_factor_lu((taucs_ccs_matrix*) A.get_taucs_matrix(),
perm.get(),
mtr.get(),
memory_mb*1048576.0);
if (success != TAUCS_SUCCESS)
throw std::runtime_error("Factorization Failed");
return true;
}
catch (std::exception& e)
{
taucs_printf((char*)"\t");
taucs_printf((char*)(e.what() != NULL ? e.what() : "Incorrect Matrix"));
taucs_printf((char*)"\n");
return false;
}
catch (...)
{
taucs_printf((char*)"\tIncorrect Matrix\n");
return false;
}
}
bool solve (const Vector& B, Vector& X)
{
int success;
success = taucs_ooc_solve_lu(mtr.get(),
X.get_taucs_vector(),
(T*) B.get_taucs_vector());
if (success != TAUCS_SUCCESS)
throw std::runtime_error("Solving Failed");
return true;
}
private: private:
// Test if a floating point number is (close to) 0.0. // Test if a floating point number is (close to) 0.0.

View File

@ -0,0 +1,33 @@
project( benchmark_for_closest_rotation )
cmake_minimum_required(VERSION 2.6.2)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 2.6)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.3)
cmake_policy(VERSION 2.8.4)
else()
cmake_policy(VERSION 2.6)
endif()
endif()
find_package(CGAL QUIET COMPONENTS Core )
if ( CGAL_FOUND )
include( ${CGAL_USE_FILE} )
find_package(Eigen3 3.1.0) #(requires 3.1.0 or greater)
if (EIGEN3_FOUND)
include( ${EIGEN3_USE_FILE} )
include( CGAL_CreateSingleSourceCGALProgram )
include_directories (BEFORE "../../include")
create_single_source_cgal_program( "benchmark_for_concept_models.cpp" )
else()
message(STATUS "This program requires the Eigen library, version 3.1 or later and will not be compiled.")
endif()
else()
message(STATUS "This program requires the CGAL library, and will not be compiled.")
endif()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
#include <CGAL/Timer.h>
#include <iostream>
#include <fstream>
#include <vector>
// models of DeformationClosestRotationTraits_3
#include <CGAL/Deformation_Eigen_closest_rotation_traits_3.h>
#include <CGAL/Deformation_Eigen_polar_closest_rotation_traits_3.h>
// provide a model of DeformationClosestRotationTraits_3
//
// Warning: we require more functionality than requested in DeformationClosestRotationTraits_3,
// so this benchmark may not compile with other models of DeformationClosestRotationTraits_3
template<class DeformationClosestRotationTraits_3>
void benchmark(int iteration_count = 1)
{
typedef typename DeformationClosestRotationTraits_3::Matrix Matrix;
DeformationClosestRotationTraits_3 model;
Matrix m = model.zero_matrix(); // no default const is required in concept (this is a workaround)
Matrix R = model.zero_matrix();
std::ifstream file("SVD_benchmark");
if (!file) {
std::cerr << "Error: can not open SVD_benchmark file!" << std::endl;
return;
}
std::vector<Matrix> matrices;
while( true ) {
bool read_ok = true;
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
if( !(file >> m(j, k)) ) // Warning: this part violates concept (in concept we do not have access to matrix coeffs)//
{ read_ok = false; break; }
}
}
if(!read_ok) { break; }
matrices.push_back(m);
}
std::cerr << "Reading matrices from file is completed ( "<< matrices.size() << " number of matrices )." << std::endl;
std::cerr << "Starting benchmark for " << matrices.size() << " matrices, and solving " <<
iteration_count << " times..." << std::endl;
CGAL::Timer task_timer; task_timer.start();
for(int i = 0; i < iteration_count; ++i )
for(typename std::vector<Matrix>::iterator it = matrices.begin();
it != matrices.end(); ++it) {
model.compute_close_rotation(*it, R);
}
std::cerr << "-----------------------------" << std::endl;
std::cerr << "Done: " << task_timer.time() << " sc" << std::endl;
std::cerr << "-----------------------------" << std::endl;
}
int main() {
const int multiple_iterate = 50;
std::cerr << "Benchmark for Deformation_Eigen_closest_rotation_traits_3 (Eigen SVD): " << std::endl;
benchmark<CGAL::Deformation_Eigen_closest_rotation_traits_3>(multiple_iterate);
std::cerr << "Benchmark for Deformation_Eigen_polar_closest_rotation_traits_3 (Eigen polar(as a filter) and SVD(as a gold standard)): " << std::endl;
benchmark<CGAL::Deformation_Eigen_polar_closest_rotation_traits_3>(multiple_iterate);
std::cerr << "All done!" << std::endl;
}

View File

@ -0,0 +1,617 @@
#if 1
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#define NR_END 1
#define FREE_ARG char*
void nrerror(char error_text[])
/*Numerical Recipes standard error handler */
{
fprintf(stderr,"Numerical Recipes run-time error...\n");
fprintf(stderr,"%s\n",error_text);
fprintf(stderr,"...now exiting to system...\n");
exit(1);
}
float *vec(long nl, long nh)
/* allocate a float vector with subscript range v[nl..nh] */
{
float *v;
v=(float *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(float)));
if (!v) nrerror("allocation failure in vector()");
return v-nl+NR_END;
}
int *ivector(long nl, long nh)
/* allocate an int vector with subscript range v[nl..nh] */
{
int *v;
v=(int *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(int)));
if (!v) nrerror("allocation failure in ivector()");
return v-nl+NR_END;
}
unsigned char *cvector(long nl, long nh)
/* allocate an unsigned char vector with subscript range v[nl..nh] */
{
unsigned char *v;
v=(unsigned char *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(unsigned char)));
if (!v) nrerror("allocation failure in cvector()");
return v-nl+NR_END;
}
unsigned long *lvector(long nl, long nh)
/* allocate an unsigned long vector with subscript range v[nl..nh] */
{
unsigned long *v;
v=(unsigned long *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(long)));
if (!v) nrerror("allocation failure in lvector()");
return v-nl+NR_END;
}
double *dvector(long nl, long nh)
/* allocate a double vector with subscript range v[nl..nh] */
{
double *v;
v=(double *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(double)));
if (!v) nrerror("allocation failure in dvector()");
return v-nl+NR_END;
}
float **matrix(long nrl, long nrh, long ncl, long nch)
/* allocate a float matrix with subscript range m[nrl..nrh][ncl..nch] */
{
long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
float **m;
/* allocate pointers to rows */
m=(float **) malloc((size_t)((nrow+NR_END)*sizeof(float*)));
if (!m) nrerror("allocation failure 1 in matrix()");
m += NR_END;
m -= nrl;
/* allocate rows and set pointers to them */
m[nrl]=(float *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(float)));
if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
m[nrl] += NR_END;
m[nrl] -= ncl;
for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
/* return pointer to array of pointers to rows */
return m;
}
double **dmatrix(long nrl, long nrh, long ncl, long nch)
/* allocate a double matrix with subscript range m[nrl..nrh][ncl..nch] */
{
long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
double **m;
/* allocate pointers to rows */
m=(double **) malloc((size_t)((nrow+NR_END)*sizeof(double*)));
if (!m) nrerror("allocation failure 1 in matrix()");
m += NR_END;
m -= nrl;
/* allocate rows and set pointers to them */
m[nrl]=(double *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(double)));
if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
m[nrl] += NR_END;
m[nrl] -= ncl;
for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
/* return pointer to array of pointers to rows */
return m;
}
int **imatrix(long nrl, long nrh, long ncl, long nch)
/* allocate a int matrix with subscript range m[nrl..nrh][ncl..nch] */
{
long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
int **m;
/* allocate pointers to rows */
m=(int **) malloc((size_t)((nrow+NR_END)*sizeof(int*)));
if (!m) nrerror("allocation failure 1 in matrix()");
m += NR_END;
m -= nrl;
/* allocate rows and set pointers to them */
m[nrl]=(int *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(int)));
if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
m[nrl] += NR_END;
m[nrl] -= ncl;
for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
/* return pointer to array of pointers to rows */
return m;
}
float **submatrix(float **a, long oldrl, long oldrh, long oldcl, long oldch,
long newrl, long newcl)
/* point a submatrix [newrl..][newcl..] to a[oldrl..oldrh][oldcl..oldch] */
{
long i,j,nrow=oldrh-oldrl+1,ncol=oldcl-newcl;
float **m;
/* allocate array of pointers to rows */
m=(float **) malloc((size_t) ((nrow+NR_END)*sizeof(float*)));
if (!m) nrerror("allocation failure in submatrix()");
m += NR_END;
m -= newrl;
/* set pointers to rows */
for(i=oldrl,j=newrl;i<=oldrh;i++,j++) m[j]=a[i]+ncol;
/* return pointer to array of pointers to rows */
return m;
}
float **convert_matrix(float *a, long nrl, long nrh, long ncl, long nch)
/* allocate a float matrix m[nrl..nrh][ncl..nch] that points to the matrix
declared in the standard C manner as a[nrow][ncol], where nrow=nrh-nrl+1
and ncol=nch-ncl+1. The routine should be called with the address
&a[0][0] as the first argument. */
{
long i,j,nrow=nrh-nrl+1,ncol=nch-ncl+1;
float **m;
/* allocate pointers to rows */
m=(float **) malloc((size_t) ((nrow+NR_END)*sizeof(float*)));
if (!m) nrerror("allocation failure in convert_matrix()");
m += NR_END;
m -= nrl;
/* set pointers to rows */
m[nrl]=a-ncl;
for(i=1,j=nrl+1;i<nrow;i++,j++) m[j]=m[j-1]+ncol;
/* return pointer to array of pointers to rows */
return m;
}
float ***f3tensor(long nrl, long nrh, long ncl, long nch, long ndl, long ndh)
/* allocate a float 3tensor with range t[nrl..nrh][ncl..nch][ndl..ndh] */
{
long i,j,nrow=nrh-nrl+1,ncol=nch-ncl+1,ndep=ndh-ndl+1;
float ***t;
/* allocate pointers to pointers to rows */
t=(float ***) malloc((size_t)((nrow+NR_END)*sizeof(float**)));
if (!t) nrerror("allocation failure 1 in f3tensor()");
t += NR_END;
t -= nrl;
/* allocate pointers to rows and set pointers to them */
t[nrl]=(float **) malloc((size_t)((nrow*ncol+NR_END)*sizeof(float*)));
if (!t[nrl]) nrerror("allocation failure 2 in f3tensor()");
t[nrl] += NR_END;
t[nrl] -= ncl;
/* allocate rows and set pointers to them */
t[nrl][ncl]=(float *) malloc((size_t)((nrow*ncol*ndep+NR_END)*sizeof(float)));
if (!t[nrl][ncl]) nrerror("allocation failure 3 in f3tensor()");
t[nrl][ncl] += NR_END;
t[nrl][ncl] -= ndl;
for(j=ncl+1;j<=nch;j++) t[nrl][j]=t[nrl][j-1]+ndep;
for(i=nrl+1;i<=nrh;i++) {
t[i]=t[i-1]+ncol;
t[i][ncl]=t[i-1][ncl]+ncol*ndep;
for(j=ncl+1;j<=nch;j++) t[i][j]=t[i][j-1]+ndep;
}
/* return pointer to array of pointers to rows */
return t;
}
void free_vector(float *v, long nl, long nh)
/* free a float vector allocated with vector() */
{
free((FREE_ARG) (v+nl-NR_END));
}
void free_ivector(int *v, long nl, long nh)
/* free an int vector allocated with ivector() */
{
free((FREE_ARG) (v+nl-NR_END));
}
void free_cvector(unsigned char *v, long nl, long nh)
/* free an unsigned char vector allocated with cvector() */
{
free((FREE_ARG) (v+nl-NR_END));
}
void free_lvector(unsigned long *v, long nl, long nh)
/* free an unsigned long vector allocated with lvector() */
{
free((FREE_ARG) (v+nl-NR_END));
}
void free_dvector(double *v, long nl, long nh)
/* free a double vector allocated with dvector() */
{
free((FREE_ARG) (v+nl-NR_END));
}
void free_matrix(float **m, long nrl, long nrh, long ncl, long nch)
/* free a float matrix allocated by matrix() */
{
free((FREE_ARG) (m[nrl]+ncl-NR_END));
free((FREE_ARG) (m+nrl-NR_END));
}
void free_dmatrix(double **m, long nrl, long nrh, long ncl, long nch)
/* free a double matrix allocated by dmatrix() */
{
free((FREE_ARG) (m[nrl]+ncl-NR_END));
free((FREE_ARG) (m+nrl-NR_END));
}
void free_imatrix(int **m, long nrl, long nrh, long ncl, long nch)
/* free an int matrix allocated by imatrix() */
{
free((FREE_ARG) (m[nrl]+ncl-NR_END));
free((FREE_ARG) (m+nrl-NR_END));
}
void free_submatrix(float **b, long nrl, long nrh, long ncl, long nch)
/* free a submatrix allocated by submatrix() */
{
free((FREE_ARG) (b+nrl-NR_END));
}
void free_convert_matrix(float **b, long nrl, long nrh, long ncl, long nch)
/* free a matrix allocated by convert_matrix() */
{
free((FREE_ARG) (b+nrl-NR_END));
}
void free_f3tensor(float ***t, long nrl, long nrh, long ncl, long nch,
long ndl, long ndh)
/* free a float f3tensor allocated by f3tensor() */
{
free((FREE_ARG) (t[nrl][ncl]+ndl-NR_END));
free((FREE_ARG) (t[nrl]+ncl-NR_END));
free((FREE_ARG) (t+nrl-NR_END));
}
#else /* ANSI */
/* traditional - K&R */
#include <stdio.h>
#define NR_END 1
#define FREE_ARG char*
void nrerror(error_text)
char error_text[];
/* Numerical Recipes standard error handler */
{
void exit();
fprintf(stderr,"Numerical Recipes run-time error...\n");
fprintf(stderr,"%s\n",error_text);
fprintf(stderr,"...now exiting to system...\n");
exit(1);
}
float *vector(nl,nh)
long nh,nl;
/* allocate a float vector with subscript range v[nl..nh] */
{
float *v;
v=(float *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(float)));
if (!v) nrerror("allocation failure in vector()");
return v-nl+NR_END;
}
int *ivector(nl,nh)
long nh,nl;
/* allocate an int vector with subscript range v[nl..nh] */
{
int *v;
v=(int *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(int)));
if (!v) nrerror("allocation failure in ivector()");
return v-nl+NR_END;
}
unsigned char *cvector(nl,nh)
long nh,nl;
/* allocate an unsigned char vector with subscript range v[nl..nh] */
{
unsigned char *v;
v=(unsigned char *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(unsigned char)));
if (!v) nrerror("allocation failure in cvector()");
return v-nl+NR_END;
}
unsigned long *lvector(nl,nh)
long nh,nl;
/* allocate an unsigned long vector with subscript range v[nl..nh] */
{
unsigned long *v;
v=(unsigned long *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(long)));
if (!v) nrerror("allocation failure in lvector()");
return v-nl+NR_END;
}
double *dvector(nl,nh)
long nh,nl;
/* allocate a double vector with subscript range v[nl..nh] */
{
double *v;
v=(double *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(double)));
if (!v) nrerror("allocation failure in dvector()");
return v-nl+NR_END;
}
float **matrix(nrl,nrh,ncl,nch)
long nch,ncl,nrh,nrl;
/* allocate a float matrix with subscript range m[nrl..nrh][ncl..nch] */
{
long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
float **m;
/* allocate pointers to rows */
m=(float **) malloc((unsigned int)((nrow+NR_END)*sizeof(float*)));
if (!m) nrerror("allocation failure 1 in matrix()");
m += NR_END;
m -= nrl;
/* allocate rows and set pointers to them */
m[nrl]=(float *) malloc((unsigned int)((nrow*ncol+NR_END)*sizeof(float)));
if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
m[nrl] += NR_END;
m[nrl] -= ncl;
for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
/* return pointer to array of pointers to rows */
return m;
}
double **dmatrix(nrl,nrh,ncl,nch)
long nch,ncl,nrh,nrl;
/* allocate a double matrix with subscript range m[nrl..nrh][ncl..nch] */
{
long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
double **m;
/* allocate pointers to rows */
m=(double **) malloc((unsigned int)((nrow+NR_END)*sizeof(double*)));
if (!m) nrerror("allocation failure 1 in matrix()");
m += NR_END;
m -= nrl;
/* allocate rows and set pointers to them */
m[nrl]=(double *) malloc((unsigned int)((nrow*ncol+NR_END)*sizeof(double)));
if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
m[nrl] += NR_END;
m[nrl] -= ncl;
for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
/* return pointer to array of pointers to rows */
return m;
}
int **imatrix(nrl,nrh,ncl,nch)
long nch,ncl,nrh,nrl;
/* allocate a int matrix with subscript range m[nrl..nrh][ncl..nch] */
{
long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
int **m;
/* allocate pointers to rows */
m=(int **) malloc((unsigned int)((nrow+NR_END)*sizeof(int*)));
if (!m) nrerror("allocation failure 1 in matrix()");
m += NR_END;
m -= nrl;
/* allocate rows and set pointers to them */
m[nrl]=(int *) malloc((unsigned int)((nrow*ncol+NR_END)*sizeof(int)));
if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
m[nrl] += NR_END;
m[nrl] -= ncl;
for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
/* return pointer to array of pointers to rows */
return m;
}
float **submatrix(a,oldrl,oldrh,oldcl,oldch,newrl,newcl)
float **a;
long newcl,newrl,oldch,oldcl,oldrh,oldrl;
/* point a submatrix [newrl..][newcl..] to a[oldrl..oldrh][oldcl..oldch] */
{
long i,j,nrow=oldrh-oldrl+1,ncol=oldcl-newcl;
float **m;
/* allocate array of pointers to rows */
m=(float **) malloc((unsigned int) ((nrow+NR_END)*sizeof(float*)));
if (!m) nrerror("allocation failure in submatrix()");
m += NR_END;
m -= newrl;
/* set pointers to rows */
for(i=oldrl,j=newrl;i<=oldrh;i++,j++) m[j]=a[i]+ncol;
/* return pointer to array of pointers to rows */
return m;
}
float **convert_matrix(a,nrl,nrh,ncl,nch)
float *a;
long nch,ncl,nrh,nrl;
/* allocate a float matrix m[nrl..nrh][ncl..nch] that points to the matrix
declared in the standard C manner as a[nrow][ncol], where nrow=nrh-nrl+1
and ncol=nch-ncl+1. The routine should be called with the address
&a[0][0] as the first argument. */
{
long i,j,nrow=nrh-nrl+1,ncol=nch-ncl+1;
float **m;
/* allocate pointers to rows */
m=(float **) malloc((unsigned int) ((nrow+NR_END)*sizeof(float*)));
if (!m) nrerror("allocation failure in convert_matrix()");
m += NR_END;
m -= nrl;
/* set pointers to rows */
m[nrl]=a-ncl;
for(i=1,j=nrl+1;i<nrow;i++,j++) m[j]=m[j-1]+ncol;
/* return pointer to array of pointers to rows */
return m;
}
float ***f3tensor(nrl,nrh,ncl,nch,ndl,ndh)
long nch,ncl,ndh,ndl,nrh,nrl;
/* allocate a float 3tensor with range t[nrl..nrh][ncl..nch][ndl..ndh] */
{
long i,j,nrow=nrh-nrl+1,ncol=nch-ncl+1,ndep=ndh-ndl+1;
float ***t;
/* allocate pointers to pointers to rows */
t=(float ***) malloc((unsigned int)((nrow+NR_END)*sizeof(float**)));
if (!t) nrerror("allocation failure 1 in f3tensor()");
t += NR_END;
t -= nrl;
/* allocate pointers to rows and set pointers to them */
t[nrl]=(float **) malloc((unsigned int)((nrow*ncol+NR_END)*sizeof(float*)));
if (!t[nrl]) nrerror("allocation failure 2 in f3tensor()");
t[nrl] += NR_END;
t[nrl] -= ncl;
/* allocate rows and set pointers to them */
t[nrl][ncl]=(float *) malloc((unsigned int)((nrow*ncol*ndep+NR_END)*sizeof(float)));
if (!t[nrl][ncl]) nrerror("allocation failure 3 in f3tensor()");
t[nrl][ncl] += NR_END;
t[nrl][ncl] -= ndl;
for(j=ncl+1;j<=nch;j++) t[nrl][j]=t[nrl][j-1]+ndep;
for(i=nrl+1;i<=nrh;i++) {
t[i]=t[i-1]+ncol;
t[i][ncl]=t[i-1][ncl]+ncol*ndep;
for(j=ncl+1;j<=nch;j++) t[i][j]=t[i][j-1]+ndep;
}
/* return pointer to array of pointers to rows */
return t;
}
void free_vector(v,nl,nh)
float *v;
long nh,nl;
/* free a float vector allocated with vector() */
{
free((FREE_ARG) (v+nl-NR_END));
}
void free_ivector(v,nl,nh)
int *v;
long nh,nl;
/* free an int vector allocated with ivector() */
{
free((FREE_ARG) (v+nl-NR_END));
}
void free_cvector(v,nl,nh)
long nh,nl;
unsigned char *v;
/* free an unsigned char vector allocated with cvector() */
{
free((FREE_ARG) (v+nl-NR_END));
}
void free_lvector(v,nl,nh)
long nh,nl;
unsigned long *v;
/* free an unsigned long vector allocated with lvector() */
{
free((FREE_ARG) (v+nl-NR_END));
}
void free_dvector(v,nl,nh)
double *v;
long nh,nl;
/* free a double vector allocated with dvector() */
{
free((FREE_ARG) (v+nl-NR_END));
}
void free_matrix(m,nrl,nrh,ncl,nch)
float **m;
long nch,ncl,nrh,nrl;
/* free a float matrix allocated by matrix() */
{
free((FREE_ARG) (m[nrl]+ncl-NR_END));
free((FREE_ARG) (m+nrl-NR_END));
}
void free_dmatrix(m,nrl,nrh,ncl,nch)
double **m;
long nch,ncl,nrh,nrl;
/* free a double matrix allocated by dmatrix() */
{
free((FREE_ARG) (m[nrl]+ncl-NR_END));
free((FREE_ARG) (m+nrl-NR_END));
}
void free_imatrix(m,nrl,nrh,ncl,nch)
int **m;
long nch,ncl,nrh,nrl;
/* free an int matrix allocated by imatrix() */
{
free((FREE_ARG) (m[nrl]+ncl-NR_END));
free((FREE_ARG) (m+nrl-NR_END));
}
void free_submatrix(b,nrl,nrh,ncl,nch)
float **b;
long nch,ncl,nrh,nrl;
/* free a submatrix allocated by submatrix() */
{
free((FREE_ARG) (b+nrl-NR_END));
}
void free_convert_matrix(b,nrl,nrh,ncl,nch)
float **b;
long nch,ncl,nrh,nrl;
/* free a matrix allocated by convert_matrix() */
{
free((FREE_ARG) (b+nrl-NR_END));
}
void free_f3tensor(t,nrl,nrh,ncl,nch,ndl,ndh)
float ***t;
long nch,ncl,ndh,ndl,nrh,nrl;
/* free a float f3tensor allocated by f3tensor() */
{
free((FREE_ARG) (t[nrl][ncl]+ndl-NR_END));
free((FREE_ARG) (t[nrl]+ncl-NR_END));
free((FREE_ARG) (t+nrl-NR_END));
}
#endif /* ANSI */

View File

@ -0,0 +1,101 @@
#ifndef _NR_UTILS_H_
#define _NR_UTILS_H_
static float sqrarg;
#define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg)
static double dsqrarg;
#define DSQR(a) ((dsqrarg=(a)) == 0.0 ? 0.0 : dsqrarg*dsqrarg)
static double dmaxarg1,dmaxarg2;
#define DMAX(a,b) (dmaxarg1=(a),dmaxarg2=(b),(dmaxarg1) > (dmaxarg2) ?\
(dmaxarg1) : (dmaxarg2))
static double dminarg1,dminarg2;
#define DMIN(a,b) (dminarg1=(a),dminarg2=(b),(dminarg1) < (dminarg2) ?\
(dminarg1) : (dminarg2))
static float maxarg1,maxarg2;
#define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\
(maxarg1) : (maxarg2))
static float minarg1,minarg2;
#define FMIN(a,b) (minarg1=(a),minarg2=(b),(minarg1) < (minarg2) ?\
(minarg1) : (minarg2))
static long lmaxarg1,lmaxarg2;
#define LMAX(a,b) (lmaxarg1=(a),lmaxarg2=(b),(lmaxarg1) > (lmaxarg2) ?\
(lmaxarg1) : (lmaxarg2))
static long lminarg1,lminarg2;
#define LMIN(a,b) (lminarg1=(a),lminarg2=(b),(lminarg1) < (lminarg2) ?\
(lminarg1) : (lminarg2))
static int imaxarg1,imaxarg2;
#define IMAX(a,b) (imaxarg1=(a),imaxarg2=(b),(imaxarg1) > (imaxarg2) ?\
(imaxarg1) : (imaxarg2))
static int iminarg1,iminarg2;
#define IMIN(a,b) (iminarg1=(a),iminarg2=(b),(iminarg1) < (iminarg2) ?\
(iminarg1) : (iminarg2))
#define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
#if 1
void nrerror(char error_text[]);
float *vec(long nl, long nh);
int *ivector(long nl, long nh);
unsigned char *cvector(long nl, long nh);
unsigned long *lvector(long nl, long nh);
double *dvector(long nl, long nh);
float **matrix(long nrl, long nrh, long ncl, long nch);
double **dmatrix(long nrl, long nrh, long ncl, long nch);
int **imatrix(long nrl, long nrh, long ncl, long nch);
float **submatrix(float **a, long oldrl, long oldrh, long oldcl, long oldch,
long newrl, long newcl);
float **convert_matrix(float *a, long nrl, long nrh, long ncl, long nch);
float ***f3tensor(long nrl, long nrh, long ncl, long nch, long ndl, long ndh);
void free_vector(float *v, long nl, long nh);
void free_ivector(int *v, long nl, long nh);
void free_cvector(unsigned char *v, long nl, long nh);
void free_lvector(unsigned long *v, long nl, long nh);
void free_dvector(double *v, long nl, long nh);
void free_matrix(float **m, long nrl, long nrh, long ncl, long nch);
void free_dmatrix(double **m, long nrl, long nrh, long ncl, long nch);
void free_imatrix(int **m, long nrl, long nrh, long ncl, long nch);
void free_submatrix(float **b, long nrl, long nrh, long ncl, long nch);
void free_convert_matrix(float **b, long nrl, long nrh, long ncl, long nch);
void free_f3tensor(float ***t, long nrl, long nrh, long ncl, long nch,
long ndl, long ndh);
#else /* ANSI */
/* traditional - K&R */
void nrerror();
float *vec();
float **matrix();
float **submatrix();
float **convert_matrix();
float ***f3tensor();
double *dvector();
double **dmatrix();
int *ivector();
int **imatrix();
unsigned char *cvector();
unsigned long *lvector();
void free_vector();
void free_dvector();
void free_ivector();
void free_cvector();
void free_lvector();
void free_matrix();
void free_submatrix();
void free_convert_matrix();
void free_dmatrix();
void free_imatrix();
void free_f3tensor();
#endif /* ANSI */
#endif /* _NR_UTILS_H_ */

View File

@ -0,0 +1,113 @@
#include <CGAL/trace.h>
#include <CGAL/Timer.h>
#include <iostream>
#include <string>
#include <fstream>
#include <Eigen/Eigen>
#include <Eigen/SVD>
double norm_1(Eigen::Matrix3d X)
{
double sum = 0;
for ( int i = 0; i < 3; i++ )
{
for ( int j = 0; j < 3; j++ )
{
sum += abs(X(i,j));
}
}
return sum;
}
double norm_inf(Eigen::Matrix3d X)
{
double max_abs = abs(X(0,0));
for ( int i = 0; i < 3; i++ )
{
for ( int j = 0; j < 3; j++ )
{
double new_abs = abs(X(i,j));
if ( new_abs > max_abs )
{
max_abs = new_abs;
}
}
}
return max_abs;
}
void algorithm_polar(Eigen::Matrix3d A, Eigen::Matrix3d &U, double tolerance)
{
Eigen::Matrix3d X = A;
int k = -1;
Eigen::Matrix3d Y;
double alpha, beta, gamma;
do
{
k++;
Y = X.inverse();
alpha = sqrt( norm_1(X) * norm_inf(X) );
beta = sqrt( norm_1(Y) * norm_inf(Y) );
gamma = sqrt(beta/alpha);
X = 0.5*( gamma*X + Y.transpose()/gamma );
} while ( abs(gamma-1) > tolerance );
U = X;
}
int main() {
std::ifstream file;
file.open("SVD_benchmark");
if (!file)
{
CGAL_TRACE_STREAM << "Error loading file!\n";
return 0;
}
int ite = 200000;
Eigen::Matrix3d A, U, H;
int matrix_idx = rand()%200;
for (int i = 0; i < matrix_idx; i++)
{
for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 3; k++)
{
file >> A(j, k);
}
}
}
file.close();
A(0,0) = 0.0014667022958104185; A(0,1) = 0.0024644551180079627; A(0,2) = 0.0040659871060825092;
A(1,0) = 0.0028327558016991478; A(1,1) = 0.0054236820249146406; A(1,2) = 0.0079280090866983826;
A(2,0) = 0.0039090073251031232; A(2,1) = 0.0066744523074963374; A(2,2) = 0.010848552426718550;
A = A*10000;
CGAL::Timer task_timer;
CGAL_TRACE_STREAM << "Start SVD decomposition...";
task_timer.start();
for (int i = 0; i < ite; i++)
{
algorithm_polar(A, U, 1e-6);
U.transposeInPlace();
double det_2 = U.determinant();
int aaa = 0;
}
task_timer.stop();
CGAL_TRACE_STREAM << "done: " << task_timer.time() << "s\n";
return 0;
}

View File

@ -0,0 +1,162 @@
#include <CGAL/trace.h>
#include <CGAL/Timer.h>
#include <iostream>
#include <string>
#include <fstream>
#include <CGAL/FPU_extension.h>
#include <Eigen/Eigen>
#include <Eigen/SVD>
double norm_1(Eigen::Matrix3d X)
{
double sum = 0;
for ( int i = 0; i < 3; i++ )
{
for ( int j = 0; j < 3; j++ )
{
sum += abs(X(i,j));
}
}
return sum;
}
double norm_inf(Eigen::Matrix3d X)
{
double max_abs = abs(X(0,0));
for ( int i = 0; i < 3; i++ )
{
for ( int j = 0; j < 3; j++ )
{
double new_abs = abs(X(i,j));
if ( new_abs > max_abs )
{
max_abs = new_abs;
}
}
}
return max_abs;
}
template<typename Mat>
void polar_eigen(const Mat& A, Mat& R, bool& SVD)
{
typedef typename Mat::Scalar Scalar;
typedef Eigen::Matrix<typename Mat::Scalar,3,1> Vec;
const Scalar th = std::sqrt(Eigen::NumTraits<Scalar>::dummy_precision());
Eigen::SelfAdjointEigenSolver<Mat> eig;
feclearexcept(FE_UNDERFLOW);
eig.computeDirect(A.transpose()*A);
if(fetestexcept(FE_UNDERFLOW) || eig.eigenvalues()(0)/eig.eigenvalues()(2)/100.0<th)
{
// The computation of the eigenvalues might have diverged.
// Fallback to an accurate SVD based decomposiiton method.
Eigen::JacobiSVD<Mat> svd;
svd.compute(A, Eigen::ComputeFullU | Eigen::ComputeFullV );
const Mat& u = svd.matrixU(); const Mat& v = svd.matrixV(); const Vec& w = svd.singularValues();
R = u*v.transpose();
SVD = true;
return;
}
Vec S = eig.eigenvalues().cwiseSqrt();
R = A * eig.eigenvectors() * S.asDiagonal().inverse()
* eig.eigenvectors().transpose();
SVD = false;
}
int main() {
std::ifstream file;
file.open("Polar_benchmark");
if (!file)
{
CGAL_TRACE_STREAM << "Error loading file!\n";
return 0;
}
Eigen::Matrix3d A, U, r;
bool SVD = false;
int num_svd = 0;
int num_mtr = 0;
CGAL_TRACE_STREAM << "start polar decomposition...\n";
while (!file.eof())
{
for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 3; k++)
{
file >> A(j, k);
}
}
num_mtr++;
double det = A.determinant();
if (A.determinant() > 0)
{
polar_eigen<Eigen::Matrix3d> (A, U, SVD);
if (SVD)
num_svd++;
U.transposeInPlace();
double det_2 = U.determinant();
if ( abs(det_2-1) > 1e-2 )
{
CGAL_TRACE_STREAM << "error matrix: det = " << det_2 << "\n";
}
}
}
file.close();
CGAL_TRACE_STREAM << "done. " << num_svd << " SVD decompositions in " << num_mtr << " matrices\n";
return 0;
/*int ite = 200000;
Eigen::JacobiSVD<Eigen::Matrix3d> svd;
Eigen::Matrix3d A, U, H, r;
int matrix_idx = rand()%200;
for (int i = 0; i < matrix_idx; i++)
{
for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 3; k++)
{
file >> A(j, k);
}
}
}
file.close();
A(0,0) = 0.0014667022958104185; A(0,1) = 0.0024644551180079627; A(0,2) = 0.0040659871060825092;
A(1,0) = 0.0028327558016991478; A(1,1) = 0.0054236820249146406; A(1,2) = 0.0079280090866983826;
A(2,0) = 0.0039090073251031232; A(2,1) = 0.0066744523074963374; A(2,2) = 0.010848552426718550;
A = A*10000;
double det = A.determinant();
CGAL::Timer task_timer;
CGAL_TRACE_STREAM << "Start SVD decomposition...";
task_timer.start();
for (int i = 0; i < ite; i++)
{
if (A.determinant() > 0)
{
polar_eigen<Eigen::Matrix3d> (A, U);
U.transposeInPlace();
double det_2 = U.determinant();
r = U*U.transpose();
}
}
task_timer.stop();
CGAL_TRACE_STREAM << "done: " << task_timer.time() << "s\n";
return 0;*/
}

View File

@ -0,0 +1,130 @@
#include <CGAL/trace.h>
#include <CGAL/Timer.h>
#include <iostream>
#include <string>
#include <fstream>
#include <Eigen/Eigen>
// #define USE_SCALAR_IMPLEMENTATION
#define USE_SSE_IMPLEMENTATION
#define COMPUTE_V_AS_MATRIX
#define COMPUTE_U_AS_MATRIX
#include <CGAL/internal/Surface_modeling/auxiliary/Singular_Value_Decomposition_Preamble.hpp>
int main() {
std::ifstream file;
file.open("SVD_benchmark");
if (!file)
{
CGAL_TRACE_STREAM << "Error loading file!\n";
return 0;
}
int ite = 200000;
Eigen::JacobiSVD<Eigen::Matrix3d> svd;
Eigen::Matrix3d u, v, m, r;
Eigen::Vector3d w;
int matrix_idx = rand()%200;
for (int i = 0; i < matrix_idx; i++)
{
for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 3; k++)
{
file >> m(j, k);
}
}
}
CGAL::Timer task_timer;
std::cout << "Start SVD decomposition...";
task_timer.start();
for (int i = 0; i < ite; i++)
{
#include <CGAL/internal/Surface_modeling/auxiliary/Singular_Value_Decomposition_Kernel_Declarations.hpp>
ENABLE_SCALAR_IMPLEMENTATION(Sa11.f=m(0,0);)
ENABLE_SCALAR_IMPLEMENTATION(Sa21.f=m(1,0);)
ENABLE_SCALAR_IMPLEMENTATION(Sa31.f=m(2,0);)
ENABLE_SCALAR_IMPLEMENTATION(Sa12.f=m(0,1);)
ENABLE_SCALAR_IMPLEMENTATION(Sa22.f=m(1,1);)
ENABLE_SCALAR_IMPLEMENTATION(Sa32.f=m(2,1);)
ENABLE_SCALAR_IMPLEMENTATION(Sa13.f=m(0,2);)
ENABLE_SCALAR_IMPLEMENTATION(Sa23.f=m(1,2);)
ENABLE_SCALAR_IMPLEMENTATION(Sa33.f=m(2,2);)
ENABLE_SSE_IMPLEMENTATION(Va11=_mm_set1_ps(m(0,0));)
ENABLE_SSE_IMPLEMENTATION(Va21=_mm_set1_ps(m(1,0));)
ENABLE_SSE_IMPLEMENTATION(Va31=_mm_set1_ps(m(2,0));)
ENABLE_SSE_IMPLEMENTATION(Va12=_mm_set1_ps(m(0,1));)
ENABLE_SSE_IMPLEMENTATION(Va22=_mm_set1_ps(m(1,1));)
ENABLE_SSE_IMPLEMENTATION(Va32=_mm_set1_ps(m(2,1));)
ENABLE_SSE_IMPLEMENTATION(Va13=_mm_set1_ps(m(0,2));)
ENABLE_SSE_IMPLEMENTATION(Va23=_mm_set1_ps(m(1,2));)
ENABLE_SSE_IMPLEMENTATION(Va33=_mm_set1_ps(m(2,2));)
#include <CGAL/internal/Surface_modeling/auxiliary/Singular_Value_Decomposition_Main_Kernel_Body.hpp>
std::pair<Eigen::Matrix3d, Eigen::Matrix3d> solver;
#ifdef USE_SCALAR_IMPLEMENTATION
solver.first(0,0) = Su11.f;
solver.first(1,0) = Su21.f;
solver.first(2,0) = Su31.f;
solver.first(0,1) = Su12.f;
solver.first(1,1) = Su22.f;
solver.first(2,1) = Su32.f;
solver.first(0,2) = Su13.f;
solver.first(1,2) = Su23.f;
solver.first(2,2) = Su33.f;
solver.second(0,0) = Sv11.f;
solver.second(1,0) = Sv21.f;
solver.second(2,0) = Sv31.f;
solver.second(0,1) = Sv12.f;
solver.second(1,1) = Sv22.f;
solver.second(2,1) = Sv32.f;
solver.second(0,2) = Sv13.f;
solver.second(1,2) = Sv23.f;
solver.second(2,2) = Sv33.f;
#endif
#ifdef USE_SSE_IMPLEMENTATION
float buf[4];
_mm_storeu_ps(buf,Vu11);solver.first(0,0)=buf[0];
_mm_storeu_ps(buf,Vu21);solver.first(1,0)=buf[0];
_mm_storeu_ps(buf,Vu31);solver.first(2,0)=buf[0];
_mm_storeu_ps(buf,Vu12);solver.first(0,1)=buf[0];
_mm_storeu_ps(buf,Vu22);solver.first(1,1)=buf[0];
_mm_storeu_ps(buf,Vu32);solver.first(2,1)=buf[0];
_mm_storeu_ps(buf,Vu13);solver.first(0,2)=buf[0];
_mm_storeu_ps(buf,Vu23);solver.first(1,2)=buf[0];
_mm_storeu_ps(buf,Vu33);solver.first(2,2)=buf[0];
_mm_storeu_ps(buf,Vv11);solver.second(0,0)=buf[0];
_mm_storeu_ps(buf,Vv21);solver.second(1,0)=buf[0];
_mm_storeu_ps(buf,Vv31);solver.second(2,0)=buf[0];
_mm_storeu_ps(buf,Vv12);solver.second(0,1)=buf[0];
_mm_storeu_ps(buf,Vv22);solver.second(1,1)=buf[0];
_mm_storeu_ps(buf,Vv32);solver.second(2,1)=buf[0];
_mm_storeu_ps(buf,Vv13);solver.second(0,2)=buf[0];
_mm_storeu_ps(buf,Vv23);solver.second(1,2)=buf[0];
_mm_storeu_ps(buf,Vv33);solver.second(2,2)=buf[0];
#endif
r = solver.first * solver.second.transpose();
}
task_timer.stop();
file.close();
std::cout << "done: " << task_timer.time() << "s\n";
return 0;
}

View File

@ -0,0 +1,57 @@
#include <CGAL/trace.h>
#include <CGAL/Timer.h>
#include <iostream>
#include <string>
#include <fstream>
#include <Eigen/Eigen>
#include <Eigen/SVD>
int main() {
std::ifstream file;
file.open("SVD_benchmark");
if (!file)
{
CGAL_TRACE_STREAM << "Error loading file!\n";
return 0;
}
int ite = 200000;
Eigen::JacobiSVD<Eigen::Matrix3d> svd;
Eigen::Matrix3d u, v, cov, r;
Eigen::Vector3d w;
int matrix_idx = rand()%200;
for (int i = 0; i < matrix_idx; i++)
{
for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 3; k++)
{
file >> cov(j, k);
}
}
}
CGAL::Timer task_timer;
CGAL_TRACE_STREAM << "Start SVD decomposition...";
task_timer.start();
for (int i = 0; i < ite; i++)
{
svd.compute( cov, Eigen::ComputeFullU | Eigen::ComputeFullV );
u = svd.matrixU(); v = svd.matrixV(); w = svd.singularValues();
r = v*u.transpose();
}
task_timer.stop();
file.close();
CGAL_TRACE_STREAM << "done: " << task_timer.time() << "s\n";
return 0;
}

View File

@ -0,0 +1,91 @@
#include "svd.h"
#include <CGAL/trace.h>
#include <CGAL/Timer.h>
#include <iostream>
#include <string>
#include <fstream>
int main() {
std::ifstream file;
file.open("SVD_benchmark");
if (!file)
{
CGAL_TRACE_STREAM << "Error loading file!\n";
return 0;
}
// initialization of matrices
int ite = 200000;
double ***u;
u = new double** [ite];
for (int i = 0; i < ite; i++)
{
u[i] = new double* [4];
for (int j = 0; j < 4; j++)
{
u[i][j] = new double [4];
}
}
int matrix_idx = rand()%200;
for (int i = 0; i < matrix_idx; i++)
{
for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 3; k++)
{
file >> u[0][j+1][k+1];
}
}
}
for (int i = 1; i < ite; i++)
{
for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 3; k++)
{
u[i][j+1][k+1] = u[0][j+1][k+1];
}
}
}
double **v;
v = new double* [4];
for (int i = 0; i < 4; i++)
{
v[i] = new double[4];
}
double *w;
w = new double[4];
CGAL::Timer task_timer;
CGAL_TRACE_STREAM << "Start SVD decomposition...";
task_timer.start();
for (int i = 0; i < ite; i++)
{
svdcmp(u[i], 3, 3, w, v);
}
task_timer.stop();
file.close();
CGAL_TRACE_STREAM << "done: " << task_timer.time() << "s\n";
delete [] w;
for (int i = 0; i < 4; i++)
{
delete [] v[i];
}
delete [] v;
for (int i = 0; i < ite; i++)
{
for (int j = 0; j < 4; j++)
{
delete [] u[i][j];
}
delete [] u[i];
}
delete [] u;
return 0;
}

View File

@ -0,0 +1,209 @@
#pragma once
#include <math.h>
#include "nrutil.h"
double pythag(double a, double b);
inline void svdcmp(double **a, int m, int n, double w[], double **v)
{
double pythag(double a, double b);
int flag,i,its,j,jj,k,l,nm;
double anorm,c,f,g,h,s,scale,x,y,z,*rv1;
rv1=dvector(1,n);
g=scale=anorm=0.0; //Householder reduction to bidiagonal form.
for (i=1;i<=n;i++) {
l=i+1;
rv1[i]=scale*g;
g=s=scale=0.0;
if (i <= m) {
for (k=i;k<=m;k++) scale += fabs(a[k][i]);
if (scale) {
for (k=i;k<=m;k++) {
a[k][i] /= scale;
s += a[k][i]*a[k][i];
}
f=a[i][i];
g = -SIGN(sqrt(s),f);
h=f*g-s;
a[i][i]=f-g;
for (j=l;j<=n;j++) {
for (s=0.0,k=i;k<=m;k++) s += a[k][i]*a[k][j];
f=s/h;
for (k=i;k<=m;k++) a[k][j] += f*a[k][i];
}
for (k=i;k<=m;k++) a[k][i] *= scale;
}
}
w[i]=scale *g;
g=s=scale=0.0;
if (i <= m && i != n) {
for (k=l;k<=n;k++) scale += fabs(a[i][k]);
if (scale) {
for (k=l;k<=n;k++) {
a[i][k] /= scale;
s += a[i][k]*a[i][k];
}
f=a[i][l];
g = -SIGN(sqrt(s),f);
h=f*g-s;
a[i][l]=f-g;
for (k=l;k<=n;k++) rv1[k]=a[i][k]/h;
for (j=l;j<=m;j++) {
for (s=0.0,k=l;k<=n;k++) s += a[j][k]*a[i][k];
for (k=l;k<=n;k++) a[j][k] += s*rv1[k];
}
for (k=l;k<=n;k++) a[i][k] *= scale;
}
}
anorm=DMAX(anorm,(fabs(w[i])+fabs(rv1[i])));
}
for (i=n;i>=1;i--) { //Accumulation of right-hand transformations.
if (i < n) {
if (g) {
for (j=l;j<=n;j++) //Double division to avoid possible underflow.
v[j][i]=(a[i][j]/a[i][l])/g;
for (j=l;j<=n;j++) {
for (s=0.0,k=l;k<=n;k++) s += a[i][k]*v[k][j];
for (k=l;k<=n;k++) v[k][j] += s*v[k][i];
}
}
for (j=l;j<=n;j++) v[i][j]=v[j][i]=0.0;
}
v[i][i]=1.0;
g=rv1[i];
l=i;
}
for (i=IMIN(m,n);i>=1;i--) { //Accumulation of left-hand transformations.
l=i+1;
g=w[i];
for (j=l;j<=n;j++) a[i][j]=0.0;
if (g) {
g=1.0/g;
for (j=l;j<=n;j++) {
for (s=0.0,k=l;k<=m;k++) s += a[k][i]*a[k][j];
f=(s/a[i][i])*g;
for (k=i;k<=m;k++) a[k][j] += f*a[k][i];
}
for (j=i;j<=m;j++) a[j][i] *= g;
} else for (j=i;j<=m;j++) a[j][i]=0.0;
++a[i][i];
}
for (k=n;k>=1;k--)
{ //Diagonalization of the bidiagonal form: Loop over
for (its=1;its<=30;its++)
{
flag=1;
for (l=k;l>=1;l--)
{ //Test for splitting.
nm=l-1; //Note that rv1[1] is always zero.
if ((double)(fabs(rv1[l])+anorm) == anorm)
{
flag=0;
break;
}
if ((double)(fabs(w[nm])+anorm) == anorm) break;
}
if (flag)
{
c=0.0; //Cancellation of rv1[l], if l > 1.
s=1.0;
for (i=l;i<=k;i++)
{
f=s*rv1[i];
rv1[i]=c*rv1[i];
if ((double)(fabs(f)+anorm) == anorm)
break;
g=w[i];
h=pythag(f,g);
w[i]=h;
h=1.0/h;
c=g*h;
s = -f*h;
for (j=1;j<=m;j++)
{
y=a[j][nm];
z=a[j][i];
a[j][nm]=y*c+z*s;
a[j][i]=z*c-y*s;
}
}
}
z=w[k];
if (l == k)
{ //Convergence.
if (z < 0.0)
{ //Singular value is made nonnegative.
w[k] = -z;
for (j=1;j<=n;j++)
v[j][k] = -v[j][k];
}
break;
}
/* if (its == 30)
nrerror("no convergence in 30 svdcmp iterations");*/
x=w[l]; //Shift from bottom 2-by-2 minor.
nm=k-1;
y=w[nm];
g=rv1[nm];
h=rv1[k];
f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
g=pythag(f,1.0);
f=((x-z)*(x+z)+h*((y/(f+SIGN(g,f)))-h))/x;
c=s=1.0; //Next QR transformation:
for (j=l;j<=nm;j++)
{
i=j+1;
g=rv1[i];
y=w[i];
h=s*g;
g=c*g;
z=pythag(f,h);
rv1[j]=z;
c=f/z;
s=h/z;
f=x*c+g*s;
g = g*c-x*s;
h=y*s;
y *= c;
for (jj=1;jj<=n;jj++)
{
x=v[jj][j];
z=v[jj][i];
v[jj][j]=x*c+z*s;
v[jj][i]=z*c-x*s;
}
z=pythag(f,h);
w[j]=z; //Rotation can be arbitrary if z = 0.
if (z)
{
z=1.0/z;
c=f*z;
s=h*z;
}
f=c*g+s*y;
x=c*y-s*g;
for (jj=1;jj<=m;jj++)
{
y=a[jj][j];
z=a[jj][i];
a[jj][j]=y*c+z*s;
a[jj][i]=z*c-y*s;
}
}
rv1[l]=0.0;
rv1[k]=f;
w[k]=x;
}
}
free_dvector(rv1,1,n);
}
inline double pythag(double a, double b)
{
double absa,absb;
absa=fabs(a);
absb=fabs(b);
if (absa > absb) return absa*sqrt(1.0+DSQR(absb/absa));
else return (absb == 0.0 ? 0.0 : absb*sqrt(1.0+DSQR(absa/absb)));
}

View File

@ -0,0 +1,38 @@
# Created by the script cgal_create_cmake_script
# This is the CMake script for compiling a CGAL application.
project( Surface_modeling_demo )
cmake_minimum_required(VERSION 2.6.2)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 2.6)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.3)
cmake_policy(VERSION 2.8.4)
else()
cmake_policy(VERSION 2.6)
endif()
endif()
find_package(CGAL QUIET COMPONENTS Core )
if ( CGAL_FOUND )
include( ${CGAL_USE_FILE} )
find_package(Eigen3 3.1.91) #(requires 3.2.0 or greater)
if (EIGEN3_FOUND)
include( ${EIGEN3_USE_FILE} )
include( CGAL_CreateSingleSourceCGALProgram )
include_directories (BEFORE "../../include")
create_single_source_cgal_program( "deform_mesh_for_botsch08_format.cpp" )
else()
message(STATUS "NOTICE: This program requires the Eigen library, version 3.2 or later and will not be compiled.")
endif()
else()
message(STATUS "NOTICE: This program requires the CGAL library, and will not be compiled.")
endif()

View File

@ -0,0 +1,96 @@
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
// HalfedgeGraph adapters for Polyhedron_3
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/properties_Polyhedron_3.h>
#include <boost/foreach.hpp>
// #define CGAL_DEFORM_MESH_USE_EXPERIMENTAL_SR_ARAP
#include <CGAL/Deform_mesh.h>
#include <fstream>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_with_id_3> Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::vertex_iterator vertex_iterator;
typedef CGAL::Deform_mesh<Polyhedron,CGAL::Default, CGAL::Default, CGAL::ORIGINAL_ARAP> Deform_mesh;
int main(int argc,char** argv)
{
if ( argc!=4){
std::cerr <<"Usage " << argv[0] << " input.off input.sel input.def\n";
return 1;
}
Polyhedron mesh;
std::ifstream input(argv[1]);
if ( !input || !(input >> mesh) || mesh.empty() ) {
std::cerr<< argv[1] << " is not a valid off file" << std::endl;
return 1;
}
input.close();
// Init the indices of the halfedges and the vertices.
set_halfedgeds_items_id(mesh);
// Create a deformation object
Deform_mesh deform_mesh(mesh);
// Definition of the region of interest (use the whole mesh)
vertex_iterator vb,ve;
boost::tie(vb, ve) = boost::vertices(mesh);
//the selection is set by a file
input.open(argv[2]);
std::string line;
std::vector<vertex_descriptor> control_vertices;
while(getline(input, line))
{
if (line[0]=='#') continue;
if (line[0]=='1') deform_mesh.insert_roi_vertex(*vb);
if (line[0]=='2') {
deform_mesh.insert_control_vertex(*vb);
control_vertices.push_back(*vb);
}
++vb;
if (vb==ve) break;
}
input.close();
std::cout << "Using " << control_vertices.size() << " control vertices\n";
// The definition of the ROI and the control vertices is done, call preprocess
bool is_matrix_factorization_OK = deform_mesh.preprocess();
if(!is_matrix_factorization_OK){
std::cerr << "Error in preprocessing, check documentation of preprocess()" << std::endl;
return 1;
}
//define the transformation
input.open(argv[3]);
double m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, hw, sink;
getline(input, line); // skip first comment line
input >> m00 >> m01 >> m02 >> m03;
input >> m10 >> m11 >> m12 >> m13;
input >> m20 >> m21 >> m22 >> m23;
input >> sink >> sink >> sink >> hw;
Kernel::Aff_transformation_3 aff(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23);
BOOST_FOREACH(vertex_descriptor vd, control_vertices)
{
Deform_mesh::Point pos = vd->point().transform(aff);
deform_mesh.set_target_position(vd, pos);
}
// Call the function deform() with one-time parameters:
deform_mesh.deform(1000, 1e-4);
// Save the deformed mesh into a file
std::ofstream output("deform_res.off");
output << mesh;
output.close();
}

View File

@ -0,0 +1,62 @@
/*!
\ingroup PkgSurfaceModelingConcepts
\cgalConcept
@brief Concept describing the set of requirements for computing a 3x3 rotation matrix that is close to a given 3x3 matrix, together with basic computations used in the class `CGAL::Deform_mesh`.
The definition of close depends on the model.
The fact that some basic operations are hidden behind a function is to allow to benefit from optimizations like expression template from libraries used
to implement models of this concept.
\cgalHasModel `CGAL::Deformation_Eigen_closest_rotation_traits_3`
\cgalHasModel `CGAL::Deformation_Eigen_polar_closest_rotation_traits_3`
*/
class DeformationClosestRotationTraits_3{
public:
/// \name Types
/// @{
/// 3x3 matrix type having a copy constructor and an assignment operator
typedef unspecified_type Matrix;
/// 3D vector type having a copy constructor
typedef unspecified_type Vector;
/// @}
/// \name Creation
/// @{
/// Default constructor.
DeformationClosestRotationTraits_3();
/// @}
/// \name Operations
/// @{
/// Equivalent to `result = result + w * (v1*v2^t)`
void add_scalar_t_vector_t_vector_transpose(Matrix& result, double w, const Vector& v1, const Vector& v2);
/// Equivalent to `result = result + (w1*m1 + w2*m2) * v`
void add__scalar_t_matrix_p_scalar_t_matrix__t_vector(Vector& result, double w1, const Matrix& m1, double w2, const Matrix& m2, const Vector& v);
/// Equivalent to `result = result + w1 * (m1 + m2 + m3) * v`
void add_scalar_t_matrix_sum_t_vector(Vector& result, double w1, const Matrix& m1, const Matrix& m2, const Matrix& m3, const Vector& v);
/// Returns the squared norm of `v1 - m*v2`
double squared_norm_vector_scalar_vector_subs(const Vector& v1, const Matrix& m, const Vector& v2);
/// Returns an identity matrix
Matrix identity_matrix();
/// Returns a matrix initialized with zeros
Matrix zero_matrix();
/// Returns the vector (x,y,z)
Vector vector(double x, double y, double z);
/// Returns `i`th coefficient of a vector
double vector_coordinate(const Vector& v, int i);
/// Computes a rotation matrix close to `m` and places it into `R`
void compute_close_rotation(const Matrix& m, Matrix& R);
/// @}
};

View File

@ -0,0 +1,22 @@
/// \ingroup PkgSurfaceModelingConcepts
/// \cgalConcept
///
/// Concept describing the set of requirements of a simple point type.
///
class RawPoint_3
{
public:
/// \name Creation
/// @{
RawPoint_3();
RawPoint_3(double x, double y, double z);
/// @}
/// \name Coordinates Accessors
/// @{
/// `i<=0 && i<3`
double& operator[](int i);
/// `i<=0 && i<3`
double operator[](int i) const;
/// @}
};

View File

@ -0,0 +1,50 @@
/*!
\ingroup PkgSurfaceModelingConcepts
\cgalConcept
@brief Concept describing the set of requirements for a direct sparse linear system solver with factorization.
A model of this concept stores the left-hand matrix (denoted \f$ A \f$) and provides an additional factorization
method to solve the system for different right-hand vectors.
\cgalRefines `SparseLinearAlgebraTraits_d`
\cgalHasModel `CGAL::Eigen_solver_traits<T>`
*/
class SparseLinearAlgebraTraitsWithFactor_d {
public:
/// \name Creation
/// @{
/*!
Default constructor.
*/
SparseLinearAlgebraTraitsWithFactor_d();
/// @}
/// \name Operations
/// @{
/*!
Factorize the sparse matrix A.
This factorization is used in SparseLinearAlgebraTraitsWithFactor_d::linear_solver to solve the system for different right-hand side vectors.
See `::SparseLinearAlgebraTraits_d::linear_solver()` for the description of `D`.
@return true if the factorization is successful
*/
bool factor(const Matrix& A, NT& D);
/*!
Solve the sparse linear system \f$ A \times X = B\f$, with \f$ A \f$ being the matrix provided in SparseLinearAlgebraTraitsWithFactor_d::factor.
@return true if the solver is successful
*/
bool linear_solver(const Vector& B, Vector& X);
/// @}
}; /* end SparseLinearAlgebraTraitsWithFactor_d */

View File

@ -0,0 +1,45 @@
/// \ingroup PkgSurfaceModelingConcepts
/// \cgalConcept
///
/// @brief Concept describing the set of requirements for calculating weights for halfedges.
///
/// \cgalHeading{Example:}
///
/// \code
/// // a simple model to SurfaceModelingWeights concept, which provides uniform weights
/// template <class HalfedgeGraph>
/// struct Identity_weight
/// {
/// typedef HalfedgeGraph Halfedge_graph;
/// template<class VertexPointMap>
/// double operator()(typename boost::graph_traits<HalfedgeGraph>::halfedge_descriptor /*e*/, const HalfedgeGraph& /*p*/, VertexPointMap /*v*/)
/// { return 1.0; }
/// };
/// \endcode
///
///
class SurfaceModelingWeights
{
public:
/// \name Types
/// @{
/// a model of HalfedgeGraph
typedef unspecified_type Halfedge_graph;
/// @}
/// \name Creation
/// @{
/// Default constructor. Required only if the default parameter is used in the constructor of `CGAL::Deform_mesh`.
SurfaceModelingWeights();
/// @}
/// \name Operations
/// @{
/// Function computing the halfedge weight of halfedge `he`
/// \tparam VertexPointMap a model of `ReadWritePropertyMap`</a> with boost::graph_traits<Halfedge_graph>::vertex_descriptor as key and a 3D point from a \cgal Kernel as value type
template <class VertexPointMap>
double operator()(boost::graph_traits<Halfedge_graph>::halfedge_descriptor he, const Halfedge_graph& halfedge_graph, VertexPointMap vpm);
/// @}
};

View File

@ -0,0 +1,10 @@
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}
PROJECT_NAME = "CGAL ${CGAL_CREATED_VERSION_NUM} - Triangulated Surface Mesh Deformation"
INPUT = ${CMAKE_SOURCE_DIR}/Surface_modeling/doc/Surface_modeling/ \
${CMAKE_SOURCE_DIR}/Surface_modeling/include
EXTRACT_ALL = false
HIDE_UNDOC_MEMBERS = true
HIDE_UNDOC_CLASSES = true

View File

@ -0,0 +1,42 @@
/// \defgroup PkgSurfaceModeling Triangulated Surface Mesh Deformation Reference
/// \defgroup PkgSurfaceModelingConcepts Concepts
/// \ingroup PkgSurfaceModeling
/*!
\addtogroup PkgSurfaceModeling
\cgalPkgDescriptionBegin{Triangulated Surface Mesh Deformation,PkgSurfaceModelingSummary}
\cgalPkgPicture{deform-ico.png}
\cgalPkgSummaryBegin
\cgalPkgAuthor{Sébastien Loriot, Olga Sorkine-Hornung, Yin Xu and Ilker %O. Yaz}
\cgalPkgDesc{This package offers surface mesh deformation algorithms which provide new positions to the vertices of a surface mesh
under positional constraints of some of its vertices, without requiring any additional structure other than the surface mesh itself.}
\cgalPkgManuals{Chapter_SurfaceModeling,PkgSurfaceModeling}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{4.5}
\cgalPkgDependsOn{Sparse symmetric solver such as those from \ref thirdpartyEigen}
\cgalPkgBib{cgal:todo}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgDemo{Edit plugin of the Polyhedron demo,polyhedron_3.zip}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
\cgalClassifedRefPages
## Main Class ##
- `#CGAL::Deform_mesh`
## Concepts ##
- `DeformationClosestRotationTraits_3`
- `RawPoint_3`
- `SparseLinearAlgebraTraitsWithFactor_d`
- `SurfaceModelingWeights`
## Algebraic Traits ##
- `#CGAL::Deformation_Eigen_closest_rotation_traits_3`
- `#CGAL::Deformation_Eigen_polar_closest_rotation_traits_3`
*/

View File

@ -0,0 +1,490 @@
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_SurfaceModeling
\cgalAutoToc
\authors Sébastien Loriot, Olga Sorkine-Hornung, Yin Xu and Ilker %O. Yaz
\image latex main_image_suggestion_4_resized.png
\image html main_image_suggestion_4_resized.png
\section Surface_Modeling_Intro Introduction
This package offers surface mesh deformation algorithms which compute new vertex positions of a surface mesh
under positional constraints of some of its vertices, without requiring any additional structure other than the surface mesh itself
This package implements the algorithm described in \cgalCite{Sorkine2007AsRigidAs} together with an
alternative energy function \cgalCite{Chao2010SimpleGeomModel}.
The algorithm minimizes a nonlinear deformation energy under positional constraints to preserve rigidity as much as possible.
The minimization of the energy relies on solving sparse linear systems and finding closest rotation matrices.
\section Surface_Modeling_Def Definitions
A <em>surface mesh deformation system</em> consists of:
- a triangulated surface mesh (<em>surface mesh</em> in the following),
- a set of vertices defining the region to deform (referred to as the <em>region-of-interest</em> and abbreviated <em>ROI</em>),
- a subset of vertices from the ROI that the user wants to move (referred to as the <em>control vertices</em>),
- a target position for each control vertex (defining the <em>deformation constraints</em>).
A vertex from the ROI that is not a control vertex is called an <em>unconstrained vertex</em>. These definitions are depicted in \cgalFigureRef{Simple_roi}.
\cgalFigureBegin{Simple_roi, roi_example.png}
The ROI features the green vertices (the unconstrained vertices) and the red vertices (the control vertices).
(Left) The initial surface mesh; (Right) A target position is defined for each control vertex,
the coordinates of the unconstrained vertices being updated by the deformation algorithm.
\cgalFigureEnd
In this package, two algorithms are implemented:
- The <em>As-Rigid-As-Possible</em> (<em>ARAP</em>) method described in \cgalCite{Sorkine2007AsRigidAs};
- The <em>Spokes and Rims</em> method \cgalCite{Chao2010SimpleGeomModel}.
Given an edge weighting scheme, both methods iteratively minimize an energy function and produce a different surface mesh at each step until convergence is reached.
<em>Spokes and Rims</em> is the default method proposed by the package. It provides an unconditional
convergence while the <em>ARAP</em> method requires the edge weights to be positive. However,
the results obtained using the <em>Spokes and Rims</em> method are more dependent on the discretization
of the deformed surface (See \cgalFigureRef{Arap_spokes_comparison}).
More details on these algorithms are provided in section \ref Surface_Modeling_Overview.
\cgalFigureBegin{Arap_spokes_comparison, arap_spokes_comparison.png}
Comparison between the As-Rigid-As-Possible and the Spokes and Rims deformation methods.
On the surface mesh of a square with spikes depicted on the left, the ROI consists of the green vertices. The control vertices are the red ones.
We translate the control vertices along the normal to the plane and observe the result produced by
the As-Rigid-As-Possible (center) and the Spokes and Rims (right) methods from the same view point. The
latter method provides unconditional convergence does not produce a symmetric result.
\cgalFigureEnd
\section Surface_Modeling_API User Interface Description
The deformation methods implemented rely on solving a sparse linear system.
The sparse matrix definition depends on the weighting scheme
and on the unconstrained and control vertices.
The right term depends only on the target positions of the control vertices.
The deformation process is handled by the class `Deform_mesh` and the
surface mesh is represented as a halfedge data structure that must
be a model of the `HalfedgeGraph` concept.
The class `Deform_mesh` provides two groups of functions for
the preprocessing (sparse matrix definition) and the deformation (right hand term definition).
\subsection Preprocess_section Preprocessing
The preprocessing consists in computing a factorization of the aforementioned sparse
matrix to speed up the linear system resolution.
It requires the ROI to be defined.
The following conventions are used for the definition of the ROI:
- A vertex inserted in the set of control vertices is inserted in the ROI;
- A control vertex erased from the ROI is no longer considered as a control vertex;
- A control vertex that is erased is not erased from the ROI.
Each time the ROI is modified, the preprocessing function `preprocess()`
must be called.
Note that if it is not done, the first deformation step calls this function automatically
and has a longer runtime compared to subsequent deformation steps.
<!--- This is a pure implementation details as it does not shows up in the ROI
This function also extends the ROI by adding as <em>fixed control vertex</em> any vertex not in the ROI that is adjacent
to a vertex of the ROI. The target position of a fixed control vertex is its current position and cannot be changed.
-->
The function `Deform_mesh::preprocess()` returns `true` if the factorization is successful, and `false` otherwise.
Rank deficiency is the main reason for failure. Typical failure cases are:
- All the vertices are in the ROI and no control vertices are set
- The weighting scheme used to fill the sparse matrix (model of `SurfaceModelingWeights`) features too many zeros and breaks the connectivity information
\cgalAdvancedBegin
The choice of the weighting scheme provides a mean to adjust the way the control vertices
influences the unconstrained vertices. The defaults provides satisfactory results in general
but other weighting schemes may be selected or designed to experiment or improve the results
in specific cases.
\cgalAdvancedEnd
\note The ROI does not have to be a connected component of the graph of the surface mesh. However, for better performances
it is better to use an individual instance of the deformation object for each connected component.
\subsection Deform_section Deformation
The deformation of the surface mesh is triggered by the displacement of the control vertices.
This is achieved through setting the target positions of the control vertices (directly or by using an affine transformation
to be applied to a control vertex or a range of control vertices).
Note that a rotation or a translation of a control vertex is always applied on its last target position set:
they are cumulative.
The deformation of the surface mesh happens when calling the function `Deform_mesh::deform()`. The number of optimization iterations
varies depending on whether the user chooses a fixed number of iterations or a stopping criterion based on the energy variation.
After the call to the deformation function, the input surface mesh is updated and the control vertices are at
their target positions and the unconstrained vertices are moved accordingly.
The function `Deform_mesh::deform()` can be called several times consecutively, in particular
if the convergence has not been reached yet (otherwise it has no effect).
\warning Vertices can be inserted into or erased from the ROI and the set of control vertices at any time.
In particular, any vertex that is no longer inside the ROI will be assigned to its original position when
`Deform_mesh::preprocess()` is first called.
The original positions can be updated by calling `Deform_mesh::overwrite_initial_geometry()` (
which will also require a new preprocessing step).
This behavior is illustrated in the video \ref Surface_Modeling_Demo_Tutorial.
\subsection Surface_modeling_arap_or_spokes_and_rims As-Rigid-As-Possible and Spokes-and-Rims Deformation Techniques
Two deformation techniques are provided by this package. This section summarizes from the user point of view what is
explained in details in the section \ref Surface_Modeling_Overview.
The As-Rigid-As-Possible deformation technique requires the use of a positive weighting scheme to garantee
the correct minimization of the energy. When using the default cotangent weighting scheme, this means that
the input surface mesh must be <em>clean</em>. That is, that for all edges in the surface mesh
the sum of the angles opposite to the edge in the incident triangles is less that \f$ \pi \f$.
If this is not the case and the targeted application allows the modification of the surface mesh connectivity,
a solution (amongst other) is to bissect (possibly recursively) the problematic edges.
See \cgalFigureRef{Cotangent_bisect_fig}.
\cgalFigureBegin{Cotangent_bisect_fig, cotangent_bisect.png}
Cotangent weight of edges. (Left) The sum of the opposite angles to the edge \f$ e \f$, \f$ \alpha + \beta \f$
is greater than \f$ \pi \f$. The cotangent weight of \f$ e \f$ is negative.
(Right) The edge \f$ e \f$ was split so that the sum of the angles opposite
to each sub-edge \f$ e_1 \f$ and \f$ e_2 \f$ is less than \f$ \pi \f$. The corresponding
cotangent weights are positive.
\cgalFigureEnd
If the mesh connectivity must be preserved, the Spokes and Rims deformation technique
is guaranteed to always correctly minimize the energy even if the weights are negative.
However, this technique is more dependent on the discretization of the deformed surface
(See \cgalFigureRef{Arap_spokes_comparison}).
\subsection Surface_modeling_examples Examples
\subsubsection SModelingExample_1 Using the Whole Surface Mesh as Region-of-Interest
In this example, the whole surface mesh is used as ROI and a few vertices are added as control vertices.
`Deform_mesh::set_target_position()` is used for setting the target positions of the control vertices.
\cgalExample{Surface_modeling/all_roi_assign_example.cpp}
\cgalFigureBegin{SModelingExample_1_results, example_1_results.png}
Deformation results when running example \ref SModelingExample_1 : `deform_1.off` and `deform_2.off`.
\cgalFigureEnd
\subsubsection SModelingExample_2 Using an Affine Transformation on a Range of Vertices
In this example, we use the functions `translate()` and `rotate()` on a range of control vertices.
Note that the translations and the rotations are defined using a 3D vector type and a quaternion type from the \ref thirdpartyEigen "Eigen library".
\cgalExample{Surface_modeling/k_ring_roi_translate_rotate_example.cpp}
\cgalFigureBegin{SModelingExample_2_results, example_2_results.png}
Deformation results when running example \ref SModelingExample_2 : `deform_1.off` and `deform_2.off`.
\cgalFigureEnd
\subsubsection SModelingExample_3 Using an Enriched Polyhedron with Ids
In the previous examples, we used an <i>enriched</i> polyhedron storing an ID
in its halfedges and vertices together with the default property maps
in the deformation object to access them.
In the following example, we show how we can use alternative property maps.
For practical performance however we recommend relying upon
the former examples instead, as using a `std::map`
to access indices increases the complexity from constant to logarithmic.
\cgalExample{Surface_modeling/deform_polyhedron_with_custom_pmap_example.cpp}
\subsubsection SModelingExample_4 Using a Custom Edge Weighting Scheme
Using a custom weighting scheme for edges is also possible if one provides a model of `SurfaceModelingWeights`.
In this example, the weight of each edge is pre-computed and an internal map is used for storing and accessing them.
Another example is given in the manual page of the concept `::SurfaceModelingWeights`.
\cgalExample{Surface_modeling/custom_weight_for_edges_example.cpp}
\section Surface_Modeling_Demo How to Use the Demo
\todo Update these videos when the demo is stable
A plugin for the polyhedron demo is available to test the algorithm. The following video tutorials explain how to use it.
\subsection Surface_Modeling_Demo_Showcase An Introduction and Real World Example
@htmlonly
<center>
<iframe width="420" height="315" src="http://www.youtube.com/embed/WvC47fVSPGU" frameborder="0" allowfullscreen></iframe>
<iframe width="420" height="315" src="http://www.youtube.com/embed/0oTi5aKdcmg" frameborder="0" allowfullscreen></iframe>
</center>
@endhtmlonly
\subsection Surface_Modeling_Demo_Tutorial Demonstration for Deleting Control Vertices / ROI and Overwrite
@htmlonly
<center>
<iframe width="420" height="315" src="http://www.youtube.com/embed/xPAG_AOLvU0" frameborder="0" allowfullscreen></iframe>
</center>
@endhtmlonly
\section Surface_Modeling_Overview Deformations Techniques, Energies and Weighting Scheme
This section gives the theorical background to make the user manual self-contained
and at the same time explains where the weights comes in. This allows advanced users
of this package to tune the weighting scheme by developing a model of the concept
`SurfaceModelingWeights` used in the class `Deform_mesh`.
\subsection Surface_Modeling_Overview_Preliminaries Preliminaries
\subsubsection Surface_Modeling_Overview_Laplacian Laplacian Representation
The <em>Laplacian representation</em> (referred to as <em>Laplace coordinates</em> in \cgalCite{botsch2010polygon})
of a vertex in a surface mesh is one way to <em>encode</em> the local neighborhood of a vertex in the surface mesh.
In this representation, a vertex \f$ \mathbf{v}_i \f$ is associated a 3D vector defined as:
\f[
\begin{equation}
L(\mathbf{v}_i) = \sum_{\mathbf{v}_j \in N(\mathbf{v}_i)} w_{ij}(\mathbf{v}_i - \mathbf{v}_j),
\label{eq:lap_open}
\end{equation}
\f]
where:
- \f$N(\mathbf{v}_i)\f$ denotes the set of vertices adjacent to \f$\mathbf{v}_i\f$;
-\f$w_{ij}\f$ denotes a weight for the directed edge \f$\mathbf{v}_i\ \mathbf{v}_j\f$.
The simplest choice for the weights is the uniform scheme where \f$ w_{ij}=1/|N(\mathbf{v}_i)| \f$ for each adjacent vertex
\f$\mathbf{v}_j\f$. In this case, the Laplacian representation of a vertex is the vector between this vertex
and the centroid of its adjacent vertices (\cgalFigureRef{Simple_laplacian}).
In the surface mesh deformation context, a popular choice is the cotangent weight scheme
that derives from the discretization of the Laplace operator \cgalCite{Pinkall1993Cotangent} :
Given an edge of the surface mesh, its corresponding cotangent weight is the mean of the
cotangents of the angles opposite to the edge. It was shown to produce results that are not biased by the surface mesh of the approximated surface
\cgalCite{Sorkine2007AsRigidAs}.
\cgalFigureBegin{Simple_laplacian,simple_mesh_with_laplacian.png}
Laplacian representation of \f$ v_i \f$ with uniform weights: the red square vertex is the centroid of the vertices adjacent to \f$ v_i \f$. The Laplacian representation \f$ L(v_i) \f$ is represented as a blue vector.
\cgalFigureEnd
Considering a surface mesh with \f$n\f$ vertices, it is possible to define its <i>Laplacian representation</i> \f$\Delta\f$ as a \f$n \times 3\f$ matrix:
\f[
\begin{equation}
\mathbf{L}\mathbf{V} = \Delta,
\label{eq:lap_system}
\end{equation}
\f]
where:
- \f$\mathbf{L}\f$ is a \f$n \times n\f$ sparse matrix, referred to as the <em>Laplacian matrix</em>. Its elements \f$ m_{ij} \f$, \f$i,j \in \{1 \dots n\} \f$ are defined as follows:
- \f$ m_{ii} \f$ = \f$ \sum_{\mathbf{v}_j \in N(\mathbf{v}_i)}w_{ij} \f$,
- \f$ m_{ij} = -w_{ij} \f$ if \f$ \mathbf{v}_j \in N(\mathbf{v_i}) \f$,
- \f$ 0 \f$ otherwise.
- \f$\mathbf{V}\f$ is a \f$n \times 3\f$ matrix made of the %Cartesian coordinates of the vertices.
\subsubsection Surface_Modeling_Overview_Laplacian_Deformation Laplacian Deformation
This section is an introduction to provide the background for the next two sub-sections describing the algorithms implemented
in this package. A system relying only on the approach described below results in non-smooth transitions in the neighborhood of
the control vertices. For a survey on different Laplacian-based editing techniques we refer to
\cgalCite{Botsch2008OnLinearVariational}.
The main idea behind Laplacian-based deformation techniques is to preserve the Laplacian representation
under deformation constraints. The Laplacian representation of a surface mesh is treated as a representative form of the discretized surface,
and the deformation process must follow the deformation constraints while preserving the Laplacian representation as much as possible.
There are different ways to incorporate deformation constraints into the deformation system \cgalCite{Botsch2008OnLinearVariational}.
This package supports hard constraints, that is, target positions of control vertices are preserved after the deformation.
Given a surface mesh deformation system with a ROI made of \f$ n \f$ vertices and \f$ k \f$ control vertices, we consider the following linear system:
\f[
\begin{equation}
\left[
\begin{array}{ccc}
\mathbf{L}_f\\
0 \; \mathbf{I}_c
\end{array}
\right]
\mathbf{V} =
\left[
\begin{array}{ccc}
{\Delta}_f \\
\mathbf{V}_c
\end{array}
\right],
\label{eq:lap_energy_system}
\end{equation}
\f]
where:
- \f$\mathbf{V}\f$ is a \f$n \times 3\f$ matrix denoting the unknowns of the system that represent the vertex coordinates after deformation. The system is built so that the \f$ k \f$ last rows correspond to the control vertices.
- \f$\mathbf{L}_f\f$ denotes the Laplacian matrix of the unconstrained vertices. It is a \f$ (n-k) \times n \f$ matrix as defined in Eq. \f$\eqref{eq:lap_system}\f$ but removing the rows corresponding to the control vertices.
- \f$\mathbf{I}_c\f$ is the \f$k \times k\f$ identity matrix.
- \f${\Delta}_f\f$ denotes the Laplacian representation of the unconstrained vertices as defined in Eq. \f$\eqref{eq:lap_system}\f$ but removing the rows corresponding to the control vertices.
- \f$\mathbf{V}_c\f$ is a \f$k \times 3\f$ matrix containing the %Cartesian coordinates of the target positions of the control vertices.
The left-hand side matrix of the system of Eq.\f$\eqref{eq:lap_energy_system}\f$ is a square non-symmetric sparse matrix. To
solve the aforementioned system, an appropriate solver (e.g. LU solver) needs to be used. Note that solving this system
preserves the Laplacian representation of the surface mesh restricted to the unconstrained vertices while satisfying the deformation constraints.
\subsection Surface_Modeling_Overview_ARAP As-Rigid-As Possible Deformation
Given a surface mesh \f$M\f$ with \f$ n \f$ vertices \f$ \{\mathbf{v}_i\} i \in \{1 \dots n \} \f$ and some deformation
constraints, we consider the following energy function:
\f[
\begin{equation}
\sum_{\mathbf{v}_i \in M}
\sum_{\mathbf{v}_j \in N(\mathbf{v}_i)} w_{ij}
\left\| (\mathbf{v}'_i - \mathbf{v}'_j) - \mathbf{R}_i(\mathbf{v}_i - \mathbf{v}_j) \right\|^2,
\label{eq:arap_energy}
\end{equation}
\f]
where:
- \f$\mathbf{R}_i\f$ is a \f$ 3 \times 3 \f$ rotation matrix
- \f$w_{ij}\f$ denotes a weight
- \f$N(\mathbf{v}_i)\f$ denotes the set of vertices adjacent to \f$\mathbf{v}_i\f$ in \f$M\f$
- \f$N(\mathbf{v}'_i)\f$ denotes a new position of the vertex \f$N(\mathbf{v}_i)\f$ after a given deformation
An as-rigid-as possible surface mesh deformation \cgalCite{Sorkine2007AsRigidAs} is defined by minimizing
this energy function under the deformation constraints, i.e. the assigned position
\f$ {v}'_i\f$ for each vertex \f$ \mathbf{v}_i\f$ in the set of control vertices.
Defining the <i>one-ring neighborhood</i> of a vertex as its set of adjacent vertices,
the intuitive idea behind this energy function is to allow each one-ring neighborhood of
vertices to have an individual rotation, and at the same time to prevent shearing by taking advantage of the
overlapping of one-ring neighborhoods of adjacent vertices (see \cgalFigureRef{Overlapping_cells}).
\cgalFigureBegin{Overlapping_cells,overlapping_cells.png}
Overlaps of one-ring neighborhoods of vertices.
The one-ring neighborhoods of four vertices
are drawn with different colors, the corresponding vertex is colored accordingly.
\cgalFigureEnd
There are two unknowns per vertex in Eq. \f$\eqref{eq:arap_energy}\f$: the new positions (\f$\mathbf{v}'_k\f$) of the unconstrained
vertices and the rotation matrices (\f$\mathbf{R}_i\f$).
If the energy contribution of each vertex is positive, this boils down to minimizing the energy contribution of each vertex \f$\mathbf{v}_i\f$.
Each such term of the energy is minimized by using a two-step optimization approach (also called local-global approach).
In the first step, the positions of the vertices are considered as fixed so that the rotation matrices are the only unknowns.
For the vertex \f$\mathbf{v}_i\f$, we consider the covariance matrix \f$\mathbf{S}_i\f$:
\f[
\begin{equation}
\mathbf{S}_i = \sum_{\mathbf{v}_j \in N(\mathbf{v}_i)} w_{ij} (\mathbf{v}_i - \mathbf{v}_j)(\mathbf{v}'_i - \mathbf{v}'_j)^T,
\label{eq:cov_matrix}
\end{equation}
\f]
It was shown \cgalCite{Sorkine2009LeastSquaresRigid} that minimizing the energy contribution of
\f$\mathbf{v}_i\f$ in Eq. \f$\eqref{eq:arap_energy}\f$ is equivalent to maximizing the trace of the matrix
\f$\mathbf{R}_i \mathbf{S}_i\f$. \f$\mathbf{R}_i \f$ is the transpose of the unitary matrix in the polar decomposition
of \f$\mathbf{S}_i\f$.
In the second step, the rotation matrices are substituted into the partial derivative of Eq.\f$\eqref{eq:arap_energy}\f$
with respect to \f$\mathbf{v}'_i\f$. Assuming the weights are symmetric, setting the derivative to zero results in the
following equation:
\f[
\begin{equation}
\sum_{\mathbf{v}_j \in N(\mathbf{v}_i)} w_{ij}(\mathbf{v}'_i - \mathbf{v}'_j) =
\sum_{\mathbf{v}_j \in N(\mathbf{v}_i)} w_{ij} \frac{(\mathbf{R}_i + \mathbf{R}_j)}{2} (\mathbf{v}_i - \mathbf{v}_j).
\label{eq:lap_ber}
\end{equation}
\f]
The left-hand side of this equation corresponds to the one of Eq.\f$\eqref{eq:lap_open}\f$,
and we can set \f$\Delta\f$ to be the right-hand side.
Solving the linear system in Eq. \f$\eqref{eq:lap_energy_system}\f$ gives the new positions of the unconstrained vertices.
This two-step optimization can be applied several times iteratively to obtain a better result.
\note The matrix built with the Laplacian matrix of the unconstrained vertices in the left-hand side of
Eq. \f$\eqref{eq:lap_energy_system}\f$ depends only on the initial surface mesh structure and on which vertices
are control vertices. Once the control vertices are set, we can use a direct solver to factorize
the sparse matrix in Eq. \f$\eqref{eq:lap_energy_system}\f$, and reuse this factorization during
each iteration of the optimization procedure.
The original algorithm \cgalCite{Sorkine2007AsRigidAs} we described assumes that:
- the weight between two vertices is symmetric. In order to support asymmetric weights in our implementation,
we slightly change Eq. \f$\eqref{eq:lap_ber}\f$ to:
\f[
\begin{equation}
\sum_{\mathbf{v}_j \in N(\mathbf{v}_i)} (w_{ij} + w_{ji})(\mathbf{v}'_i - \mathbf{v}'_j) =
\sum_{\mathbf{v}_j \in N(\mathbf{v}_i)} (w_{ij}\mathbf{R}_i + w_{ji}\mathbf{R}_j)(\mathbf{v}_i - \mathbf{v}_j).
\label{eq:lap_ber_asym}
\end{equation}
\f]
- The energy contribution of each vertex is positive. If the weight between two vertices is always positive, this is always the case.
However, when using the cotangent weighting scheme (the default in our implementation), if the sum of the angles opposite to an edge is greater than \f$ \pi \f$,
its cotangent weight is negative. As a workaround for bad quality meshes, we eliminate those negative weights
by setting them to zero.
A method minimizing another energy function is described next to avoid the latter issue.
\subsection Surface_Modeling_Overview_ARAP_Rims Spokes and Rims Version
The elastic energy function proposed by \cgalCite{Chao2010SimpleGeomModel} additionally takes into account
all the opposite edges in the facets incident to a vertex. The energy function to minimize becomes:
\f[
\begin{equation}
\sum_{\mathbf{v}_i \in M}
\sum_{(\mathbf{v}_j, \mathbf{v}_k) \in E(\mathbf{v}_i)} w_{jk}
\left\| (\mathbf{v}'_j - \mathbf{v}'_k) - \mathbf{R}_i(\mathbf{v}_j - \mathbf{v}_k) \right\|^2,
\label{eq:arap_energy_rims}
\end{equation}
\f]
where \f$E(\mathbf{v}_i)\f$ consists of the set of edges incident to \f$\mathbf{v}_i\f$ (the <em>spokes</em>) and
the set of edges in the link (the <em>rims</em>) of \f$\mathbf{v}_i\f$ in the surface mesh \f$M\f$
(see \cgalFigureRef{Spoke_and_rim_edges}).
\cgalFigureBegin{Spoke_and_rim_edges, spoke_and_rim_edges_2.png}
The vertices \f$ \mathbf{v}_n\f$ and \f$ \mathbf{v}_m\f$ are the opposite vertices to the edge
\f$ \mathbf{v}_i \mathbf{v}_j\f$.
\cgalFigureEnd
The method to get the new positions of the unconstrained vertices is similar to the two-step optimization
method explained in \ref Surface_Modeling_Overview_ARAP.
For the first step, the Eq. \f$\eqref{eq:cov_matrix}\f$ is modified to take into account the edges in \f$E(\mathbf{v}_i)\f$:
\f[
\begin{equation}
\mathbf{S}_i = \sum_{(\mathbf{v}_j, \mathbf{v}_k) \in E(\mathbf{v}_i)} w_{jk} (\mathbf{v}_j - \mathbf{v}_k)(\mathbf{v}'_j - \mathbf{v}'_k)^T,
\label{eq:cov_matrix_sr}
\end{equation}
\f]
For the second step, setting partial derivative of Eq. \f$\eqref{eq:arap_energy_rims}\f$ to zero with
respect to \f$\mathbf{v}_i\f$ gives the following equation:
\f[
\begin{equation}
\sum_{\mathbf{v}_j \in N(\mathbf{v}_i)} (w_{ij} + w_{ji})(\mathbf{v}'_i - \mathbf{v}'_j) =
\sum_{\mathbf{v}_j \in N(\mathbf{v}_i)} \frac{w_{ij}(\mathbf{R}_i + \mathbf{R}_j + \mathbf{R}_m) + w_{ji}(\mathbf{R}_i + \mathbf{R}_j + \mathbf{R}_n)}{3} (\mathbf{v}_i - \mathbf{v}_j).
\label{eq:lap_ber_rims}
\end{equation}
\f]
where \f$\mathbf{R}_m\f$ and \f$\mathbf{R}_n\f$ are the rotation matrices of the vertices \f$\mathbf{v}_m\f$,
\f$\mathbf{v}_n\f$ which are the opposite vertices of the edge \f$\mathbf{v}_i \mathbf{v}_j\f$
(see \cgalFigureRef{Spoke_and_rim_edges}). Note that if the edge \f$ \mathbf{v}_i \mathbf{v}_j \f$ is on
the boundary of the surface mesh, then \f$ w_{ij} \f$ must be 0 and \f$ \mathbf{v}_m
\f$ does not exist.
An important property of this approach compared to \ref Surface_Modeling_Overview_ARAP is that the contribution to the global energy
of each vertex is guaranteed to be non-negative when using the cotangent weights \cgalCite{Chao2010SimpleGeomModel}.
Thus even with negative weights, the minimization of the energy with the iterative method presented is always guaranteed.
However, this method is more dependent on the discretization of the deformed surface (See \cgalFigureRef{Arap_spokes_comparison}).
The implementation in this package uses the cotangent weights by default (negative values included) as proposed in \cgalCite{Chao2010SimpleGeomModel}.
\section Surface_Modeling_History Design and Implementation History
An initial version of this package has been implemented during the 2011 Google Summer of Code by Yin Xu under the guidance of Olga Sorkine and Andreas Fabri.
Ilker O. Yaz took over the finalization of the package with the help of Sébastien Loriot for the documentation and the API.
The authors are grateful to Gaël Guennebaud for his great help on using the Eigen library and for providing the code to compute
the closest rotation.
*/
} /* namespace CGAL */

View File

@ -0,0 +1,9 @@
Manual
Kernel_23
STL_Extension
Algebraic_foundations
Circulator
Stream_support
Polyhedron
BGL
Surface_mesh_parameterization

View File

@ -0,0 +1,6 @@
/*!
\example Surface_modeling/all_roi_assign_example.cpp
\example Surface_modeling/k_ring_roi_translate_rotate_example.cpp
\example Surface_modeling/deform_polyhedron_with_custom_pmap_example.cpp
\example Surface_modeling/custom_weight_for_edges_example.cpp
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,299 @@
<?xml version="1.0"?>
<!DOCTYPE ipe SYSTEM "ipe.dtd">
<ipe version="70005" creator="Ipe 7.1.4">
<info created="D:20140603085835" modified="D:20140603085835"/>
<ipestyle name="basic">
<symbol name="arrow/arc(spx)">
<path stroke="sym-stroke" fill="sym-stroke" pen="sym-pen">
0 0 m
-1 0.333 l
-1 -0.333 l
h
</path>
</symbol>
<symbol name="arrow/farc(spx)">
<path stroke="sym-stroke" fill="white" pen="sym-pen">
0 0 m
-1 0.333 l
-1 -0.333 l
h
</path>
</symbol>
<symbol name="mark/circle(sx)" transformations="translations">
<path fill="sym-stroke">
0.6 0 0 0.6 0 0 e
0.4 0 0 0.4 0 0 e
</path>
</symbol>
<symbol name="mark/disk(sx)" transformations="translations">
<path fill="sym-stroke">
0.6 0 0 0.6 0 0 e
</path>
</symbol>
<symbol name="mark/fdisk(sfx)" transformations="translations">
<group>
<path fill="sym-fill">
0.5 0 0 0.5 0 0 e
</path>
<path fill="sym-stroke" fillrule="eofill">
0.6 0 0 0.6 0 0 e
0.4 0 0 0.4 0 0 e
</path>
</group>
</symbol>
<symbol name="mark/box(sx)" transformations="translations">
<path fill="sym-stroke" fillrule="eofill">
-0.6 -0.6 m
0.6 -0.6 l
0.6 0.6 l
-0.6 0.6 l
h
-0.4 -0.4 m
0.4 -0.4 l
0.4 0.4 l
-0.4 0.4 l
h
</path>
</symbol>
<symbol name="mark/square(sx)" transformations="translations">
<path fill="sym-stroke">
-0.6 -0.6 m
0.6 -0.6 l
0.6 0.6 l
-0.6 0.6 l
h
</path>
</symbol>
<symbol name="mark/fsquare(sfx)" transformations="translations">
<group>
<path fill="sym-fill">
-0.5 -0.5 m
0.5 -0.5 l
0.5 0.5 l
-0.5 0.5 l
h
</path>
<path fill="sym-stroke" fillrule="eofill">
-0.6 -0.6 m
0.6 -0.6 l
0.6 0.6 l
-0.6 0.6 l
h
-0.4 -0.4 m
0.4 -0.4 l
0.4 0.4 l
-0.4 0.4 l
h
</path>
</group>
</symbol>
<symbol name="mark/cross(sx)" transformations="translations">
<group>
<path fill="sym-stroke">
-0.43 -0.57 m
0.57 0.43 l
0.43 0.57 l
-0.57 -0.43 l
h
</path>
<path fill="sym-stroke">
-0.43 0.57 m
0.57 -0.43 l
0.43 -0.57 l
-0.57 0.43 l
h
</path>
</group>
</symbol>
<symbol name="arrow/fnormal(spx)">
<path stroke="sym-stroke" fill="white" pen="sym-pen">
0 0 m
-1 0.333 l
-1 -0.333 l
h
</path>
</symbol>
<symbol name="arrow/pointed(spx)">
<path stroke="sym-stroke" fill="sym-stroke" pen="sym-pen">
0 0 m
-1 0.333 l
-0.8 0 l
-1 -0.333 l
h
</path>
</symbol>
<symbol name="arrow/fpointed(spx)">
<path stroke="sym-stroke" fill="white" pen="sym-pen">
0 0 m
-1 0.333 l
-0.8 0 l
-1 -0.333 l
h
</path>
</symbol>
<symbol name="arrow/linear(spx)">
<path stroke="sym-stroke" pen="sym-pen">
-1 0.333 m
0 0 l
-1 -0.333 l
</path>
</symbol>
<symbol name="arrow/fdouble(spx)">
<path stroke="sym-stroke" fill="white" pen="sym-pen">
0 0 m
-1 0.333 l
-1 -0.333 l
h
-1 0 m
-2 0.333 l
-2 -0.333 l
h
</path>
</symbol>
<symbol name="arrow/double(spx)">
<path stroke="sym-stroke" fill="sym-stroke" pen="sym-pen">
0 0 m
-1 0.333 l
-1 -0.333 l
h
-1 0 m
-2 0.333 l
-2 -0.333 l
h
</path>
</symbol>
<pen name="heavier" value="0.8"/>
<pen name="fat" value="1.2"/>
<pen name="ultrafat" value="2"/>
<symbolsize name="large" value="5"/>
<symbolsize name="small" value="2"/>
<symbolsize name="tiny" value="1.1"/>
<arrowsize name="large" value="10"/>
<arrowsize name="small" value="5"/>
<arrowsize name="tiny" value="3"/>
<color name="red" value="1 0 0"/>
<color name="green" value="0 1 0"/>
<color name="blue" value="0 0 1"/>
<color name="yellow" value="1 1 0"/>
<color name="orange" value="1 0.647 0"/>
<color name="gold" value="1 0.843 0"/>
<color name="purple" value="0.627 0.125 0.941"/>
<color name="gray" value="0.745"/>
<color name="brown" value="0.647 0.165 0.165"/>
<color name="navy" value="0 0 0.502"/>
<color name="pink" value="1 0.753 0.796"/>
<color name="seagreen" value="0.18 0.545 0.341"/>
<color name="turquoise" value="0.251 0.878 0.816"/>
<color name="violet" value="0.933 0.51 0.933"/>
<color name="darkblue" value="0 0 0.545"/>
<color name="darkcyan" value="0 0.545 0.545"/>
<color name="darkgray" value="0.663"/>
<color name="darkgreen" value="0 0.392 0"/>
<color name="darkmagenta" value="0.545 0 0.545"/>
<color name="darkorange" value="1 0.549 0"/>
<color name="darkred" value="0.545 0 0"/>
<color name="lightblue" value="0.678 0.847 0.902"/>
<color name="lightcyan" value="0.878 1 1"/>
<color name="lightgray" value="0.827"/>
<color name="lightgreen" value="0.565 0.933 0.565"/>
<color name="lightyellow" value="1 1 0.878"/>
<dashstyle name="dashed" value="[4] 0"/>
<dashstyle name="dotted" value="[1 3] 0"/>
<dashstyle name="dash dotted" value="[4 2 1 2] 0"/>
<dashstyle name="dash dot dotted" value="[4 2 1 2 1 2] 0"/>
<textsize name="large" value="\large"/>
<textsize name="Large" value="\Large"/>
<textsize name="LARGE" value="\LARGE"/>
<textsize name="huge" value="\huge"/>
<textsize name="Huge" value="\Huge"/>
<textsize name="small" value="\small"/>
<textsize name="footnote" value="\footnotesize"/>
<textsize name="tiny" value="\tiny"/>
<textstyle name="center" begin="\begin{center}" end="\end{center}"/>
<textstyle name="itemize" begin="\begin{itemize}" end="\end{itemize}"/>
<textstyle name="item" begin="\begin{itemize}\item{}" end="\end{itemize}"/>
<gridsize name="4 pts" value="4"/>
<gridsize name="8 pts (~3 mm)" value="8"/>
<gridsize name="16 pts (~6 mm)" value="16"/>
<gridsize name="32 pts (~12 mm)" value="32"/>
<gridsize name="10 pts (~3.5 mm)" value="10"/>
<gridsize name="20 pts (~7 mm)" value="20"/>
<gridsize name="14 pts (~5 mm)" value="14"/>
<gridsize name="28 pts (~10 mm)" value="28"/>
<gridsize name="56 pts (~20 mm)" value="56"/>
<anglesize name="90 deg" value="90"/>
<anglesize name="60 deg" value="60"/>
<anglesize name="45 deg" value="45"/>
<anglesize name="30 deg" value="30"/>
<anglesize name="22.5 deg" value="22.5"/>
<tiling name="falling" angle="-60" step="4" width="1"/>
<tiling name="rising" angle="30" step="4" width="1"/>
</ipestyle>
<page>
<layer name="alpha"/>
<view layers="alpha" active="alpha"/>
<path layer="alpha" matrix="1 0 0 1 -304 128" stroke="black">
377.775 632.694 m
320 704 l
425.291 703.249 l
448 640 l
377.775 632.694 l
</path>
<path matrix="1 0 0 1 -304 128" stroke="black">
320 704 m
448 640 l
</path>
<path matrix="1 0 0 1 -144 128" stroke="black">
377.775 632.694 m
320 704 l
425.291 703.249 l
448 640 l
377.775 632.694 l
</path>
<path matrix="1 0 0 1 -144.426 128" stroke="black">
320 704 m
448 640 l
</path>
<path matrix="1 0 0 1 -112.426 64" stroke="black">
350.484 736.758 m
345.775 696.694 l
</path>
<path matrix="1 0 0 1 -112.426 64" stroke="black">
350.484 736.758 m
393.291 767.249 l
</path>
<path matrix="1 0 0 1 -48 64" stroke="black">
112.538 708.095 m
14.6728 0 0 -14.6728 121.775 696.694 136.369 698.212 a
</path>
<path matrix="1 0 0 1 -48 64" stroke="black">
173.942 754.295 m
13.7637 0 0 -13.7637 169.291 767.249 155.528 767.348 a
</path>
<path matrix="1 0 0 1 -112.426 64" stroke="black">
381.147 758.599 m
14.9108 0 0 -14.9108 393.291 767.249 378.381 767.356 a
</path>
<path matrix="1 0 0 1 -112.426 64" stroke="black">
399.056 751.193 m
17.0601 0 0 -17.0601 393.291 767.249 379.396 757.352 a
</path>
<text matrix="1 0 0 1 -38.8999 63.3792" transformations="translations" pos="144 752" stroke="black" type="label" width="6.41" height="4.289" depth="0" valign="baseline">$\alpha$</text>
<text matrix="1 0 0 1 -32.2377 58.8879" transformations="translations" pos="112 720" stroke="black" type="label" width="6.161" height="6.926" depth="1.93" valign="baseline">$\beta$</text>
<text matrix="1 0 0 1 109.252 71.4733" transformations="translations" pos="144 752" stroke="black" type="label" width="10.842" height="4.294" depth="1.49" valign="baseline">$\alpha_1$</text>
<text matrix="1 0 0 1 111.382 60.3971" transformations="translations" pos="112 720" stroke="black" type="label" width="10.104" height="6.926" depth="1.93" valign="baseline">$\beta_1$</text>
<text matrix="1 0 0 1 123.736 55.711" transformations="translations" pos="144 752" stroke="black" type="label" width="10.842" height="4.294" depth="1.49" valign="baseline">$\alpha_2$</text>
<text matrix="1 0 0 1 138.417 49.7142" transformations="translations" pos="112 720" stroke="black" type="label" width="10.104" height="6.926" depth="1.93" valign="baseline">$\beta_2$</text>
<path stroke="black">
235.329 777.543 m
16.9209 0 0 -16.9209 233.775 760.694 250.605 762.445 a
</path>
<path stroke="black">
224.168 772.551 m
15.2598 0 0 -15.2598 233.775 760.694 235.135 775.893 a
</path>
<text matrix="1 0 0 1 5.24316 -4.58777" transformations="translations" pos="75.4504 806.966" stroke="black" type="label" width="4.639" height="4.289" depth="0" valign="baseline">$e$</text>
<text matrix="1 0 0 1 -4.91547 0.327698" transformations="translations" pos="222.914 813.52" stroke="black" type="label" width="9.108" height="4.294" depth="1.49" valign="baseline">$e_1$</text>
<text matrix="1 0 0 1 -8.19244 1.31079" transformations="translations" pos="272.397 789.598" stroke="black" type="label" width="9.108" height="4.294" depth="1.49" valign="baseline">$e_2$</text>
</page>
</ipe>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,650 @@
<?xml version="1.0"?>
<!DOCTYPE ipe SYSTEM "ipe.dtd">
<ipe version="70005" creator="Ipe 7.1.4">
<info created="D:20130806142632" modified="D:20140304121427"/>
<ipestyle name="basic">
<symbol name="arrow/arc(spx)">
<path stroke="sym-stroke" fill="sym-stroke" pen="sym-pen">
0 0 m
-1 0.333 l
-1 -0.333 l
h
</path>
</symbol>
<symbol name="arrow/farc(spx)">
<path stroke="sym-stroke" fill="white" pen="sym-pen">
0 0 m
-1 0.333 l
-1 -0.333 l
h
</path>
</symbol>
<symbol name="mark/circle(sx)" transformations="translations">
<path fill="sym-stroke">
0.6 0 0 0.6 0 0 e
0.4 0 0 0.4 0 0 e
</path>
</symbol>
<symbol name="mark/disk(sx)" transformations="translations">
<path fill="sym-stroke">
0.6 0 0 0.6 0 0 e
</path>
</symbol>
<symbol name="mark/fdisk(sfx)" transformations="translations">
<group>
<path fill="sym-fill">
0.5 0 0 0.5 0 0 e
</path>
<path fill="sym-stroke" fillrule="eofill">
0.6 0 0 0.6 0 0 e
0.4 0 0 0.4 0 0 e
</path>
</group>
</symbol>
<symbol name="mark/box(sx)" transformations="translations">
<path fill="sym-stroke" fillrule="eofill">
-0.6 -0.6 m
0.6 -0.6 l
0.6 0.6 l
-0.6 0.6 l
h
-0.4 -0.4 m
0.4 -0.4 l
0.4 0.4 l
-0.4 0.4 l
h
</path>
</symbol>
<symbol name="mark/square(sx)" transformations="translations">
<path fill="sym-stroke">
-0.6 -0.6 m
0.6 -0.6 l
0.6 0.6 l
-0.6 0.6 l
h
</path>
</symbol>
<symbol name="mark/fsquare(sfx)" transformations="translations">
<group>
<path fill="sym-fill">
-0.5 -0.5 m
0.5 -0.5 l
0.5 0.5 l
-0.5 0.5 l
h
</path>
<path fill="sym-stroke" fillrule="eofill">
-0.6 -0.6 m
0.6 -0.6 l
0.6 0.6 l
-0.6 0.6 l
h
-0.4 -0.4 m
0.4 -0.4 l
0.4 0.4 l
-0.4 0.4 l
h
</path>
</group>
</symbol>
<symbol name="mark/cross(sx)" transformations="translations">
<group>
<path fill="sym-stroke">
-0.43 -0.57 m
0.57 0.43 l
0.43 0.57 l
-0.57 -0.43 l
h
</path>
<path fill="sym-stroke">
-0.43 0.57 m
0.57 -0.43 l
0.43 -0.57 l
-0.57 0.43 l
h
</path>
</group>
</symbol>
<symbol name="arrow/fnormal(spx)">
<path stroke="sym-stroke" fill="white" pen="sym-pen">
0 0 m
-1 0.333 l
-1 -0.333 l
h
</path>
</symbol>
<symbol name="arrow/pointed(spx)">
<path stroke="sym-stroke" fill="sym-stroke" pen="sym-pen">
0 0 m
-1 0.333 l
-0.8 0 l
-1 -0.333 l
h
</path>
</symbol>
<symbol name="arrow/fpointed(spx)">
<path stroke="sym-stroke" fill="white" pen="sym-pen">
0 0 m
-1 0.333 l
-0.8 0 l
-1 -0.333 l
h
</path>
</symbol>
<symbol name="arrow/linear(spx)">
<path stroke="sym-stroke" pen="sym-pen">
-1 0.333 m
0 0 l
-1 -0.333 l
</path>
</symbol>
<symbol name="arrow/fdouble(spx)">
<path stroke="sym-stroke" fill="white" pen="sym-pen">
0 0 m
-1 0.333 l
-1 -0.333 l
h
-1 0 m
-2 0.333 l
-2 -0.333 l
h
</path>
</symbol>
<symbol name="arrow/double(spx)">
<path stroke="sym-stroke" fill="sym-stroke" pen="sym-pen">
0 0 m
-1 0.333 l
-1 -0.333 l
h
-1 0 m
-2 0.333 l
-2 -0.333 l
h
</path>
</symbol>
<pen name="heavier" value="0.8"/>
<pen name="fat" value="1.2"/>
<pen name="ultrafat" value="2"/>
<symbolsize name="large" value="5"/>
<symbolsize name="small" value="2"/>
<symbolsize name="tiny" value="1.1"/>
<arrowsize name="large" value="10"/>
<arrowsize name="small" value="5"/>
<arrowsize name="tiny" value="3"/>
<color name="red" value="1 0 0"/>
<color name="green" value="0 1 0"/>
<color name="blue" value="0 0 1"/>
<color name="yellow" value="1 1 0"/>
<color name="orange" value="1 0.647 0"/>
<color name="gold" value="1 0.843 0"/>
<color name="purple" value="0.627 0.125 0.941"/>
<color name="gray" value="0.745"/>
<color name="brown" value="0.647 0.165 0.165"/>
<color name="navy" value="0 0 0.502"/>
<color name="pink" value="1 0.753 0.796"/>
<color name="seagreen" value="0.18 0.545 0.341"/>
<color name="turquoise" value="0.251 0.878 0.816"/>
<color name="violet" value="0.933 0.51 0.933"/>
<color name="darkblue" value="0 0 0.545"/>
<color name="darkcyan" value="0 0.545 0.545"/>
<color name="darkgray" value="0.663"/>
<color name="darkgreen" value="0 0.392 0"/>
<color name="darkmagenta" value="0.545 0 0.545"/>
<color name="darkorange" value="1 0.549 0"/>
<color name="darkred" value="0.545 0 0"/>
<color name="lightblue" value="0.678 0.847 0.902"/>
<color name="lightcyan" value="0.878 1 1"/>
<color name="lightgray" value="0.827"/>
<color name="lightgreen" value="0.565 0.933 0.565"/>
<color name="lightyellow" value="1 1 0.878"/>
<dashstyle name="dashed" value="[4] 0"/>
<dashstyle name="dotted" value="[1 3] 0"/>
<dashstyle name="dash dotted" value="[4 2 1 2] 0"/>
<dashstyle name="dash dot dotted" value="[4 2 1 2 1 2] 0"/>
<textsize name="large" value="\large"/>
<textsize name="small" value="\small"/>
<textsize name="tiny" value="\tiny"/>
<textsize name="Large" value="\Large"/>
<textsize name="LARGE" value="\LARGE"/>
<textsize name="huge" value="\huge"/>
<textsize name="Huge" value="\Huge"/>
<textsize name="footnote" value="\footnotesize"/>
<textstyle name="center" begin="\begin{center}" end="\end{center}"/>
<textstyle name="itemize" begin="\begin{itemize}" end="\end{itemize}"/>
<textstyle name="item" begin="\begin{itemize}\item{}" end="\end{itemize}"/>
<gridsize name="4 pts" value="4"/>
<gridsize name="8 pts (~3 mm)" value="8"/>
<gridsize name="16 pts (~6 mm)" value="16"/>
<gridsize name="32 pts (~12 mm)" value="32"/>
<gridsize name="10 pts (~3.5 mm)" value="10"/>
<gridsize name="20 pts (~7 mm)" value="20"/>
<gridsize name="14 pts (~5 mm)" value="14"/>
<gridsize name="28 pts (~10 mm)" value="28"/>
<gridsize name="56 pts (~20 mm)" value="56"/>
<anglesize name="90 deg" value="90"/>
<anglesize name="60 deg" value="60"/>
<anglesize name="45 deg" value="45"/>
<anglesize name="30 deg" value="30"/>
<anglesize name="22.5 deg" value="22.5"/>
<tiling name="falling" angle="-60" step="4" width="1"/>
<tiling name="rising" angle="30" step="4" width="1"/>
</ipestyle>
<page>
<layer name="alpha"/>
<view layers="alpha" active="alpha"/>
<path layer="alpha" stroke="black" pen="fat">
245.053 689.286 m
243.685 701.923 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
335.817 645.11 m
319.661 655.875 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
335.817 645.11 m
329.449 632.515 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
348.524 659.787 m
335.817 645.11 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
321.599 641.278 m
335.817 645.11 l
</path>
<path matrix="2.46798 0 0 2.46798 -493.088 -915.205" stroke="black" pen="fat">
299.087 650.12 m
310.758 645.398 l
</path>
<use matrix="2.46798 0 0 2.46798 -510.341 -929.477" name="mark/disk(sx)" pos="306.078 655.906" size="large" stroke="gray"/>
<path matrix="2.46798 0 0 2.46798 -493.088 -915.205" stroke="blue" pen="fat" rarrow="normal/tiny">
299.087 650.12 m
296.782 653.501 l
</path>
<path stroke="black" pen="fat">
240.106 725.757 m
242.173 712.51 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
292.847 683.929 m
282.404 682.281 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
288.502 673.731 m
297.386 667.817 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
317.743 609.104 m
312.091 618.452 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
330.476 610.813 m
329.98 621.665 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
333.813 659.892 m
341.229 668.306 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
333.813 659.892 m
331.838 674.47 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
333.813 659.892 m
324.421 666.042 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
341.101 607.706 m
350.514 613.533 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
321.599 641.278 m
329.449 632.515 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
367.234 633.539 m
352.267 624.765 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
304.23 649.771 m
292.101 647.112 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
340.479 618.874 m
350.514 613.533 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
340.479 618.874 m
330.476 610.813 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
341.101 607.706 m
340.479 618.874 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
330.476 610.813 m
341.101 607.706 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
345.189 678.8 m
356.539 670.585 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
312.091 618.452 m
319.193 626.751 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
312.091 618.452 m
303.126 624.693 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
340.479 618.874 m
352.267 624.765 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
340.479 618.874 m
341.214 632.537 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
329.98 621.665 m
340.479 618.874 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
361.707 652.233 m
367.234 633.539 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
358.257 683.923 m
356.539 670.585 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
341.229 668.306 m
348.524 659.787 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
356.539 670.585 m
341.229 668.306 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
317.221 677.617 m
302.277 678.101 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
317.221 677.617 m
307.435 664.225 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
358.257 683.923 m
370.983 679.043 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
290.754 659.456 m
288.502 673.731 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
348.524 659.787 m
333.813 659.892 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
333.813 659.892 m
319.661 655.875 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
319.193 626.751 m
321.599 641.278 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
309.395 635.837 m
321.599 641.278 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
331.838 674.47 m
317.221 677.617 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
324.421 666.042 m
331.838 674.47 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
317.221 677.617 m
324.421 666.042 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
369.011 665.556 m
361.707 652.233 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
361.707 652.233 m
375.165 645.181 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
297.386 667.817 m
290.754 659.456 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
307.435 664.225 m
302.277 678.101 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
297.386 667.817 m
307.435 664.225 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
302.277 678.101 m
297.386 667.817 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
288.502 673.731 m
302.277 678.101 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
292.847 683.929 m
288.502 673.731 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
302.277 678.101 m
292.847 683.929 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
319.193 626.751 m
317.743 609.104 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
317.743 609.104 m
329.98 621.665 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
329.98 621.665 m
341.214 632.537 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
367.234 633.539 m
363.12 617.289 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
363.12 617.289 m
352.267 624.765 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
352.267 624.765 m
350.514 613.533 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
329.449 632.515 m
319.193 626.751 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
329.98 621.665 m
329.449 632.515 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
319.193 626.751 m
329.98 621.665 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
324.421 666.042 m
307.435 664.225 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
319.661 655.875 m
324.421 666.042 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
376.031 610.703 m
363.12 617.289 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
356.539 670.585 m
369.011 665.556 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
370.983 679.043 m
356.539 670.585 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
370.983 679.043 m
369.011 665.556 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
319.661 655.875 m
307.435 664.225 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
319.193 626.751 m
309.395 635.837 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
303.126 624.693 m
319.193 626.751 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
309.395 635.837 m
303.126 624.693 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
319.661 655.875 m
304.23 649.771 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
304.23 649.771 m
321.599 641.278 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
329.449 632.515 m
341.214 632.537 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
345.189 678.8 m
358.257 683.923 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
348.524 659.787 m
361.707 652.233 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
356.539 670.585 m
348.524 659.787 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
361.707 652.233 m
356.539 670.585 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
304.23 649.771 m
296.043 635.337 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
309.395 635.837 m
304.23 649.771 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
296.043 635.337 m
309.395 635.837 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
345.189 678.8 m
331.838 674.47 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
341.229 668.306 m
345.189 678.8 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
331.838 674.47 m
341.229 668.306 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
375.165 645.181 m
367.234 633.539 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
312.091 618.452 m
300.313 612.221 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
288.502 673.731 m
282.404 682.281 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
330.476 610.813 m
317.743 609.104 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
363.12 617.289 m
350.514 613.533 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
290.754 659.456 m
304.23 649.771 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
307.435 664.225 m
290.754 659.456 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
304.23 649.771 m
307.435 664.225 l
</path>
<path matrix="2.46798 0 0 2.46798 -583.738 -902.84" stroke="black" pen="fat">
319.661 655.875 m
321.599 641.278 l
</path>
<use matrix="2.46798 0 0 2.46798 -493.088 -915.205" name="mark/disk(sx)" pos="282.931 660.885" size="large" stroke="gray"/>
<use matrix="2.46798 0 0 2.46798 -493.088 -915.205" name="mark/disk(sx)" pos="284.869 646.288" size="large" stroke="gray"/>
<use matrix="2.46798 0 0 2.46798 -493.088 -915.205" name="mark/disk(sx)" pos="297.083 664.902" size="large" stroke="gray"/>
<path matrix="2.46798 0 0 2.46798 -493.088 -915.205" stroke="black" pen="fat">
310.758 645.398 m
304.484 637.547 l
</path>
<path matrix="2.46798 0 0 2.46798 -493.088 -915.205" stroke="black" pen="fat">
310.758 645.398 m
311.794 664.797 l
</path>
<path matrix="2.46798 0 0 2.46798 -493.088 -915.205" stroke="black" pen="fat">
292.719 637.525 m
310.758 645.398 l
</path>
<path matrix="2.46798 0 0 2.46798 -493.088 -915.205" stroke="black" pen="fat">
304.484 637.547 m
315.537 629.775 l
</path>
<path matrix="2.46798 0 0 2.46798 -493.088 -915.205" stroke="black" pen="fat">
310.758 645.398 m
315.537 629.775 l
</path>
<path matrix="2.46798 0 0 2.46798 -493.088 -915.205" stroke="black" pen="fat">
310.758 645.398 m
330.504 638.549 l
</path>
<path matrix="2.46798 0 0 2.46798 -493.088 -915.205" stroke="black" pen="fat">
310.758 645.398 m
324.977 657.243 l
</path>
<use matrix="2.46798 0 0 2.46798 -493.088 -915.205" name="mark/square(sx)" pos="296.782 653.501" size="normal" stroke="red"/>
<use matrix="2.46798 0 0 2.46798 -493.088 -915.205" name="mark/disk(sx)" pos="292.719 637.525" size="large" stroke="gray"/>
<use matrix="2.46798 0 0 2.46798 -501.673 -935.886" name="mark/disk(sx)" pos="314.237 653.779" size="large" stroke="gray"/>
<use matrix="2.46798 0 0 2.46798 -493.088 -915.205" name="mark/disk(sx)" pos="311.794 664.797" size="large" stroke="gray"/>
<text matrix="0.666923 0 0 0.666923 58.6122 231.151" transformations="translations" pos="278.911 673.967" stroke="black" type="label" width="8.146" height="4.294" depth="1.49" valign="baseline">$v_i$</text>
<text matrix="0.666923 0 0 0.666923 44.4961 255.35" transformations="translations" pos="278.911 673.967" stroke="black" type="label" width="22.675" height="7.473" depth="2.49" valign="baseline">$L(v_i)$</text>
</page>
</ipe>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,52 @@
# Created by the script cgal_create_cmake_script
# This is the CMake script for compiling a CGAL application.
project( Surface_modeling_ )
cmake_minimum_required(VERSION 2.6.2)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 2.6)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.3)
cmake_policy(VERSION 2.8.4)
else()
cmake_policy(VERSION 2.6)
endif()
endif()
find_package(CGAL QUIET COMPONENTS Core )
if ( CGAL_FOUND )
include( ${CGAL_USE_FILE} )
find_package(Eigen3 3.1.91) #(requires 3.2.0 or greater)
if (EIGEN3_FOUND)
include( ${EIGEN3_USE_FILE} )
include( CGAL_CreateSingleSourceCGALProgram )
include_directories (BEFORE "../../include")
create_single_source_cgal_program( "all_roi_assign_example.cpp" )
create_single_source_cgal_program( "all_roi_assign_example_custom_polyhedron.cpp" )
create_single_source_cgal_program( "custom_weight_for_edges_example.cpp" )
create_single_source_cgal_program( "deform_polyhedron_with_custom_pmap_example.cpp" )
create_single_source_cgal_program( "k_ring_roi_translate_rotate_example.cpp" )
find_package( OpenMesh QUIET )
if ( OpenMesh_FOUND )
include( UseOpenMesh )
create_single_source_cgal_program( "all_roi_assign_example_with_OpenMesh.cpp" )
target_link_libraries( all_roi_assign_example_with_OpenMesh ${OPENMESH_LIBRARIES} )
else()
message(STATUS "Example that use OpenMesh will not be compiled.")
endif()
else()
message(STATUS "NOTICE: These examples require the Eigen library, version 3.2 or later and will not be compiled.")
endif()
else()
message(STATUS "NOTICE: These exmaples require the CGAL library, and will not be compiled.")
endif()

View File

@ -0,0 +1,99 @@
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
// HalfedgeGraph adapters for Polyhedron_3
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/properties_Polyhedron_3.h>
#include <CGAL/Deform_mesh.h>
#include <fstream>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_with_id_3> Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::vertex_iterator vertex_iterator;
typedef CGAL::Deform_mesh<Polyhedron> Deform_mesh;
int main()
{
Polyhedron mesh;
std::ifstream input("data/plane.off");
if ( !input || !(input >> mesh) || mesh.empty() ) {
std::cerr<< "Cannot open data/plane.off" << std::endl;
return 1;
}
// Init the indices of the halfedges and the vertices.
set_halfedgeds_items_id(mesh);
// Create a deformation object
Deform_mesh deform_mesh(mesh);
// Definition of the region of interest (use the whole mesh)
vertex_iterator vb,ve;
boost::tie(vb, ve) = vertices(mesh);
deform_mesh.insert_roi_vertices(vb, ve);
// Select two control vertices ...
vertex_descriptor control_1 = *CGAL::cpp11::next(vb, 213);
vertex_descriptor control_2 = *CGAL::cpp11::next(vb, 157);
// ... and insert them
deform_mesh.insert_control_vertex(control_1);
deform_mesh.insert_control_vertex(control_2);
// The definition of the ROI and the control vertices is done, call preprocess
bool is_matrix_factorization_OK = deform_mesh.preprocess();
if(!is_matrix_factorization_OK){
std::cerr << "Error in preprocessing, check documentation of preprocess()" << std::endl;
return 1;
}
// Use set_target_position() to set the constained position
// of control_1. control_2 remains at the last assigned positions
Deform_mesh::Point constrained_pos_1(-0.35, 0.40, 0.60);
deform_mesh.set_target_position(control_1, constrained_pos_1);
// Deform the mesh, the positions of vertices of 'mesh' are updated
deform_mesh.deform();
// The function deform() can be called several times if the convergence has not been reached yet
deform_mesh.deform();
// Set the constained position of control_2
Deform_mesh::Point constrained_pos_2(0.55, -0.30, 0.70);
deform_mesh.set_target_position(control_2, constrained_pos_2);
// Call the function deform() with one-time parameters:
// iterate 10 times and do not use energy based termination criterion
deform_mesh.deform(10, 0.0);
// Save the deformed mesh into a file
std::ofstream output("deform_1.off");
output << mesh;
output.close();
// Add another control vertex which requires another call to preprocess
vertex_descriptor control_3 = *CGAL::cpp11::next(vb, 92);
deform_mesh.insert_control_vertex(control_3);
// The prepocessing step is again needed
if(!deform_mesh.preprocess()){
std::cerr << "Error in preprocessing, check documentation of preprocess()" << std::endl;
return 1;
}
// Deform the mesh
Deform_mesh::Point constrained_pos_3(0.55, 0.30, -0.70);
deform_mesh.set_target_position(control_3, constrained_pos_3);
deform_mesh.deform(15, 0.0);
output.open("deform_2.off");
output << mesh;
}

View File

@ -0,0 +1,155 @@
#include <fstream>
#include <map>
#include <cmath>
#include <CGAL/property_map.h>
struct Custom_point_3{
// Required by File_scanner_OFF
struct R{
typedef double RT;
};
double coords[3];
Custom_point_3(){}
Custom_point_3(double x, double y, double z)
{ coords[0]=x; coords[1]=y; coords[2]=z; }
Custom_point_3(double x, double y, double z, double w)
{ coords[0]=x/w; coords[1]=y/w; coords[2]=z/w; }
double x() const {return coords[0];}
double y() const {return coords[1];}
double z() const {return coords[2];}
double& operator[](int i) { return coords[i]; }
double operator[](int i) const { return coords[i]; }
friend std::ostream& operator<<(std::ostream& out, const Custom_point_3& p)
{
out << p.x() << " " << p.y() << " " << p.z();
return out;
}
friend std::istream& operator<<(std::istream& in, Custom_point_3& p)
{
in >> p.coords[0] >> p.coords[1] >> p.coords[2];
return in;
}
};
#include <CGAL/basic.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
// Halfedge adapters for Polyhedron_3
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/properties_Polyhedron_3.h>
#include <CGAL/Deform_mesh.h>
struct Custom_traits{
typedef Custom_point_3 Point_3;
struct Plane_3{};
};
typedef CGAL::Polyhedron_3<Custom_traits> Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::vertex_iterator vertex_iterator;
typedef boost::graph_traits<Polyhedron>::halfedge_descriptor halfedge_descriptor;
typedef boost::graph_traits<Polyhedron>::halfedge_iterator halfedge_iterator;
typedef std::map<vertex_descriptor, std::size_t> Internal_vertex_map;
typedef std::map<halfedge_descriptor, std::size_t> Internal_hedge_map;
typedef boost::associative_property_map<Internal_vertex_map> Vertex_index_map;
typedef boost::associative_property_map<Internal_hedge_map> Hedge_index_map;
typedef CGAL::Deform_mesh<Polyhedron, Vertex_index_map, Hedge_index_map> Deform_mesh;
int main()
{
Polyhedron mesh;
std::ifstream input("data/plane.off");
if ( !input || !(input >> mesh) || mesh.empty() ) {
std::cerr<< "Cannot open data/plane.off" << std::endl;
return 1;
}
// Index maps must contain an index unique per vertex starting from 0
// to the total number of vertices
Internal_vertex_map internal_vertex_index_map;
Vertex_index_map vertex_index_map(internal_vertex_index_map);
vertex_iterator vb, ve;
std::size_t counter = 0;
for(boost::tie(vb, ve) = vertices(mesh); vb != ve; ++vb, ++counter) {
put(vertex_index_map, *vb, counter);
}
Internal_hedge_map internal_hedge_index_map;
Hedge_index_map hedge_index_map(internal_hedge_index_map);
counter = 0;
halfedge_iterator eb, ee;
for(boost::tie(eb, ee) = halfedges(mesh); eb != ee; ++eb, ++counter) {
put(hedge_index_map, *eb, counter);
}
Deform_mesh deform_mesh(mesh, vertex_index_map, hedge_index_map);
// Insert the whole mesh as region of interest
boost::tie(vb, ve) = vertices(mesh);
deform_mesh.insert_roi_vertices(vb, ve);
// Insert two control vertices
vertex_descriptor control_1 = *CGAL::cpp11::next(vb, 213);
vertex_descriptor control_2 = *CGAL::cpp11::next(vb, 157);
deform_mesh.insert_control_vertex(control_1);
deform_mesh.insert_control_vertex(control_2);
// The definition of the ROI and the control vertices is done, call preprocess
bool is_matrix_factorization_OK = deform_mesh.preprocess();
if(!is_matrix_factorization_OK){
std::cerr << "Check documentation of preprocess()" << std::endl;
return 1;
}
// Use set_target_position() to set the constained position
// of control_1. control_2 remains at the last assigned positions
Deform_mesh::Point constrained_pos_1(-0.35, 0.40, 0.60);
deform_mesh.set_target_position(control_1, constrained_pos_1);
// Deform the mesh, the positions of vertices of 'mesh' are updated
deform_mesh.deform();
// The function deform() can be called several times if the convergence has not been reached yet
deform_mesh.deform();
// Set the constained position of control_2
Deform_mesh::Point constrained_pos_2(0.55, -0.30, 0.70);
deform_mesh.set_target_position(control_2, constrained_pos_2);
// Call the function deform() with one-time parameters:
// iterate 10 times and do not use energy based termination criterion
deform_mesh.deform(10, 0.0);
std::ofstream output("deform_1.off");
output << mesh; // save deformed mesh
output.close();
// Add another control vertex
vertex_descriptor control_3 = *CGAL::cpp11::next(vb, 92);
deform_mesh.insert_control_vertex(control_3);
// The prepocessing step is again needed
if(!deform_mesh.preprocess()) {
std::cerr << "Check documentation of preprocess()" << std::endl;
return 1;
}
Deform_mesh::Point constrained_pos_3(0.55, 0.30, -0.70);
deform_mesh.set_target_position(control_3, constrained_pos_3);
deform_mesh.deform(15, 0.0);
output.open("deform_2.off");
output << mesh;
}

View File

@ -0,0 +1,83 @@
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
// HalfedgeGraph adapters
#include <CGAL/boost/graph/graph_traits_PolyMesh_ArrayKernelT.h>
#include <CGAL/Deform_mesh.h>
typedef OpenMesh::PolyMesh_ArrayKernelT</* MyTraits*/> Mesh;
typedef boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Mesh>::vertex_iterator vertex_iterator;
typedef boost::graph_traits<Mesh>::halfedge_iterator halfedge_iterator;
typedef CGAL::Deform_mesh<Mesh> Deform_mesh;
int main()
{
Mesh mesh;
OpenMesh::IO::read_mesh(mesh, "data/plane.off");
// Create a deformation object
Deform_mesh deform_mesh(mesh);
// Definition of the region of interest (use the whole mesh)
vertex_iterator vb,ve;
boost::tie(vb, ve) = vertices(mesh);
deform_mesh.insert_roi_vertices(vb, ve);
// Select two control vertices ...
vertex_descriptor control_1 = *CGAL::cpp11::next(vb, 213);
vertex_descriptor control_2 = *CGAL::cpp11::next(vb, 157);
// ... and insert them
deform_mesh.insert_control_vertex(control_1);
deform_mesh.insert_control_vertex(control_2);
// The definition of the ROI and the control vertices is done, call preprocess
bool is_matrix_factorization_OK = deform_mesh.preprocess();
if(!is_matrix_factorization_OK){
std::cerr << "Error in preprocessing, check documentation of preprocess()" << std::endl;
return 1;
}
// Use set_target_position() to set the constained position
// of control_1. control_2 remains at the last assigned positions
Deform_mesh::Point constrained_pos_1(-0.35, 0.40, 0.60);
deform_mesh.set_target_position(control_1, constrained_pos_1);
// Deform the mesh, the positions of vertices of 'mesh' are updated
deform_mesh.deform();
// The function deform() can be called several times if the convergence has not been reached yet
deform_mesh.deform();
// Set the constained position of control_2
Deform_mesh::Point constrained_pos_2(0.55, -0.30, 0.70);
deform_mesh.set_target_position(control_2, constrained_pos_2);
// Call the function deform() with one-time parameters:
// iterate 10 times and do not use energy based termination criterion
deform_mesh.deform(10, 0.0);
// Save the deformed mesh into a file
OpenMesh::IO::write_mesh(mesh,"deform_1.off");
// Add another control vertex which requires another call to preprocess
vertex_descriptor control_3 = *CGAL::cpp11::next(vb, 92);
deform_mesh.insert_control_vertex(control_3);
// The prepocessing step is again needed
if(!deform_mesh.preprocess()){
std::cerr << "Error in preprocessing, check documentation of preprocess()" << std::endl;
return 1;
}
// Deform the mesh
Deform_mesh::Point constrained_pos_3(0.55, 0.30, -0.70);
deform_mesh.set_target_position(control_3, constrained_pos_3);
deform_mesh.deform(15, 0.0);
OpenMesh::IO::write_mesh(mesh,"deform_2.off");
}

View File

@ -0,0 +1,85 @@
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
// HalfedgeGraph adapters for Polyhedron_3
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/properties_Polyhedron_3.h>
#include <CGAL/Deform_mesh.h>
#include <fstream>
#include <map>
#include <CGAL/property_map.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::vertex_iterator vertex_iterator;
typedef boost::graph_traits<Polyhedron>::halfedge_descriptor halfedge_descriptor;
typedef boost::graph_traits<Polyhedron>::halfedge_iterator halfedge_iterator;
typedef std::map<vertex_descriptor, std::size_t> Internal_vertex_map;
typedef std::map<halfedge_descriptor, std::size_t> Internal_hedge_map;
typedef boost::associative_property_map<Internal_vertex_map> Vertex_index_map;
typedef boost::associative_property_map<Internal_hedge_map> Hedge_index_map;
// A model of SurfaceModelingWeights using a map of pre-computed weights
struct Weights_from_map
{
typedef Polyhedron Halfedge_graph;
Weights_from_map(std::map<halfedge_descriptor, double>* weight_map) : weight_map(weight_map)
{ }
template<class VertexPointMap>
double operator()(halfedge_descriptor e, Polyhedron& /*P*/, VertexPointMap /*vpm*/) {
return (*weight_map)[e];
}
std::map<halfedge_descriptor, double>* weight_map;
};
typedef CGAL::Deform_mesh<Polyhedron, Vertex_index_map, Hedge_index_map, CGAL::ORIGINAL_ARAP, Weights_from_map> Deform_mesh;
int main()
{
Polyhedron mesh;
std::ifstream input("data/plane.off");
if ( !input || !(input >> mesh) || mesh.empty() ) {
std::cerr << "Cannot open data/plane.off" << std::endl;
return 1;
}
std::map<halfedge_descriptor, double> weight_map;
// Store all the weights
halfedge_iterator eb, ee;
for(boost::tie(eb, ee) = halfedges(mesh); eb != ee; ++eb)
{
weight_map[*eb] = 1.0; // store some precomputed weights
}
// Create and initialize the vertex index map
Internal_vertex_map internal_vertex_index_map;
Vertex_index_map vertex_index_map(internal_vertex_index_map);
vertex_iterator vb, ve;
std::size_t counter = 0;
for(boost::tie(vb, ve) = vertices(mesh); vb != ve; ++vb, ++counter) {
put(vertex_index_map, *vb, counter);
}
// Create and initialize the halfedge index map
Internal_hedge_map internal_hedge_index_map;
Hedge_index_map hedge_index_map(internal_hedge_index_map);
counter = 0;
for(boost::tie(eb, ee) = halfedges(mesh); eb != ee; ++eb, ++counter) {
put(hedge_index_map, *eb, counter);
}
Deform_mesh deform_mesh(mesh,
vertex_index_map,
hedge_index_map,
get(CGAL::vertex_point, mesh),
Weights_from_map(&weight_map));
// Deform mesh as desired
// .....
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,60 @@
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
// Halfedge adaptors for Polyhedron_3
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/properties_Polyhedron_3.h>
#include <CGAL/property_map.h>
#include <CGAL/Deform_mesh.h>
#include <fstream>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::vertex_iterator vertex_iterator;
typedef boost::graph_traits<Polyhedron>::halfedge_descriptor halfedge_descriptor;
typedef boost::graph_traits<Polyhedron>::halfedge_iterator halfedge_iterator;
// Define the maps
typedef std::map<vertex_descriptor, std::size_t> Vertex_id_map;
typedef std::map<halfedge_descriptor, std::size_t> Hedge_id_map;
typedef boost::associative_property_map<Vertex_id_map> Vertex_id_pmap;
typedef boost::associative_property_map<Hedge_id_map> Hedge_id_pmap;
typedef CGAL::Deform_mesh<Polyhedron, Vertex_id_pmap, Hedge_id_pmap> Deform_mesh;
int main()
{
Polyhedron mesh;
std::ifstream input("data/plane.off");
if ( !input || !(input >> mesh) || mesh.empty() ) {
std::cerr<< "Cannot open data/plane.off";
return 1;
}
// Init the indices of the vertices from 0 to num_vertices(mesh)-1
Vertex_id_map vertex_index_map;
vertex_iterator vb, ve;
std::size_t counter = 0;
for(boost::tie(vb, ve) = vertices(mesh); vb != ve; ++vb, ++counter)
vertex_index_map[*vb]=counter;
// Init the indices of the halfedges from 0 to 2*num_edges(mesh)-1
Hedge_id_map hedge_index_map;
counter = 0;
halfedge_iterator eb, ee;
for(boost::tie(eb, ee) = halfedges(mesh); eb != ee; ++eb, ++counter)
hedge_index_map[*eb]=counter;
Deform_mesh deform_mesh( mesh,
Vertex_id_pmap(vertex_index_map),
Hedge_id_pmap(hedge_index_map) );
// Now deform mesh as desired
// .....
}

View File

@ -0,0 +1,111 @@
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
// HalfedgeGraph adaptors for Polyhedron_3
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/properties_Polyhedron_3.h>
#include <CGAL/Deform_mesh.h>
#include <fstream>
#include <map>
#include <queue>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel,CGAL::Polyhedron_items_with_id_3> Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::vertex_iterator vertex_iterator;
typedef boost::graph_traits<Polyhedron>::halfedge_descriptor halfedge_descriptor;
typedef boost::graph_traits<Polyhedron>::out_edge_iterator out_edge_iterator;
typedef Eigen::Vector3d Vector3d;
typedef CGAL::Deform_mesh<Polyhedron> Deform_mesh;
// Collect the vertices which are at distance less or equal to k
// from the vertex v in the graph of vertices connected by the edges of P
std::vector<vertex_descriptor> extract_k_ring(const Polyhedron &P, vertex_descriptor v, int k)
{
std::map<vertex_descriptor, int> D;
std::vector<vertex_descriptor> Q;
Q.push_back(v); D[v] = 0;
std::size_t current_index = 0;
int dist_v;
while( current_index < Q.size() && (dist_v = D[ Q[current_index] ]) < k ) {
v = Q[current_index++];
out_edge_iterator e, e_end;
for(boost::tie(e, e_end) = out_edges(v, P); e != e_end; e++)
{
halfedge_descriptor he = halfedge(*e, P);
vertex_descriptor new_v = target(he, P);
if(D.insert(std::make_pair(new_v, dist_v + 1)).second) {
Q.push_back(new_v);
}
}
}
return Q;
}
int main()
{
Polyhedron mesh;
std::ifstream input("data/plane.off");
if ( !input || !(input >> mesh) || mesh.empty() ) {
std::cerr<< "Cannot open data/plane.off";
return 1;
}
// Init the indices of the halfedges and the vertices.
set_halfedgeds_items_id(mesh);
// Create the deformation object
Deform_mesh deform_mesh(mesh);
// Select and insert the vertices of the region of interest
vertex_iterator vb, ve;
boost::tie(vb,ve) = vertices(mesh);
std::vector<vertex_descriptor> roi = extract_k_ring(mesh, *CGAL::cpp11::next(vb, 47), 9);
deform_mesh.insert_roi_vertices(roi.begin(), roi.end());
// Select and insert the control vertices
std::vector<vertex_descriptor> cvertices_1 = extract_k_ring(mesh, *CGAL::cpp11::next(vb, 39), 1);
std::vector<vertex_descriptor> cvertices_2 = extract_k_ring(mesh, *CGAL::cpp11::next(vb, 97), 1);
deform_mesh.insert_control_vertices(cvertices_1.begin(), cvertices_1.end());
deform_mesh.insert_control_vertices(cvertices_2.begin(), cvertices_2.end());
// Apply a rotation to the control vertices
Eigen::Quaternion<double> quad(0.92, 0, 0, -0.38);
deform_mesh.rotate(cvertices_1.begin(), cvertices_1.end(), Vector3d(0,0,0), quad);
deform_mesh.rotate(cvertices_2.begin(), cvertices_2.end(), Vector3d(0,0,0), quad);
deform_mesh.deform();
// Save the deformed mesh
std::ofstream output("deform_1.off");
output << mesh;
output.close();
// Restore the positions of the vertices
deform_mesh.reset();
// Apply a translation on the original positions of the vertices (reset() was called before)
deform_mesh.translate(cvertices_1.begin(), cvertices_1.end(), Vector3d(0,0.3,0));
deform_mesh.translate(cvertices_2.begin(), cvertices_2.end(), Vector3d(0,0.3,0));
// Call the function deform() with one-time parameters:
// iterate 10 times and do not use energy based termination criterion
deform_mesh.set_iterations(10);
deform_mesh.set_tolerance(0.0);
deform_mesh.deform();
// Save the deformed mesh
output.open("deform_2.off");
output << mesh;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,109 @@
// Copyright (c) 2013 INRIA Bordeaux Sud-Ouest (France), All rights reserved.
// Copyright (c) 2013 GeometryFactory
//
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 3 of the License,
// or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
//
// Author(s) : Gael Guennebaud and Ilker O. Yaz
#ifndef CGAL_DEFORMATION_EIGEN_CLOSEST_ROTATION_TRAITS_3_H
#define CGAL_DEFORMATION_EIGEN_CLOSEST_ROTATION_TRAITS_3_H
#include <Eigen/Eigen>
#include <Eigen/SVD>
namespace CGAL {
/// \ingroup PkgSurfaceModeling
/// A class to compute the closest rotation in Frobenius norm to a 3x3 Matrix using the \link thirdpartyEigen `Eigen` library \endlink.
/// The internal computation relies on `Eigen::JacobiSVD<>` solver.
///
/// \cgalModels `DeformationClosestRotationTraits_3`
class Deformation_Eigen_closest_rotation_traits_3{
public:
/// \cond SKIP_FROM_MANUAL
typedef Eigen::Matrix3d Matrix;
typedef Eigen::Vector3d Vector;
/// Equivalent to `result += w * (v1*v2^t)`
void add_scalar_t_vector_t_vector_transpose(Matrix& result, double w, const Vector& v1, const Vector& v2)
{
result += w * (v1*v2.transpose());
}
/// Equivalent to `result += (w1*m1 + w2*m2) * v`
void add__scalar_t_matrix_p_scalar_t_matrix__t_vector(Vector& result, double w1, const Matrix& m1, double w2, const Matrix& m2, const Vector& v)
{
result += (w1*m1 + w2*m2) * v;
}
/// Equivalent to `result += w * (m1 + m2 + m3) * v`
void add_scalar_t_matrix_sum_t_vector(Vector& result, double w, const Matrix& m1, const Matrix& m2, const Matrix& m3, const Vector& v)
{
result += w * (m1 + m2 + m3) * v;
}
/// Returns the squared norm of `v1 - m*v2`
double squared_norm_vector_scalar_vector_subs(const Vector& v1, const Matrix& m, const Vector& v2)
{
return (v1 - m*v2).squaredNorm();
}
/// Returns an identity matrix
Matrix identity_matrix()
{
return Matrix().setIdentity();
}
/// Returns a zero initialized matrix
Matrix zero_matrix()
{
return Matrix().setZero();
}
/// Returns vector initialized with parameters
Vector vector(double x, double y, double z)
{
return Vector(x, y, z);
}
/// Returns a coefficient of a vector
double vector_coordinate(const Vector& v, int i)
{
return v(i);
}
/// Computes the closest rotation to `m` and places it into `R`
void compute_close_rotation(const Matrix& m, Matrix& R)
{
Eigen::JacobiSVD<Eigen::Matrix3d> solver;
solver.compute( m, Eigen::ComputeFullU | Eigen::ComputeFullV );
const Matrix& u = solver.matrixU(); const Matrix& v = solver.matrixV();
R = v * u.transpose();
if( R.determinant() < 0 ) {
Matrix u_copy = u;
u_copy.col(2) *= -1; // singular values sorted ascendingly
R = v * u_copy.transpose(); // re-extract rotation matrix
}
}
/// \endcond
};
}//namespace CGAL
#endif // CGAL_DEFORMATION_EIGEN_CLOSEST_ROTATION_TRAITS_3_H

View File

@ -0,0 +1,84 @@
// Copyright (c) 2013 INRIA Bordeaux Sud-Ouest (France), All rights reserved.
// Copyright (c) 2013 GeometryFactory
//
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 3 of the License,
// or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
//
// Author(s) : Gael Guennebaud Ilker O. Yaz
#ifndef CGAL_DEFORMATION_EIGEN_POLAR_CLOSEST_ROTATION_TRAITS_3_H
#define CGAL_DEFORMATION_EIGEN_POLAR_CLOSEST_ROTATION_TRAITS_3_H
#include <CGAL/Deformation_Eigen_closest_rotation_traits_3.h>
#include <CGAL/FPU_extension.h>
#include <CGAL/Profile_counter.h>
namespace CGAL {
/// \ingroup PkgSurfaceModeling
/// A class to compute the closest rotation in Frobenius norm to a 3x3 Matrix using the \link thirdpartyEigen `Eigen` library \endlink.
/// The internal computation relies on a hybrid system using the solvers `Eigen::SelfAdjointEigenSolver<>`
/// and `Eigen::JacobiSVD<>` (polar decomposition).
///
/// \cgalModels `DeformationClosestRotationTraits_3`
class Deformation_Eigen_polar_closest_rotation_traits_3 :
public Deformation_Eigen_closest_rotation_traits_3{
public:
/// \cond SKIP_FROM_MANUAL
/// Computes closest rotation to `m` and places it into `R`
void compute_close_rotation(const Matrix& m, Matrix& R)
{
CGAL_PROFILER(" times closest rotation is computed");
bool solved = polar_eigen(m, R);
if(!solved) {
CGAL_PROFILER(" times polar_eigen failed and SVD is called");
Deformation_Eigen_closest_rotation_traits_3::compute_close_rotation(m, R);
}
}
private:
// polar decomposition using Eigen, 5 times faster than SVD
bool polar_eigen(const Matrix& A, Matrix& R)
{
if(A.determinant() < 0)
{ return false; }
typedef Matrix::Scalar Scalar;
const Scalar th = std::sqrt(Eigen::NumTraits<Scalar>::dummy_precision());
Eigen::SelfAdjointEigenSolver<Matrix> eig;
CGAL::feclearexcept(FE_UNDERFLOW);
eig.computeDirect(A.transpose()*A);
if(CGAL::fetestexcept(FE_UNDERFLOW) || eig.eigenvalues()(0)/eig.eigenvalues()(2)<th)
{ return false; }
Vector S = eig.eigenvalues().cwiseSqrt();
R = A * eig.eigenvectors() * S.asDiagonal().inverse()
* eig.eigenvectors().transpose();
if(std::abs(R.squaredNorm()-3.) > th || R.determinant() < 0)
{ return false; }
R.transposeInPlace(); // the optimal rotation matrix should be transpose of decomposition result
return true;
}
/// \endcond
};
}//namespace CGAL
#endif // CGAL_DEFORMATION_EIGEN_POLAR_CLOSEST_ROTATION_TRAITS_3_H

View File

@ -0,0 +1,81 @@
// Copyright (c) 2014 GeometryFactory
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
// You can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// $URL:$
// $Id:$
//
// Author(s) : Ilker O. Yaz
#ifndef CGAL_SURFACE_MODELING_SPOKES_AND_RIMS_ITERATOR_H
#define CGAL_SURFACE_MODELING_SPOKES_AND_RIMS_ITERATOR_H
/// @cond CGAL_DOCUMENT_INTERNAL
namespace CGAL {
namespace internal {
/**
* Currently this class is not used by surface modeling package, just leave it for possible future need.
* Provide simple functionality for iterating over spoke and rim edges
* - use get_descriptor() to obtain active edge
* - get_iterator() always holds spoke edges */
/// \code
/// // how to use Spokes_and_rims_iterator
/// boost::tie(e_begin, e_end) = out_edges(vertex, halfedge_graph);
/// Spokes_and_rims_iterator<HalfedgeGraph> rims_it(e_begin, halfedge_graph);
///
/// for ( ; rims_it.get_iterator() != e_end; ++rims_it )
/// {
/// halfedge_descriptor active_hedge = rims_it.get_descriptor();
/// // use active_edge as you like
/// }
/// \endcode
template<class HalfedgeGraph>
class Spokes_and_rims_iterator
{
public:
typedef typename boost::graph_traits<HalfedgeGraph>::out_edge_iterator out_edge_iterator;
typedef typename boost::graph_traits<HalfedgeGraph>::halfedge_descriptor halfedge_descriptor;
Spokes_and_rims_iterator(out_edge_iterator edge_iterator, HalfedgeGraph& halfedge_graph)
: is_current_rim(false), iterator(edge_iterator), descriptor(halfedge(*edge_iterator)), halfedge_graph(halfedge_graph)
{ }
/// descriptor will be assigned to next valid edge, note that iterator might not change
Spokes_and_rims_iterator<HalfedgeGraph>&
operator++()
{
// loop through one spoke then one rim edge
if(!is_current_rim && !is_border(descriptor, halfedge_graph)) // it is rim edge's turn
{
is_current_rim = true;
descriptor = next(descriptor, halfedge_graph);
}
else // if current edge is rim OR there is no rim edge (current spoke edge is boudary)
{ // then iterate to next spoke edge
is_current_rim = false;
descriptor = halfedge(*(++iterator));
}
return *this;
}
out_edge_iterator get_iterator() { return iterator; }
edge_descriptor get_descriptor() { return descriptor; }
private:
bool is_current_rim; ///< current descriptor is rim or spoke
out_edge_iterator iterator; ///< holds spoke edges (i.e. descriptor is not always = *iterator)
halfedge_descriptor descriptor; ///< current active halfedge descriptor for looping
HalfedgeGraph& halfedge_graph;
};
}//namespace internal
/// @endcond
}//namespace CGAL
#endif //CGAL_SURFACE_MODELING_SPOKES_AND_RIMS_ITERATOR_H

View File

@ -0,0 +1,309 @@
// Copyright (c) 2014 GeometryFactory
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
// You can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
//
// Author(s) : Ilker O. Yaz
#ifndef CGAL_SURFACE_MODELING_WEIGHTS_H
#define CGAL_SURFACE_MODELING_WEIGHTS_H
/// @cond CGAL_DOCUMENT_INTERNAL
#include <CGAL/boost/graph/helpers.h>
#include <CGAL/Simple_cartesian.h>
typedef CGAL::Simple_cartesian<double>::Vector_3 Vector;
namespace CGAL {
namespace internal {
template<class Point>
Vector to_vector(const Point& b, const Point& a) {
return Vector(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
}
// Returns the cotangent value of half angle v0 v1 v2
// using formula in -[Meyer02] Discrete Differential-Geometry Operators for- page 19
// The potential problem with previous one (Cotangent_value) is that it does not produce symmetric results
// (i.e. for v0, v1, v2 and v2, v1, v0 returned cot weights can be slightly different)
// This one provides stable results.
template<class HalfedgeGraph>
class Cotangent_value_Meyer
{
public:
typedef typename boost::graph_traits<HalfedgeGraph>::vertex_descriptor vertex_descriptor;
typedef HalfedgeGraph Halfedge_graph;
template<class VertexPointMap>
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2, VertexPointMap vpm)
{
const Vector& a = to_vector(get(vpm, v1), get(vpm, v0));
const Vector& b = to_vector(get(vpm, v1), get(vpm, v2));
double dot_ab = a*b;
Vector cross_ab = CGAL::cross_product(a, b);
double divider = std::sqrt(cross_ab*cross_ab);
if(divider == 0 /*|| divider != divider*/)
{
this->collinear(get(vpm, v0), get(vpm, v1), get(vpm, v2)) ?
CGAL_warning(!"Infinite Cotangent value with degenerate triangle!") :
CGAL_warning(!"Infinite Cotangent value due to floating point arithmetic!");
return dot_ab > 0 ? (std::numeric_limits<double>::max)() :
-(std::numeric_limits<double>::max)();
}
return dot_ab / divider;
}
///////////////////////////////////////////////////////////////////////////////////////
// WARNING: this two functions are just used when cotangent weight turns out to be +-inf,
// just for raising a proper warning message (i.e nothing functional)
template<class Point>
bool collinear(const Point&, const Point&, const Point&) {
return true;
}
template<class Kernel>
bool collinear(const CGAL::Point_3<Kernel>& a, const CGAL::Point_3<Kernel>& b, const CGAL::Point_3<Kernel>& c) {
return CGAL::collinear(a, b, c);
}
///////////////////////////////////////////////////////////////////////////////////////
};
// Returns the cotangent value of half angle v0 v1 v2 by clamping between [1, 89] degrees
// as suggested by -[Friedel] Unconstrained Spherical Parameterization-
template<class HalfedgeGraph, class CotangentValue = Cotangent_value_Meyer<HalfedgeGraph> >
class Cotangent_value_clamped : CotangentValue
{
public:
typedef typename boost::graph_traits<HalfedgeGraph>::vertex_descriptor vertex_descriptor;
typedef HalfedgeGraph Halfedge_graph;
template<class VertexPointMap>
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2, VertexPointMap vpm)
{
const double cot_1 = 57.289962;
const double cot_89 = 0.017455;
double value = CotangentValue::operator()(v0, v1, v2, vpm);
return (std::max)(cot_89, (std::min)(value, cot_1));
}
};
template<class HalfedgeGraph, class CotangentValue = Cotangent_value_Meyer<HalfedgeGraph> >
class Cotangent_value_minimum_zero : CotangentValue
{
public:
typedef typename boost::graph_traits<HalfedgeGraph>::vertex_descriptor vertex_descriptor;
template<class VertexPointMap>
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2, VertexPointMap vpm)
{
double value = CotangentValue::operator()(v0, v1, v2, vpm);
return (std::max)(0.0, value);
}
};
///////////////////////////// Halfedge Weight Calculators ///////////////////////////////////
// Cotangent weight calculator
// Cotangent_value: as suggested by -[Sorkine07] ARAP Surface Modeling-
// Cotangent_value_area_weighted: as suggested by -[Mullen08] Spectral Conformal Parameterization-
template<class HalfedgeGraph,
class CotangentValue = Cotangent_value_minimum_zero<HalfedgeGraph> >
class Cotangent_weight : CotangentValue
{
public:
typedef typename boost::graph_traits<HalfedgeGraph>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<HalfedgeGraph>::vertex_descriptor vertex_descriptor;
typedef HalfedgeGraph Halfedge_graph;
// Returns the cotangent weight of specified halfedge_descriptor
// Edge orientation is trivial
template<class VertexPointMap>
double operator()(halfedge_descriptor he, HalfedgeGraph& halfedge_graph, VertexPointMap vpm)
{
vertex_descriptor v0 = target(he, halfedge_graph);
vertex_descriptor v1 = source(he, halfedge_graph);
// Only one triangle for border edges
if ( is_border(he, halfedge_graph) ||
is_border(opposite(he, halfedge_graph), halfedge_graph) )
{
halfedge_descriptor he_cw = opposite( next(he, halfedge_graph), halfedge_graph );
vertex_descriptor v2 = source(he_cw, halfedge_graph);
if ( is_border(he_cw, halfedge_graph) ||
is_border(opposite(he_cw, halfedge_graph), halfedge_graph) )
{
halfedge_descriptor he_ccw = prev( opposite(he, halfedge_graph), halfedge_graph );
v2 = source(he_ccw, halfedge_graph);
}
return ( CotangentValue::operator()(v0, v2, v1, vpm)/2.0 );
}
else
{
halfedge_descriptor he_cw = opposite( next(he, halfedge_graph), halfedge_graph );
vertex_descriptor v2 = source(he_cw, halfedge_graph);
halfedge_descriptor he_ccw = prev( opposite(he, halfedge_graph), halfedge_graph );
vertex_descriptor v3 = source(he_ccw, halfedge_graph);
return ( CotangentValue::operator()(v0, v2, v1, vpm)/2.0 + CotangentValue::operator()(v0, v3, v1, vpm)/2.0 );
}
}
};
// Single cotangent from -[Chao10] Simple Geometric Model for Elastic Deformation
template<class HalfedgeGraph,
class CotangentValue = Cotangent_value_Meyer<HalfedgeGraph> >
class Single_cotangent_weight : CotangentValue
{
public:
typedef typename boost::graph_traits<HalfedgeGraph>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<HalfedgeGraph>::vertex_descriptor vertex_descriptor;
typedef HalfedgeGraph Halfedge_graph;
// Returns the cotangent of the opposite angle of the halfedge
// 0 for border edges (which does not have an opposite angle)
template<class VertexPointMap>
double operator()(halfedge_descriptor he, HalfedgeGraph& halfedge_graph, VertexPointMap vpm)
{
if(is_border(he, halfedge_graph)) { return 0.0;}
vertex_descriptor v0 = target(he, halfedge_graph);
vertex_descriptor v1 = source(he, halfedge_graph);
vertex_descriptor v_op = target(next(he, halfedge_graph), halfedge_graph);
return CotangentValue::operator()(v0, v_op, v1, vpm);
}
};
// Mean value calculator described in -[Floater04] Mean Value Coordinates-
// WARNING: Need to be updated to use point pmap
template<class HalfedgeGraph>
class Mean_value_weight
{
public:
typedef typename boost::graph_traits<HalfedgeGraph>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<HalfedgeGraph>::vertex_descriptor vertex_descriptor;
typedef HalfedgeGraph Halfedge_graph;
// Returns the mean-value coordinate of specified halfedge_descriptor
// Returns different value for different halfedge orientation (which is a normal behaviour according to formula)
double operator()(halfedge_descriptor he, HalfedgeGraph& halfedge_graph)
{
vertex_descriptor v0 = target(he, halfedge_graph);
vertex_descriptor v1 = source(he, halfedge_graph);
Vector vec(v1->point(), v0->point());
double norm = std::sqrt( vec.squared_length() );
// Only one triangle for border edges
if ( is_border(he, halfedge_graph) ||
is_border( opposite(he, halfedge_graph), halfedge_graph) )
{
halfedge_descriptor he_cw = opposite( next(he, halfedge_graph), halfedge_graph );
vertex_descriptor v2 = source(he_cw, halfedge_graph);
if ( is_border(he_cw, halfedge_graph) ||
is_border(opposite(he_cw, halfedge_graph), halfedge_graph) )
{
halfedge_descriptor he_ccw = prev( opposite(he, halfedge_graph), halfedge_graph );
v2 = source(he_ccw, halfedge_graph);
}
return ( half_tan_value_2(v1, v0, v2)/norm);
}
else
{
halfedge_descriptor he_cw = opposite( next(he, halfedge_graph), halfedge_graph );
vertex_descriptor v2 = source(he_cw, halfedge_graph);
halfedge_descriptor he_ccw = prev( opposite(he, halfedge_graph), halfedge_graph );
vertex_descriptor v3 = source(he_ccw, halfedge_graph);
return ( half_tan_value_2(v1, v0, v2)/norm + half_tan_value_2(v1, v0, v3)/norm);
}
}
private:
// Returns the tangent value of half angle v0_v1_v2/2
double half_tan_value(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2)
{
Vector vec0(v2->point(), v1->point());
Vector vec1(v0->point(), v2->point());
Vector vec2(v1->point(), v0->point());
double e0_square = vec0.squared_length();
double e1_square = vec1.squared_length();
double e2_square = vec2.squared_length();
double e0 = std::sqrt(e0_square);
double e2 = std::sqrt(e2_square);
double cos_angle = ( e0_square + e2_square - e1_square ) / 2.0 / e0 / e2;
cos_angle = (std::max)(-1.0, (std::min)(1.0, cos_angle)); // clamp into [-1, 1]
double angle = acos(cos_angle);
return ( tan(angle/2.0) );
}
// My deviation built on Meyer_02
double half_tan_value_2(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2)
{
Vector a(v1->point(), v0->point());
Vector b(v1->point(), v2->point());
double dot_ab = a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
double dot_aa = a.squared_length();
double dot_bb = b.squared_length();
double dot_aa_bb = dot_aa * dot_bb;
double cos_rep = dot_ab;
double sin_rep = std::sqrt(dot_aa_bb - dot_ab * dot_ab);
double normalizer = std::sqrt(dot_aa_bb); // |a|*|b|
return (normalizer - cos_rep) / sin_rep; // formula from [Floater04] page 4
// tan(Q/2) = (1 - cos(Q)) / sin(Q)
}
};
template< class HalfedgeGraph,
class PrimaryWeight = Cotangent_weight<HalfedgeGraph>,
class SecondaryWeight = Mean_value_weight<HalfedgeGraph> >
class Hybrid_weight : public PrimaryWeight, SecondaryWeight
{
public:
typedef typename boost::graph_traits<HalfedgeGraph>::halfedge_descriptor halfedge_descriptor;
typedef HalfedgeGraph Halfedge_graph;
double operator()(halfedge_descriptor he, HalfedgeGraph& halfedge_graph)
{
double weight = PrimaryWeight::operator()(he, halfedge_graph);
//if(weight < 0) { std::cout << "Negative weight" << std::endl; }
return (weight >= 0) ? weight : SecondaryWeight::operator()(he, halfedge_graph);
}
};
// Trivial uniform weights (created for test purposes)
template<class HalfedgeGraph>
class Uniform_weight
{
public:
typedef HalfedgeGraph Halfedge_graph;
typedef typename boost::graph_traits<HalfedgeGraph>::halfedge_descriptor halfedge_descriptor;
double operator()(halfedge_descriptor /*he*/, HalfedgeGraph& /*halfedge_graph*/)
{ return 1.0; }
};
}//namespace internal
/// @endcond
}//namespace CGAL
#endif //CGAL_SURFACE_MODELING_WEIGHTS_H

View File

@ -0,0 +1 @@
GeometryFactory (France)

View File

@ -0,0 +1 @@
GPL (v3 or later)

View File

@ -0,0 +1 @@
Andreas Fabri <Andreas.Fabri@geometryfactory.com>

View File

@ -0,0 +1,48 @@
# Created by the script cgal_create_cmake_script
# This is the CMake script for compiling a CGAL application.
project( Surface_modeling_test )
cmake_minimum_required(VERSION 2.6.2)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 2.6)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.3)
cmake_policy(VERSION 2.8.4)
else()
cmake_policy(VERSION 2.6)
endif()
endif()
find_package(CGAL QUIET COMPONENTS Core )
if ( CGAL_FOUND )
include( ${CGAL_USE_FILE} )
include( CGAL_CreateSingleSourceCGALProgram )
include_directories (BEFORE "../../include")
find_package(Eigen3 3.1.91) #(requires 3.2.0 or greater)
if (EIGEN3_FOUND)
include( ${EIGEN3_USE_FILE} )
create_single_source_cgal_program( "Cactus_deformation_session.cpp" )
create_single_source_cgal_program( "Cactus_performance_test.cpp" )
create_single_source_cgal_program( "Symmetry_test.cpp" )
find_package( OpenMesh QUIET )
if ( OpenMesh_FOUND )
include( UseOpenMesh )
create_single_source_cgal_program( "Cactus_deformation_session_OpenMesh.cpp" )
target_link_libraries( Cactus_deformation_session_OpenMesh ${OPENMESH_LIBRARIES} )
else()
message(STATUS "Example that use OpenMesh will not be compiled.")
endif()
else()
message(STATUS "NOTICE: These tests require the Eigen library, version 3.2 or later and will not be compiled.")
endif()
else()
message(STATUS "NOTICE: These tests require the CGAL library, and will not be compiled.")
endif()

View File

@ -0,0 +1,111 @@
#include "Surface_modeling_test_commons.h"
#include <algorithm>
#include <sstream>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/Deform_mesh.h>
#include <CGAL/Timer.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_with_id_3> Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef CGAL::Deform_mesh<Polyhedron, CGAL::Default, CGAL::Default, CGAL::ORIGINAL_ARAP> Deform_mesh_arap;
typedef CGAL::Deform_mesh<Polyhedron, CGAL::Default, CGAL::Default, CGAL::SPOKES_AND_RIMS> Deform_mesh_spoke;
const double squared_threshold = 0.001; // alert if average difs between precomputed and deformed mesh models is above threshold
void compare_mesh(const Polyhedron& mesh_1, const Polyhedron& mesh_2)
{
Polyhedron::Vertex_const_iterator it_1 = mesh_1.vertices_begin();
Polyhedron::Vertex_const_iterator it_2 = mesh_2.vertices_begin();
Kernel::Vector_3 total_dif(0,0,0);
for( ; it_1 != mesh_1.vertices_end(); ++it_1 , ++it_2)
{
total_dif = total_dif + (it_1->point() - it_2->point());
}
double average_mesh_dif = (total_dif / (double) mesh_1.size_of_vertices()).squared_length();
std::cerr << "Average mesh difference: " << average_mesh_dif << std::endl;
assert( average_mesh_dif < squared_threshold);
}
// read deformation session saved as a handle differences
template<class DeformMesh, class InputIterator>
void read_handle_difs_and_deform(DeformMesh& deform_mesh, InputIterator begin, InputIterator end)
{
typedef CGAL::Simple_cartesian<double>::Vector_3 Vector;
if(!deform_mesh.preprocess()) {
std::cerr << "Error: preprocess() failed!" << std::endl;
assert(false);
}
std::ifstream dif_stream("data/cactus_handle_differences.txt");
std::vector<Vector> dif_vector;
double x, y, z;
while(dif_stream >> x >> y >> z)
{ dif_vector.push_back(Vector(x, y, z)); }
CGAL::Timer timer;
//the original behavior of translate was to overwrite the previous
//translation. Now that it is cumulative, we need to substract the
//previous translation vector to mimic the overwrite
Vector previous(0,0,0);
for(std::size_t i = 0; i < dif_vector.size(); ++i)
{
timer.start();
deform_mesh.translate(begin, end, dif_vector[i]-previous);
deform_mesh.deform();
timer.stop();
previous=dif_vector[i];
// read pre-deformed cactus
std::stringstream predeformed_cactus_file;
predeformed_cactus_file << "data/cactus_deformed/cactus_deformed_" << i << ".off";
Polyhedron predeformed_cactus;
read_to_polyhedron(predeformed_cactus_file.str().c_str(), predeformed_cactus);
compare_mesh(predeformed_cactus, deform_mesh.halfedge_graph());
// for saving deformation
//std::ofstream(predeformed_cactus_file) << deform_mesh.halfedge_graph();
//std::cerr << predeformed_cactus_file << std::endl;
}
std::cerr << "Deformation performance (with default number_of_iteration and tolerance) " << std::endl
<< dif_vector.size() << " translation: " << timer.time() << std::endl;
}
int main()
{
Polyhedron mesh_1;
read_to_polyhedron("data/cactus.off", mesh_1);
Polyhedron mesh_2 = mesh_1;
init_indices(mesh_1);
init_indices(mesh_2);
Deform_mesh_arap deform_mesh_arap(mesh_1);
Deform_mesh_spoke deform_mesh_spoke(mesh_2);
// For original arap
std::vector<vertex_descriptor> hg_1 =
read_rois(deform_mesh_arap, "data/cactus_roi.txt", "data/cactus_handle.txt");
read_handle_difs_and_deform(deform_mesh_arap, hg_1.begin(), hg_1.end());
std::cerr << "ORIGINAL ARAP Success!" << std::endl;
// For spokes rims
std::vector<vertex_descriptor> hg_2 =
read_rois(deform_mesh_spoke, "data/cactus_roi.txt", "data/cactus_handle.txt");
read_handle_difs_and_deform(deform_mesh_spoke, hg_2.begin(), hg_2.end());
std::cerr << "SPOKES AND RIMS ARAP Success!" << std::endl;
std::cerr << "All done!" << std::endl;
}

View File

@ -0,0 +1,118 @@
#include "Surface_modeling_test_commons.h"
#include <algorithm>
#include <sstream>
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
// HalfedgeGraph adapters
#define CGAL_USE_OM_POINTS // use OpenMesh point type
#include <CGAL/boost/graph/graph_traits_PolyMesh_ArrayKernelT.h>
#include <CGAL/Deform_mesh.h>
#include <CGAL/Timer.h>
typedef OpenMesh::PolyMesh_ArrayKernelT</* MyTraits*/> Mesh;
typedef Mesh::Point Point;
typedef boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Mesh>::vertex_iterator vertex_iterator;
typedef CGAL::Deform_mesh<Mesh, CGAL::Default, CGAL::Default, CGAL::ORIGINAL_ARAP> Deform_mesh_arap;
typedef CGAL::Deform_mesh<Mesh, CGAL::Default, CGAL::Default, CGAL::SPOKES_AND_RIMS> Deform_mesh_spoke;
const double squared_threshold = 0.001; // alert if average difs between precomputed and deformed mesh models is above threshold
double squared_length(const Point& p)
{
return p[0]*p[0]+p[1]*p[1]+p[2]*p[2];
}
void compare_mesh(const Mesh& mesh_1, const Mesh& mesh_2)
{
vertex_iterator it_1,end_1, it_2, end_2;
CGAL::cpp11::tie(it_1, end_1) = vertices(mesh_1);
CGAL::cpp11::tie(it_2, end_2) = vertices(mesh_2);
boost::property_map<Mesh, boost::vertex_point_t>::type
ppmap_1 = get(boost::vertex_point, mesh_1), ppmap_2 = get(boost::vertex_point, mesh_2);
Point total_dif(0,0,0);
for( ; it_1 != end_1; ++it_1 , ++it_2)
{
total_dif = total_dif + (get(ppmap_1, *it_1) - get(ppmap_2, *it_2));
}
double average_mesh_dif = squared_length(total_dif / num_vertices(mesh_1));
std::cerr << "Average mesh difference: " << average_mesh_dif << std::endl;
assert( average_mesh_dif < squared_threshold);
}
// read deformation session saved as a handle differences
template<class DeformMesh, class InputIterator>
void read_handle_difs_and_deform(DeformMesh& deform_mesh, InputIterator begin, InputIterator end)
{
typedef CGAL::Simple_cartesian<double>::Vector_3 Vector;
if(!deform_mesh.preprocess()) {
std::cerr << "Error: preprocess() failed!" << std::endl;
assert(false);
}
std::ifstream dif_stream("data/cactus_handle_differences.txt");
std::vector<Vector> dif_vector;
double x, y, z;
while(dif_stream >> x >> y >> z)
{ dif_vector.push_back(Vector(x, y, z)); }
CGAL::Timer timer;
//the original behavior of translate was to overwrite the previous
//translation. Now that it is cumulative, we need to substract the
//previous translation vector to mimic the overwrite
Vector previous(0,0,0);
for(std::size_t i = 0; i < dif_vector.size(); ++i)
{
timer.start();
deform_mesh.translate(begin, end, dif_vector[i]-previous);
deform_mesh.deform();
timer.stop();
previous=dif_vector[i];
// read pre-deformed cactus
std::stringstream predeformed_cactus_file;
predeformed_cactus_file << "data/cactus_deformed/cactus_deformed_" << i << ".off";
Mesh predeformed_cactus;
OpenMesh::IO::read_mesh(predeformed_cactus, predeformed_cactus_file.str().c_str());
compare_mesh(predeformed_cactus, deform_mesh.halfedge_graph());
// for saving deformation
//std::ofstream(predeformed_cactus_file) << deform_mesh.halfedge_graph();
//std::cerr << predeformed_cactus_file << std::endl;
}
std::cerr << "Deformation performance (with default number_of_iteration and tolerance) " << std::endl
<< dif_vector.size() << " translation: " << timer.time() << std::endl;
}
int main()
{
Mesh mesh_1;
OpenMesh::IO::read_mesh(mesh_1, "data/cactus.off");
Mesh mesh_2 = mesh_1;
Deform_mesh_arap deform_mesh_arap(mesh_1);
Deform_mesh_spoke deform_mesh_spoke(mesh_2);
// For original arap
std::vector<vertex_descriptor> hg_1 =
read_rois(deform_mesh_arap, "data/cactus_roi.txt", "data/cactus_handle.txt");
read_handle_difs_and_deform(deform_mesh_arap, hg_1.begin(), hg_1.end());
std::cerr << "ORIGINAL ARAP Success!" << std::endl;
// For spokes rims
std::vector<vertex_descriptor> hg_2 =
read_rois(deform_mesh_spoke, "data/cactus_roi.txt", "data/cactus_handle.txt");
read_handle_difs_and_deform(deform_mesh_spoke, hg_2.begin(), hg_2.end());
std::cerr << "SPOKES AND RIMS ARAP Success!" << std::endl;
std::cerr << "All done!" << std::endl;
}

View File

@ -0,0 +1,66 @@
#include "Surface_modeling_test_commons.h"
#include <vector>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Timer.h>
#include <CGAL/Deform_mesh.h>
typedef CGAL::Simple_cartesian<double> Kernel;
//typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_with_id_3> Polyhedron;
typedef CGAL::Deform_mesh<Polyhedron, CGAL::Default, CGAL::Default,
CGAL::ORIGINAL_ARAP> Deform_mesh_arap;
typedef CGAL::Deform_mesh<Polyhedron, CGAL::Default, CGAL::Default,
CGAL::SPOKES_AND_RIMS> Deform_mesh_spoke;
int main()
{
Polyhedron mesh_1;
read_to_polyhedron("data/cactus.off", mesh_1);
Polyhedron mesh_2 = mesh_1;
init_indices(mesh_1);
init_indices(mesh_2);
Deform_mesh_arap deform_mesh_arap(mesh_1);
Deform_mesh_spoke deform_mesh_spoke(mesh_2);
const int deformation_iteration = 500;
const double x = -0.55; const double y = -0.50; const double z = -0.0;
std::cerr << "ORIGINAL_ARAP performance: " << std::endl;
preprocess_and_deform(deform_mesh_arap,
"data/cactus_roi.txt",
"data/cactus_handle.txt",
CGAL::Simple_cartesian<double>::Vector_3(x, y, z),
deformation_iteration);
std::cerr << "SPOKES_AND_RIMS performance: " << std::endl;
preprocess_and_deform(deform_mesh_spoke,
"data/cactus_roi.txt",
"data/cactus_handle.txt",
CGAL::Simple_cartesian<double>::Vector_3(x, y, z),
deformation_iteration);
std::cerr << "Save deformed models" << std::endl;
std::ofstream output("data/cactus_deformed_arap.off");
output << mesh_1;
output.close();
output.open("data/cactus_deformed_spokes.off");
output << mesh_2;
std::cerr << "All done!" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,108 @@
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/properties_Polyhedron_3.h>
#include <CGAL/property_map.h>
#include <boost/optional.hpp>
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Timer.h>
template<class Polyhedron>
void read_to_polyhedron(const char* file_name, Polyhedron& mesh)
{
std::ifstream input(file_name);
if ( !input || !(input >> mesh) || mesh.empty() ){
std::cerr << "Error: can not read " << file_name << std::endl;
assert(false);
}
}
template<class Polyhedron>
void init_indices(Polyhedron& poly) {
typedef typename boost::graph_traits<Polyhedron>::vertex_iterator vertex_iterator;
typedef typename boost::graph_traits<Polyhedron>::halfedge_iterator halfedge_iterator;
vertex_iterator vb, ve;
std::size_t counter = 0;
for(boost::tie(vb, ve) = vertices(poly); vb != ve; ++vb, ++counter) {
(*vb)->id() = counter;
}
counter = 0;
halfedge_iterator heb, hee;
for(boost::tie(heb, hee) = halfedges(poly); heb != hee; ++heb, ++counter) {
(*heb)->id() = counter;
}
}
template<class DeformMesh>
std::vector<typename DeformMesh::vertex_descriptor>
read_rois(DeformMesh& deform_mesh,
const std::string& roi_file,
const std::string& handle_file)
{
std::ifstream roi_stream(roi_file.c_str());
std::ifstream handle_stream(handle_file.c_str());
if(!roi_stream || !handle_stream) {
std::cerr << "Error: can not read roi or handle files" << std::endl;
assert(false);
}
typedef typename boost::graph_traits<typename DeformMesh::Halfedge_graph>::vertex_iterator vertex_iterator;
typedef typename DeformMesh::vertex_descriptor vertex_descriptor;
// put all vertices to a vector
typename DeformMesh::Halfedge_graph const& polyhedron = deform_mesh.halfedge_graph();
std::vector<vertex_descriptor> vvertices;
vvertices.reserve(num_vertices(polyhedron));
vertex_iterator vb, ve;
for(boost::tie(vb, ve) = vertices(polyhedron); vb != ve; ++vb) {
vvertices.push_back(*vb);
}
// load handles and roi from txt
// put all handles into one handle group
std::size_t id;
std::vector<typename DeformMesh::vertex_descriptor> hg;
while(handle_stream >> id) {
deform_mesh.insert_control_vertex(vvertices[id]);
hg.push_back(vvertices[id]);
}
while(roi_stream >> id) {
deform_mesh.insert_roi_vertex(vvertices[id]);
}
return hg;
}
template<class DeformMesh>
void preprocess_and_deform(DeformMesh& deform_mesh,
const std::string& roi_file,
const std::string& handle_file,
CGAL::Simple_cartesian<double>::Vector_3 translate,
int deformation_iteration)
{
std::vector<typename DeformMesh::vertex_descriptor> hg =
read_rois(deform_mesh, roi_file, handle_file);
CGAL::Timer timer; timer.start();
if(!deform_mesh.preprocess()) {
std::cerr << "Error: preprocess() failed!" << std::endl;
assert(false);
}
std::cerr << "Preprocess time: " << timer.time() << std::endl;
timer.reset();
deform_mesh.translate(hg.begin(), hg.end(), translate);
deform_mesh.deform(deformation_iteration, 0);
std::cerr << "Deformation time (one handle translated and deform method iterated " <<
deformation_iteration << " times): " << timer.time() << std::endl;
}

View File

@ -0,0 +1,56 @@
#include "Surface_modeling_test_commons.h"
#include <vector>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Deform_mesh.h>
#include <CGAL/IO/Polyhedron_iostream.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_with_id_3> Polyhedron;
typedef CGAL::Deform_mesh<Polyhedron, CGAL::Default, CGAL::Default, CGAL::ORIGINAL_ARAP> Deform_mesh_arap;
typedef CGAL::Deform_mesh<Polyhedron, CGAL::Default, CGAL::Default, CGAL::SPOKES_AND_RIMS> Deform_mesh_spoke;
int main()
{
Polyhedron mesh_1;
read_to_polyhedron("data/square.off", mesh_1);
Polyhedron mesh_2 = mesh_1;
init_indices(mesh_1);
init_indices(mesh_2);
Deform_mesh_arap deform_mesh_arap(mesh_1);
Deform_mesh_spoke deform_mesh_spoke(mesh_2);
const int deformation_iteration = 500;
const double x = -0.45; const double y = -0.65; const double z = -0.0;
std::cerr << "ORIGINAL_ARAP performance: " << std::endl;
preprocess_and_deform(deform_mesh_arap,
"data/Symmetry_test_roi.txt",
"data/Symmetry_test_handle.txt",
CGAL::Simple_cartesian<double>::Vector_3(x, y, z),
deformation_iteration);
std::cerr << "SPOKES_AND_RIMS performance: " << std::endl;
preprocess_and_deform(deform_mesh_spoke,
"data/Symmetry_test_roi.txt",
"data/Symmetry_test_handle.txt",
CGAL::Simple_cartesian<double>::Vector_3(x, y, z),
deformation_iteration);
std::cerr << "Save deformed models" << std::endl;
std::ofstream output("data/Symmetry_test_deformed_arap.off");
output << mesh_1; output.close();
output.open("data/Symmetry_test_deformed_spokes.off");
output << mesh_2;
std::cerr << "All done!" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1 @@
419 418 417 416 415 414 435 434 433 432 440 439 438 437 436 413 412 411 410 409 408 407 406 405 404 403 402 401 400 399 431 430 429 428 427 426 425 424 423 422 421 420

View File

@ -0,0 +1 @@
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 381 380 379 378 419 418 417 416 415 414 435 434 433 432 440 439 438 437 436 413 412 411 410 409 408 407 406 405 404 403 402 401 400 399 431 430 429 428 427 426 425 424 423 422 421 420

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More