314 lines
10 KiB
C++
314 lines
10 KiB
C++
|
/*
|
||
|
* Copyright (C) 2015 Open Source Robotics Foundation
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "gazebo/common/SVGLoader.hh"
|
||
|
|
||
|
#include "gazebo/gui/model/ExtrudeDialogPrivate.hh"
|
||
|
#include "gazebo/gui/model/ExtrudeDialog.hh"
|
||
|
|
||
|
using namespace gazebo;
|
||
|
using namespace gui;
|
||
|
|
||
|
/////////////////////////////////////////////////
|
||
|
ExtrudeDialog::ExtrudeDialog(const std::string &_filename, QWidget *_parent)
|
||
|
: QDialog(_parent), dataPtr(new ExtrudeDialogPrivate)
|
||
|
{
|
||
|
this->setObjectName("ExtrudeDialog");
|
||
|
this->setWindowTitle(tr("Extrude Link"));
|
||
|
this->setWindowFlags(Qt::Window | Qt::WindowCloseButtonHint |
|
||
|
Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint);
|
||
|
|
||
|
// Title
|
||
|
QLabel *titleLabel = new QLabel(tr(
|
||
|
"Extrude a 2D polyline into a 3D mesh.<br>"));
|
||
|
|
||
|
// Thickness
|
||
|
this->dataPtr->thicknessSpin = new QDoubleSpinBox();
|
||
|
this->dataPtr->thicknessSpin->setObjectName("thicknessSpin");
|
||
|
this->dataPtr->thicknessSpin->setRange(0.001, 1000);
|
||
|
this->dataPtr->thicknessSpin->setSingleStep(0.1);
|
||
|
this->dataPtr->thicknessSpin->setDecimals(3);
|
||
|
this->dataPtr->thicknessSpin->setValue(0.1);
|
||
|
|
||
|
// Resolution
|
||
|
this->dataPtr->resolutionSpin = new QDoubleSpinBox();
|
||
|
this->dataPtr->resolutionSpin->setObjectName("resolutionSpin");
|
||
|
this->dataPtr->resolutionSpin->setRange(1, 100000);
|
||
|
this->dataPtr->resolutionSpin->setSingleStep(100);
|
||
|
this->dataPtr->resolutionSpin->setDecimals(3);
|
||
|
// 3543.3 px/m == 90 dpi
|
||
|
this->dataPtr->resolutionSpin->setValue(3543.3);
|
||
|
connect(this->dataPtr->resolutionSpin, SIGNAL(valueChanged(double)),
|
||
|
this, SLOT(OnUpdateView(double)));
|
||
|
|
||
|
// Samples
|
||
|
this->dataPtr->samplesSpin = new QSpinBox();
|
||
|
this->dataPtr->samplesSpin->setObjectName("samplesSpin");
|
||
|
this->dataPtr->samplesSpin->setRange(2, 100);
|
||
|
this->dataPtr->samplesSpin->setSingleStep(1);
|
||
|
this->dataPtr->samplesSpin->setValue(5);
|
||
|
QLabel *samplesTips = new QLabel(tr("<b><font size=4>?</font></b>"));
|
||
|
samplesTips->setToolTip(
|
||
|
"Number of points to divide each curve segment into.");
|
||
|
connect(this->dataPtr->samplesSpin, SIGNAL(valueChanged(int)),
|
||
|
this, SLOT(OnUpdateView(int)));
|
||
|
|
||
|
QGridLayout *inputsLayout = new QGridLayout();
|
||
|
inputsLayout->addWidget(new QLabel("Thickness:"), 0, 0);
|
||
|
inputsLayout->addWidget(this->dataPtr->thicknessSpin, 0, 1);
|
||
|
inputsLayout->addWidget(new QLabel("m"), 0, 2);
|
||
|
inputsLayout->addWidget(new QLabel("Resolution:"), 1, 0);
|
||
|
inputsLayout->addWidget(this->dataPtr->resolutionSpin, 1, 1);
|
||
|
inputsLayout->addWidget(new QLabel("px/m"), 1, 2);
|
||
|
inputsLayout->addWidget(new QLabel("Samples per segment:"), 2, 0);
|
||
|
inputsLayout->addWidget(this->dataPtr->samplesSpin, 2, 1);
|
||
|
inputsLayout->addWidget(samplesTips, 2, 2);
|
||
|
|
||
|
// Buttons
|
||
|
QPushButton *backButton = new QPushButton(tr("Back"));
|
||
|
connect(backButton, SIGNAL(clicked()), this, SLOT(OnReject()));
|
||
|
|
||
|
QPushButton *okButton = new QPushButton("Ok");
|
||
|
okButton->setDefault(true);
|
||
|
connect(okButton, SIGNAL(clicked()), this, SLOT(OnAccept()));
|
||
|
|
||
|
QHBoxLayout *buttonsLayout = new QHBoxLayout();
|
||
|
buttonsLayout->addWidget(backButton);
|
||
|
buttonsLayout->addWidget(okButton);
|
||
|
buttonsLayout->setAlignment(Qt::AlignRight);
|
||
|
|
||
|
// Left column
|
||
|
QWidget *leftColumn = new QWidget();
|
||
|
leftColumn->setSizePolicy(QSizePolicy::Fixed,
|
||
|
QSizePolicy::Fixed);
|
||
|
QVBoxLayout *leftColumnLayout = new QVBoxLayout();
|
||
|
leftColumn->setLayout(leftColumnLayout);
|
||
|
leftColumnLayout->addWidget(titleLabel);
|
||
|
leftColumnLayout->addLayout(inputsLayout);
|
||
|
leftColumnLayout->addSpacing(30);
|
||
|
leftColumnLayout->addLayout(buttonsLayout);
|
||
|
|
||
|
// Image view
|
||
|
this->dataPtr->filename = _filename;
|
||
|
|
||
|
this->dataPtr->view = new QGraphicsView(this);
|
||
|
QGraphicsScene *scene = new QGraphicsScene();
|
||
|
scene->setBackgroundBrush(Qt::white);
|
||
|
this->dataPtr->view->setSizePolicy(QSizePolicy::Expanding,
|
||
|
QSizePolicy::Expanding);
|
||
|
this->dataPtr->view->setScene(scene);
|
||
|
this->dataPtr->view->setViewportUpdateMode(
|
||
|
QGraphicsView::FullViewportUpdate);
|
||
|
this->dataPtr->view->setDragMode(QGraphicsView::ScrollHandDrag);
|
||
|
this->dataPtr->view->setMinimumWidth(200);
|
||
|
this->dataPtr->viewWidth = 500;
|
||
|
this->dataPtr->view->installEventFilter(this);
|
||
|
this->UpdateView();
|
||
|
|
||
|
// Main layout
|
||
|
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||
|
mainLayout->addWidget(leftColumn, 0, Qt::AlignTop);
|
||
|
mainLayout->addWidget(this->dataPtr->view);
|
||
|
|
||
|
this->setLayout(mainLayout);
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////
|
||
|
ExtrudeDialog::~ExtrudeDialog()
|
||
|
{
|
||
|
delete this->dataPtr;
|
||
|
this->dataPtr = NULL;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////
|
||
|
void ExtrudeDialog::OnAccept()
|
||
|
{
|
||
|
this->accept();
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////
|
||
|
void ExtrudeDialog::OnReject()
|
||
|
{
|
||
|
// back to import
|
||
|
this->reject();
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////
|
||
|
double ExtrudeDialog::GetThickness() const
|
||
|
{
|
||
|
return this->dataPtr->thicknessSpin->value();
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////
|
||
|
unsigned int ExtrudeDialog::GetSamples() const
|
||
|
{
|
||
|
return this->dataPtr->samplesSpin->value();
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////
|
||
|
double ExtrudeDialog::GetResolution() const
|
||
|
{
|
||
|
return this->dataPtr->resolutionSpin->value();
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////
|
||
|
void ExtrudeDialog::OnUpdateView(int /*_value*/)
|
||
|
{
|
||
|
this->UpdateView();
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////
|
||
|
void ExtrudeDialog::OnUpdateView(double /*_value*/)
|
||
|
{
|
||
|
this->UpdateView();
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////
|
||
|
void ExtrudeDialog::UpdateView()
|
||
|
{
|
||
|
QGraphicsScene *scene = this->dataPtr->view->scene();
|
||
|
scene->clear();
|
||
|
|
||
|
common::SVGLoader svgLoader(this->GetSamples());
|
||
|
std::vector<common::SVGPath> paths;
|
||
|
svgLoader.Parse(this->dataPtr->filename, paths);
|
||
|
|
||
|
if (paths.empty())
|
||
|
{
|
||
|
gzerr << "An empty path should never get here." << std::endl;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Find extreme values to center and scale
|
||
|
math::Vector2d min(paths[0].polylines[0][0]);
|
||
|
math::Vector2d max(min);
|
||
|
for (const auto &p : paths)
|
||
|
{
|
||
|
for (const auto &poly : p.polylines)
|
||
|
{
|
||
|
for (const auto &pt : poly)
|
||
|
{
|
||
|
if (pt.X() < min.x)
|
||
|
min.x = pt.X();
|
||
|
if (pt.Y() < min.y)
|
||
|
min.y = pt.Y();
|
||
|
if (pt.X() > max.x)
|
||
|
max.x = pt.X();
|
||
|
if (pt.Y() > max.y)
|
||
|
max.y = pt.Y();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int margin = 50;
|
||
|
double svgWidth = this->dataPtr->viewWidth - margin * 2;
|
||
|
double resolutionView = svgWidth / (max.x-min.x);
|
||
|
double svgHeight = (max.y - min.y) * resolutionView;
|
||
|
double viewHeight = svgHeight + 2 * margin;
|
||
|
scene->setSceneRect(0, 0, this->dataPtr->viewWidth, viewHeight);
|
||
|
|
||
|
// Draw grid lines
|
||
|
double sceneMeter = 1 * this->GetResolution() * resolutionView;
|
||
|
for (double r = 0; r <= viewHeight; r += sceneMeter/10.0)
|
||
|
{
|
||
|
scene->addLine(- margin, r, this->dataPtr->viewWidth + margin, r,
|
||
|
QPen(QColor(230, 230, 255)));
|
||
|
}
|
||
|
for (double c = 0; c <= this->dataPtr->viewWidth; c += sceneMeter/10.0)
|
||
|
{
|
||
|
scene->addLine(c, - margin, c, viewHeight + margin,
|
||
|
QPen(QColor(230, 230, 255)));
|
||
|
}
|
||
|
for (double r = 0; r <= viewHeight; r += sceneMeter)
|
||
|
{
|
||
|
scene->addLine(- margin, r, this->dataPtr->viewWidth + margin, r,
|
||
|
QPen(QColor(108, 108, 255)));
|
||
|
}
|
||
|
for (double c = 0; c <= this->dataPtr->viewWidth; c += sceneMeter)
|
||
|
{
|
||
|
scene->addLine(c, - margin, c, viewHeight + margin,
|
||
|
QPen(QColor(108, 108, 255)));
|
||
|
}
|
||
|
|
||
|
// Draw origin
|
||
|
scene->addLine(this->dataPtr->viewWidth/2.0 - this->dataPtr->viewWidth/30.0,
|
||
|
viewHeight/2.0,
|
||
|
this->dataPtr->viewWidth/2.0 + this->dataPtr->viewWidth/30.0,
|
||
|
viewHeight/2.0,
|
||
|
QPen(QColor(50, 50, 255), 2));
|
||
|
scene->addLine(this->dataPtr->viewWidth/2.0,
|
||
|
viewHeight/2.0 - this->dataPtr->viewWidth/30.0,
|
||
|
this->dataPtr->viewWidth/2.0,
|
||
|
viewHeight/2.0 + this->dataPtr->viewWidth/30.0,
|
||
|
QPen(QColor(50, 50, 255), 2));
|
||
|
|
||
|
// Draw polygons
|
||
|
for (common::SVGPath path : paths)
|
||
|
{
|
||
|
for (std::vector<ignition::math::Vector2d> poly : path.polylines)
|
||
|
{
|
||
|
QPainterPath painterPath;
|
||
|
bool firstPoint = true;
|
||
|
for (ignition::math::Vector2d pt : poly)
|
||
|
{
|
||
|
// Centroid at SVG 0,0
|
||
|
pt = pt - min.Ign() - (max-min).Ign()*0.5;
|
||
|
// Scale to view while keeping aspect ratio
|
||
|
pt = ignition::math::Vector2d(
|
||
|
pt.X() * resolutionView, pt.Y() * resolutionView);
|
||
|
// Translate to view center
|
||
|
pt.X() += this->dataPtr->viewWidth/2.0;
|
||
|
pt.Y() += viewHeight/2.0;
|
||
|
|
||
|
// Draw point
|
||
|
double pointSize = 5;
|
||
|
QGraphicsEllipseItem *ptItem = new QGraphicsEllipseItem(
|
||
|
pt.X() - pointSize/2.0, pt.Y() - pointSize/2.0,
|
||
|
pointSize, pointSize);
|
||
|
ptItem->setBrush(Qt::red);
|
||
|
ptItem->setZValue(5);
|
||
|
scene->addItem(ptItem);
|
||
|
if (firstPoint)
|
||
|
{
|
||
|
firstPoint = false;
|
||
|
painterPath.moveTo(pt.X(), pt.Y());
|
||
|
}
|
||
|
else
|
||
|
painterPath.lineTo(pt.X(), pt.Y());
|
||
|
}
|
||
|
|
||
|
QGraphicsPathItem *pathItem = new QGraphicsPathItem(painterPath);
|
||
|
// Draw polygon
|
||
|
pathItem->setPen(QPen(Qt::black, 3, Qt::SolidLine));
|
||
|
scene->addItem(pathItem);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////
|
||
|
bool ExtrudeDialog::eventFilter(QObject *_obj, QEvent *_event)
|
||
|
{
|
||
|
QGraphicsView *graphicsView = qobject_cast<QGraphicsView *>(_obj);
|
||
|
if (graphicsView && _event->type() == QEvent::Resize)
|
||
|
{
|
||
|
QResizeEvent *resizeEv = dynamic_cast<QResizeEvent *>(_event);
|
||
|
this->dataPtr->viewWidth = resizeEv->size().width() - 20;
|
||
|
this->UpdateView();
|
||
|
}
|
||
|
return QObject::eventFilter(_obj, _event);
|
||
|
}
|