mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 00:28:42 +00:00
R Canvas with Basic Primitives
.. not complete, but we now have a canvas (QGraphicsView) to plot the R output without needing to use x11() or quartz(), window() etc. .. the primitives do not honour the graphic engine context so all lines etc are white on black. .. will fix and improve in followup commits, need to test with QT4.8 and cross-platform.
This commit is contained in:
102
src/Charts/RCanvas.cpp
Normal file
102
src/Charts/RCanvas.cpp
Normal file
@@ -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; i<n;i++) {
|
||||
|
||||
double x1=x[i], y1=y[i];
|
||||
line(x0,y0,x1,y1,p);
|
||||
|
||||
x0=x1;
|
||||
y0=y1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RCanvas::rectangle(double x0, double y0, double x1, double y1,QPen p, QBrush b)
|
||||
{
|
||||
QGraphicsRectItem *r=new QGraphicsRectItem(x0,y0,x1-x0,y1-y0);
|
||||
r->setPen(p);
|
||||
r->setBrush(b);
|
||||
scene->addItem(r);
|
||||
}
|
||||
56
src/Charts/RCanvas.h
Normal file
56
src/Charts/RCanvas.h
Normal file
@@ -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 <QGraphicsView>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsItem>
|
||||
#include <QGraphicsEllipseItem>
|
||||
|
||||
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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,13 +26,14 @@
|
||||
#include <QTextEdit>
|
||||
#include <QScrollBar>
|
||||
#include <QSplitter>
|
||||
#include <QSvgWidget>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
|
||||
@@ -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"<<n;
|
||||
for(int i=0; i<n; i++) qDebug()<<i<<":"<<x[i]<<y[i];
|
||||
//XXXqDebughandler::polyline(n, x, y, gc, dev);
|
||||
//XXX todo honour colors
|
||||
if (rtool && rtool->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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user