cgal/Mesh_3/demo/Mesh_3/Volume_planes_plugin.cpp

380 lines
11 KiB
C++

#include <CGAL/Image_3.h>
#include "Volume_plane.h"
#include "Volume_plane_thread.h"
#include "Volume_plane_intersection.h"
#include "Scene_segmented_image_item.h"
#include <CGAL_demo/Plugin_helper.h>
#include <CGAL_demo/Plugin_interface.h>
#include <CGAL_demo/Messages_interface.h>
#include <CGAL_demo/Scene_interface.h>
#include <CGAL_demo/Scene_item.h>
#include <CGAL_demo/Viewer.h>
#include "MainWindow.h"
#include <QAction>
#include <QMenu>
#include <QList>
#include <QInputDialog>
#include <QSlider>
#include <QLabel>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QDockWidget>
#include <QMainWindow>
#include <QMessageBox>
#include <QString>
#include <QFontMetrics>
#include <cassert>
#include <iostream>
#include <boost/type_traits.hpp>
#include <boost/optional.hpp>
// Covariant return types don't work for scalar types and we cannot
// have templates here, hence this unfortunate hack.
// The input float value we are reading is always in
// 0..1 and min_max is the range it came from.
struct IntConverter {
std::pair<int, int> min_max;
int operator()(float f) {
float s = f * (min_max.second - min_max.first);
return s + min_max.first;
}
};
struct DoubleConverter {
std::pair<float, float> min_max;
float operator()(float f) {
float s = f * (min_max.second - min_max.first);
return s + min_max.first;
}
};
class PixelReader : public QObject
{
Q_OBJECT
public slots:
void update(const QPoint& p) {
getPixel(p);
}
signals:
void x(int);
public:
void setIC(const IntConverter& x) { ic = x; fc = boost::optional<DoubleConverter>(); }
void setFC(const DoubleConverter& x) { fc = x; ic = boost::optional<IntConverter>(); }
private:
boost::optional<IntConverter> ic;
boost::optional<DoubleConverter> fc;
void getPixel(const QPoint& e) {
float data[3];
int vp[4];
glGetIntegerv(GL_VIEWPORT, vp);
glReadPixels(e.x(), vp[3] - e.y(), 1, 1, GL_RGB, GL_FLOAT, data);
if(fc) {
emit x( (*fc)(data[0]) );
} else if(ic) {
emit x( (*ic)(data[0]) );
}
}
};
class Plane_slider : public QSlider
{
Q_OBJECT
public:
Plane_slider(const qglviewer::Vec& v, int id, Scene_interface* scene,
qglviewer::ManipulatedFrame* frame, Qt::Orientation ori, QWidget* widget)
: QSlider(ori, widget), v(v), id(id), scene(scene), frame(frame) {
this->setTracking(true);
connect(frame, SIGNAL(manipulated()), this, SLOT(updateValue()));
}
public:
void sliderChange(SliderChange c) {
QSlider::sliderChange(c);
if(c == SliderValueChange) {
qglviewer::Vec v2 = v * (this->value() / scale);
frame->setTranslationWithConstraint(v2);
scene->itemChanged(id);
}
emit realChange(this->value() / scale);
}
public slots:
void updateValue() {
float a, b, c;
frame->getPosition(a, b, c);
float sum1 = a + b + c;
float sum2 = v.x + v.y + v.z;
sum1 /= sum2;
setValue(sum1 * scale);
}
signals:
void realChange(int);
private:
const static unsigned int scale;
qglviewer::Vec v;
int id;
Scene_interface* scene;
qglviewer::ManipulatedFrame* frame;
};
const unsigned int Plane_slider::scale = 100;
class Volume_plane_plugin :
public QObject,
public Plugin_interface
{
Q_OBJECT
Q_INTERFACES(Plugin_interface)
public:
Volume_plane_plugin() : planeSwitch(NULL), sc(NULL), mw(NULL)
{
}
virtual void init(QMainWindow* mw, Scene_interface* sc) {
assert(mw != NULL);
assert(sc != NULL);
this->sc = sc;
this->mw = mw;
QList<QMenu*> menus = mw->findChildren<QMenu*>();
planeSwitch = new QAction(mw);
planeSwitch->setText("Add Volume Planes");
connect(planeSwitch, SIGNAL(triggered()), this, SLOT(selectPlanes()));
// evil
MainWindow* mwTmp;
if( !(mwTmp = dynamic_cast<MainWindow*>(mw)) ) {
std::cerr << "Volume_planes_plugin cannot init mousegrabber" << std::endl;
}
Viewer* v = mwTmp->getViewer();
connect(v, SIGNAL(pointSelected(QPoint)), &pxr_, SLOT(update(QPoint)));
createOrGetDockLayout();
}
virtual QList<QAction*> actions() const {
return QList<QAction*>() << planeSwitch;
}
public slots:
void selectPlanes() {
std::vector< Scene_segmented_image_item* > seg_items;
Scene_segmented_image_item* seg_img = NULL;
for(unsigned int i = 0; i < sc->numberOfEntries(); ++i) {
Scene_segmented_image_item* tmp = qobject_cast<Scene_segmented_image_item*>(sc->item(i));
if(tmp != NULL)
seg_items.push_back(tmp);
}
if(seg_items.empty()) {
QMessageBox::warning(mw, tr("No suitable item found"), tr("Load an inrimage or hdr file to enable Volume Planes."));
return;
} else {
QList<QString> items;
for(std::vector< Scene_segmented_image_item* >::const_iterator it = seg_items.begin();
it != seg_items.end(); ++it) {
items << (*it)->name();
}
bool ok;
QString selected = QInputDialog::getItem(mw, tr("Select a dataset:"), tr("Items"), items, 0, false, &ok);
if(!ok || selected.isEmpty())
return;
for(std::vector< Scene_segmented_image_item*>::const_iterator it = seg_items.begin();
it != seg_items.end(); ++it) {
if(selected == (*it)->name())
seg_img = *it;
}
}
if(!(seg_img == NULL)) {
const CGAL::Image_3* img = seg_img->image();
CGAL_IMAGE_IO_CASE(img->image(), this->launchAdders<Word>(img, seg_img->name()))
Volume_plane_intersection* i = new Volume_plane_intersection(img->xdim() * img->vx(),
img->ydim() * img->vy(),
img->zdim() * img->vz());
this->intersectionId = sc->addItem(i);
} else {
QMessageBox::warning(mw, tr("Something went wrong"), tr("Selected a suitable Object but couldn't get an image pointer."));
return;
}
}
void addVP(Volume_plane_thread* thread) {
Volume_plane_interface* plane = thread->getItem();
plane->init();
// add the interface for this Volume_plane
int id = sc->addItem(plane);
QLayout* layout = createOrGetDockLayout();
QWidget* controls = new QWidget;
QHBoxLayout* box = new QHBoxLayout(controls);
layout->addWidget(controls);
QLabel* label = new QLabel(controls);
label->setText(plane->name());
QLabel* cubeLabel = new QLabel(controls);
cubeLabel->setNum(static_cast<int>(plane->getCurrentCube()));
// Find the right width for the label to accommodate at least 9999
QFontMetrics metric = cubeLabel->fontMetrics();
cubeLabel->setFixedWidth(metric.width(QString("9999")));
QSlider* slider = new Plane_slider(plane->translationVector(), id, sc, plane->manipulatedFrame(),
Qt::Horizontal, controls);
slider->setRange(0, (plane->cDim() - 1) * 100);
connect(slider, SIGNAL(realChange(int)), cubeLabel, SLOT(setNum(int)));
connect(plane, SIGNAL(manipulated(int)), cubeLabel, SLOT(setNum(int)));
box->addWidget(label);
box->addWidget(slider);
box->addWidget(cubeLabel);
connect(plane, SIGNAL(aboutToBeDestroyed()), controls, SLOT(deleteLater()));
std::vector<Volume_plane_thread*>::iterator it = std::find(threads.begin(), threads.end(), thread);
// this slot has been connected to a thread that hasn't been
// registered here.
assert(it != threads.end());
delete *it;
threads.erase(it);
Volume_plane_intersection* intersection = dynamic_cast<Volume_plane_intersection*>(sc->item(intersectionId));
if(!intersection) {
// the intersection is gone before it was initialized
return;
}
// FIXME downcasting mode
// FIXME this will bug if two volume planes are generated simultaneously by the plugin
if(Volume_plane<x_tag>* p = dynamic_cast< Volume_plane<x_tag>* >(plane)) {
intersection->setX(p);
} else if(Volume_plane<y_tag>* p = dynamic_cast< Volume_plane<y_tag>* >(plane)) {
intersection->setY(p);
} else if(Volume_plane<z_tag>* p = dynamic_cast< Volume_plane<z_tag>* >(plane)) {
intersection->setZ(p);
}
connect(plane, SIGNAL(planeDestructionIncoming(Volume_plane_interface*)),
intersection, SLOT(planeRemoved(Volume_plane_interface*)));
}
private:
QAction* planeSwitch;
Scene_interface* sc;
QMainWindow* mw;
PixelReader pxr_;
std::vector<Volume_plane_thread*> threads;
unsigned int intersectionId;
QLayout* createOrGetDockLayout() {
QLayout* layout = NULL;
QDockWidget* controlDockWidget = mw->findChild<QDockWidget*>("volumePlanesControl");
if(!controlDockWidget) {
controlDockWidget = new QDockWidget(mw);
controlDockWidget->setObjectName("volumePlanesControl");
QWidget* content = new QWidget(controlDockWidget);
layout = new QVBoxLayout(content);
layout->setObjectName("vpSliderLayout");
controlDockWidget->setWindowTitle("Control Widget");
mw->addDockWidget(Qt::LeftDockWidgetArea, controlDockWidget);
QWidget* vlabels = new QWidget(content);
layout->addWidget(vlabels);
QHBoxLayout* vbox = new QHBoxLayout(vlabels);
vbox->setAlignment(Qt::AlignJustify);
QLabel* text = new QLabel(vlabels);
text->setText("Isovalue at point:");
QLabel* x = new QLabel(vlabels);
connect(&pxr_, SIGNAL(x(int)), x, SLOT(setNum(int)));
vbox->addWidget(text); vbox->addWidget(x);
controlDockWidget->setWidget(content);
} else {
layout = controlDockWidget->findChild<QLayout*>("vpSliderLayout");
}
return layout;
}
template<typename Word>
void launchAdders(const CGAL::Image_3* img, const QString& name) {
const Word* begin = (const Word*)img->data();
const Word* end = (const Word*)img->data() + img->size();
std::pair<Word, Word> minmax = std::make_pair(*std::min_element(begin, end), *std::max_element(begin, end));
Clamp_to_one_zero_range clamper = { minmax };
switchReaderConverter< Word >(minmax);
threads.push_back(new X_plane_thread<Word>(img, clamper, name));
connect(threads.back(), SIGNAL(finished(Volume_plane_thread*)), this, SLOT(addVP(Volume_plane_thread*)));
threads.back()->start();
threads.push_back(new Y_plane_thread<Word>(img, clamper, name));
connect(threads.back(), SIGNAL(finished(Volume_plane_thread*)), this, SLOT(addVP(Volume_plane_thread*)));
threads.back()->start();
threads.push_back(new Z_plane_thread<Word>(img, clamper, name));
connect(threads.back(), SIGNAL(finished(Volume_plane_thread*)), this, SLOT(addVP(Volume_plane_thread*)));
threads.back()->start();
}
template<typename T>
void switchReaderConverter(std::pair<T, T> minmax) {
switchReaderConverter(minmax, typename boost::is_integral<T>::type());
}
template<typename T>
void switchReaderConverter(std::pair<T, T> minmax, boost::true_type) {
// IntConverter
IntConverter x = { minmax }; pxr_.setIC(x);
}
template<typename T>
void switchReaderConverter(std::pair<T, T> minmax, boost::false_type) {
// IntConverter
DoubleConverter x = { minmax }; pxr_.setFC(x);
}
};
Q_EXPORT_PLUGIN2(Volume_plane_plugin, Volume_plane_plugin)
#include "Volume_planes_plugin.moc"