ISeeML  1.0
QtGuiFwd.cpp

This file uses ISeeML in a Graphical User Interface, drawing Dubins' and FSC paths to connect a fixed configuration to a moving one, whose position follows the mouse and whose orientation can be changed using keyboard.This interface is built using (simply) Qt.

//
// This program defines a real-time Qt interface drawing ISeeML'
// forward-only paths connecting two configurations
//
#include "QtGuiFwd.hpp" // Associated header file
// To get the definition of various Qt classes
#include <QApplication>
#include <QStatusBar>
#include <QMouseEvent>
#include <QBrush>
#include <QPen>
#include <QLine>
#include <QSize>
#include <QMenuBar>
#include <QMessageBox>
using namespace iSeeML::rob; // to avoid writing iSeeML::rob::...
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
ISeeMLFrame frame("ISeeML V1.0 Qt Interface");
frame.show();
return app.exec();
}
//### Drawing Area ##############################################
// === Static Field =============================================
const OrPtConfig ISeeMLDrawingArea::start = OrPtConfig();
// === Event Handlers ===========================================
void ISeeMLDrawingArea::mousePressEvent(QMouseEvent *event) {
movingConfig = !movingConfig;
mouseMoveEvent(event);
QString msg;
if (movingConfig) {
setMouseTracking(true);
msg = msg + "Clic to fix, use wheel, arrows or" +
" page up/down to change orientation";
} else {
setMouseTracking(false);
msg += "Clic to modify goal configuration";
}
parentFrame->statusBar()->showMessage(msg);
}
// --------------------------------------------------------------
void ISeeMLDrawingArea::mouseMoveEvent(QMouseEvent *event) {
if (movingConfig) {
const OrPtConfig newGoal( (event->x() - centerPoint.x()) / scale,
(-event->y()+ centerPoint.y()) / scale,
goal.orientation() ); ;
changeGoal(newGoal);
} // end of if (movingConfig)
} // end of ISeeMLDrawingArea::OnMouseMove(QMouseEvent*)
// --------------------------------------------------------------
void ISeeMLDrawingArea::wheelEvent(QWheelEvent *event) {
if (movingConfig) {
static const double PI_196 = M_PI / 196, PI_28 = M_PI / 28,
PI_4 = M_PI / 4;
// motion of the wheel, in +/- 1
const int change = goal.sign( event->delta() );
// using event.GetWheelDelta() does not work (it returns 0)
double newTh = goal.orientation();
if (event->modifiers() & Qt::ShiftModifier)
newTh += change * PI_4; // SHIFT => big changes
else if (event->modifiers() & Qt::ControlModifier)
newTh += change * PI_196; // CTRL => small changes
else newTh += change * PI_28; // else normal changes
// change goal configuration and redraw
OrPtConfig newGoal(goal.position(), newTh);
changeGoal(newGoal);
} // end of if (movingConfig)
} // end of ISeeMLDrawingArea::wheelEvent(QWheelEvent*)
// --------------------------------------------------------------
void ISeeMLDrawingArea::resizeEvent(QResizeEvent *event) {
const QSize& sz = event->size();
centerPoint = QPoint(sz.width() / 2, sz.height() / 2);
}
// --------------------------------------------------------------
void ISeeMLDrawingArea::paintEvent(QPaintEvent *) {
static const QBrush cvGP; // curve fill brush
static const QPen
bgGP(Qt::white), configGP(Qt::red), // background & config. pens
#ifndef SHOW_TYPE
dubinsGP(Qt::blue), fscpGP(Qt::green), // paths pens
#endif
dubinsTCGP(QColor("darkorange"), 1, Qt::DashLine),
fscpTCGP(QColor("gold"), 1, Qt::DashLine); // for turning circles
QPainter pntr(this);
// graphical properties setting
if (drawing) pntr.setPen(configGP);
else pntr.setPen(bgGP);
pntr.setBrush(cvGP);
// drawing starting and goal configurations
draw(pntr, start);
draw(pntr, goal);
// drawing Dubins path and turning centers (if asked)
if (drawing && drawDubins) {
#ifndef SHOW_TYPE
pntr.setPen(dubinsGP);
#endif
draw(pntr, dubins);
if (showTC) {
pntr.setPen(dubinsTCGP);
drawTC(pntr, dubins);
}
}
// drawing FSC path and turning centers (if asked)
if (drawing && drawFSC) {
#ifndef SHOW_TYPE
pntr.setPen(fscpGP);
#endif
draw(pntr, fscp);
if (showTC) {
pntr.setPen(fscpTCGP);
drawTC(pntr, fscp);
}
}
} // end of void paintEvent(QPaintEvent *)
// --------------------------------------------------------------
void ISeeMLDrawingArea::keyPressEvent(QKeyEvent *event) {
static const double PI_196 = M_PI / 196, PI_28 = M_PI / 28,
PI_4 = M_PI / 4;
double newTh = goal.orientation();
bool handled = movingConfig;
if (movingConfig)
switch ( event->key() ) {
case Qt::Key_Home : newTh = 0; break;
case Qt::Key_End : newTh = M_PI; break;
case Qt::Key_PageUp : newTh += PI_4; break;
case Qt::Key_PageDown: newTh -= PI_4; break;
case Qt::Key_Up : newTh += PI_28; break;
case Qt::Key_Down : newTh -= PI_28; break;
case Qt::Key_Right : newTh += PI_196; break;
case Qt::Key_Left : newTh -= PI_196; break;
default: handled = false;
} // end of switch
if (handled) {
// change goal configuration and redraw
OrPtConfig newGoal(goal.position(), newTh);
changeGoal(newGoal);
} // end of if (handled)
else
QWidget::keyPressEvent(event);
} // end of void keyPressEvent(QKeyEvent *)
// === Drawing Handlers =========================================
void ISeeMLDrawingArea::draw(QPainter& pntr, const OrPtConfig& q) {
// coordinates computation
const static int configSize = 20;
QPoint startP = dblPt2intPt( q.position() );
int di = (int)(configSize * cos( q.orientation() )),
dj = - (int)(configSize * sin( q.orientation() )),
hfMn = (di + dj) / 4, delta = (di - dj) / 4,
iF = startP.x() + di, jF = startP.y() + dj;
// drawing
pntr.drawLine(startP.x(), startP.y(), iF, jF);
pntr.drawLine(iF, jF, iF - hfMn, jF + delta);
pntr.drawLine(iF, jF, iF - delta, jF - hfMn);
} // end of void Draw(QPainter&, const OrPtConfig&)
// --------------------------------------------------------------
void ISeeMLDrawingArea::draw(QPainter& pntr,
const DubinsLikePath& P) {
#define rad2int(th) (int)( (th) * 16 * 180 / M_PI )
int i = 0, startOr = rad2int( P.start().orientation() );
QPoint startP = dblPt2intPt( P.start().position() );
#ifdef SHOW_TYPE
/* to show different type of paths */
static const QPen lineGP(QColor("limegreen"), 2),
circleGP(QColor("blue"), 2), transGP(QColor("cyan"), 2);
#endif
while( i < P.nbPieces() ) {
const LinCurvPath& linPiece = P.lcPiece(++i);
QPoint endP = dblPt2intPt( linPiece.end().position() );
// short circular arcs are drawn as complete circles,
// it is easily avoided (no drawing when piece's length
// is smaller than a pixel's maximum size = diagonal)
if (linPiece.length() > 1.5 / scale)
if ( P.isZero( linPiece.sharpness() ) )
// curvature does not change along piece
if ( P.isZero( linPiece.start().curvature() ) ) {
// piece = line segment
#ifdef SHOW_TYPE
pntr.setPen(lineGP);
#endif
pntr.drawLine( startP.x(), startP.y(), endP.x(), endP.y() );
} else { // piece = circular arc
const int endOr = rad2int( linPiece.end().orientation() );
// computing the bounding box (of the circle)
const CurvConfig start = linPiece.start();
const double rad = fabs( 1 / start.curvature() );
const int sgnTrn = P.sign( start.curvature() ),
Pi_2 = 90 * 16;
const iSeeML::geom::Vector diag(1, -1);
const iSeeML::geom::Point cntr = start.position()
+ v.rotate(M_PI_2) * sgnTrn * rad;
// bounding box -> int
const QPoint hautGch = dblPt2intPt(cntr - diag * rad),
basDt = dblPt2intPt(cntr + diag * rad);
// drawing
const int minX = hautGch.x(), minY = hautGch.y(),
dX = basDt.x() - minX, dY = basDt.y() - minY,
startAngle = startOr - sgnTrn * Pi_2;
int diffAngle = endOr - startOr;
if (diffAngle * sgnTrn < 0)
diffAngle += sgnTrn * 360 * 16;
#ifdef SHOW_TYPE
pntr.setPen(circleGP);
#endif
pntr.drawArc(minX, minY, dX, dY, startAngle, diffAngle);
startOr = endOr;
}
else {
// piece = piece of clothoid
const int endOr = rad2int( linPiece.end().orientation() );
const double step = 5. / scale;
double arcLngth = step;
#ifdef SHOW_TYPE
pntr.setPen(transGP);
#endif
while (arcLngth < linPiece.length()) {
QPoint midP =
dblPt2intPt( linPiece[arcLngth].position() );
pntr.drawLine( startP.x(), startP.y(), midP.x(), midP.y() );
startP = midP;
arcLngth += step; }
pntr.drawLine( startP.x(), startP.y(), endP.x(), endP.y() );
startOr = endOr; }
else
pntr.drawLine( startP.x(), startP.y(), endP.x(), endP.y() );
startP = endP;
} // end of while
#undef rad2int
} // end of void Draw(QPainter&, const DubinsLikePath&)
// --------------------------------------------------------------
void ISeeMLDrawingArea::drawTC(QPainter& pntr,
const DubinsLikePath& P) {
// compute turning circles' centers
P.computeCenters(P.start(), P.end(), centers);
int i, radius = (int) (P.turnRadius() * scale);
for(i = 0; i < P.nbTurningCircles; i++)
pntr.drawEllipse(dblPt2intPt(centers[i]), radius, radius);
} // end of void DrawTC(QPainter&, const DubinsLikePath&)
// --------------------------------------------------------------
void ISeeMLDrawingArea::changeGoal(const OrPtConfig& q){
draw(false); // erase the drawing
// change goal config. and paths
goal = q;
dubins.connect(start, goal);
fscp.connect(start, goal);
draw(); // redraw new path(s)
} // end of ChangeGoal(const OrPtConfig&)
//### Main Window ###############################################
// === Constructor ==============================================
ISeeMLFrame::ISeeMLFrame(const QString& title)
: drawingArea(*this, 50) {
int idx;
setWindowTitle(title);
QMenuBar *menuBar = new QMenuBar;
QMenu *menu = new QMenu(tr("File"), menuBar);
menu->addAction(tr("Exit"), this, SLOT( close() ), 'X')
->setStatusTip( tr("Close GUI and quit") );
menuBar->addMenu(menu);
menu = new QMenu(tr("Edit"), menuBar);
menuBar->addMenu(menu);
menu = new QMenu(tr("View"), menuBar);
QMenu *pathVisible = menu->addMenu( tr("Drawn Paths") );
QActionGroup *pathGroup = new QActionGroup(pathVisible);
static const char *pathLabels[nbPathShow][3] =
// label, shortcut and tool tip
{ {"Dubins", "D", "Only draw Dubins' path"},
{"Linear Curvature (FSCP)", "L", "Only draw FSC path"},
{"Both", "B", "Draw both Dubins' and FSC paths"} };
for(idx = 0; idx < nbPathShow; idx++) {
pathShow[idx] = pathVisible->addAction( tr(pathLabels[idx][0]) );
pathGroup->addAction(pathShow[idx]);
pathShow[idx]->setShortcut( QString(pathLabels[idx][1]) );
pathShow[idx]->setToolTip( tr(pathLabels[idx][2]) );
pathShow[idx]->setCheckable(true);
if (idx == bothShow)
pathShow[idx]->setChecked(true);
// indicates to drawing area which paths to draw
connect( pathShow[idx], SIGNAL( triggered() ),
this, SLOT( setPathShow() ) );
}
circleShow = menu->addAction( tr("Show Turning Circles") );
circleShow->setShortcut( QString("T") );
circleShow->setToolTip( tr("Show turning circles for drawn paths") );
circleShow->setCheckable(true);
connect( circleShow, SIGNAL( triggered() ),
this, SLOT( setTurningCircles() ) );
menuBar->addMenu(menu);
menu = new QMenu(tr("About"), menuBar);
menu->addAction(tr("About"), this, SLOT( about() ), 'A')
->setStatusTip( tr("Display information about this application") );
menuBar->addMenu(menu);
setMenuBar(menuBar);
setCentralWidget(&drawingArea);
setMinimumSize(750, 800);
setStatusBar( new QStatusBar() );
QString msg("Clic in the drawing array to change");
statusBar()->showMessage(msg + " goal configuration");
} // fin de ISeeMLFrame::ISeeMLFrame(const QString&)
// === Event Handlers ===========================================
void ISeeMLFrame::about() {
QString message( tr("This is ISeeML V1.1 GUI\n\n") );
message += tr("It uses Qt (www.qt.io),\n")
+ tr("and will probably become\n")
+ tr("ISeeML V2.0 base GUI toolkit.\n\n")
+ tr("Written by A. Scheuer\n")
+ tr("(Alexis.Scheuer@loria.fr)");
QMessageBox::information(this, tr("About ISeeML V1.1 Interface"),
message);
} // end of ISeeMLFrame::about() const
iSeeML::rob::DubinsLikePath::turnRadius
virtual double turnRadius() const =0
Description method, giving the radius of the turning circle (the circle of all the configurations whi...
iSeeML::rob::CurvConfig
This class defines a more precise configuration than classical ones for mobile robots.
Definition: CurvConfig.hpp:30
iSeeML::rob::OrPtConfig::orientation
const double & orientation() const
Description method, giving the orientation of the robot's main axis in this configuration.
Definition: OrPtConfig.hpp:139
iSeeML::rob::LinCurvPath::sharpness
const double & sharpness() const
Description method, giving the path's constant curvature derivative with respect to the arc length.
Definition: LinCurvPath.hpp:106
iSeeML::rob::OrPtConfig
This class defines a standard robotic configuration, containing the position of a reference point and...
Definition: OrPtConfig.hpp:45
iSeeML::rob::DubinsLikePath::nbTurningCircles
@ nbTurningCircles
the number of usefull turning circles (4).
Definition: DubinsLikePath.hpp:66
iSeeML::rob::LinCurvPath::end
const iSeeML::rob::CurvConfig & end() const
Description method, giving the path's final configuration (as a configuration for mobile robot with c...
Definition: LinCurvPath.hpp:98
iSeeML::rob::OrPtConfig::position
const iSeeML::geom::Point & position() const
Description method, giving the position of the robot's reference point in this configuration.
Definition: OrPtConfig.hpp:132
iSeeML::rob::CurvConfig::curvature
const double & curvature() const
Description method, giving the intantaneous curvature, in this configuration, of the curve followed b...
Definition: CurvConfig.hpp:110
iSeeML::rob::LinCurvPath::start
const iSeeML::rob::CurvConfig & start() const
Description method, giving the path's starting configuration.
Definition: LinCurvPath.hpp:91
iSeeML::rob::DubinsLikePath::lcPiece
const iSeeML::rob::LinCurvPath & lcPiece(const int index) const
Description method, returning the (linear curvature) piece of the path with a given index.
Definition: DubinsLikePath.hpp:194
iSeeML::Object::isZero
static bool isZero(const double &x)
Method comparing a double to zero.
Definition: Object.hpp:312
iSeeML::rob::DubinsLikePath
This class defines Dubins like paths, which are similar to Dubins paths.
Definition: DubinsLikePath.hpp:33
iSeeML::rob::CompoundPath::start
const iSeeML::rob::OrPtConfig & start() const
Description method, giving the path's starting configuration (first piece's starting configuration).
Definition: CompoundPath.hpp:79
iSeeML::rob
ISeeML' robotic objects are gathered in this namespace.
Definition: NameSpaces.hpp:33
iSeeML::geom::Vector
This class defines 2D geometric vectors, which can be defined by their Polar or Cartesian coordinates...
Definition: Vector.hpp:34
iSeeML::rob::LinCurvPath::length
double length() const
Description method, giving the path's length.
Definition: LinCurvPath.hpp:109
iSeeML::rob::DubinsLikePath::nbPieces
int nbPieces() const
Description method, giving the number of pieces of the path.
Definition: DubinsLikePath.hpp:164
iSeeML::rob::CompoundPath::end
const iSeeML::rob::OrPtConfig & end() const
Description method, giving the path's final configuration (last piece's starting configuration).
Definition: CompoundPath.hpp:88
iSeeML::geom::Point
This class defines 2D geometric points, defined by their Cartesian coordinates.
Definition: Point.hpp:29
iSeeML::Object::sign
static int sign(const double &x)
Method giving the sign of a double.
Definition: Object.hpp:325
iSeeML::rob::DubinsLikePath::computeCenters
virtual void computeCenters(const iSeeML::rob::OrPtConfig &start, const iSeeML::rob::OrPtConfig &goal, iSeeML::geom::Point centers[nbTurningCircles]) const =0
Various method, computing the set of circle centers used to turn from starting configuration of the c...
iSeeML::rob::LinCurvPath
This class defines linear curvature elementary paths, for which the curvature derivative (with respec...
Definition: LinCurvPath.hpp:27