diff --git a/src/Charts/RCanvas.cpp b/src/Charts/RCanvas.cpp new file mode 100644 index 000000000..d4b33bbe4 --- /dev/null +++ b/src/Charts/RCanvas.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016 Mark Liversedge (liversedge@gmail.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "RCanvas.h" + +#include "Context.h" +#include "Colors.h" + +RCanvas::RCanvas(Context *context, QWidget *parent) : QGraphicsView(parent), context(context) +{ + // no frame, its ugly + setFrameStyle(QFrame::NoFrame); + + // add a scene + scene = new QGraphicsScene(this); + this->setScene(scene); + + // flip horizontal as y-co-ordinates place the + // origin in the bottom left, not screen co-ords + // of top left. x axis is fine though. + scale(1,-1); + + // set colors etc + configChanged(CONFIG_APPEARANCE); + + // connect + connect(context, SIGNAL(configChanged(qint32)), this, SLOT(configChanged(qint32))); +} + +void +RCanvas::configChanged(qint32) +{ + // set background etc to the prevailing defaults + QPalette p = palette(); + p.setColor(QPalette::Base, GColor(CPLOTBACKGROUND)); + p.setColor(QPalette::Text, GCColor::invertColor(GColor(CPLOTBACKGROUND))); + setPalette(p); +} + +void +RCanvas::newPage() +{ + scene->clear(); +} + +void +RCanvas::circle(double x, double y, double r, QPen p, QBrush b) +{ + QGraphicsEllipseItem *c=new QGraphicsEllipseItem(x,y,r+r,r+r); + c->setPen(p); + c->setBrush(b); + scene->addItem(c); +} + +void +RCanvas::line(double x1, double y1, double x2, double y2, QPen p) +{ + QGraphicsLineItem *l=new QGraphicsLineItem(x1,y1,x2,y2); + l->setPen(p); + scene->addItem(l); +} + +void +RCanvas::polyline(int n, double *x, double *y, QPen p) +{ + // origin + double x0=x[0], y0=y[0]; + + // create a line per path + for(int i=1; isetPen(p); + r->setBrush(b); + scene->addItem(r); +} diff --git a/src/Charts/RCanvas.h b/src/Charts/RCanvas.h new file mode 100644 index 000000000..57a563772 --- /dev/null +++ b/src/Charts/RCanvas.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016 Mark Liversedge (liversedge@gmail.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _GC_RCanvas_h +#define _GC_RCanvas_h 1 + +#include +#include +#include +#include + +class Context; + +// the chart +class RCanvas : public QGraphicsView { + + Q_OBJECT + + public: + RCanvas(Context *, QWidget *parent); + + public slots: + void configChanged(qint32); + + // graphic events + void newPage(); + void circle(double x, double y, double r, QPen, QBrush); + void line(double x1, double y1, double x2, double y2, QPen); + void polyline(int n, double *x, double *y, QPen); + void rectangle(double x0, double y0, double x1, double y1,QPen,QBrush); + + protected: + QGraphicsScene *scene; + + private: + QWidget *parent; + Context *context; +}; + + +#endif diff --git a/src/Charts/RChart.cpp b/src/Charts/RChart.cpp index 7d6dfe50d..775aace7b 100644 --- a/src/Charts/RChart.cpp +++ b/src/Charts/RChart.cpp @@ -25,9 +25,9 @@ // unique identifier for each chart static int id=0; -RConsole::RConsole(Context *context, QWidget *parent) +RConsole::RConsole(Context *context, RChart *parent) : QTextEdit(parent) - , context(context), localEchoEnabled(true) + , context(context), localEchoEnabled(true), parent(parent) { setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); setFrameStyle(QFrame::NoFrame); @@ -138,6 +138,8 @@ void RConsole::keyPressEvent(QKeyEvent *e) // set the context for the call - used by all the // R functions to access the athlete data/model etc rtool->context = context; + rtool->canvas = parent->canvas; + (*rtool->R)["GC.athlete"] = context->athlete->cyclist.toStdString(); (*rtool->R)["GC.athlete.home"] = context->athlete->home->root().absolutePath().toStdString(); @@ -174,7 +176,7 @@ void RConsole::keyPressEvent(QKeyEvent *e) // clear context rtool->context = NULL; - + rtool->canvas = NULL; } // next prompt @@ -243,14 +245,11 @@ RChart::RChart(Context *context) : GcChartWindow(context) console = new RConsole(context, this); splitter->addWidget(console); - QWidget *surface = new QSvgWidget(this); - surface->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); - splitter->addWidget(surface); - QPalette p = palette(); - p.setColor(QPalette::Base, GColor(CPLOTBACKGROUND)); - p.setColor(QPalette::Text, GCColor::invertColor(GColor(CPLOTBACKGROUND))); - surface->setPalette(p); + + canvas = new RCanvas(context, this); + canvas->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + splitter->addWidget(canvas); } } diff --git a/src/Charts/RChart.h b/src/Charts/RChart.h index 3538dcebb..326cf6f8f 100644 --- a/src/Charts/RChart.h +++ b/src/Charts/RChart.h @@ -26,13 +26,14 @@ #include #include #include -#include #include #include "GoldenCheetah.h" #include "Context.h" #include "Athlete.h" +#include "RCanvas.h" +class RChart; // a console widget to type commands and display response class RConsole : public QTextEdit { @@ -47,7 +48,7 @@ public slots: void rMessage(QString); public: - explicit RConsole(Context *context, QWidget *parent = 0); + explicit RConsole(Context *context, RChart *parent = 0); void putData(QString data); void putData(QColor color, QString data); @@ -70,6 +71,7 @@ protected: private: Context *context; bool localEchoEnabled; + RChart *parent; }; // the chart @@ -80,10 +82,12 @@ class RChart : public GcChartWindow { public: RChart(Context *context); + // receives all the events + RCanvas *canvas; + protected: QSplitter *splitter; RConsole *console; - QSvgWidget *surface; private: Context *context; diff --git a/src/Charts/RGraphicsDevice.cpp b/src/Charts/RGraphicsDevice.cpp index 3d12ceab0..f7e5b0d7d 100644 --- a/src/Charts/RGraphicsDevice.cpp +++ b/src/Charts/RGraphicsDevice.cpp @@ -32,10 +32,17 @@ RGraphicsDevice::RGraphicsDevice () // set the inital graphics device to GC createGD(); - // add GC.display() to create a new device + // once only initialisation initialize(); } +bool RGraphicsDevice::initialize() +{ + // register device creation routine + (*(rtool->R))["GC.display"] = Rcpp::InternalFunction(GCdisplay); + return true; +} + void RGraphicsDevice::NewPage(const pGEcontext gc, pDevDesc dev) { qDebug()<<"RGD: NewPage"; @@ -43,10 +50,10 @@ void RGraphicsDevice::NewPage(const pGEcontext gc, pDevDesc dev) //XXXqDebughandler::newPage(gc, dev); // fire event (pass previousPageSnapshot) - if (!rtool || !rtool->dev || !rtool->dev->gcGEDevDesc) return; + if (!rtool || !rtool->canvas) return; // replay snapshot - //SEXP previousPageSnapshot = rtool->dev->gcGEDevDesc->savedSnapshot; + rtool->canvas->newPage(); } Rboolean RGraphicsDevice::NewFrameConfirm_(pDevDesc dd) @@ -89,8 +96,8 @@ void RGraphicsDevice::Clip(double x0, double x1, double y0, double y1, pDevDesc void RGraphicsDevice::Rect(double x0, double y0, double x1, double y1, const pGEcontext gc, pDevDesc dev) { - qDebug()<<"RGD: Rect"; - //XXXqDebughandler::rect(x0, y0, x1, y1, gc, dev); + //XXX todo honour colors + if (rtool && rtool->canvas) rtool->canvas->rectangle(x0,y0,x1,y1,QPen(Qt::white),QBrush(Qt::NoBrush)); } void RGraphicsDevice::Path(double *x, double *y, int npoly, int *nper, Rboolean winding, const pGEcontext gc, pDevDesc dd) @@ -114,21 +121,20 @@ SEXP RGraphicsDevice::Cap(pDevDesc dd) void RGraphicsDevice::Circle(double x, double y, double r, const pGEcontext gc, pDevDesc dev) { - qDebug()<<"RGD: Circle"; - //XXXqDebughandler::circle(x, y, r, gc, dev); + //XXX todo honour colors + if (rtool && rtool->canvas) rtool->canvas->circle(x,y,r,QPen(Qt::white),QBrush(Qt::NoBrush)); } void RGraphicsDevice::Line(double x1, double y1, double x2, double y2, const pGEcontext gc, pDevDesc dev) { - qDebug()<<"RGD: Line"; - //XXXqDebughandler::line(x1, y1, x2, y2, gc, dev); + //XXX todo honour colors + if (rtool && rtool->canvas) rtool->canvas->line(x1,y1,x2,y2,QPen(Qt::white)); } void RGraphicsDevice::Polyline(int n, double *x, double *y, const pGEcontext gc, pDevDesc dev) { - qDebug()<<"RGD: PolyLine"<canvas && n > 1) rtool->canvas->polyline(n,x,y,QPen(Qt::white)); } void RGraphicsDevice::Polygon(int n, double *x, double *y, const pGEcontext gc, pDevDesc dev) @@ -177,38 +183,6 @@ void RGraphicsDevice::TextUTF8(double x, double y, const char *str, double rot, //XXXqDebughandler::text(x, y, str, rot, hadj, gc, dev); } -Rboolean RGraphicsDevice::Locator(double *x, double *y, pDevDesc dev) -{ - qDebug()<<"RGD: Locator"; -#if 0 - if (s_locatorFunction) - { - s_graphicsDeviceEvents.onDrawing(); - - if(s_locatorFunction(x,y)) - { - // if our graphics device went away while we were waiting - // for locator input then we need to return false - - if (s_pGEDevDesc != NULL) - return TRUE; - else - return FALSE; - } - else - { - s_graphicsDeviceEvents.onDrawing(); - return FALSE; - } - } - else - { - return FALSE; - } -#endif - return FALSE; -} - void RGraphicsDevice::Activate(pDevDesc dev) { qDebug()<<"RGD: Activate"; @@ -359,7 +333,7 @@ SEXP RGraphicsDevice::createGD() pDev->line = RGraphicsDevice::Line; pDev->polyline = RGraphicsDevice::Polyline; pDev->polygon = RGraphicsDevice::Polygon; - pDev->locator = RGraphicsDevice::Locator; + pDev->locator = NULL; pDev->mode = RGraphicsDevice::Mode; pDev->metricInfo = RGraphicsDevice::MetricInfo; pDev->strWidth = RGraphicsDevice::StrWidth; @@ -382,7 +356,7 @@ SEXP RGraphicsDevice::createGD() pDev->haveTransparentBg = 2; pDev->haveRaster = 2; pDev->haveCapture = 1; - pDev->haveLocator = 2; + pDev->haveLocator = 0; //XXX todo - not sure what we might need pDev->deviceSpecific = NULL; @@ -430,10 +404,6 @@ bool RGraphicsDevice::isActive() return gcGEDevDesc != NULL && Rf_ndevNumber(gcGEDevDesc->dev) == Rf_curDevice(); } -SEXP RGraphicsDevice::GCactivate() -{ - return rtool->dev->activateGD(); -} SEXP RGraphicsDevice::activateGD() { @@ -443,13 +413,6 @@ SEXP RGraphicsDevice::activateGD() return R_NilValue; } -#if 0 -DisplaySize RGraphicsDevice::displaySize() -{ - return DisplaySize(s_width, s_height); -} -#endif - double RGraphicsDevice::grconvert(double val, const std::string& type, const std::string& from, const std::string& to) { //XXXr::exec::RFunction grconvFunc("graphics:::grconvert" + type, val, from, to); @@ -546,47 +509,6 @@ const int kDefaultWidth = 500; const int kDefaultHeight = 500; const double kDefaultDevicePixelRatio = 1.0; -bool RGraphicsDevice::initialize() -{ -#if 0 - // initialize shadow handler - //XXXX ???? r::session::graphics::handler::installShadowHandler(); - - // save reference to locator function - //XXX don't want this ???? s_locatorFunction = locatorFunction; - - // device conversion functions - UnitConversionFunctions convert; - convert.deviceToUser = deviceToUser; - convert.deviceToNDC = deviceToNDC; - - // create plot manager (provide functions & events) - GraphicsDeviceFunctions graphicsDevice; - graphicsDevice.isActive = isActive; - graphicsDevice.displaySize = displaySize; - graphicsDevice.convert = convert; - graphicsDevice.saveSnapshot = saveSnapshot; - graphicsDevice.restoreSnapshot = restoreSnapshot; - graphicsDevice.copyToActiveDevice = copyToActiveDevice; - graphicsDevice.imageFileExtension = imageFileExtension; - graphicsDevice.close = close; - graphicsDevice.onBeforeExecute = onBeforeExecute; - Error error = plotManager().initialize(graphicsPath, - graphicsDevice, - &s_graphicsDeviceEvents); - if (error) - return error; - - // set size - setSize(kDefaultWidth, kDefaultHeight, kDefaultDevicePixelRatio); - -#endif - - // register device creation routine - (*(rtool->R))["GC.display"] = Rcpp::InternalFunction(GCdisplay); - return true; -} - void RGraphicsDevice::setDeviceAttributes(pDevDesc pDev) { double pointsize = 12; diff --git a/src/Charts/RGraphicsDevice.h b/src/Charts/RGraphicsDevice.h index 193149947..bfe010e27 100644 --- a/src/Charts/RGraphicsDevice.h +++ b/src/Charts/RGraphicsDevice.h @@ -52,7 +52,6 @@ class RGraphicsDevice { // exported as R methods static SEXP GCdisplay(); // R> GC.display() - static SEXP GCactivate(); // R> GC.activate() // R Graphic Device API methods static void NewPage(const pGEcontext gc, pDevDesc dev); @@ -76,7 +75,6 @@ class RGraphicsDevice { static void Activate(pDevDesc dev); static void Deactivate(pDevDesc dev); static void Close(pDevDesc dev); - static Rboolean Locator(double *x, double *y, pDevDesc dev); static void OnExit(pDevDesc dd); static int HoldFlush(pDevDesc dd, int level); @@ -95,6 +93,7 @@ class RGraphicsDevice { SEXP createGD(); SEXP activateGD(); bool initialize(); + void setDeviceAttributes(pDevDesc pDev); bool isActive(); bool makeActive(); @@ -104,9 +103,10 @@ class RGraphicsDevice { double grconvertY(double y, const std::string& from, const std::string& to); void deviceToUser(double* x, double* y); void deviceToNDC(double* x, double* y); + + // resizing the display void setSize(int width, int height, double devicePixelRatio); void setSize(pDevDesc pDev); - void setDeviceAttributes(pDevDesc pDev); int getWidth(); int getHeight(); double devicePixelRatio(); diff --git a/src/Charts/RTool.cpp b/src/Charts/RTool.cpp index 7587cb52a..c505c197b 100644 --- a/src/Charts/RTool.cpp +++ b/src/Charts/RTool.cpp @@ -57,6 +57,7 @@ RTool::RTool(int argc, char**argv) // set the "GC" object and methods context = NULL; + canvas = NULL; (*R)["GC.version"] = VERSION_STRING; (*R)["GC.build"] = VERSION_LATEST; diff --git a/src/Charts/RTool.h b/src/Charts/RTool.h index 8d6762b45..36ae45e90 100644 --- a/src/Charts/RTool.h +++ b/src/Charts/RTool.h @@ -33,6 +33,10 @@ class RTool { RCallbacks *callbacks; RGraphicsDevice *dev; + // the canvas to plot on, it may be null + // if no canvas is active + RCanvas *canvas; + Context *context; QString version; diff --git a/src/src.pro b/src/src.pro index c5852f27a..092b18dae 100644 --- a/src/src.pro +++ b/src/src.pro @@ -263,9 +263,9 @@ contains(DEFINES, "GC_WANT_R") { QMAKE_CXXFLAGS += $$RCPPWARNING $$RCPPFLAGS $$RCPPINCL $$RINSIDEINCL LIBS += $$RLDFLAGS $$RBLAS $$RLAPACK $$RCPPLIBS $$RINSIDELIBS - ## Chart, Tool (R api), Grahics (R device bridge to QWidget) - HEADERS += Charts/RChart.h Charts/RTool.h Charts/RGraphicsDevice.h - SOURCES += Charts/RChart.cpp Charts/RTool.cpp Charts/RGraphicsDevice.cpp + ## Chart, Tool (R api), Grahics (R GraphicDevice and Qt canvas widget) + HEADERS += Charts/RChart.h Charts/RTool.h Charts/RGraphicsDevice.h Charts/RCanvas.h + SOURCES += Charts/RChart.cpp Charts/RTool.cpp Charts/RGraphicsDevice.cpp Charts/RCanvas.cpp }