mirror of https://github.com/CGAL/cgal
945 lines
29 KiB
C++
945 lines
29 KiB
C++
//General Plugin Data
|
|
#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
|
|
#include <CGAL/Three/Three.h>
|
|
|
|
#include "ui_Engrave_dock_widget.h"
|
|
//Items
|
|
#include "Scene_surface_mesh_item.h"
|
|
#include "Scene_polyhedron_selection_item.h"
|
|
#include "Scene_polylines_item.h"
|
|
#include "Messages_interface.h"
|
|
|
|
#include <CGAL/Surface_mesh_parameterization/Error_code.h>
|
|
#include <CGAL/surface_mesh_parameterization.h>
|
|
#include <CGAL/Polygon_mesh_processing/bbox.h>
|
|
#include <CGAL/Polygon_mesh_processing/extrude.h>
|
|
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
|
|
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
|
|
#include <CGAL/Polygon_mesh_processing/corefinement.h>
|
|
#include <CGAL/Polygon_mesh_processing/self_intersections.h>
|
|
#include <CGAL/Surface_mesh_shortest_path.h>
|
|
#include <CGAL/double.h>
|
|
#include <CGAL/AABB_tree.h>
|
|
#include <CGAL/AABB_traits.h>
|
|
#include <CGAL/AABB_face_graph_triangle_primitive.h>
|
|
#include <CGAL/Triangulation_face_base_with_info_2.h>
|
|
#include <CGAL/Projection_traits_xy_3.h>
|
|
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
|
|
|
|
#include <CGAL/linear_least_squares_fitting_2.h>
|
|
#include <CGAL/Polygon_mesh_processing/measure.h>
|
|
#include <CGAL/centroid.h>
|
|
|
|
#include <CGAL/boost/graph/Face_filtered_graph.h>
|
|
|
|
#include <QPainterPath>
|
|
#include <QGraphicsScene>
|
|
#include <QGraphicsItem>
|
|
#include <QDialog>
|
|
|
|
#include <CGAL/Qt/GraphicsViewNavigation.h>
|
|
|
|
using namespace CGAL::Three;
|
|
namespace SMP = CGAL::Surface_mesh_parameterization;
|
|
typedef EPICK::Point_2 Point_2;
|
|
typedef EPICK::Point_3 Point_3;
|
|
|
|
typedef boost::graph_traits<SMesh>::
|
|
edge_descriptor edge_descriptor;
|
|
typedef boost::graph_traits<SMesh>::
|
|
halfedge_descriptor halfedge_descriptor;
|
|
typedef boost::graph_traits<SMesh>::
|
|
vertex_descriptor vertex_descriptor;
|
|
|
|
typedef boost::unordered_set<boost::graph_traits<SMesh>::
|
|
face_descriptor> Component;
|
|
|
|
struct FaceInfo2
|
|
{
|
|
FaceInfo2(){}
|
|
int nesting_level;
|
|
bool in_domain(){
|
|
return nesting_level%2 == 1;
|
|
}
|
|
};
|
|
|
|
template<typename PMAP,
|
|
typename NMAP>
|
|
struct Bot
|
|
{
|
|
Bot(NMAP nmap,
|
|
double d,
|
|
PMAP pmap):d(d),
|
|
pmap(pmap),
|
|
nmap(nmap){}
|
|
template<typename VD, typename T>
|
|
void operator()(const T& v1,VD v2) const
|
|
{
|
|
put(pmap, v2, get(pmap, v2)-d*get(nmap, v1));
|
|
}
|
|
double d;
|
|
PMAP pmap;
|
|
NMAP nmap;
|
|
|
|
};
|
|
|
|
template<typename PMAP,
|
|
typename NMAP>
|
|
struct Top
|
|
{
|
|
Top(NMAP nmap,
|
|
PMAP pmap,
|
|
double d):d(d),
|
|
nmap(nmap),
|
|
pmap(pmap){}
|
|
|
|
template<typename VD, typename T>
|
|
void operator()(const T& v1, VD v2) const
|
|
{
|
|
put(pmap, v2, get(pmap, v2)+d*get(nmap, v1));
|
|
}
|
|
double d;
|
|
NMAP nmap;
|
|
PMAP pmap;
|
|
};
|
|
|
|
typedef EPICK Gt;
|
|
typedef CGAL::Triangulation_vertex_base_2<Gt> Vb;
|
|
typedef CGAL::Triangulation_face_base_with_info_2<FaceInfo2,Gt > Fbb;
|
|
typedef CGAL::Constrained_triangulation_face_base_2<Gt,Fbb> Fb;
|
|
typedef CGAL::Triangulation_data_structure_2<Vb, Fb> TDS;
|
|
typedef CGAL::No_intersection_tag Tag;
|
|
typedef CGAL::Constrained_Delaunay_triangulation_2<Gt, TDS, Tag> CDT;
|
|
|
|
//Parameterization and text displaying
|
|
class ParamItem : public QGraphicsItem
|
|
{
|
|
public :
|
|
ParamItem(Component* component,
|
|
const std::vector<std::vector<EPICK::Point_2> > &polylines,
|
|
EPICK::Aff_transformation_2 transfo,
|
|
SMesh* graph,
|
|
QRectF brect)
|
|
:
|
|
QGraphicsItem(),
|
|
bounding_rect(brect),
|
|
component(component),
|
|
polylines(polylines),
|
|
graph(graph),
|
|
transfo(transfo){}
|
|
|
|
~ParamItem()
|
|
{
|
|
delete component;
|
|
}
|
|
|
|
QRectF boundingRect() const
|
|
{
|
|
return bounding_rect;
|
|
}
|
|
|
|
void set_transfo(EPICK::Aff_transformation_2 t){ transfo = t;}
|
|
|
|
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
|
|
{
|
|
QPen pen;
|
|
QBrush brush;
|
|
brush.setColor(QColor(100, 100, 255));
|
|
brush.setStyle(Qt::SolidPattern);
|
|
pen.setColor(Qt::black);
|
|
pen.setWidth(0);
|
|
painter->setPen(pen);
|
|
painter->setBrush(brush);
|
|
SMesh::Property_map<halfedge_descriptor, float> u;
|
|
SMesh::Property_map<halfedge_descriptor, float> v;
|
|
|
|
u = graph->add_property_map<halfedge_descriptor, float>
|
|
("h:u", 0.0f).first;
|
|
v = graph->add_property_map<halfedge_descriptor, float>
|
|
("h:v", 0.0f).first;
|
|
|
|
for( Component::iterator
|
|
fi = component->begin();
|
|
fi != component->end();
|
|
++fi)
|
|
{
|
|
boost::graph_traits<SMesh>::face_descriptor f(*fi);
|
|
QPointF points[3];
|
|
boost::graph_traits<SMesh>::halfedge_descriptor h = halfedge(f, *graph);;
|
|
points[0] = QPointF(get(u, h), -get(v, h));
|
|
h = next(halfedge(f, *graph), *graph);
|
|
points[1] = QPointF(get(u, h), -get(v, h));
|
|
h = next(next(halfedge(f, *graph), *graph), *graph);
|
|
points[2] = QPointF(get(u, h), -get(v, h));
|
|
painter->drawPolygon(points,3);
|
|
}
|
|
|
|
pen.setColor(Qt::red);
|
|
pen.setWidth(0);
|
|
painter->setPen(pen);
|
|
for(std::size_t i =0; i<polylines.size(); ++i)
|
|
{
|
|
std::vector<QPointF> points;
|
|
points.reserve(polylines[i].size());
|
|
for(std::size_t j =0; j<polylines[i].size(); ++j)
|
|
{
|
|
Point_2 transfo_point = transfo.transform(polylines[i][j]);
|
|
points.push_back(QPointF(transfo_point.x(),
|
|
-transfo_point.y()));
|
|
}
|
|
painter->drawPolyline(points.data(), static_cast<int>(points.size()));
|
|
}
|
|
}
|
|
|
|
private:
|
|
QString texMesh_name;
|
|
QRectF bounding_rect;
|
|
Component* component;
|
|
const std::vector<std::vector<EPICK::Point_2> >& polylines;
|
|
|
|
SMesh* graph;
|
|
EPICK::Aff_transformation_2 transfo;
|
|
};
|
|
|
|
class Navigation : public CGAL::Qt::GraphicsViewNavigation
|
|
{
|
|
public:
|
|
Navigation()
|
|
:CGAL::Qt::GraphicsViewNavigation(),
|
|
prev_pos(QPoint(0,0))
|
|
{ }
|
|
|
|
protected:
|
|
bool eventFilter(QObject *obj, QEvent *ev)
|
|
{
|
|
QGraphicsView* v = qobject_cast<QGraphicsView*>(obj);
|
|
if(v == NULL) {
|
|
QWidget* viewport = qobject_cast<QWidget*>(obj);
|
|
if(viewport == NULL) {
|
|
return false;
|
|
}
|
|
v = qobject_cast<QGraphicsView*>(viewport->parent());
|
|
if(v == NULL) {
|
|
return false;
|
|
}
|
|
}
|
|
switch(ev->type())
|
|
{
|
|
case QEvent::MouseMove: {
|
|
QMouseEvent* me = static_cast<QMouseEvent*>(ev);
|
|
if(is_dragging)
|
|
{
|
|
qreal dir[2] = {v->mapToScene(me->pos()).x() - prev_pos.x(),
|
|
v->mapToScene(me->pos()).y() - prev_pos.y()};
|
|
|
|
v->translate(dir[0],dir[1]);
|
|
v->update();
|
|
}
|
|
prev_pos = v->mapToScene(me->pos());
|
|
break;
|
|
}
|
|
|
|
case QEvent::MouseButtonPress: {
|
|
is_dragging = true;
|
|
break;
|
|
}
|
|
case QEvent::MouseButtonRelease: {
|
|
is_dragging = false;
|
|
break;
|
|
}
|
|
case QEvent::Wheel: {
|
|
QWheelEvent* event = static_cast<QWheelEvent*>(ev);
|
|
QPointF old_pos = v->mapToScene(event->pos());
|
|
if(event->delta() <0)
|
|
v->scale(1.2, 1.2);
|
|
else
|
|
v->scale(0.8, 0.8);
|
|
QPointF new_pos = v->mapToScene(event->pos());
|
|
QPointF delta = new_pos - old_pos;
|
|
v->translate(delta.x(), delta.y());
|
|
v->update();
|
|
break;
|
|
}
|
|
|
|
case QEvent::MouseButtonDblClick: {
|
|
v->fitInView(v->scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
|
|
break;
|
|
}
|
|
default:
|
|
CGAL::Qt::GraphicsViewNavigation::eventFilter(obj, ev);
|
|
}
|
|
return false;
|
|
}
|
|
private:
|
|
bool is_dragging;
|
|
QPointF prev_pos;
|
|
};
|
|
|
|
|
|
class EngraveWidget :
|
|
public QDockWidget,
|
|
public Ui::EngraveWidget
|
|
{
|
|
public:
|
|
EngraveWidget(QString name, QWidget *parent)
|
|
:QDockWidget(name,parent)
|
|
{
|
|
setupUi(this);
|
|
}
|
|
};
|
|
|
|
class Q_DECL_EXPORT Engrave_text_plugin :
|
|
public QObject,
|
|
public Polyhedron_demo_plugin_helper
|
|
{
|
|
Q_OBJECT
|
|
Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
|
|
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
|
|
|
|
private:
|
|
typedef CGAL::Surface_mesh_shortest_path_traits<EPICK, SMesh> SP_traits;
|
|
typedef CGAL::Surface_mesh_shortest_path<SP_traits> Surface_mesh_shortest_path;
|
|
typedef Surface_mesh_shortest_path::Face_location Face_location;
|
|
typedef CGAL::AABB_face_graph_triangle_primitive<SMesh> Primitive;
|
|
typedef CGAL::AABB_traits<EPICK, Primitive> Tree_traits;
|
|
typedef CGAL::AABB_tree<Tree_traits> Tree;
|
|
typedef EPICK::Point_3 Point_3;
|
|
Messages_interface* messages;
|
|
|
|
public :
|
|
~Engrave_text_plugin()
|
|
{
|
|
delete graphics_scene;
|
|
delete navigation;
|
|
}
|
|
void init(QMainWindow*,
|
|
CGAL::Three::Scene_interface*,
|
|
Messages_interface* m) Q_DECL_OVERRIDE{
|
|
//get refs
|
|
this->scene = Three::scene();
|
|
this->mw = Three::mainWindow();
|
|
messages = m;
|
|
|
|
//action
|
|
QAction* actionFitText= new QAction("Fit Text", mw);
|
|
connect(actionFitText, SIGNAL(triggered()),
|
|
this, SLOT(showWidget()));
|
|
_actions << actionFitText;
|
|
//widget
|
|
dock_widget = new EngraveWidget("Engraving", mw);
|
|
dock_widget->setVisible(false); // do not show at the beginning
|
|
addDockWidget(dock_widget);
|
|
connect(dock_widget->visualizeButton, &QPushButton::clicked,
|
|
this, &Engrave_text_plugin::visualize);
|
|
connect(dock_widget->engraveButton, &QPushButton::clicked,
|
|
this, &Engrave_text_plugin::engrave);
|
|
connect(dock_widget->text_meshButton, &QPushButton::clicked,
|
|
this, &Engrave_text_plugin::generateTextItem);
|
|
connect(dock_widget->letter_checkBox, &QCheckBox::toggled,
|
|
this, &Engrave_text_plugin::generateTextItem);
|
|
|
|
//items
|
|
visu_item = nullptr;
|
|
sel_item = nullptr;
|
|
textMesh = nullptr;
|
|
sm = nullptr;
|
|
|
|
//transfo
|
|
angle = 0.0;
|
|
scalX=1.0;
|
|
scalY=1.0;
|
|
translation = EPICK::Vector_2(0,0);
|
|
pointsize = 15;
|
|
locked = false;
|
|
connect(dock_widget->text_prec_slider, &QSlider::valueChanged,
|
|
this, [this](){
|
|
pointsize = dock_widget->text_prec_slider->value();
|
|
scene->setSelectedItem(scene->item_id(sel_item));
|
|
visualize();
|
|
});
|
|
connect(dock_widget->scalX_slider, &QSlider::valueChanged,
|
|
this, [this](){
|
|
scalX = dock_widget->scalX_slider->value()/1000.0;
|
|
scene->setSelectedItem(scene->item_id(sel_item));
|
|
visualize();
|
|
});
|
|
connect(dock_widget->scalY_slider, &QSlider::valueChanged,
|
|
this, [this](){
|
|
scalY = dock_widget->scalY_slider->value()/1000.0;
|
|
scene->setSelectedItem(scene->item_id(sel_item));
|
|
visualize();
|
|
});
|
|
|
|
connect(dock_widget->bot_slider, &QSlider::valueChanged,
|
|
this, [this](){
|
|
if(textMesh)
|
|
generateTextItem();
|
|
});
|
|
|
|
connect(dock_widget->top_slider, &QSlider::valueChanged,
|
|
this, [this](){
|
|
if(textMesh)
|
|
generateTextItem();
|
|
});
|
|
|
|
connect(dock_widget->reset_button, &QPushButton::clicked,
|
|
this, [this](){
|
|
cleanup();
|
|
});
|
|
|
|
connect(dock_widget->t_up_pushButton, &QPushButton::clicked,
|
|
this, [this](){
|
|
translation += EPICK::Vector_2(0,0.005);
|
|
scene->setSelectedItem(scene->item_id(sel_item));
|
|
visualize();
|
|
});
|
|
|
|
connect(dock_widget->t_down_pushButton, &QPushButton::clicked,
|
|
this, [this](){
|
|
translation -= EPICK::Vector_2(0,0.005);
|
|
scene->setSelectedItem(scene->item_id(sel_item));
|
|
visualize();
|
|
});
|
|
|
|
connect(dock_widget->t_right_pushButton, &QPushButton::clicked,
|
|
this, [this](){
|
|
translation += EPICK::Vector_2(0.005,0);
|
|
scene->setSelectedItem(scene->item_id(sel_item));
|
|
visualize();
|
|
});
|
|
|
|
connect(dock_widget->t_left_pushButton, &QPushButton::clicked,
|
|
this, [this](){
|
|
translation -= EPICK::Vector_2(0.005,0);
|
|
scene->setSelectedItem(scene->item_id(sel_item));
|
|
visualize();
|
|
});
|
|
connect(dock_widget->rot_slider, &QSlider::valueChanged,
|
|
this, [this](){
|
|
if(!locked)
|
|
{
|
|
angle = dock_widget->rot_slider->value() * CGAL_PI/180.0;
|
|
scene->setSelectedItem(scene->item_id(sel_item));
|
|
visualize();
|
|
}
|
|
});
|
|
graphics_scene = new QGraphicsScene(dock_widget);
|
|
dock_widget->graphicsView->setScene(graphics_scene);
|
|
dock_widget->graphicsView->setRenderHints(QPainter::Antialiasing);
|
|
navigation = new Navigation();
|
|
dock_widget->graphicsView->installEventFilter(navigation);
|
|
dock_widget->graphicsView->viewport()->installEventFilter(navigation);
|
|
}
|
|
bool applicable(QAction*) const Q_DECL_OVERRIDE
|
|
{
|
|
return qobject_cast<Scene_polyhedron_selection_item*>
|
|
(scene->item(scene->mainSelectionIndex()));
|
|
}
|
|
QList<QAction*> actions() const Q_DECL_OVERRIDE{
|
|
return _actions;
|
|
}
|
|
public Q_SLOTS:
|
|
void showWidget()
|
|
{
|
|
dock_widget->setVisible(!dock_widget->isVisible());
|
|
}
|
|
|
|
void visualize() {
|
|
if(!sel_item)
|
|
sel_item =
|
|
qobject_cast<Scene_polyhedron_selection_item*>
|
|
(scene->item(scene->mainSelectionIndex()));
|
|
if(!sel_item)
|
|
return;
|
|
if(sel_item->selected_facets.empty())
|
|
{
|
|
cleanup();
|
|
return;
|
|
}
|
|
if(!CGAL::is_closed(*sel_item->polyhedron()))
|
|
{
|
|
cleanup();
|
|
return;
|
|
}
|
|
if(visu_item)
|
|
scene->erase(scene->item_id(visu_item));
|
|
visu_item = nullptr;
|
|
|
|
if(!sm)
|
|
{
|
|
sm = new SMesh();
|
|
sel_item->export_selected_facets_as_polyhedron(sm);
|
|
SMesh::Halfedge_index hd =
|
|
CGAL::Polygon_mesh_processing::longest_border(*sm).first;
|
|
SMesh::Property_map<SMesh::Vertex_index, EPICK::Point_2> uv_map =
|
|
sm->add_property_map<SMesh::Vertex_index, EPICK::Point_2>("v:uv").first;
|
|
|
|
// Parameterized bool pmap
|
|
boost::unordered_set<SMesh::Vertex_index> vs;
|
|
SMP::internal::Bool_property_map< boost::unordered_set<SMesh::Vertex_index> > vpm(vs);
|
|
|
|
// Parameterizer
|
|
SMP::ARAP_parameterizer_3<SMesh> parameterizer;
|
|
|
|
SMP::Error_code status = parameterizer.parameterize(*sm, hd, uv_map,
|
|
get(boost::vertex_index, *sm), vpm);
|
|
if(status != SMP::OK) {
|
|
std::cout << "Encountered a problem: " << status << std::endl;
|
|
cleanup();
|
|
return ;
|
|
}
|
|
|
|
std::cout << "Parameterized with ARAP (SM) computed." << std::endl;
|
|
xmin = std::numeric_limits<double>::max();
|
|
xmax = std::numeric_limits<double>::min();
|
|
ymin = std::numeric_limits<double>::max();
|
|
ymax = std::numeric_limits<double>::min();
|
|
uv_map_3 =
|
|
sm->add_property_map<SMesh::Vertex_index, Point_3>("v:uv3").first;
|
|
for(SMesh::Vertex_index v : sm->vertices())
|
|
{
|
|
uv_map_3[v] = Point_3(uv_map[v][0], uv_map[v]
|
|
[1], 0);
|
|
if(uv_map[v][0] > xmax)
|
|
xmax = uv_map[v][0];
|
|
if(uv_map[v][0] < xmin)
|
|
xmin = uv_map[v][0];
|
|
|
|
if(uv_map[v][1] > ymax)
|
|
ymax = uv_map[v][1];
|
|
if(uv_map[v][1] < ymin)
|
|
ymin = uv_map[v][1];
|
|
}
|
|
|
|
CGAL::linear_least_squares_fitting_2(
|
|
uv_map.begin(),
|
|
uv_map.end(),
|
|
bf_line,
|
|
CGAL::Dimension_tag<0>());
|
|
|
|
EPICK::Vector_2 A(bf_line.to_vector()),
|
|
B(EPICK::Point_2(0,0),
|
|
EPICK::Point_2(1,0));
|
|
if (A.x()<0) A=-A;
|
|
angle = std::acos(A.x()/CGAL::sqrt(A.squared_length()));
|
|
if ( A.y()<0 ) angle+=3*CGAL_PI/2.;
|
|
if (angle>2*CGAL_PI) angle-=2*CGAL_PI;
|
|
|
|
locked = true;
|
|
dock_widget->rot_slider->setSliderPosition(angle*180.0/CGAL_PI);
|
|
locked = false;
|
|
}
|
|
//create Text Polyline
|
|
QPainterPath path;
|
|
QFont font;
|
|
font.setPointSize(pointsize);
|
|
path.addText(QPoint(xmin,ymin), font, dock_widget->lineEdit->text());
|
|
QList<QPolygonF> polys = path.toSubpathPolygons();
|
|
polylines.clear();
|
|
float pxmin(8000),pxmax(-8000),
|
|
pymin(8000), pymax(-8000);
|
|
|
|
Q_FOREACH(QPolygonF poly, polys){
|
|
Q_FOREACH(QPointF pf, poly)
|
|
{
|
|
EPICK::Point_2 v = EPICK::Point_2(pf.x(),-pf.y());
|
|
if(v.x() < pxmin)
|
|
pxmin = v.x();
|
|
if(v.x() > pxmax)
|
|
pxmax = v.x();
|
|
if(v.y() < pymin)
|
|
pymin = v.y();
|
|
if(v.y() > pymax)
|
|
pymax = v.y();
|
|
}
|
|
}
|
|
Q_FOREACH(QPolygonF poly, polys){
|
|
polylines.push_back(std::vector<EPICK::Point_2>());
|
|
Q_FOREACH(QPointF pf, poly)
|
|
{
|
|
EPICK::Point_2 v = EPICK::Point_2(pf.x(),-pf.y());
|
|
polylines.back().push_back(EPICK::Point_2(v.x()*(xmax-xmin)/(pxmax-pxmin) +xmin ,
|
|
v.y()*(ymax-ymin)/(pymax-pymin)+ymin
|
|
));
|
|
}
|
|
}
|
|
|
|
// build AABB-tree for face location queries
|
|
Tree aabb_tree(faces(*sm).first, faces(*sm).second, *sm, uv_map_3);
|
|
|
|
visu_item = new Scene_polylines_item;
|
|
|
|
|
|
// compute 3D coordinates
|
|
transfo =
|
|
EPICK::Aff_transformation_2(CGAL::TRANSLATION,
|
|
EPICK::Vector_2((xmax-xmin)/2+xmin,
|
|
(ymax-ymin)/2+ymin)+ translation)
|
|
* EPICK::Aff_transformation_2(CGAL::ROTATION,sin(angle), cos(angle))
|
|
* EPICK::Aff_transformation_2(scalX, 0.0,0.0,scalY)
|
|
* EPICK::Aff_transformation_2(CGAL::TRANSLATION,
|
|
EPICK::Vector_2(-(xmax-xmin)/2-xmin,
|
|
-(ymax-ymin)/2-ymin));
|
|
for(const std::vector<EPICK::Point_2>& polyline : polylines)
|
|
{
|
|
visu_item->polylines.push_back(std::vector<Point_3>());
|
|
for(const EPICK::Point_2& p : polyline)
|
|
{
|
|
EPICK::Point_2 p_2 = transfo.transform(p);
|
|
|
|
Face_location loc = Surface_mesh_shortest_path::locate(
|
|
Point_3(p_2.x(), p_2.y(), 0),
|
|
aabb_tree, *sm, uv_map_3);
|
|
visu_item->polylines.back().push_back(
|
|
Surface_mesh_shortest_path::point(loc.first, loc.second, *sm, sm->points()));
|
|
}
|
|
}
|
|
visu_item->setName("Text");
|
|
visu_item->setColor(QColor(Qt::red));
|
|
scene->addItem(visu_item);
|
|
dock_widget->engraveButton->setEnabled(true);
|
|
dock_widget->text_meshButton->setEnabled(true);
|
|
|
|
if(graphics_scene->items().empty())
|
|
{
|
|
Component* component = new Component();
|
|
face_iterator bfit;
|
|
for(bfit = faces(*sm).begin();
|
|
bfit != faces(*sm).end();
|
|
++bfit)
|
|
{
|
|
component->insert(*bfit);
|
|
}
|
|
SMesh::Property_map<halfedge_descriptor, float> umap;
|
|
SMesh::Property_map<halfedge_descriptor, float> vmap;
|
|
umap = sm->add_property_map<halfedge_descriptor, float>
|
|
("h:u", 0.0f).first;
|
|
vmap = sm->add_property_map<halfedge_descriptor, float>
|
|
("h:v", 0.0f).first;
|
|
SMesh::Halfedge_iterator it;
|
|
SMesh::Property_map<SMesh::Vertex_index, EPICK::Point_2> uv_map =
|
|
sm->property_map<SMesh::Vertex_index, EPICK::Point_2>("v:uv").first;
|
|
for(it = sm->halfedges_begin();
|
|
it != sm->halfedges_end();
|
|
++it)
|
|
{
|
|
halfedge_descriptor hd(*it);
|
|
EPICK::FT u = uv_map[target(hd, *sm)].x();
|
|
EPICK::FT v = uv_map[target(hd, *sm)].y();
|
|
put(umap, *it, static_cast<float>(u));
|
|
put(vmap, *it, static_cast<float>(v));
|
|
}
|
|
|
|
//ParamItem does not take ownership of text_mesh_bottom
|
|
ParamItem *param_item= new ParamItem(component, polylines, transfo, sm,
|
|
QRectF(QPointF(xmin, -ymax), QPointF(xmax, -ymin)));
|
|
graphics_scene->addItem(param_item);
|
|
dock_widget->graphicsView->fitInView(param_item->boundingRect(), Qt::KeepAspectRatio);
|
|
}
|
|
else
|
|
{
|
|
ParamItem* param_item = static_cast<ParamItem*>(graphics_scene->items().first());
|
|
param_item->set_transfo(transfo);
|
|
dock_widget->graphicsView->activateWindow();
|
|
graphics_scene->update();
|
|
}
|
|
// dock_widget->visualizeButton->setEnabled(false);
|
|
}
|
|
|
|
void create_text_mesh(SMesh& text_mesh)
|
|
{
|
|
if(!visu_item)
|
|
return;
|
|
if(!sel_item)
|
|
return;
|
|
if(sel_item->selected_facets.empty())
|
|
return;
|
|
if(!CGAL::is_closed(*sel_item->polyhedron()))
|
|
return;
|
|
CDT cdt;
|
|
//polylines is duplicated so the transformation is only performed once
|
|
std::vector<std::vector<EPICK::Point_2> > local_polylines
|
|
= polylines;
|
|
try{
|
|
for(std::size_t i = 0;
|
|
i < local_polylines.size(); ++i)
|
|
{
|
|
std::vector<Point_2>& points = local_polylines[i];
|
|
for(std::size_t j = 0; j< points.size(); ++j)
|
|
{
|
|
Point_2 &p = points[j];
|
|
p = transfo.transform(p);
|
|
}
|
|
cdt.insert_constraint(points.begin(),points.end());
|
|
}
|
|
}catch(std::runtime_error&)
|
|
{
|
|
QApplication::restoreOverrideCursor();
|
|
throw;
|
|
}
|
|
if (cdt.dimension()!=2){
|
|
QApplication::restoreOverrideCursor();
|
|
std::cout << "Triangulation is not of dimension 2" << std::endl;
|
|
return;
|
|
}
|
|
CGAL::Bbox_2 bbox= CGAL::bbox_2(local_polylines.front().begin(),
|
|
local_polylines.front().end(),
|
|
EPICK());
|
|
Q_FOREACH(const std::vector<EPICK::Point_2>& points,
|
|
local_polylines)
|
|
{
|
|
bbox += CGAL::bbox_2(points.begin(), points.end(), EPICK());
|
|
}
|
|
mark_nested_domains(cdt);
|
|
|
|
SMesh text_mesh_bottom;
|
|
cdt2_to_face_graph(cdt,
|
|
text_mesh_bottom);
|
|
typedef boost::property_map<SMesh, CGAL::vertex_point_t>::type VPMap;
|
|
typedef SMesh::Property_map<vertex_descriptor, EPICK::Vector_3> NPMAP;
|
|
NPMAP vnormals =
|
|
text_mesh_bottom.add_property_map<vertex_descriptor,
|
|
EPICK::Vector_3 >("v:normal").first;
|
|
|
|
if(!dock_widget->letter_checkBox->isChecked())
|
|
{
|
|
CGAL::Polygon_mesh_processing::compute_vertex_normals(text_mesh_bottom, vnormals);
|
|
}
|
|
else{
|
|
// \todo Computing normals before the final
|
|
// mesh would be better.
|
|
|
|
//foreach CC
|
|
SMesh::Property_map<face_descriptor, int> fcmap =
|
|
text_mesh_bottom.add_property_map<face_descriptor, int>("f:cc", 0).first;
|
|
std::size_t nb_cc = PMP::connected_components(text_mesh_bottom,
|
|
fcmap);
|
|
for(std::size_t cc = 0; cc<nb_cc; ++cc)
|
|
{
|
|
//compute the average normal for the cc give it to every vertex
|
|
EPICK::Vector_3 normal(0,0,0);
|
|
CGAL::Face_filtered_graph<SMesh> fmesh(text_mesh_bottom,
|
|
static_cast<int>(cc),
|
|
fcmap);
|
|
for(vertex_descriptor vd : vertices(fmesh))
|
|
{
|
|
normal += CGAL::Polygon_mesh_processing::compute_vertex_normal(vd, fmesh);
|
|
}
|
|
normal /= CGAL::sqrt(normal.squared_length());
|
|
for(vertex_descriptor vd : vertices(fmesh))
|
|
{
|
|
put(vnormals, vd, normal);
|
|
}
|
|
}
|
|
}
|
|
Bot<VPMap, NPMAP> bot(vnormals, dock_widget->bot_slider->value()/100000.0,
|
|
get(CGAL::vertex_point, text_mesh));
|
|
Top<VPMap, NPMAP> top(vnormals, get(CGAL::vertex_point, text_mesh),
|
|
dock_widget->top_slider->value()/100000.0);
|
|
PMP::extrude_mesh(text_mesh_bottom, text_mesh, bot, top);
|
|
}
|
|
|
|
void engrave() {
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
SMesh text_mesh_complete;
|
|
create_text_mesh(text_mesh_complete);
|
|
|
|
if (PMP::does_self_intersect(text_mesh_complete))
|
|
{
|
|
QApplication::restoreOverrideCursor();
|
|
CGAL::Three::Three::information("Error: text mesh self-intersects!");
|
|
return;
|
|
}
|
|
|
|
SMesh result;
|
|
CGAL::copy_face_graph(*sel_item->polyhedron(), result);
|
|
bool OK = PMP::corefine_and_compute_difference(result, text_mesh_complete, result);
|
|
|
|
if (!OK)
|
|
{
|
|
QApplication::restoreOverrideCursor();
|
|
CGAL::Three::Three::information("Error: the output mesh is not manifold!");
|
|
return;
|
|
}
|
|
|
|
CGAL::Polygon_mesh_processing::triangulate_faces(result);
|
|
Scene_surface_mesh_item* result_item = new Scene_surface_mesh_item(
|
|
result);
|
|
scene->addItem(result_item);
|
|
graphics_scene->clear();
|
|
cleanup();
|
|
if(textMesh)
|
|
{
|
|
textMesh->setVisible(false);
|
|
}
|
|
QApplication::restoreOverrideCursor();
|
|
dock_widget->engraveButton->setEnabled(false);
|
|
dock_widget->text_meshButton->setEnabled(false);
|
|
dock_widget->visualizeButton->setEnabled(true);
|
|
}
|
|
|
|
void generateTextItem()
|
|
{
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
|
|
if(textMesh)
|
|
{
|
|
textMesh->face_graph()->clear();
|
|
create_text_mesh(*textMesh->face_graph());
|
|
textMesh->invalidateOpenGLBuffers();
|
|
}
|
|
else
|
|
{
|
|
SMesh text_mesh;
|
|
create_text_mesh(text_mesh);
|
|
textMesh = new Scene_surface_mesh_item(text_mesh);
|
|
connect(textMesh, &Scene_surface_mesh_item::aboutToBeDestroyed,
|
|
this, [this](){
|
|
textMesh = nullptr;});
|
|
textMesh->setName("Extruded Text");
|
|
scene->addItem(textMesh);
|
|
}
|
|
QApplication::restoreOverrideCursor();
|
|
}
|
|
|
|
void closure()Q_DECL_OVERRIDE
|
|
{
|
|
dock_widget->hide();
|
|
}
|
|
|
|
private:
|
|
template <class CDT>
|
|
void
|
|
mark_domains(CDT& ct,
|
|
typename CDT::Face_handle start,
|
|
int index,
|
|
std::list<typename CDT::Edge>& border )
|
|
{
|
|
if(start->info().nesting_level != -1){
|
|
return;
|
|
}
|
|
std::list<typename CDT::Face_handle> queue;
|
|
queue.push_back(start);
|
|
while(! queue.empty()){
|
|
typename CDT::Face_handle fh = queue.front();
|
|
queue.pop_front();
|
|
if(fh->info().nesting_level == -1){
|
|
fh->info().nesting_level = index;
|
|
for(int i = 0; i < 3; i++){
|
|
typename CDT::Edge e(fh,i);
|
|
typename CDT::Face_handle n = fh->neighbor(i);
|
|
if(n->info().nesting_level == -1){
|
|
if(ct.is_constrained(e)) border.push_back(e);
|
|
else queue.push_back(n);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
template <class CDT>
|
|
void
|
|
mark_nested_domains(CDT& cdt)
|
|
{
|
|
for(typename CDT::All_faces_iterator it = cdt.all_faces_begin(); it !=
|
|
cdt.all_faces_end(); ++it){
|
|
it->info().nesting_level = -1;
|
|
}
|
|
std::list<typename CDT::Edge> border;
|
|
mark_domains(cdt, cdt.infinite_face(), 0, border);
|
|
while(! border.empty()){
|
|
typename CDT::Edge e = border.front();
|
|
border.pop_front();
|
|
typename CDT::Face_handle n = e.first->neighbor(e.second);
|
|
if(n->info().nesting_level == -1){
|
|
mark_domains(cdt, n, e.first->info().nesting_level+1, border);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class CDT, class TriangleMesh>
|
|
void cdt2_to_face_graph(const CDT& cdt,
|
|
TriangleMesh& tm)
|
|
{
|
|
|
|
Tree aabb_tree(faces(*sm).first, faces(*sm).second, *sm, uv_map_3);
|
|
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
|
|
|
|
typedef std::map<typename CDT::Vertex_handle, vertex_descriptor> Map;
|
|
Map descriptors;
|
|
for (typename CDT::Finite_faces_iterator fit=cdt.finite_faces_begin(),
|
|
fit_end=cdt.finite_faces_end();
|
|
fit!=fit_end; ++fit)
|
|
{
|
|
if (!fit->info().in_domain()) continue;
|
|
std::array<vertex_descriptor,3> vds;
|
|
for(int i=0; i<3; ++i)
|
|
{
|
|
typename Map::iterator it;
|
|
bool insert_ok;
|
|
boost::tie(it,insert_ok) =
|
|
descriptors.insert(std::make_pair(fit->vertex(i),vertex_descriptor()));
|
|
if (insert_ok){
|
|
const EPICK::Point_2& pt=fit->vertex(i)->point();
|
|
Face_location loc = Surface_mesh_shortest_path::locate(
|
|
Point_3(pt.x(), pt.y(), 0),
|
|
aabb_tree, *sm, uv_map_3);
|
|
it->second = add_vertex(Surface_mesh_shortest_path::point(loc.first, loc.second,
|
|
*sm, sm->points()), tm);
|
|
}
|
|
vds[i]=it->second;
|
|
}
|
|
|
|
CGAL::Euler::add_face(vds, tm);
|
|
}
|
|
}
|
|
|
|
void cleanup()
|
|
{
|
|
dock_widget->scalX_slider->setValue(1000);
|
|
dock_widget->scalY_slider->setValue(1000);
|
|
dock_widget->rot_slider->setValue(0);
|
|
translation = EPICK::Vector_2(0,0);
|
|
uv_map_3.reset();
|
|
graphics_scene->clear();
|
|
if(sel_item)
|
|
{
|
|
scene->erase(scene->item_id(sel_item));
|
|
sel_item =nullptr;
|
|
}
|
|
if(sm)
|
|
{
|
|
delete sm;
|
|
sm = nullptr;
|
|
}
|
|
if(visu_item)
|
|
{
|
|
scene->erase(scene->item_id(visu_item));
|
|
visu_item = nullptr;
|
|
}
|
|
}
|
|
|
|
QList<QAction*> _actions;
|
|
EngraveWidget* dock_widget;
|
|
Scene_polylines_item* visu_item;
|
|
Scene_polyhedron_selection_item* sel_item;
|
|
Scene_surface_mesh_item* textMesh;
|
|
double angle;
|
|
double scalX;
|
|
double scalY;
|
|
EPICK::Vector_2 translation;
|
|
EPICK::Aff_transformation_2 transfo;
|
|
std::vector<std::vector<EPICK::Point_2> > polylines;
|
|
SMesh::Property_map<SMesh::Vertex_index, Point_3> uv_map_3;
|
|
SMesh* sm;
|
|
float xmin, xmax, ymin, ymax;
|
|
int pointsize;
|
|
bool locked;
|
|
EPICK::Line_2 bf_line;
|
|
QGraphicsScene *graphics_scene;
|
|
Navigation* navigation;
|
|
};
|
|
#include "Engrave_text_plugin.moc"
|
|
|