diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index 5060fc94b21..05abaaa3e82 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -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 diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt index 92f4874bca2..3baa705dad3 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt @@ -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()` diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/extrude.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/extrude.h new file mode 100644 index 00000000000..aecf3c39b92 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/extrude.h @@ -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 + + +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Polygon_mesh_processing { +namespace extrude_impl{ + +template +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::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::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 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 +struct Const_dist_translation{ + Const_dist_translation(PMAP map, const Vector& dir) + :map(map), dir(dir){} + + template + void operator()(const VertexDescriptor vd, const U&) const + { + typename boost::property_traits::value_type p = get(map, vd) + dir; + put(map, vd, p); + } + + PMAP map; + Vector dir; +}; + +struct Identity_functor +{ + template + 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::vertex_descriptor input_v,boost::graph_traits::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 +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::vertex_descriptor input_vertex_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor input_halfedge_descriptor; + + typedef typename boost::graph_traits::vertex_descriptor output_vertex_descriptor; + typedef typename boost::graph_traits::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 > bottom_v2v; + std::vector > 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 > top_v2v; + std::vector > 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 border_hedges; + std::vector 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 +void extrude_mesh(const InputMesh& input, + OutputMesh& output, + #ifdef DOXYGEN_RUNNING + Vector_3 v, + #else + typename GetGeomTraits::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::type, + typename GetGeomTraits::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 +void extrude_mesh(const InputMesh& input, + OutputMesh& output, + Vector dir) +{ + extrude_mesh(input, output, dir, + parameters::all_default(), + parameters::all_default()); +} + +template +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 +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 diff --git a/Polygon_mesh_processing/include/CGAL/polygon_mesh_processing.h b/Polygon_mesh_processing/include/CGAL/polygon_mesh_processing.h index 9379a5f472d..7d587ad4b84 100644 --- a/Polygon_mesh_processing/include/CGAL/polygon_mesh_processing.h +++ b/Polygon_mesh_processing/include/CGAL/polygon_mesh_processing.h @@ -44,6 +44,11 @@ #include #include #include +#include +#include +#include +#include +#include // the named parameter header being not documented the doc is put here for now #ifdef DOXYGEN_RUNNING diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index f7af9794708..6ffde721a7d 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -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) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/data/quad.off b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/quad.off new file mode 100644 index 00000000000..3e2d5bf595b --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/quad.off @@ -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 diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/extrude_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/extrude_test.cpp new file mode 100644 index 00000000000..bee7e23c253 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/extrude_test.cpp @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef CGAL::Surface_mesh SMesh; +typedef CGAL::Polyhedron_3 Polyhedron; + +template +struct Bot +{ + Bot(MAP map):map(map){} + template + 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 +struct Top +{ + Top(MAP map):map(map){} + + template + 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 +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::type VPMap; + Bot bot(get(CGAL::vertex_point, out)); + Top 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(filename); + test_mesh(filename); + return 0; +} diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt index 6970f0255c5..a626bc567d9 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt @@ -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" ) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Extrude_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Extrude_plugin.cpp new file mode 100644 index 00000000000..6f7bedf349f --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Extrude_plugin.cpp @@ -0,0 +1,478 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#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(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(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(event)->modifiers().testFlag(Qt::ControlModifier); + } + if(event->type() == QEvent::Wheel && ctrl_pressing) + { + QWheelEvent *mouseEvent = static_cast(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 vertices; + std::vector normals; + int prec = 60; + //Head + const float Rf = static_cast(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(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(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 +CGAL::Bbox_3 triangles(const TriangleMesh& mesh, + OutputIterator out) +{ + CGAL::Bbox_3 bb; + typename boost::property_map::const_type vpm = + get(CGAL::vertex_point, mesh); + BOOST_FOREACH(typename boost::graph_traits::face_descriptor fd, faces(mesh)){ + typename boost::graph_traits::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& 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->item(scene->mainSelectionIndex())) + || qobject_cast(scene->item(scene->mainSelectionIndex()))); + } + else if(oliver_queen) + return true; + return false; + } + + QList 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->item(scene->mainSelectionIndex())); + Scene_face_graph_item* fg_item = qobject_cast(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 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 _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" diff --git a/Polyhedron/demo/Polyhedron/cgal_test_with_cmake b/Polyhedron/demo/Polyhedron/cgal_test_with_cmake index 6bc5f895d86..740946b6ccc 100755 --- a/Polyhedron/demo/Polyhedron/cgal_test_with_cmake +++ b/Polyhedron/demo/Polyhedron/cgal_test_with_cmake @@ -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 \