ISeeML  1.0
wxGuiFwd.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) wxWindows. Graphical objects' classes will be defined in ISeeML v1.1 to build a GUI toolkit (using either wxWindows or some Java classes).

//
// This program defines a real-time wxWindows interface drawing
// ISeeML' forward-only paths connecting two configurations
//
// To get the definition of wxWindows classes
#include "wx/wxprec.h" // for compilers that support precompilation
#ifdef __BORLANDC__ // addition for Borland C++
#pragma hdrstop
#endif
#ifndef WX_PRECOMP // for other compilers
#include "wx/wx.h"
#endif
// To get the definition of ISeeML' forward paths (Dubins & FSC).
#include <iSeeML/fwdPaths>
// To avoid writing ISeeML::Rob::...
using namespace iSeeML::rob;;
//### wxWindows Drawing Area ####################################
class ISeeMLDrawingArea : public wxPanel {
wxFrame *parentFrame;
// Graphic Properties of drawing
const wxBrush *daGP, *cvGP; // drawing area and curve fill GP
const wxPen *bgGP, // background pen
*configGP, // configurations pen
*dubinsGP, *fscpGP, // Dubins' and FSCP paths pens
*dubinsTCGP, *fscpTCGP; // turning circles' pens
wxPoint centerPoint; // middle point of drawing area
double scale; // drawing scale
static const OrPtConfig start; // path(s) starting config.
OrPtConfig goal; // goal config
bool movingConfig; // does goal config follow mouse
double maxCurv, maxDCurv; // max. curvature and derivative
DubinsPath dubins; // Dubins' path
FscPath fscp; // forward continuous-curvature path
public:
// === Constructor ============================================
ISeeMLDrawingArea(wxFrame& parent, double scl) :
wxPanel(&parent), parentFrame(&parent),
scale(scl), movingConfig(false),
maxCurv(1), maxDCurv(0.8),
dubins(start, goal, maxCurv),
fscp(start, goal, maxCurv, maxDCurv) {
daGP = wxWHITE_BRUSH;
cvGP = wxTRANSPARENT_BRUSH;
bgGP = wxWHITE_PEN;
configGP = wxRED_PEN;
dubinsGP = new wxPen( wxT("BLUE") );
fscpGP = wxGREEN_PEN;
dubinsTCGP = new wxPen( wxT("ORANGE"), 1, wxLONG_DASH );
fscpTCGP = new wxPen( wxT("GOLD"), 1, wxLONG_DASH );
SetBackgroundColour(*wxWHITE);
}
// === Event Handlers =========================================
void OnMouseClick(wxMouseEvent& event);
void OnMouseMove (wxMouseEvent& event);
void OnMouseWheel(wxMouseEvent& event);
void OnSize (wxSizeEvent& event);
void OnPaint (wxPaintEvent& event);
void OnChar (wxKeyEvent& event);
private:
// Conversion from real points to pixel points
wxPoint dblPt2intPt(const iSeeML::geom::Point& p) const {
return wxPoint( centerPoint.x + (int)(p.xCoord() * scale),
centerPoint.y + (int)(p.yCoord() * scale) );
}
// === Drawing Handlers =======================================
void Draw (wxDC& dc, const OrPtConfig& q);
void Draw (wxDC& dc, const DubinsLikePath& P);
void DrawTC(wxDC& dc, const DubinsLikePath& P);
void Draw (wxDC& dc, const bool erase = false);
void ChangeGoal(const OrPtConfig& q);
DECLARE_EVENT_TABLE()
}; // end of class ISeeMLDrawingArea
//### wxWindows Window ##########################################
class ISeeMLFrame : public wxFrame
{
enum { ID_MENU_EDIT_CONFIG = 1, ID_MENU_EDIT_MAXIMA,
ID_MENU_EDIT_ZOOM, ID_MENU_VIEW_PATH,
ID_MENU_VIEW_PATH_DUBINS, ID_MENU_VIEW_PATH_FSCP,
ID_MENU_VIEW_PATH_BOTH, ID_MENU_VIEW_TURN_CIRCLES
};
wxMenu* pathSubMenu;
ISeeMLDrawingArea* drawingArea;
public:
// === Constructor ============================================
ISeeMLFrame(const wxString& title);
// === Descriptions ===========================================
bool DrawDubins() const {
return ( pathSubMenu->IsChecked(ID_MENU_VIEW_PATH_DUBINS) ||
pathSubMenu->IsChecked(ID_MENU_VIEW_PATH_BOTH) );}
bool DrawFSCP() const {
return ( pathSubMenu->IsChecked(ID_MENU_VIEW_PATH_FSCP) ||
pathSubMenu->IsChecked(ID_MENU_VIEW_PATH_BOTH) ); }
bool ShowTurnCircles() const {
wxMenu *editMenu = GetMenuBar()->GetMenu(2);
return editMenu->IsChecked(ID_MENU_VIEW_TURN_CIRCLES); }
// === Modifications =========================================
void enableSave(bool enable)
{ GetMenuBar()->GetMenu(0)->Enable(wxID_SAVEAS, enable); }
// === Event Handlers =========================================
// --- File Menu's Events -------------------------------------
void OnSaveAs(wxCommandEvent& event);
void OnQuit (wxCommandEvent& WXUNUSED(event)) { Close(TRUE); }
// --- Edit Menu's Events -------------------------------------
void OnSetGoal (wxCommandEvent& event);
void OnSetMaxima (wxCommandEvent& event);
void OnSetZoom (wxCommandEvent& event);
// --- View Menu's Events -------------------------------------
void OnSelectPath (wxCommandEvent& event);
void OnShowTurnCirc(wxCommandEvent& WXUNUSED(event))
{ drawingArea->Refresh(); }
// --- Help Menu's Events -------------------------------------
void OnAbout (wxCommandEvent& event);
private:
DECLARE_EVENT_TABLE()
}; // end of class ISeeMLFrame
//### wxWindows application #####################################
class ISeeMLApp : public wxApp
{
bool OnInit()
{
ISeeMLFrame *frame =
new ISeeMLFrame( wxT("ISeeML V1.0 Interface") );
frame->Show(TRUE);
SetTopWindow(frame);
return TRUE;
} // end of bool OnInit()
}; // end of class ISeeMLApp
IMPLEMENT_APP(ISeeMLApp)
//### wxWindows Drawing Area ####################################
// === Static Field =============================================
const OrPtConfig ISeeMLDrawingArea::start
// === Event Handlers ===========================================
BEGIN_EVENT_TABLE(ISeeMLDrawingArea, wxPanel)
EVT_LEFT_DOWN (ISeeMLDrawingArea::OnMouseClick)
EVT_MOTION (ISeeMLDrawingArea::OnMouseMove)
EVT_MOUSEWHEEL (ISeeMLDrawingArea::OnMouseWheel)
EVT_SIZE (ISeeMLDrawingArea::OnSize)
EVT_PAINT (ISeeMLDrawingArea::OnPaint)
EVT_KEY_DOWN (ISeeMLDrawingArea::OnChar)
END_EVENT_TABLE()
// --- Mouse Click Handler --------------------------------------
void ISeeMLDrawingArea::OnMouseClick(wxMouseEvent&
event)
{
movingConfig = !movingConfig;
OnMouseMove(event);
wxString message;
if (movingConfig) {
((ISeeMLFrame *) parentFrame)->enableSave(false);
message.Printf(wxT("Clic to fix, use arrows or")
wxT(" page up/down to change orientation")
); }
else {
((ISeeMLFrame *) parentFrame)->enableSave(true);
message.Printf(wxT("Clic to change again")
wxT(" goal configuration")
); }
parentFrame->SetStatusText(message);
} // end of ISeeMLDrawingArea::OnMouseClick(...)
// --- Mouse Motion Handler -------------------------------------
void ISeeMLDrawingArea::OnMouseMove(wxMouseEvent
&event)
{
if (movingConfig) {
// prepare the device context associated to the drawing area
wxClientDC dc(this);
PrepareDC(dc);
parentFrame->PrepareDC(dc);
// get mouse coordinates in this device context
wxPoint pos = event.GetPosition();
int i = (int) dc.DeviceToLogicalX(pos.x),
j = (int) dc.DeviceToLogicalY(pos.y);
// change goal configuration and redraw
OrPtConfig newGoal( (i - centerPoint.x) / scale,
(j - centerPoint.y) / scale,
goal.orientation() );
ChangeGoal(newGoal);
} // end of if (movingConfig)
} // end of ISeeMLDrawingArea::OnMouseMove(...)
// --- Mouse's Wheel Motion Handler -----------------------------
void ISeeMLDrawingArea::OnMouseWheel(wxMouseEvent
&event)
{
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.GetWheelRotation() );
// using event.GetWheelDelta() does not work (it returns 0)
double newTh = goal.orientation();
if ( event.ShiftDown() ) // SHIFT => big changes
newTh += change * PI_4;
else if ( event.MiddleIsDown() ) // wheel pressed & not SHIFT
newTh += change * PI_196; // => small changes
else newTh += change * PI_28; // else normal changes
// change goal configuration and redraw
OrPtConfig newGoal(goal.position(), newTh);
ChangeGoal(newGoal);
} // end of ISeeMLDrawingArea::OnMouseWheel(...)
// --- Window's Resize Handler ----------------------------------
void ISeeMLDrawingArea::OnSize(wxSizeEvent &event)
{
wxSize size = event.GetSize();
centerPoint.x = size.GetWidth() / 2;
centerPoint.y = size.GetHeight() / 2;
Refresh();
} // end of ISeeMLDrawingArea::OnSize(wxSizeEvent&)
// --- Window's Refresh Handler ---------------------------------
void ISeeMLDrawingArea::OnPaint(wxPaintEvent &event)
{
// prepare the device context associated to the drawing area
wxClientDC dc(this);
PrepareDC(dc);
parentFrame->PrepareDC(dc);
// redraw background
dc.StartPage(); // replace BeginDrawing() ?
ClearBackground();
// redraw path(s) and configuations
dc.SetBrush(*cvGP);
Draw(dc);
dc.EndPage(); // replace EndDrawing() ?
} // end of ISeeMLDrawingArea::OnPaint(wxPaintEvent&)
// --- Keyboard Handler -----------------------------------------
void ISeeMLDrawingArea::OnChar(wxKeyEvent &event)
{
static const double PI_196 = M_PI / 196, PI_28 = M_PI / 28,
PI_4 = M_PI / 4;
double newTh = goal.orientation();
if (movingConfig)
switch ( event.GetKeyCode() ) {
case WXK_HOME : newTh = 0; break;
case WXK_END : newTh = M_PI; break;
case WXK_PAGEDOWN: newTh += PI_4; break;
case WXK_PAGEUP : newTh -= PI_4; break;
case WXK_DOWN : newTh += PI_28; break;
case WXK_UP : newTh -= PI_28; break;
case WXK_RIGHT : newTh += PI_196; break;
case WXK_LEFT : newTh -= PI_196; break;
default : event.Skip();
} // end of switch
else
event.Skip();
// change goal configuration and redraw
OrPtConfig newGoal(goal.position(), newTh);
ChangeGoal(newGoal);
} // end of ISeeMLDrawingArea::OnChar(wxKeyEvent&)
// === Drawing Handlers =========================================
// --- Configuration Drawing ------------------------------------
void ISeeMLDrawingArea::Draw(wxDC& dc, const OrPtConfig& q)
{
// coordinates computation
const static int configSize = 20;
wxPoint startP = dblPt2intPt( q.position() );
int di = (int)(configSize * cos( q.orientation() )),
dj = (int)(configSize * sin( q.orientation() )),
iF = startP.x + di, jF = startP.y + dj;
// drawing
dc.DrawLine(startP.x, startP.y, iF, jF);
dc.DrawLine(iF, jF, iF - (di + dj) / 4, jF + (di - dj) / 4);
dc.DrawLine(iF, jF, iF - (di - dj) / 4, jF - (di + dj) / 4);
} // end of ISeeMLDrawingArea::Draw(... OrPtConfig&)
// --- Path Drawing ---------------------------------------------
void ISeeMLDrawingArea::Draw(wxDC& dc, const DubinsLikePath& P)
{
int i = 0;
wxPoint startP = dblPt2intPt( P.start().position() );
while( i < P.nbPieces() ) {
const LinCurvPath& linPiece = P.lcPiece(++i);
wxPoint 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
dc.DrawLine(startP.x, startP.y, endP.x, endP.y);
else {
// piece = circular arc
wxPoint cntr = dblPt2intPt
( linPiece.start().position() +
v.rotate(M_PI_2) / linPiece.start().curvature() );
if (linPiece.start().curvature() > 0)
dc.DrawArc(endP.x, endP.y, startP.x, startP.y,
cntr.x, cntr.y);
else
dc.DrawArc(startP.x, startP.y, endP.x, endP.y,
cntr.x, cntr.y); } // enf of else (circ arc)
else {
// piece = piece of clothoid
double step = 5. / scale, arcLngth = step;
while (arcLngth < linPiece.length()) {
wxPoint midP =
dblPt2intPt( linPiece[arcLngth].position() );
dc.DrawLine(startP.x, startP.y, midP.x, midP.y);
startP = midP;
arcLngth += step; }
dc.DrawLine(startP.x, startP.y, endP.x, endP.y); }
else
dc.DrawLine(startP.x, startP.y, endP.x, endP.y);
startP = endP;
} // end of while
} // end of ISeeMLDrawingArea::Draw(...Path&)
// --- Path's Turning Circles Drawing ---------------------------
void ISeeMLDrawingArea::DrawTC(wxDC& dc, 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++)
dc.DrawCircle(dblPt2intPt(centers[i]), radius);
} // end of ISeeMLDrawingArea::DrawTC(...Path&)
// --- Drawing / Erasing Everything -----------------------------
void ISeeMLDrawingArea::Draw(wxDC& dc, const bool clean)
{
// graphical properties setting
if (clean) dc.SetPen(*bgGP);
else dc.SetPen(*configGP);
dc.SetBrush(*cvGP);
// drawing starting and goal configurations
Draw(dc, start);
Draw(dc, goal);
ISeeMLFrame *parent =
(ISeeMLFrame *) parentFrame;
// drawing paths
if ( clean || parent->DrawDubins() ) {
if (!clean) dc.SetPen(*dubinsGP);
Draw(dc, dubins);
if ( clean || parent->ShowTurnCircles() ) {
if (!clean) dc.SetPen(*dubinsTCGP);
DrawTC(dc, dubins); } }
if ( clean || parent->DrawFSCP() ) {
if (!clean) dc.SetPen(*fscpGP);
Draw(dc, fscp);
if ( clean || parent->ShowTurnCircles() ) {
if (!clean) dc.SetPen(*fscpTCGP);
DrawTC(dc, fscp); } }
} // end of ISeeMLDrawingArea::Draw(..., const wxPen&)
// --- Goal Config. Change --------------------------------------
void ISeeMLDrawingArea::ChangeGoal(const OrPtConfig&
q)
{
// prepare the device context associated to the drawing area
wxClientDC dc(this);
PrepareDC(dc);
parentFrame->PrepareDC(dc);
dc.StartPage(); // replace BeginDrawing() ?
// erase previous path(s) (faster than dc.clear()!)
Draw(dc, true);
// change goal config. and paths
goal = q;
dubins.connect(start, goal);
fscp.connect(start, goal);
// redraw new path(s)
Draw(dc);
dc.EndPage(); // replace EndDrawing() ?
} // end of ISeeMLDrawingArea::ChangeGoal(...)
//### wxWindows Window ##########################################
// === Constructor ==============================================
ISeeMLFrame::ISeeMLFrame(const wxString& title)
: wxFrame((wxFrame *) NULL, -1, title, wxDefaultPosition,
wxSize(750, 800))
{
// Shortcuts used in menus :
// A, B, C, D, L, M, S, T, X, Z
wxMenuBar *menuBar = new wxMenuBar;
wxMenu *menu = new wxMenu;
menu->Append( wxID_SAVEAS, wxT("&Save/Export as...\tS"),
wxT("Save drawing into various formats") );
menu->AppendSeparator();
menu->Append( wxID_EXIT, wxT("E&xit\tX"),
wxT("Close GUI and quit") );
menuBar->Append( menu, wxT("File") );
menu = new wxMenu;
menu->Append( ID_MENU_EDIT_CONFIG,
wxT("Set Goal &Configuration...\tC"),
wxT("Not yet available!") );
menu->AppendSeparator();
menu->Append( ID_MENU_EDIT_MAXIMA,
wxT("Modify Paths' &Maxima...\tM"),
wxT("Not yet available!") );
menu->AppendSeparator();
menu->Append( ID_MENU_EDIT_ZOOM, wxT("Change &Zoom/Scale...\tZ"),
wxT("Not yet available!") );
menuBar->Append( menu, wxT("Edit") );
menu = new wxMenu;
pathSubMenu = new wxMenu;
pathSubMenu->AppendCheckItem( ID_MENU_VIEW_PATH_DUBINS,
wxT("&Dubins'\tD"),
wxT("Only draw Dubin's path") );
pathSubMenu->AppendCheckItem( ID_MENU_VIEW_PATH_FSCP,
wxT("&Linear Curvature (FSCP)\tL"),
wxT("Only draw FSC path") );
pathSubMenu->AppendCheckItem( ID_MENU_VIEW_PATH_BOTH,
wxT("&Both\tB"),
wxT("Draw both Dubin's and FSC paths") );
pathSubMenu->Check(ID_MENU_VIEW_PATH_DUBINS, true);
menu->Append(ID_MENU_VIEW_PATH, wxT("Draw Paths"), pathSubMenu);
menu->AppendCheckItem( ID_MENU_VIEW_TURN_CIRCLES,
wxT("Show &Turning Circles...\tT"),
wxT("Display turning circles")
wxT(" of drawn path(s)") );
menuBar->Append( menu, wxT("View") );
menu = new wxMenu;
menu->Append( wxID_ABOUT, wxT("&About...\tA"),
wxT("Display information about this application") );
menuBar->Append( menu, wxT("Help") );
SetMenuBar(menuBar);
CreateStatusBar();
wxString message;
message.Printf(wxT("Clic in the drawing array to change")
wxT(" goal configuration")
);
SetStatusText(message);
drawingArea = new ISeeMLDrawingArea(*this, 50);
} // end of ISeeMLFrame(const wxString&, ...)
// === Event Handlers ===========================================
BEGIN_EVENT_TABLE(ISeeMLFrame, wxFrame)
// --- File Menu ---
EVT_MENU (wxID_SAVEAS, ISeeMLFrame::OnSaveAs)
EVT_MENU (wxID_EXIT, ISeeMLFrame::OnQuit)
// --- Edit Menu ---
EVT_MENU (ID_MENU_EDIT_CONFIG,
ISeeMLFrame::OnSetGoal)
EVT_MENU (ID_MENU_EDIT_MAXIMA,
ISeeMLFrame::OnSetMaxima)
EVT_MENU (ID_MENU_EDIT_ZOOM,
ISeeMLFrame::OnSetZoom)
// --- View Menu ---
EVT_MENU (ID_MENU_VIEW_PATH_DUBINS,
ISeeMLFrame::OnSelectPath)
EVT_MENU (ID_MENU_VIEW_PATH_FSCP,
ISeeMLFrame::OnSelectPath)
EVT_MENU (ID_MENU_VIEW_PATH_BOTH,
ISeeMLFrame::OnSelectPath)
EVT_MENU (ID_MENU_VIEW_TURN_CIRCLES,
ISeeMLFrame::OnShowTurnCirc)
// --- Help Menu ---
EVT_MENU (wxID_ABOUT, ISeeMLFrame::OnAbout)
END_EVENT_TABLE()
// --- "Save/Export as..." MenuItem Handler ---------------------
void ISeeMLFrame::OnSaveAs(wxCommandEvent&
WXUNUSED(event))
{
SetStatusText( wxT("Sorry, exporting drawing ")
wxT("is not yet implemented!") );
} // end of ISeeMLFrame::OnSaveAs(wxCommandEvent&)
// --- "Set Goal Configuration..." MenuItem Handler -------------
void ISeeMLFrame::OnSetGoal(wxCommandEvent&
WXUNUSED(event))
{
SetStatusText( wxT("Sorry, goal definition's dialog box ")
wxT("is not yet implemented!") );
} // end of ISeeMLFrame::OnSetGoal(wxCommandEvent&)
// --- "Modify Paths' Maxima" MenuItem Handler ------------------
void ISeeMLFrame::OnSetMaxima(wxCommandEvent&
WXUNUSED(event))
{
SetStatusText( wxT("Sorry, maxima changing is not yet implemented!") );
} // end of ISeeMLFrame::OnSetMaxima(wxCommandEvent&)
// --- "Change Zoom/Scale" MenuItem Handler ---------------------
void ISeeMLFrame::OnSetZoom(wxCommandEvent&
WXUNUSED(event))
{
SetStatusText( wxT("Sorry, zoom/scale changing ")
wxT("is not yet implemented!") );
} // end of ISeeMLFrame::OnSetZoom(wxCommandEvent&)
// --- "Draw Paths" MenuItem Handler ----------------------------
void ISeeMLFrame::OnSelectPath(wxCommandEvent& event)
{
pathSubMenu->Check(ID_MENU_VIEW_PATH_DUBINS, false);
pathSubMenu->Check(ID_MENU_VIEW_PATH_FSCP, false);
pathSubMenu->Check(ID_MENU_VIEW_PATH_BOTH, false);
pathSubMenu->Check(event.GetId(), true);
drawingArea->Refresh();
} // end of ISeeMLFrame::OnSelectPath(wxCommandEvent&)
// --- "About..." MenuItem Handler ------------------------------
void ISeeMLFrame::OnAbout(wxCommandEvent&
WXUNUSED(event))
{
wxString message;
message.Printf(wxT("This is ISeeML V1.0 GUI\n\n")
wxT("It uses wxWidgets (www.wxWidgets.org),\n")
wxT("which should become\n")
wxT("ISeeML V1.1 base GUI toolkit.\n\n")
wxT("Written by A. Scheuer\n")
wxT("(Alexis.Scheuer@inria.fr)")
);
wxMessageBox(message,
_T("About ISeeML V1.0 Interface"),
wxOK | wxICON_INFORMATION, this);
} // end of ISeeMLFrame::OnAbout(wxCommandEvent&)