mirror of https://github.com/CGAL/cgal
Merge pull request #3106 from maxGimeno/PMP-Extrude-GF
PMP: extrude_mesh()
This commit is contained in:
commit
a2c30daa17
|
|
@ -83,6 +83,8 @@ Release date: September 2018
|
|||
- Fix a bug in `isotropic_remeshing()` making constrained vertices missing in the output
|
||||
- Guarantee that constrained vertices are kept in the mesh after calling `isotropic_remeshing()`
|
||||
(and not only the points associated to constrained vertices as it was before).
|
||||
- Added a function in Polygon Mesh Processing to perform an extrusion of an open polygon mesh:
|
||||
- `CGAL::Polygon_mesh_processing::extrude_mesh()`
|
||||
|
||||
### 3D Mesh Generation
|
||||
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ and provides a list of the parameters that are used in this package.
|
|||
- \link PMP_meshing_grp `CGAL::Polygon_mesh_processing::isotropic_remeshing()` \endlink
|
||||
- \link PMP_meshing_grp `CGAL::Polygon_mesh_processing::split_long_edges()` \endlink
|
||||
- `CGAL::Polygon_mesh_processing::random_perturbation()`
|
||||
- `CGAL::Polygon_mesh_processing::extrude_mesh()`
|
||||
|
||||
## Hole Filling Functions ##
|
||||
- `CGAL::Polygon_mesh_processing::triangulate_hole()`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,323 @@
|
|||
// Copyright (c) 2018 GeometryFactory (France).
|
||||
// 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$
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
//
|
||||
//
|
||||
// Author(s) : Sebastien Loriot, Maxime Gimeno
|
||||
|
||||
|
||||
#ifndef CGAL_POLYGON_MESH_PROCESSING_EXTRUDE_H
|
||||
#define CGAL_POLYGON_MESH_PROCESSING_EXTRUDE_H
|
||||
|
||||
#include <CGAL/license/Polygon_mesh_processing/meshing_hole_filling.h>
|
||||
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/orientation.h>
|
||||
#include <CGAL/boost/graph/named_params_helper.h>
|
||||
#include <CGAL/boost/graph/named_function_params.h>
|
||||
#include <CGAL/boost/graph/copy_face_graph.h>
|
||||
#include <CGAL/Kernel_traits.h>
|
||||
#include <CGAL/boost/graph/Euler_operations.h>
|
||||
#include <vector>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Polygon_mesh_processing {
|
||||
namespace extrude_impl{
|
||||
|
||||
template<typename BorderHalfedgesRange, class PolygonMesh>
|
||||
void create_strip(const BorderHalfedgesRange& input_halfedges,
|
||||
const BorderHalfedgesRange& output_halfedges,
|
||||
PolygonMesh& mesh)
|
||||
{
|
||||
CGAL_assertion(input_halfedges.size() == output_halfedges.size());
|
||||
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
typedef typename boost::graph_traits<PolygonMesh>::face_descriptor face_descriptor;
|
||||
for(std::size_t i = 0; i < input_halfedges.size(); ++i)
|
||||
{
|
||||
halfedge_descriptor h1 = input_halfedges[i], h2=output_halfedges[i],
|
||||
nh1 = next(h1, mesh), ph2 = prev(h2, mesh);
|
||||
halfedge_descriptor newh = halfedge(add_edge(mesh), mesh),
|
||||
newh_opp = opposite(newh, mesh);
|
||||
// set target vertices of the new halfedges
|
||||
set_target(newh, target(h1, mesh), mesh);
|
||||
set_target(newh_opp, target(ph2, mesh), mesh);
|
||||
// update next/prev pointers
|
||||
set_next(h1, newh_opp, mesh);
|
||||
set_next(newh_opp, h2, mesh);
|
||||
set_next(ph2, newh, mesh);
|
||||
set_next(newh, nh1, mesh);
|
||||
}
|
||||
for(std::size_t i = 0; i < input_halfedges.size(); ++i)
|
||||
{
|
||||
halfedge_descriptor h = input_halfedges[i];
|
||||
face_descriptor nf = add_face(mesh);
|
||||
|
||||
CGAL::cpp11::array<halfedge_descriptor, 4> hedges;
|
||||
for (int k=0; k<4; ++k)
|
||||
{
|
||||
hedges[k]=h;
|
||||
h = next(h, mesh);
|
||||
}
|
||||
|
||||
set_face(hedges[0], nf, mesh);
|
||||
set_face(hedges[1], nf, mesh);
|
||||
set_face(hedges[2], nf, mesh);
|
||||
set_face(hedges[3], nf, mesh);
|
||||
set_halfedge(nf, hedges[0], mesh);
|
||||
Euler::split_face(hedges[0], hedges[2], mesh);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename PMAP, typename Vector>
|
||||
struct Const_dist_translation{
|
||||
Const_dist_translation(PMAP map, const Vector& dir)
|
||||
:map(map), dir(dir){}
|
||||
|
||||
template<typename VertexDescriptor, typename U>
|
||||
void operator()(const VertexDescriptor vd, const U&) const
|
||||
{
|
||||
typename boost::property_traits<PMAP>::value_type p = get(map, vd) + dir;
|
||||
put(map, vd, p);
|
||||
}
|
||||
|
||||
PMAP map;
|
||||
Vector dir;
|
||||
};
|
||||
|
||||
struct Identity_functor
|
||||
{
|
||||
template<typename T, typename U>
|
||||
void operator()(const T&, const U&) const {}
|
||||
};
|
||||
}//end extrude_impl
|
||||
|
||||
/**
|
||||
* \ingroup PMP_meshing_grp
|
||||
* \brief performs a generalized extrusion of `input` and puts it in `output`.
|
||||
*
|
||||
* This function extrudes the open surface mesh `input` and puts the result in `output`. The mesh generated is a closed
|
||||
* surface mesh with a bottom and top part, both having the same graph combinatorics as `input` (except
|
||||
* that the orientation of the faces of the bottom part is reversed). The bottom and the top parts are
|
||||
* connected by a triangle strip between boundary cycles. The coordinates of the points associated to the
|
||||
* vertices of the bottom and top part are first initialized to the same value as the corresponding
|
||||
* vertices of `input`. Then for each vertex, a call to `bot` and `top` is done for the vertices of the
|
||||
* bottom part and the top part, respectively.
|
||||
* \attention `output` may be self intersecting.
|
||||
* @tparam InputMesh a model of `FaceListGraph`
|
||||
* @tparam OutputMesh a model of `FaceListGraph` and `MutableFaceGraph`
|
||||
* @tparam NamedParameters1 a sequence of \ref pmp_namedparameters "Named Parameters" for `InputMesh`
|
||||
* @tparam NamedParameters2 a sequence of \ref pmp_namedparameters "Named Parameters" for `OutputMesh`
|
||||
* @tparam BottomFunctor a functor providing
|
||||
* \code {.cpp}
|
||||
* void operator()`(boost::graph_traits<InputMesh>::vertex_descriptor input_v,boost::graph_traits<OutputMesh>::vertex_descriptor output_v)
|
||||
* \endcode
|
||||
* where `output_v` is the copy of `input_v` from `input` into the bottom part of `output`.
|
||||
*
|
||||
* @tparam TopFunctor a functor providing a similar `operator()` as `BottomFunctor`.
|
||||
* @param input an open surface mesh to extrude.
|
||||
* @param output a surface mesh that will contain the result of the extrusion.
|
||||
* @param bot functor that will transform all points copied from
|
||||
* `input` in order to shape the bottom part of the extrusion.
|
||||
* @param top functor that will transform all points copied from
|
||||
* `input` in order to shape the top part of the extrusion.
|
||||
* @param np_in an optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamBegin{vertex_point_map}
|
||||
* the property map that contains the points associated to the vertices of `input`.
|
||||
* If this parameter is omitted, an internal property map for `CGAL::vertex_point_t`
|
||||
* should be available for the vertices of `input` \cgalParamEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* * @param np_out an optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamBegin{vertex_point_map}
|
||||
* the property map that will contain the points associated to the vertices of `output`.
|
||||
* If this parameter is omitted, an internal property map for `CGAL::vertex_point_t`
|
||||
* should be available for the vertices of `output` \cgalParamEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*/
|
||||
template <class InputMesh,
|
||||
class OutputMesh,
|
||||
class BottomFunctor,
|
||||
class TopFunctor,
|
||||
class NamedParameters1,
|
||||
class NamedParameters2
|
||||
>
|
||||
void extrude_mesh(const InputMesh& input,
|
||||
OutputMesh& output,
|
||||
const BottomFunctor& bot,
|
||||
const TopFunctor& top,
|
||||
const NamedParameters1& np_in,
|
||||
const NamedParameters2& np_out)
|
||||
{
|
||||
typedef typename boost::graph_traits<InputMesh>::vertex_descriptor input_vertex_descriptor;
|
||||
typedef typename boost::graph_traits<InputMesh>::halfedge_descriptor input_halfedge_descriptor;
|
||||
|
||||
typedef typename boost::graph_traits<OutputMesh>::vertex_descriptor output_vertex_descriptor;
|
||||
typedef typename boost::graph_traits<OutputMesh>::halfedge_descriptor output_halfedge_descriptor;
|
||||
|
||||
CGAL_assertion(!CGAL::is_closed(input));
|
||||
typedef typename GetVertexPointMap < OutputMesh, NamedParameters2>::type VPMap;
|
||||
typedef typename GetVertexPointMap < InputMesh, NamedParameters1>::const_type IVPMap;
|
||||
|
||||
VPMap output_vpm = choose_param(get_param(np_out, internal_np::vertex_point),
|
||||
get_property_map(vertex_point, output));
|
||||
IVPMap input_vpm = choose_param(get_param(np_in, internal_np::vertex_point),
|
||||
get_const_property_map(vertex_point, input));
|
||||
|
||||
std::vector<std::pair<input_vertex_descriptor, output_vertex_descriptor> > bottom_v2v;
|
||||
std::vector<std::pair<input_halfedge_descriptor, output_halfedge_descriptor> > bottom_h2h;
|
||||
copy_face_graph(input, output, std::back_inserter(bottom_v2v),
|
||||
std::back_inserter(bottom_h2h), Emptyset_iterator(),
|
||||
input_vpm, output_vpm);
|
||||
|
||||
// create the offset for the other side
|
||||
for(std::size_t i = 0; i< bottom_v2v.size(); ++i)
|
||||
{
|
||||
bot(bottom_v2v[i].first, bottom_v2v[i].second);
|
||||
}
|
||||
CGAL::Polygon_mesh_processing::reverse_face_orientations(output);
|
||||
|
||||
// collect border halfedges for the creation of the triangle strip
|
||||
std::vector<std::pair<input_vertex_descriptor, output_vertex_descriptor> > top_v2v;
|
||||
std::vector<std::pair<input_halfedge_descriptor, output_halfedge_descriptor> > top_h2h;
|
||||
copy_face_graph(input, output, std::inserter(top_v2v, top_v2v.end()),
|
||||
std::inserter(top_h2h, top_h2h.end()), Emptyset_iterator(),
|
||||
input_vpm, output_vpm);
|
||||
for(std::size_t i = 0; i< top_v2v.size(); ++i)
|
||||
{
|
||||
top(top_v2v[i].first, top_v2v[i].second);
|
||||
}
|
||||
std::vector<output_halfedge_descriptor> border_hedges;
|
||||
std::vector<output_halfedge_descriptor> offset_border_hedges;
|
||||
for(std::size_t i = 0; i< top_h2h.size(); ++i)
|
||||
{
|
||||
input_halfedge_descriptor h = top_h2h[i].first;
|
||||
if( CGAL::is_border(h, input) )
|
||||
{
|
||||
border_hedges.push_back(top_h2h[i].second);
|
||||
offset_border_hedges.push_back(bottom_h2h[i].second);
|
||||
CGAL_assertion(is_border(border_hedges.back(), output));
|
||||
CGAL_assertion(is_border(offset_border_hedges.back(), output));
|
||||
}
|
||||
}
|
||||
// now create a triangle strip
|
||||
extrude_impl::create_strip(border_hedges, offset_border_hedges, output);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \ingroup PMP_meshing_grp
|
||||
* fills `output` with a closed mesh bounding the volume swept by `input` when translating its
|
||||
* vertices by `v`. The mesh is oriented so that the faces corresponding to `input`
|
||||
* in `output` have the same orientation.
|
||||
* \attention `output` may be self intersecting.
|
||||
* @tparam InputMesh a model of the concept `FaceListGraph`
|
||||
* @tparam OutputMesh a model of the concept `FaceListGraph` and `MutableFaceGraph`
|
||||
* @tparam Vector_3 vector type from the same CGAL kernel as the point of the vertex point map used for `OutputMesh`.
|
||||
* @tparam NamedParameters1 a sequence of \ref pmp_namedparameters "Named Parameters" for `InputMesh`
|
||||
* @tparam NamedParameters2 a sequence of \ref pmp_namedparameters "Named Parameters" for `OutputMesh`
|
||||
* @param input an open surface mesh to extrude.
|
||||
* @param output a surface mesh that will contain the result of the extrusion.
|
||||
* @param v the vector defining the direction of the extrusion
|
||||
* @param np_in an optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamBegin{vertex_point_map}
|
||||
* the property map that contains the points associated to the vertices of `input`.
|
||||
* If this parameter is omitted, an internal property map for `CGAL::vertex_point_t`
|
||||
* should be available for the vertices of `input` \cgalParamEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* * @param np_out an optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamBegin{vertex_point_map}
|
||||
* the property map that will contain the points associated to the vertices of `output`.
|
||||
* If this parameter is omitted, an internal property map for `CGAL::vertex_point_t`
|
||||
* should be available for the vertices of `output` \cgalParamEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*/
|
||||
template <class InputMesh,
|
||||
class OutputMesh,
|
||||
class NamedParameters1,
|
||||
class NamedParameters2>
|
||||
void extrude_mesh(const InputMesh& input,
|
||||
OutputMesh& output,
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
Vector_3 v,
|
||||
#else
|
||||
typename GetGeomTraits<OutputMesh, NamedParameters2>::type::Vector_3 v,
|
||||
#endif
|
||||
const NamedParameters1& np_in,
|
||||
const NamedParameters2& np_out)
|
||||
{
|
||||
typedef typename GetVertexPointMap < OutputMesh, NamedParameters2>::type VPMap;
|
||||
VPMap output_vpm = choose_param(get_param(np_out, internal_np::vertex_point),
|
||||
get_property_map(vertex_point, output));
|
||||
|
||||
extrude_impl::Const_dist_translation<
|
||||
typename GetVertexPointMap<OutputMesh, NamedParameters2>::type,
|
||||
typename GetGeomTraits<OutputMesh, NamedParameters2>::type::Vector_3> bot(output_vpm,
|
||||
v);
|
||||
extrude_impl::Identity_functor top;
|
||||
extrude_mesh(input, output, bot,top, np_in, np_out);
|
||||
}
|
||||
//convenience overload
|
||||
template <class InputMesh,
|
||||
class OutputMesh,
|
||||
typename Vector>
|
||||
void extrude_mesh(const InputMesh& input,
|
||||
OutputMesh& output,
|
||||
Vector dir)
|
||||
{
|
||||
extrude_mesh(input, output, dir,
|
||||
parameters::all_default(),
|
||||
parameters::all_default());
|
||||
}
|
||||
|
||||
template <class InputMesh,
|
||||
class OutputMesh,
|
||||
typename Vector,
|
||||
typename CGAL_PMP_NP_TEMPLATE_PARAMETERS>
|
||||
void extrude_mesh(const InputMesh& input,
|
||||
OutputMesh& output,
|
||||
Vector dir,
|
||||
const CGAL_PMP_NP_CLASS& np)
|
||||
{
|
||||
extrude_mesh(input, output, dir,
|
||||
np,
|
||||
parameters::all_default());
|
||||
}
|
||||
|
||||
template <class InputMesh,
|
||||
class OutputMesh,
|
||||
class BottomFunctor,
|
||||
class TopFunctor>
|
||||
void extrude_mesh(const InputMesh& input,
|
||||
OutputMesh& output,
|
||||
const BottomFunctor& bot,
|
||||
const TopFunctor& top)
|
||||
{
|
||||
extrude_mesh(input, output, bot, top,
|
||||
parameters::all_default(), parameters::all_default());
|
||||
}
|
||||
|
||||
}} //end CGAL::PMP
|
||||
#endif //CGAL_POLYGON_MESH_PROCESSING_EXTRUDE_H
|
||||
|
|
@ -44,6 +44,11 @@
|
|||
#include <CGAL/Polygon_mesh_processing/remesh.h>
|
||||
#include <CGAL/Polygon_mesh_processing/corefinement.h>
|
||||
#include <CGAL/Polygon_mesh_processing/detect_features.h>
|
||||
#include <CGAL/Polygon_mesh_processing/extrude.h>
|
||||
#include <CGAL/Polygon_mesh_processing/random_perturbation.h>
|
||||
#include <CGAL/Polygon_mesh_processing/distance.h>
|
||||
#include <CGAL/Polygon_mesh_processing/intersection.h>
|
||||
#include <CGAL/Polygon_mesh_processing/transform.h>
|
||||
|
||||
// the named parameter header being not documented the doc is put here for now
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ endif()
|
|||
create_single_source_cgal_program("surface_intersection_sm_poly.cpp" )
|
||||
create_single_source_cgal_program("test_orient_cc.cpp")
|
||||
create_single_source_cgal_program("test_pmp_transform.cpp")
|
||||
create_single_source_cgal_program("extrude_test.cpp")
|
||||
|
||||
if( TBB_FOUND )
|
||||
CGAL_target_use_TBB(test_pmp_distance)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
OFF
|
||||
4 2 0
|
||||
-0.5 -0.5 0
|
||||
0.5 -0.5 0
|
||||
0.5 0.5 0
|
||||
-0.5 0.5 0
|
||||
|
||||
3 0 1 2
|
||||
3 0 2 3
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
#include <CGAL/Surface_mesh.h>
|
||||
#include <CGAL/Polyhedron_3.h>
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
#include <CGAL/Polygon_mesh_processing/extrude.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
|
||||
typedef CGAL::Surface_mesh<Kernel::Point_3> SMesh;
|
||||
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
|
||||
|
||||
template<typename MAP>
|
||||
struct Bot
|
||||
{
|
||||
Bot(MAP map):map(map){}
|
||||
template<typename VD, typename T>
|
||||
void operator()(const T&,VD vd) const
|
||||
{
|
||||
put(map, vd, get(map, vd)+Kernel::Vector_3(-2.0,0.0,1.0));
|
||||
}
|
||||
MAP map;
|
||||
|
||||
};
|
||||
|
||||
template<typename MAP>
|
||||
struct Top
|
||||
{
|
||||
Top(MAP map):map(map){}
|
||||
|
||||
template<typename VD, typename T>
|
||||
void operator()(const T&, VD vd) const
|
||||
{
|
||||
put(map, vd, get(map, vd)+Kernel::Vector_3(0.0,2.0,-1.0));
|
||||
}
|
||||
|
||||
MAP map;
|
||||
};
|
||||
|
||||
template <class Mesh>
|
||||
void test_mesh(const char* filename)
|
||||
{
|
||||
Mesh in, out;
|
||||
std::ifstream input(filename);
|
||||
|
||||
if (!input || !(input >> in))
|
||||
{
|
||||
std::cerr << "Error: cannot read Surface Mesh : " << filename << "\n";
|
||||
assert(!CGAL::is_empty(in));
|
||||
assert(false);
|
||||
return ;
|
||||
}
|
||||
CGAL::Polygon_mesh_processing::extrude_mesh(in, out, Kernel::Vector_3(0.0, 0.0, -1.0));
|
||||
std::ofstream extruded_off("extruded.off");
|
||||
extruded_off << out;
|
||||
extruded_off.close();
|
||||
out.clear();
|
||||
|
||||
typedef typename boost::property_map<Mesh, CGAL::vertex_point_t>::type VPMap;
|
||||
Bot<VPMap> bot(get(CGAL::vertex_point, out));
|
||||
Top<VPMap> top(get(CGAL::vertex_point, out));
|
||||
CGAL::Polygon_mesh_processing::extrude_mesh(in, out, bot, top);
|
||||
std::ofstream gen_extruded_off("gen_extruded.off");
|
||||
gen_extruded_off << out;
|
||||
gen_extruded_off.close();
|
||||
std::cerr << "All done." << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
const char* filename = (argc > 1) ? argv[1] : "data/quad.off";
|
||||
test_mesh<SMesh>(filename);
|
||||
test_mesh<Polyhedron>(filename);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -163,3 +163,10 @@ polyhedron_demo_plugin(degenerated_faces_sm_plugin Degenerated_faces_plugin)
|
|||
target_link_libraries(degenerated_faces_sm_plugin PUBLIC scene_surface_mesh_item scene_surface_mesh_selection_item)
|
||||
target_compile_definitions(degenerated_faces_sm_plugin PUBLIC "-DUSE_SURFACE_MESH" )
|
||||
|
||||
|
||||
polyhedron_demo_plugin(extrude_poly_plugin Extrude_plugin)
|
||||
target_link_libraries(extrude_poly_plugin PUBLIC scene_polyhedron_item scene_polyhedron_selection_item)
|
||||
|
||||
polyhedron_demo_plugin(extrude_sm_plugin Extrude_plugin)
|
||||
target_link_libraries(extrude_sm_plugin PUBLIC scene_surface_mesh_item scene_surface_mesh_selection_item)
|
||||
target_compile_definitions(extrude_sm_plugin PUBLIC "-DUSE_SURFACE_MESH" )
|
||||
|
|
|
|||
|
|
@ -0,0 +1,478 @@
|
|||
#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
|
||||
#include <QApplication>
|
||||
#include <QObject>
|
||||
#include <QAction>
|
||||
#include <QMainWindow>
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <CGAL/Three/Scene_item.h>
|
||||
#include <CGAL/Three/Viewer_interface.h>
|
||||
|
||||
#include <CGAL/linear_least_squares_fitting_3.h>
|
||||
#include <CGAL/Polygon_mesh_processing/extrude.h>
|
||||
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
|
||||
|
||||
#include <CGAL/Qt/manipulatedFrame.h>
|
||||
#include <CGAL/Qt/constraint.h>
|
||||
|
||||
#include <CGAL/number_type_config.h>
|
||||
|
||||
#include "Messages_interface.h"
|
||||
#ifdef USE_SURFACE_MESH
|
||||
#include "Kernel_type.h"
|
||||
#include "Scene_surface_mesh_item.h"
|
||||
#else
|
||||
#include "Scene_polyhedron_item.h"
|
||||
#include "Polyhedron_type.h"
|
||||
#endif
|
||||
#include "Scene_polyhedron_selection_item.h"
|
||||
#include "Scene.h"
|
||||
#ifdef USE_SURFACE_MESH
|
||||
typedef Scene_surface_mesh_item Scene_face_graph_item;
|
||||
#else
|
||||
typedef Scene_polyhedron_item Scene_face_graph_item;
|
||||
#endif
|
||||
|
||||
typedef Scene_face_graph_item::Face_graph Face_graph;
|
||||
typedef CGAL::qglviewer::Vec Vec;
|
||||
using namespace CGAL::Three;
|
||||
//use frame to get dist and dir.
|
||||
//fix frame in translation and use mousewheel to choose dist
|
||||
//finding frame's position : first try at the center of the item's bbox
|
||||
//maybe find intersection between surface and diag bbox.
|
||||
//orientation : PCA : normal.
|
||||
typedef Kernel::Plane_3 Plane;
|
||||
typedef Kernel::Triangle_3 Triangle;
|
||||
typedef Kernel::Point_3 Point;
|
||||
typedef Kernel::Vector_3 Vector;
|
||||
class Scene_arrow_item : public Scene_item
|
||||
{
|
||||
Q_OBJECT
|
||||
public :
|
||||
Scene_arrow_item(Vec center_, double r, double length_)
|
||||
:Scene_item(2, 1), center_(center_), length_(length_), R(r),
|
||||
frame(new Scene_item::ManipulatedFrame())
|
||||
{
|
||||
const CGAL::qglviewer::Vec offset = static_cast<Viewer_interface*>(CGAL::QGLViewer::QGLViewerPool().first())->offset();
|
||||
frame->setPosition( center_+offset);
|
||||
nb_pos = 0;
|
||||
tick = length_/10.0f;
|
||||
ctrl_pressing = false;
|
||||
}
|
||||
// Indicates if rendering mode is supported
|
||||
bool supportsRenderingMode(RenderingMode m) const Q_DECL_OVERRIDE {
|
||||
return (m == Gouraud);
|
||||
}
|
||||
//Displays the item
|
||||
void draw(Viewer_interface* viewer) const Q_DECL_OVERRIDE
|
||||
{
|
||||
if(!are_buffers_filled)
|
||||
{
|
||||
initializeBuffers(viewer);
|
||||
}
|
||||
vaos[0]->bind();
|
||||
program = getShaderProgram(PROGRAM_WITH_LIGHT);
|
||||
GLdouble d_mat[16];
|
||||
QMatrix4x4 mvp_mat;
|
||||
GLdouble matrix[16];
|
||||
QMatrix4x4 f_matrix;
|
||||
frame->getMatrix(matrix);
|
||||
for (int i=0; i<16; ++i)
|
||||
f_matrix.data()[i] = (float)matrix[i];
|
||||
viewer->camera()->getModelViewProjectionMatrix(d_mat);
|
||||
for (int i=0; i<16; ++i)
|
||||
mvp_mat.data()[i] = GLfloat(d_mat[i]);
|
||||
mvp_mat = mvp_mat*f_matrix;
|
||||
QMatrix4x4 mv_mat;
|
||||
viewer->camera()->getModelViewMatrix(d_mat);
|
||||
for (int i=0; i<16; ++i)
|
||||
mv_mat.data()[i] = GLfloat(d_mat[i]);
|
||||
mv_mat = mv_mat*f_matrix;
|
||||
attribBuffers(viewer, PROGRAM_WITH_LIGHT);
|
||||
program->bind();
|
||||
program->setUniformValue("mvp_matrix", mvp_mat);
|
||||
program->setUniformValue("mv_matrix", mv_mat);
|
||||
program->setUniformValue("is_clipbox_on", false);
|
||||
program->setAttributeValue("radius",0.01*diagonalBbox());
|
||||
program->setAttributeValue("colors", QColor(Qt::red));
|
||||
program->setAttributeValue("colors", this->color());
|
||||
viewer->glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(nb_pos/3));
|
||||
vaos[0]->release();
|
||||
program->release();
|
||||
}
|
||||
void invalidateOpenGLBuffers() Q_DECL_OVERRIDE
|
||||
{
|
||||
are_buffers_filled = false;
|
||||
}
|
||||
Scene_item* clone() const Q_DECL_OVERRIDE {return 0;}
|
||||
QString toolTip() const Q_DECL_OVERRIDE {return QString();}
|
||||
Vec center()const { return center_; }
|
||||
Scene_item::ManipulatedFrame* manipulatedFrame() Q_DECL_OVERRIDE { return frame; }
|
||||
bool manipulatable() const Q_DECL_OVERRIDE { return true; }
|
||||
bool eventFilter(QObject *, QEvent *event) Q_DECL_OVERRIDE
|
||||
{
|
||||
if(event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
|
||||
{
|
||||
ctrl_pressing = static_cast<QKeyEvent*>(event)->modifiers().testFlag(Qt::ControlModifier);
|
||||
}
|
||||
if(event->type() == QEvent::Wheel && ctrl_pressing)
|
||||
{
|
||||
QWheelEvent *mouseEvent = static_cast<QWheelEvent*>(event);
|
||||
int steps = mouseEvent->delta() / 120;
|
||||
if (steps > 0)
|
||||
length_+=tick;
|
||||
else
|
||||
length_-=tick;
|
||||
invalidateOpenGLBuffers();
|
||||
redraw();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
double length()const { return length_; }
|
||||
private:
|
||||
//make an arrow showing the length and direction of the transformation for the extrusion.
|
||||
void initializeBuffers(Viewer_interface *viewer)const
|
||||
{
|
||||
std::vector<float> vertices;
|
||||
std::vector<float> normals;
|
||||
int prec = 60;
|
||||
//Head
|
||||
const float Rf = static_cast<float>(R);
|
||||
for(int d = 0; d<360; d+= 360/prec)
|
||||
{
|
||||
float D = (float) (d * CGAL_PI / 180.);
|
||||
float a = (float) std::atan(Rf / 0.33);
|
||||
QVector4D pR(0., 1.*length_, 0, 1.);
|
||||
QVector4D nR(Rf*2.*sin(D), sin(a), Rf*2.*cos(D), 1.);
|
||||
|
||||
//point A1
|
||||
vertices.push_back(pR.x());
|
||||
vertices.push_back(pR.y());
|
||||
vertices.push_back(pR.z());
|
||||
normals.push_back(nR.x());
|
||||
normals.push_back(nR.y());
|
||||
normals.push_back(nR.z());
|
||||
|
||||
//point B1
|
||||
pR = QVector4D(Rf*2.*sin(D), 0.66f*length_, Rf*2.* cos(D), 1.f);
|
||||
nR = QVector4D(sin(D), sin(a), cos(D), 1.);
|
||||
vertices.push_back(pR.x());
|
||||
vertices.push_back(pR.y());
|
||||
vertices.push_back(pR.z());
|
||||
normals.push_back(nR.x());
|
||||
normals.push_back(nR.y());
|
||||
normals.push_back(nR.z());
|
||||
//point C1
|
||||
D = (d+360/prec)*CGAL_PI/180.0;
|
||||
pR = QVector4D(Rf*2.* sin(D), 0.66f*length_, Rf *2.* cos(D), 1.f);
|
||||
nR = QVector4D(sin(D), sin(a), cos(D), 1.0);
|
||||
|
||||
vertices.push_back(pR.x());
|
||||
vertices.push_back(pR.y());
|
||||
vertices.push_back(pR.z());
|
||||
normals.push_back(nR.x());
|
||||
normals.push_back(nR.y());
|
||||
normals.push_back(nR.z());
|
||||
}
|
||||
|
||||
//cylinder
|
||||
//body of the cylinder
|
||||
for(int d = 0; d<360; d+= 360/prec)
|
||||
{
|
||||
//point A1
|
||||
double D = d*CGAL_PI/180.0;
|
||||
QVector4D pR(Rf*sin(D), 0.66f*length_, Rf*cos(D), 1.f);
|
||||
QVector4D nR(sin(D), 0.f, cos(D), 1.f);
|
||||
|
||||
vertices.push_back(pR.x());
|
||||
vertices.push_back(pR.y());
|
||||
vertices.push_back(pR.z());
|
||||
normals.push_back(nR.x());
|
||||
normals.push_back(nR.y());
|
||||
normals.push_back(nR.z());
|
||||
//point B1
|
||||
pR = QVector4D(Rf * sin(D),0,Rf*cos(D), 1.0);
|
||||
nR = QVector4D(sin(D), 0, cos(D), 1.0);
|
||||
|
||||
vertices.push_back(pR.x());
|
||||
vertices.push_back(pR.y());
|
||||
vertices.push_back(pR.z());
|
||||
normals.push_back(nR.x());
|
||||
normals.push_back(nR.y());
|
||||
normals.push_back(nR.z());
|
||||
//point C1
|
||||
D = (d+360/prec)*CGAL_PI/180.0;
|
||||
pR = QVector4D(Rf * sin(D),0,Rf*cos(D), 1.0);
|
||||
nR = QVector4D(sin(D), 0, cos(D), 1.0);
|
||||
vertices.push_back(pR.x());
|
||||
vertices.push_back(pR.y());
|
||||
vertices.push_back(pR.z());
|
||||
normals.push_back(nR.x());
|
||||
normals.push_back(nR.y());
|
||||
normals.push_back(nR.z());
|
||||
//point A2
|
||||
D = (d+360/prec)*CGAL_PI/180.0;
|
||||
|
||||
pR = QVector4D(Rf * sin(D),0,Rf*cos(D), 1.0);
|
||||
nR = QVector4D(sin(D), 0, cos(D), 1.0);
|
||||
|
||||
vertices.push_back(pR.x());
|
||||
vertices.push_back(pR.y());
|
||||
vertices.push_back(pR.z());
|
||||
normals.push_back(nR.x());
|
||||
normals.push_back(nR.y());
|
||||
normals.push_back(nR.z());
|
||||
//point B2
|
||||
pR = QVector4D(Rf * sin(D), 0.66f*length_, Rf*cos(D), 1.f);
|
||||
nR = QVector4D(sin(D), 0, cos(D), 1.0);
|
||||
|
||||
vertices.push_back(pR.x());
|
||||
vertices.push_back(pR.y());
|
||||
vertices.push_back(pR.z());
|
||||
normals.push_back(nR.x());
|
||||
normals.push_back(nR.y());
|
||||
normals.push_back(nR.z());
|
||||
//point C2
|
||||
D = d*CGAL_PI/180.0;
|
||||
pR = QVector4D(Rf * sin(D), 0.66f*length_, Rf*cos(D), 1.f);
|
||||
nR = QVector4D(sin(D), 0.f, cos(D), 1.f);
|
||||
|
||||
vertices.push_back(pR.x());
|
||||
vertices.push_back(pR.y());
|
||||
vertices.push_back(pR.z());
|
||||
normals.push_back(nR.x());
|
||||
normals.push_back(nR.y());
|
||||
normals.push_back(nR.z());
|
||||
}
|
||||
|
||||
//fill buffers
|
||||
//vao containing the data for the facets
|
||||
program = getShaderProgram(PROGRAM_WITH_LIGHT, viewer);
|
||||
program->bind();
|
||||
vaos[0]->bind();
|
||||
buffers[0].bind();
|
||||
buffers[0].allocate(vertices.data(),
|
||||
static_cast<GLsizei>(vertices.size()*sizeof(float)));
|
||||
program->enableAttributeArray("vertex");
|
||||
program->setAttributeBuffer("vertex",GL_FLOAT,0,3);
|
||||
buffers[0].release();
|
||||
buffers[1].bind();
|
||||
buffers[1].allocate(normals.data(),
|
||||
static_cast<GLsizei>(normals.size()*sizeof(float)));
|
||||
program->enableAttributeArray("normals");
|
||||
program->setAttributeBuffer("normals",GL_FLOAT,0,3);
|
||||
buffers[1].release();
|
||||
vaos[0]->release();
|
||||
program->release();
|
||||
//once the buffers are filled, we can empty the vectors to optimize memory consumption
|
||||
nb_pos = vertices.size();
|
||||
_bbox = Bbox(0,0,0,0,0,0);
|
||||
for(std::size_t i = 0; i< vertices.size(); i+=3)
|
||||
{
|
||||
_bbox += Point(vertices[i],
|
||||
vertices[i+1],
|
||||
vertices[i+2]).bbox();
|
||||
}
|
||||
|
||||
are_buffers_filled = true;
|
||||
}
|
||||
|
||||
mutable std::size_t nb_pos;
|
||||
Vec center_;
|
||||
double length_;
|
||||
double tick;
|
||||
double R;
|
||||
bool ctrl_pressing;
|
||||
mutable QOpenGLShaderProgram *program;
|
||||
Scene_item::ManipulatedFrame* frame;
|
||||
}; //end of class Scene_arrow_item
|
||||
|
||||
|
||||
template <typename TriangleMesh, typename OutputIterator>
|
||||
CGAL::Bbox_3 triangles(const TriangleMesh& mesh,
|
||||
OutputIterator out)
|
||||
{
|
||||
CGAL::Bbox_3 bb;
|
||||
typename boost::property_map<TriangleMesh,CGAL::vertex_point_t>::const_type vpm =
|
||||
get(CGAL::vertex_point, mesh);
|
||||
BOOST_FOREACH(typename boost::graph_traits<TriangleMesh>::face_descriptor fd, faces(mesh)){
|
||||
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor hd = halfedge(fd,mesh);
|
||||
Triangle t(get(vpm,source(hd,mesh)),
|
||||
get(vpm,target(hd,mesh)),
|
||||
get(vpm,target(next(hd,mesh),mesh)));
|
||||
*out++ = t;
|
||||
bb = bb + t.bbox();
|
||||
}
|
||||
return bb;
|
||||
}
|
||||
|
||||
Vector estimate_normals(const std::vector<Triangle>& tris)
|
||||
{
|
||||
Vector moy(0,0,0);
|
||||
|
||||
BOOST_FOREACH(const Triangle& tri, tris)
|
||||
{
|
||||
Vector norm = CGAL::Polygon_mesh_processing::internal::triangle_normal(
|
||||
tri[0], tri[1], tri[2], Kernel());
|
||||
norm /= CGAL::sqrt(norm.squared_length());
|
||||
moy += norm;
|
||||
}
|
||||
return moy;
|
||||
}
|
||||
|
||||
|
||||
class ExtrudePlugin :
|
||||
public QObject,
|
||||
public Polyhedron_demo_plugin_interface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
|
||||
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
|
||||
public:
|
||||
|
||||
bool applicable(QAction* action) const Q_DECL_OVERRIDE
|
||||
{
|
||||
if(action == actionCreateItem)
|
||||
{
|
||||
return !oliver_queen &&
|
||||
(qobject_cast<Scene_face_graph_item*>(scene->item(scene->mainSelectionIndex()))
|
||||
|| qobject_cast<Scene_polyhedron_selection_item*>(scene->item(scene->mainSelectionIndex())));
|
||||
}
|
||||
else if(oliver_queen)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
QList<QAction*> actions() const Q_DECL_OVERRIDE
|
||||
{
|
||||
return _actions;
|
||||
}
|
||||
|
||||
void init(QMainWindow* mainWindow, Scene_interface* sc, Messages_interface* mi) Q_DECL_OVERRIDE
|
||||
{
|
||||
this->messageInterface = mi;
|
||||
this->scene = sc;
|
||||
this->mw = mainWindow;
|
||||
oliver_queen = NULL;
|
||||
target = NULL;
|
||||
actionCreateItem = new QAction(QString("Extrude Item"), mw);
|
||||
actionCreateItem->setProperty("submenuName", "Polygon Mesh Processing");
|
||||
connect(actionCreateItem, SIGNAL(triggered()),
|
||||
this, SLOT(createItem()));
|
||||
_actions << actionCreateItem;
|
||||
actionExtrude = new QAction(QString("Perform Extrusion"), mw);
|
||||
actionExtrude->setProperty("submenuName", "Polygon Mesh Processing");
|
||||
connect(actionExtrude, SIGNAL(triggered()),
|
||||
this, SLOT(do_extrude()));
|
||||
_actions << actionExtrude;
|
||||
}
|
||||
private Q_SLOTS:
|
||||
void createItem()
|
||||
{
|
||||
Scene_item * item = scene->item(scene->mainSelectionIndex());
|
||||
Scene_polyhedron_selection_item* sel_item = qobject_cast<Scene_polyhedron_selection_item*>(scene->item(scene->mainSelectionIndex()));
|
||||
Scene_face_graph_item* fg_item = qobject_cast<Scene_face_graph_item*>(item);
|
||||
Face_graph* pMesh = NULL;
|
||||
if(sel_item)
|
||||
{
|
||||
pMesh = new Face_graph();
|
||||
if(!sel_item->export_selected_facets_as_polyhedron(pMesh))
|
||||
{
|
||||
messageInterface->error("Face selection is not valid. Aborting.");
|
||||
|
||||
return;
|
||||
}
|
||||
fg_item = new Scene_facegraph_item(pMesh);
|
||||
fg_item->setName(QString("%1 selection").arg(sel_item->polyhedron_item()->name()));
|
||||
scene->addItem(fg_item);
|
||||
sel_item->polyhedron_item()->setWireframeMode();
|
||||
sel_item->polyhedron_item()->redraw();
|
||||
}
|
||||
if(fg_item)
|
||||
pMesh = fg_item->face_graph();
|
||||
else
|
||||
return;
|
||||
if(CGAL::is_closed(*pMesh))
|
||||
{
|
||||
messageInterface->error("The face graph must be open. Aborting.");
|
||||
return;
|
||||
}
|
||||
std::vector<Triangle> triangles;
|
||||
::triangles(*pMesh,std::back_inserter(triangles));
|
||||
Plane plane;
|
||||
CGAL::linear_least_squares_fitting_3(triangles.begin(),triangles.end(),plane,CGAL::Dimension_tag<2>());
|
||||
|
||||
// compute centroid
|
||||
Point c = CGAL::centroid(triangles.begin(),triangles.end());
|
||||
|
||||
oliver_queen = new Scene_arrow_item(Vec(c.x(),c.y(),c.z()), fg_item->diagonalBbox() / 50.0f,
|
||||
fg_item->diagonalBbox()/3.0f);
|
||||
Vec dir(plane.orthogonal_vector().x(),
|
||||
plane.orthogonal_vector().y(),
|
||||
plane.orthogonal_vector().z());
|
||||
if(CGAL::scalar_product(Vector(dir.x, dir.y, dir.z), estimate_normals(triangles)) > 0)
|
||||
dir = -dir;
|
||||
|
||||
CGAL::qglviewer::Quaternion orientation(CGAL::qglviewer::Vec(0,1,0), dir);
|
||||
oliver_queen->manipulatedFrame()->setOrientation(orientation);
|
||||
constraint.setRotationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE);
|
||||
constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FORBIDDEN);
|
||||
oliver_queen->manipulatedFrame()->setConstraint(&constraint);
|
||||
oliver_queen->setColor(QColor(Qt::green));
|
||||
oliver_queen->setName("Extrude item");
|
||||
CGAL::QGLViewer* viewer = *CGAL::QGLViewer::QGLViewerPool().begin();
|
||||
viewer->installEventFilter(oliver_queen);
|
||||
mw->installEventFilter(oliver_queen);
|
||||
scene->addItem(oliver_queen);
|
||||
target = fg_item;
|
||||
|
||||
connect(oliver_queen, &Scene_arrow_item::aboutToBeDestroyed,
|
||||
[this](){
|
||||
oliver_queen = NULL;
|
||||
});
|
||||
|
||||
//!@todo : add a way to track scene's bbox recomputation and reset frame's position when triggered.
|
||||
}
|
||||
void do_extrude()
|
||||
{
|
||||
if(!target)
|
||||
return;
|
||||
Face_graph pMesh = *target->face_graph();
|
||||
target->face_graph()->clear();
|
||||
double length = oliver_queen->length();
|
||||
double matrix[16];
|
||||
oliver_queen->manipulatedFrame()->getMatrix(matrix);
|
||||
QMatrix4x4 rotate_matrix;
|
||||
QMatrix4x4 transform_matrix;
|
||||
for(int i=0; i<16; ++i)
|
||||
transform_matrix.data()[i] = (float)matrix[i];
|
||||
rotate_matrix = transform_matrix;
|
||||
rotate_matrix.setColumn(3, QVector4D(0,0,0,1));
|
||||
QVector3D dir = rotate_matrix * QVector3D(0,1,0);
|
||||
dir.normalize();
|
||||
dir = length * dir;
|
||||
|
||||
CGAL::Polygon_mesh_processing::extrude_mesh(pMesh, *target->face_graph(),
|
||||
Kernel::Vector_3(dir.x(), dir.y(), dir.z()));
|
||||
scene->erase(scene->item_id(oliver_queen));
|
||||
oliver_queen = NULL;
|
||||
target->invalidateOpenGLBuffers();
|
||||
target->itemChanged();
|
||||
target = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
QList<QAction*> _actions;
|
||||
Messages_interface* messageInterface;
|
||||
Scene_interface* scene;
|
||||
QMainWindow* mw;
|
||||
QAction *actionCreateItem;
|
||||
QAction *actionExtrude;
|
||||
Scene_arrow_item* oliver_queen;
|
||||
Scene_face_graph_item* target;
|
||||
CGAL::qglviewer::LocalConstraint constraint;
|
||||
};
|
||||
#include "Extrude_plugin.moc"
|
||||
|
|
@ -140,6 +140,8 @@ distance_plugin \
|
|||
distance_sm_plugin \
|
||||
edit_polyhedron_plugin \
|
||||
edit_sm_plugin \
|
||||
extrude_poly_plugin \
|
||||
extrude_sm_plugin \
|
||||
fairing_plugin \
|
||||
features_detection_plugin \
|
||||
gocad_plugin \
|
||||
|
|
|
|||
Loading…
Reference in New Issue