OSCAR-code/sleepyhead/Graphs/gYAxis.cpp
Sean Stangl 9dbe702a40 Standardize project modelines and include license and copyright.
Signed-off-by: Mark Watkins <jedimark@users.sourceforge.net>
2014-04-10 13:29:53 +10:00

436 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* gYAxis Implementation
*
* Copyright (c) 2011-2014 Mark Watkins <jedimark@users.sourceforge.net>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the Linux
* distribution for more details. */
#include <math.h>
#include <QDebug>
#include "gYAxis.h"
#include "SleepLib/profiles.h"
gXGrid::gXGrid(QColor col)
:Layer(NoChannel)
{
Q_UNUSED(col)
m_major_color=QColor(180,180,180,64);
//m_major_color=QColor(180,180,180,92);
m_minor_color=QColor(230,230,230,64);
m_show_major_lines=true;
m_show_minor_lines=true;
}
gXGrid::~gXGrid()
{
}
void gXGrid::paint(gGraph & w,int left,int top, int width, int height)
{
int x,y;
gVertexBuffer * stippled, * lines;
EventDataType miny,maxy;
if (w.zoomY()==0 && PROFILE.appearance->allowYAxisScaling()) {
miny=w.physMinY();
maxy=w.physMaxY();
} else {
miny=w.min_y;
maxy=w.max_y;
if (miny<0) { // even it up if it's starts negative
miny=-MAX(fabs(miny),fabs(maxy));
}
}
w.roundY(miny,maxy);
//EventDataType dy=maxy-miny;
if (height<0) return;
static QString fd="0";
GetTextExtent(fd,x,y);
double max_yticks=round(height / (y+14.0)); // plus spacing between lines
//double yt=1/max_yticks;
double mxy=MAX(fabs(maxy),fabs(miny));
double mny=miny;
if (miny<0) {
mny=-mxy;
}
double rxy=mxy-mny;
int myt;
bool fnd=false;
for (myt=max_yticks;myt>=1;myt--) {
float v=rxy/float(myt);
if (float(v)==int(v)) {
fnd=true;
break;
}
}
if (fnd) max_yticks=myt;
else {
max_yticks=2;
}
double yt=1/max_yticks;
double ymult=height/rxy;
double min_ytick=rxy*yt;
float ty,h;
if (min_ytick<=0) {
qDebug() << "min_ytick error in gXGrid::paint() in" << w.title();
return;
}
if (min_ytick>=1000000) {
min_ytick=100;
}
stippled=w.backlines();
lines=w.backlines();
for (double i=miny; i<=maxy+min_ytick-0.00001; i+=min_ytick) {
ty=(i - miny) * ymult;
h=top+height-ty;
if (m_show_major_lines && (i > miny)) {
stippled->add(left,h,left+width,h,m_major_color.rgba());
}
double z=(min_ytick/4)*ymult;
double g=h;
for (int i=0;i<3;i++) {
g+=z;
if (g>top+height) break;
//if (vertcnt>=maxverts) {
// qWarning() << "vertarray bounds exceeded in gYAxis for " << w.title() << "graph" << "MinY =" <<miny << "MaxY =" << maxy << "min_ytick=" <<min_ytick;
// break;
// }
if (m_show_minor_lines) {// && (i > miny)) {
stippled->add(left,g,left+width,g,m_minor_color.rgba());
}
if (stippled->full()) {
break;
}
}
if (lines->full() || stippled->full()) {
qWarning() << "vertarray bounds exceeded in gYAxis for " << w.title() << "graph" << "MinY =" <<miny << "MaxY =" << maxy << "min_ytick=" <<min_ytick;
break;
}
}
}
gYAxis::gYAxis(QColor col)
:Layer(NoChannel)
{
m_line_color=col;
m_text_color=col;
m_textureID=0;
m_yaxis_scale=1;
}
gYAxis::~gYAxis()
{
}
void gYAxis::paint(gGraph & w,int left,int top, int width, int height)
{
int x,y;//,yh=0;
//Todo: clean this up as there is a lot of duplicate code between the sections
if (0) {//w.graphView()->usePixmapCache()) {
/* if (w.invalidate_yAxisImage) {
if (!m_image.isNull()) {
w.graphView()->deleteTexture(m_textureID);
m_image=QImage();
}
if (height<0) return;
if (height>2000) return;
int labelW=0;
EventDataType miny=w.min_y;
EventDataType maxy=w.max_y;
if (miny<0) { // even it up if it's starts negative
miny=-MAX(fabs(miny),fabs(maxy));
}
w.roundY(miny,maxy);
EventDataType dy=maxy-miny;
static QString fd="0";
GetTextExtent(fd,x,y);
yh=y;
m_image=QImage(width,height+y+4,QImage::Format_ARGB32_Premultiplied);
m_image.fill(Qt::transparent);
QPainter paint(&m_image);
double max_yticks=round(height / (y+14.0)); // plus spacing between lines
double mxy=MAX(fabs(maxy),fabs(miny));
double mny=miny;
if (miny<0) {
mny=-mxy;
}
double rxy=mxy-mny;
int myt;
bool fnd=false;
for (myt=max_yticks;myt>2;myt--) {
float v=rxy/float(myt);
if (v==int(v)) {
fnd=true;
break;
}
}
if (fnd) max_yticks=myt;
double yt=1/max_yticks;
double ymult=height/rxy;
double min_ytick=rxy*yt;
float ty,h;
if (min_ytick<=0) {
qDebug() << "min_ytick error in gYAxis::paint() in" << w.title();
return;
}
if (min_ytick>=1000000) {
min_ytick=100;
}
//lines=w.backlines();
for (double i=miny; i<=maxy+min_ytick-0.00001; i+=min_ytick) {
ty=(i - miny) * ymult;
if (dy<5) {
fd=Format(i*m_yaxis_scale,2);
} else {
fd=Format(i*m_yaxis_scale,1);
}
GetTextExtent(fd,x,y);
if (x>labelW) labelW=x;
h=(height-2)-ty;
h+=yh;
#ifndef Q_OS_MAC
// stupid pixel alignment rubbish, I really should be using floats..
h+=1;
#endif
if (h<0)
continue;
paint.setBrush(Qt::black);
paint.drawText(width-8-x,h+y/2,fd);
paint.setPen(m_line_color);
paint.drawLine(width-4,h,width,h);
double z=(min_ytick/4)*ymult;
double g=h;
for (int i=0;i<3;i++) {
g+=z;
if (g>height+yh) break;
paint.drawLine(width-3,g,width,g);
}
}
paint.end();
m_image=QGLWidget::convertToGLFormat(m_image);
m_textureID=w.graphView()->bindTexture(m_image,GL_TEXTURE_2D,GL_RGBA,QGLContext::NoBindOption);
w.invalidate_yAxisImage=false;
}
if (!m_image.isNull()) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
w.graphView()->drawTexture(QPoint(left,(top+height)-m_image.height()+5),m_textureID);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
}
*/
} else {
if (height<0) return;
if (height>2000) return;
int labelW=0;
EventDataType miny;
EventDataType maxy;
if (w.zoomY()==0 && PROFILE.appearance->allowYAxisScaling()) {
miny=w.physMinY();
maxy=w.physMaxY();
} else {
miny=w.min_y;
maxy=w.max_y;
if (miny<0) { // even it up if it's starts negative
miny=-MAX(fabs(miny),fabs(maxy));
}
}
w.roundY(miny,maxy);
EventDataType dy=maxy-miny;
static QString fd="0";
GetTextExtent(fd,x,y);
double max_yticks=round(height / (y+14.0)); // plus spacing between lines
double mxy=MAX(fabs(maxy),fabs(miny));
double mny=miny;
if (miny<0) {
mny=-mxy;
}
double rxy=mxy-mny;
int myt;
bool fnd=false;
for (myt=max_yticks;myt>2;myt--) {
float v=rxy/float(myt);
if (v==int(v)) {
fnd=true;
break;
}
}
if (fnd) max_yticks=myt;
double yt=1/max_yticks;
double ymult=height/rxy;
double min_ytick=rxy*yt;
//if (dy>5) {
// min_ytick=round(min_ytick);
//} else {
//}
float ty,h;
if (min_ytick<=0) {
qDebug() << "min_ytick error in gYAxis::paint() in" << w.title();
return;
}
if (min_ytick>=1000000) {
min_ytick=100;
}
lines=w.backlines();
GLuint line_color=m_line_color.rgba();
for (double i=miny; i<=maxy+min_ytick-0.00001; i+=min_ytick) {
ty=(i - miny) * ymult;
if (dy<5) {
fd=Format(i*m_yaxis_scale,2);
} else {
fd=Format(i*m_yaxis_scale,1);
}
GetTextExtent(fd,x,y); // performance bottleneck..
if (x>labelW) labelW=x;
h=top+height-ty;
if (h<top) continue;
w.renderText(fd,left+width-8-x,(h+(y/2.0)),0,m_text_color,defaultfont);
lines->add(left+width-4,h,left+width,h,line_color);
double z=(min_ytick/4)*ymult;
double g=h;
for (int i=0;i<3;i++) {
g+=z;
if (g>top+height) break;
lines->add(left+width-3,g,left+width,g,line_color);
if (lines->full()) {
qWarning() << "vertarray bounds exceeded in gYAxis for " << w.title() << "graph" << "MinY =" <<miny << "MaxY =" << maxy << "min_ytick=" <<min_ytick;
break;
}
}
if (lines->full()) {
qWarning() << "vertarray bounds exceeded in gYAxis for " << w.title() << "graph" << "MinY =" <<miny << "MaxY =" << maxy << "min_ytick=" <<min_ytick;
break;
}
}
}
}
const QString gYAxis::Format(EventDataType v, int dp) {
return QString::number(v,'f',dp);
}
bool gYAxis::mouseMoveEvent(QMouseEvent * event, gGraph * graph)
{
if (!p_profile->appearance->graphTooltips())
return false;
int x=event->x();
int y=event->y();
if (!graph->units().isEmpty()) {
graph->ToolTip(graph->units(),x,y-20,0);
// graph->redraw();
}
return true;
}
bool gYAxis::mouseDoubleClickEvent(QMouseEvent * event, gGraph * graph)
{
if (graph) {
// int x=event->x();
// int y=event->y();
short z=(graph->zoomY()+1) % gGraph::maxZoomY;
graph->setZoomY(z);
qDebug() << "Mouse double clicked for" << graph->title() << z;
}
Q_UNUSED(event);
return false;
}
const QString gYAxisTime::Format(EventDataType v, int dp)
{
int h=int(v) % 24;
int m=int(v*60) % 60;
int s=int(v*3600) % 60;
char pm[3]={"am"};
if (show_12hr) {
h>=12 ? pm[0]='p' : pm[0]='a';
h %= 12;
if (h==0) h=12;
} else {
pm[0]=0;
}
if (dp>2) return QString().sprintf("%02i:%02i:%02i%s",h,m,s,pm);
return QString().sprintf("%i:%02i%s",h,m,pm);
}
const QString gYAxisWeight::Format(EventDataType v, int dp)
{
Q_UNUSED(dp)
return weightString(v,m_unitsystem);
}