Files
GoldenCheetah/deprecated/ModelPlot.cpp
Mark Liversedge 09f71f7451 Deprecate 3D chart
.. deprecate 3D chart as QwtPlot3d has very questionable support
   and the chart is not widely used.
2018-02-12 13:44:32 +00:00

1475 lines
53 KiB
C++

/*
* Copyright (c) 2009 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 "ModelPlot.h"
#include "ModelWindow.h"
#include "MainWindow.h"
#include "IntervalItem.h"
#include "RideItem.h"
#include "Context.h"
#include "Context.h"
#include "Athlete.h"
#include "Settings.h"
#include "Zones.h"
#include "Colors.h"
#include "RideFile.h"
#include "Units.h" // for MILES_PER_KM
#include <QWidget>
using namespace Qwt3D; // namespace ref is only visible in this file (is not in any headers)
/*----------------------------------------------------------------------
* MODEL DATA PROVIDER
*
* This function is called by the surface plot to get z values for an
* xy pair. The data is set by a call from ModelPlot (from ModelWindow)
* when a user either selects a new ridefile or changes the x/y/z/color
* comboboxes.
*
* setData - sets the model data from a ridefile using the axis passed
* and returns the mesh values as an xy to set the surface
* plot. Will clear existing data first.
*
* operator () - called by the surface plot to set z for a given x and y
*
*----------------------------------------------------------------------*/
// util function to create an x/y key for QHash, effective and quick enough
static QString xystring(double x, double y) { return QString("%1:%2").arg((int)x).arg((int)y); }
// returns the color for an xyz point
class ModelDataColor : public Color
{
// return RGBA color for x,y,z
// qwtplot3d api changes between 0.2.x and 0.3.x
#if QWT3D_MINOR_VERSION > 2
Qwt3D::RGBA rgba (double x, double y, double) const
#else
Qwt3D::RGBA operator () (double x, double y, double) const
#endif
{
QColor cHSV, cRGB;
double val = color.value(xystring(x,y), 0.0);
RGBA colour;
if (!val) {
return RGBA(255,255,255,0); // see thru
} if (iszones == true) { // zone 0 is set to zone 1 to distinguish from no value
cHSV = zonecolor.value(val-1, QColor(0,0,0));
} else if (val) {
if (max <= min) return RGBA(255,255,255,0); // see thru
if (val < min) val = min;
if (val > max) val = max;
int hue = (double)255 - (double)255 * (double)((val-min)/(max-min));
cHSV.setHsv(hue, 255, 255);
}
cRGB = cHSV.convertTo(QColor::Rgb);
colour.r = (double) cRGB.red()/(double)255;
colour.g = (double) cRGB.green()/(double)255;
colour.b = (double) cRGB.blue()/(double)255;
colour.a = 1.0;
return colour;
}
// qwtplot3d api changes between 0.2.x and 0.3.x
#if QWT3D_MINOR_VERSION > 2
Color *clone() const { ModelDataColor *x = new ModelDataColor; *x = *this; return x; }
#endif
Qwt3D::ColorVector &createVector(Qwt3D::ColorVector &vec)
{
RGBA colour;
QColor cHSV, cRGB;
Qwt3D::ColorVector::iterator it;
// clear current
vec.clear();
// update the color vector to use our color scheme
if (iszones) {
for (int i=0; i< 7; i++) {
cRGB = zonecolor.value(i, QColor(0,0,0)).convertTo(QColor::Rgb);
colour.r = (double) cRGB.red()/(double)255;
colour.g = (double) cRGB.green()/(double)255;
colour.b = (double) cRGB.blue()/(double)255;
colour.a = 1.0;
it = vec.end();
vec.insert(it, 1, colour);
}
} else {
if (max <= min) return vec;
double val;
int i;
// just add 100 colors graded from min to max
for (i=0, val=min; i<100 && val<=max; i++, val += ((max-min)/100)) {
int hue = (double)255 - (double)255 * (double)((val-min)/(max-min));
cHSV.setHsv(hue, 255, 255);
cRGB = cHSV.convertTo(QColor::Rgb);
colour.r = (double) cRGB.red()/(double)255;
colour.g = (double) cRGB.green()/(double)255;
colour.b = (double) cRGB.blue()/(double)255;
colour.a = 1.0;
it = vec.end();
vec.insert(it, 1, colour);
}
}
return vec;
}
public:
QHash<QString, double> color;
QHash<QString, int> num; // xy map with count of values for averaging
double min, max;
bool iszones; // if the color value is a zone number
QMap<double, QColor> zonecolor;
};
class ModelDataProvider : public Function
{
Q_DECLARE_TR_FUNCTIONS(ModelDataProvider)
public:
ModelDataProvider (ModelPlot &plot, ModelSettings *settings);
void setData(RideFile *ride, int x, int y, int z, int col); // set the maps and return mesh dimension
// return z value for x,y - std qwt3plot method
double operator () (double x, double y)
{
// return the z value for x and y
return mz.value(xystring(x,y), 0.0);
}
double intervals (double x, double y) // return value for selected intervals
{
return plot.iz.value(xystring(x,y), 0.0)-minz;
}
double getMinz() { return minz; }
double getMaxz() { return maxz; }
~ModelDataProvider()
{
mz.clear();
mnum.clear();
plot.iz.clear();
plot.inum.clear();
}
QHash<QString, double> mz; // xy map with max z values;
QHash<QString, int> mnum; // xy map with count of values for averaging
private:
double pointType(const RideFilePoint *, int);
QString describeType(int, bool);
double maxz, minz;
double cranklength; // used for CPV/AEPF calculation
bool useMetricUnits;
ModelPlot &plot;
};
double
ModelDataProvider::pointType(const RideFilePoint *point, int type)
{
// return the point value for the given type
switch(type) {
case MODEL_POWER : return point->watts;
case MODEL_CADENCE : return point->cad;
case MODEL_HEARTRATE : return point->hr;
case MODEL_SPEED :
if (useMetricUnits == true){
return point->kph;
}else {
return point->kph * MILES_PER_KM;
}
case MODEL_ALT :
if (useMetricUnits == true){
return point->alt;
}else {
return point->alt * FEET_PER_METER;
}
case MODEL_TORQUE : return point->nm;
case MODEL_TIME : return point->secs;
case MODEL_DISTANCE :
if (useMetricUnits == true){
return point->km;
}else {
return point->km * MILES_PER_KM;
}
case MODEL_INTERVAL : return point->interval;
case MODEL_LAT :
{
if (ceil(point->lat) > 90 || floor(ceil(point->lat)) < -90)
return 0;
else
return point->lat *1000;
}
break;
case MODEL_LONG :
{
if (ceil(point->lon) > 180 || floor(point->lon) < -180)
return 0;
else
return point->lon *1000;
}
break;
case MODEL_AEPF :
if (point->watts == 0 || point->cad == 0) return 0;
else return ((point->watts * 60.0) / (point->cad * cranklength * 2.0 * PI));
case MODEL_CPV :
return 100 * ((point->cad * cranklength * 2.0 * PI) / 60.0);
// these you need to do yourself cause there is some
// logic needed and I'm just lookup table!
case MODEL_XYTIME : return 1;
case MODEL_POWERZONE : return point->watts;
case MODEL_LRBALANCE : return point->lrbalance;
case MODEL_RV : return point->rvert;
case MODEL_RGCT : return point->rcontact;
case MODEL_RCAD : return point->rcad;
case MODEL_GEAR : return point->gear;
case MODEL_SMO2 : return point->smo2;
case MODEL_THB : return point->thb;
case MODEL_SLOPE : return point->slope;
}
return 0; // ? unknown channel ?
}
QString
ModelDataProvider::describeType(int type, bool longer)
{
// return the point value for the given type
if (longer == true) {
switch(type) {
case MODEL_POWER : return tr("Power (watts)");
case MODEL_CADENCE : return tr("Cadence (rpm)");
case MODEL_HEARTRATE : return tr("Heartrate (bpm)");
case MODEL_SPEED :
if (useMetricUnits == true){
return tr("Speed (kph)");
}else {
return tr("Speed (mph)");
}
case MODEL_ALT :
if (useMetricUnits == true){
return tr("Altitude (meters)");
}else {
return tr("Altitude (feet)");
}
case MODEL_TORQUE : return tr("Torque (N)");
case MODEL_TIME : return tr("Elapsed Time (secs)");
case MODEL_DISTANCE :
if (useMetricUnits == true){
return tr("Elapsed Distance (km)");
}else {
return tr("Elapsed Distance (mi)");
}
case MODEL_INTERVAL : return tr("Interval Number");
case MODEL_LAT : return tr("Latitude (degree x 1000)");
case MODEL_LONG : return tr("Longitude (degree x 1000)");
case MODEL_CPV : return tr("Circumferential Pedal Velocity (cm/s)");
case MODEL_AEPF : return tr("Average Effective Pedal Force (N)");
// these you need to do yourself cause there is some
// logic needed and I'm just lookup table!
case MODEL_XYTIME : return tr("Time at X/Y (%)");
case MODEL_POWERZONE : return tr("Power Zone");
case MODEL_LRBALANCE : return (tr("L/R Balance"));
case MODEL_RV : return (tr("Running Vertical Oscillation"));
case MODEL_RGCT : return (tr("Running Ground Contact Time"));
case MODEL_RCAD : return (tr("Running Cadence"));
case MODEL_GEAR : return (tr("Gear Ratio"));
case MODEL_SMO2 : return (tr("Muscle Oxygen"));
case MODEL_THB : return (tr("Haemoglobin Mass"));
case MODEL_SLOPE : return (tr("Slope (gradient)"));
}
return tr("Unknown");; // ? unknown channel ?
} else {
switch(type) {
case MODEL_POWER : return tr("Power");
case MODEL_CADENCE : return tr("Cadence");
case MODEL_HEARTRATE : return tr("Heartrate");
case MODEL_SPEED : return tr("Speed");
case MODEL_ALT : return tr("Altitude");
case MODEL_TORQUE : return tr("Pedal Force");
case MODEL_TIME : return tr("Time");
case MODEL_DISTANCE : return tr("Distance");
case MODEL_INTERVAL : return tr("Interval");
case MODEL_LAT : return tr("Latitude");
case MODEL_LONG : return tr("Longitude");
case MODEL_XYTIME : return tr("Time at X/Y");
case MODEL_POWERZONE : return tr("Zone");
case MODEL_CPV : return tr("CPV");
case MODEL_AEPF : return tr("AEPF");
case MODEL_LRBALANCE : return (tr("Balance"));
case MODEL_RV : return (tr("RV"));
case MODEL_RGCT : return (tr("GCT"));
case MODEL_RCAD : return (tr("Run Cad"));
case MODEL_GEAR : return (tr("Gear"));
case MODEL_SMO2 : return (tr("SmO2"));
case MODEL_THB : return (tr("tHb"));
case MODEL_SLOPE : return (tr("Slope"));
}
return tr("None");; // ? unknown channel ?
}
}
/*----------------------------------------------------------------------
* Setup the data model and plot according to the settings passed from
* mainwindow.
*
* Truthfully, this is where all the hard work is done!
* Edit it with caution, there be dragons here.
*
*----------------------------------------------------------------------*/
ModelDataProvider::ModelDataProvider (ModelPlot &plot, ModelSettings *settings) : Function(plot), plot(plot)
{
// get application settings
cranklength = appsettings->cvalue(plot.context->athlete->cyclist, GC_CRANKLENGTH, 0.0).toDouble() / 1000.0;
useMetricUnits = plot.context->athlete->useMetricUnits;
// if there are no settings or incomplete settings
// create a null data plot
if (settings == NULL || settings->ride == NULL || settings->ride->ride() == NULL ||
settings->x == 0 || settings->y == 0 || settings->z == 0) {
// initialise a null plot
setDomain(0,0,0,0);
setMinZ(0);
create();
return;
}
// if its not setup or no settings exist default to 175mm cranks
if (cranklength == 0.0) cranklength = 0.175;
// Run through the ridefile points putting the selected
// values into the appropriate bins
settings->colorProvider->color.clear();
settings->colorProvider->num.clear();
settings->colorProvider->zonecolor.clear();
//plot.makeCurrent();
double maxbinx =-180000, maxbiny =-180000; // was 65535
double minbinx =180000, minbiny =180000; // 180000 is the max value (for longitude)
double mincol =180000, maxcol =-180000;
//
// Create Plot dataset, filter on values and calculate averages etc
//
foreach(const RideFilePoint *point, settings->ride->ride()->dataPoints()) {
// get x and z bin values - round to nearest bin
double dx = pointType(point, settings->x);
int binx = settings->xbin * floor(dx / settings->xbin);
double dy = pointType(point, settings->y);
int biny = settings->ybin * floor(dy / settings->ybin);
// ignore zero points
if (settings->ignore && (dx==0 || dy==0)) continue;
// even further ignore 0 for lat/lon
if ((settings->y == MODEL_LAT || settings->y == MODEL_LONG) && dy == 0) continue;
if ((settings->x == MODEL_LAT || settings->x == MODEL_LONG) && dx == 0) continue;
// get z value
double zed=0;
if (settings->z == MODEL_XYTIME) zed = settings->ride->ride()->recIntSecs(); // time at
else zed = pointType(point, settings->z); // raw data
// get color value
double color=0;
if (settings->color == MODEL_XYTIME) color = settings->ride->ride()->recIntSecs(); // time at
else color = pointType(point, settings->color); // raw data
// min max
if (color > maxcol) maxcol = color;
if (color < mincol) mincol = color;
if (binx > maxbinx) maxbinx = binx;
if (binx < minbinx) minbinx = binx;
if (biny > maxbiny) maxbiny = biny;
if (biny < minbiny) minbiny = biny;
// How many & Curent Value
// ZED
QString lookup = xystring(binx,biny);
int count = mnum.value(lookup, 0.0);
double currentz = mz.value(lookup, 0.0);
if (settings->z == MODEL_XYTIME) {
count++;
zed += currentz;
} else {
if (count) zed = ((currentz*count)+zed)/(count+1); // average
}
// always update the max values
mz.insert(lookup, zed);
mnum.insert(lookup, count);
// NO INTERVALS COLOR IS FOR ALL SAMPLES
if (settings->intervals.count() == 0 ) {
plot.intervals_ = 0;
int colcount = settings->colorProvider->num.value(lookup, 0.0);
double currentcol = settings->colorProvider->color.value(lookup,0.0);
if (settings->color == MODEL_XYTIME) { // color in time
colcount++;
color += currentcol;
} else { // color in average of
if (colcount) color = ((currentcol*colcount)+color)/(colcount+1); // average
}
settings->colorProvider->color.insert(lookup, color);
settings->colorProvider->num.insert(lookup, colcount);
}
// WE HAVE INTERVALS! COLOR AND INTERVAL Z VALUES NEED TO BE TREATED
// DIFFERENTLY NOW - COLOR IS FOR SELECTED INTERVALS AND WE MAINTAIN
// A SECOND SET OF Z VALUES SO WE HAVE MAX + INTERVALS
if (settings->intervals.count() > 0) {
plot.intervals_ = SHOW_INTERVALS;
if (settings->frame == true) plot.intervals_ |= SHOW_FRAME;
QString lookup = xystring(binx, biny);
// filter for interval
for(int i=0; i<settings->intervals.count(); i++) {
IntervalItem *curr = settings->intervals.at(i);
if ((point->secs + settings->ride->ride()->recIntSecs()) > curr->start
&& point->secs < curr->stop) {
// update colors
int colcount = settings->colorProvider->num.value(lookup,0.0);
double currentcol = settings->colorProvider->color.value(lookup, 0.0);
if (settings->color == MODEL_XYTIME) { // color in time
colcount++;
color += currentcol;
} else { // color in average of
if (colcount) color = ((currentcol*colcount)+color)/(colcount+1); // average
}
settings->colorProvider->color.insert(lookup, color);
settings->colorProvider->num.insert(lookup, colcount);
double ized=0;
if (settings->z == MODEL_XYTIME) ized = settings->ride->ride()->recIntSecs(); // time at
else ized = pointType(point, settings->z); // raw data
// update interval values
int count = plot.inum.value(lookup, 0.0);
double currentz = plot.iz.value(lookup, 0.0);
if (settings->z == MODEL_XYTIME) {
count++;
ized += currentz;
} else {
if (count) ized = ((currentz*count)+ized)/(count+1); // average
}
plot.iz.insert(lookup, ized);
plot.inum.insert(lookup, count);
break;
}
}
}
}
if (mz.count() == 0) {
// create a null plot -- bin too large!
plot.setTitle(tr("No data or bin size too large"));
// initialise a null plot
setDomain(0,0,0,0);
setMinZ(0);
setMesh(0,0);
mz.clear();
settings->colorProvider->color.clear();
create();
return;
}
// POST PROCESS DATA SET
// COLOR
// if needed convert average power to the related power zone
// but only if ranges are defined i.e. user has set CP
const Zones *zones = plot.context->athlete->zones(settings->ride->isRun);
int zone_range = -1;
if (zones) zone_range = zones->whichRange(settings->ride->dateTime.date());
if (settings->color == MODEL_POWERZONE && zone_range >= 0) {
// if we need to color by power zones
// zones are setup and we want to color by them
maxcol = settings->colorProvider->max = zones->numZones(zone_range);
mincol = settings->colorProvider->min = 1;
// add the zone colors to the color map
for (int i=0; i<maxcol; i++) {
settings->colorProvider->zonecolor.insert
(i, zoneColor(i, zones->numZones(zone_range)));
}
// iterate over the existing power values converting to a power zone
QHashIterator<QString, double> coli(settings->colorProvider->color);
while (coli.hasNext()) {
coli.next();
QString lookup = coli.key();
double color = coli.value();
// turn into power zone
color = zones->whichZone(zone_range, color);
// overwrite existing power average with zone number
// BUT! the zone numbers start at 1 not 0 here to distinguish
// between zone 0 and no value at all
settings->colorProvider->color.insert(lookup, color+1);
}
settings->colorProvider->iszones = true;
} else if (settings->color == MODEL_NONE) {
settings->colorProvider->iszones = false;
settings->colorProvider->color.clear();
} else {
// otherwise just turn off zoning
settings->colorProvider->iszones = false;
}
// TIME
//
// Convert from absolute values to %age of the entire ride
double duration = settings->ride->ride()->dataPoints().last()->secs +
settings->ride->ride()->recIntSecs();
// Multis...
if (settings->z == MODEL_XYTIME) {
// time on Z axis
QHashIterator<QString, double> zi(mz);
while (duration && settings->z == MODEL_XYTIME && zi.hasNext()) {
zi.next();
double timePercent = (zi.value()/duration) * 100;
mz.insert(zi.key(), timePercent);
}
}
// Intervals
if (settings->z == MODEL_XYTIME) {
// time on Z axis
QHashIterator<QString, double> ii(plot.iz);
while (duration && settings->z == MODEL_XYTIME && ii.hasNext()) {
ii.next();
double timePercent = (ii.value()/duration) * 100;
plot.iz.insert(ii.key(), timePercent);
}
}
// time on Color
if (settings->color == MODEL_XYTIME) {
mincol=65535; maxcol=0;
QHashIterator<QString, double> ci(settings->colorProvider->color);
while (duration && settings->color == MODEL_XYTIME && ci.hasNext()) {
ci.next();
double timePercent = (ci.value()/duration) * 100;
if (timePercent > maxcol) maxcol = timePercent;
if (timePercent < mincol) mincol = timePercent;
settings->colorProvider->color.insert(ci.key(), timePercent);
}
}
// MIN and MAX Z (impacts chart geometry)
// We DO NOT do the same for color since they represent
// the entire data set and not just the intervals selected (if any)
bool first = true;
QHashIterator <QString, double> iz(mz);
while (iz.hasNext()) {
double z;
iz.next();
z = iz.value();
if (first == true) {
minz = 0;
maxz = iz.value();
} else {
if (z > maxz) maxz = z;
if (z < minz) minz = z;
}
first = false;
}
settings->colorProvider->min = mincol;
settings->colorProvider->max = maxcol;
//
// Adjust the plot settings to reflect the data set
//
setMinZ(minz);
setMaxZ(maxz);
QFont font; // has all the defaults in it
// qwtplot3d api changes between 0.2.x and 0.3.x
#if QWT3D_MINOR_VERSION > 2
plot.setDataColor(*settings->colorProvider);
#endif
//
// Setup the color legend
//
if (settings->legend == true && settings->color != MODEL_NONE) {
plot.showColorLegend(true);
plot.legend()->setTitleFont(font.family(), 8, QFont::Normal);
plot.legend()->setOrientation(Qwt3D::ColorLegend::BottomTop, Qwt3D::ColorLegend::Left);
plot.legend()->setLimits(mincol, maxcol);
if (settings->colorProvider->iszones == true) {
plot.legend()->setMajors(maxcol);
plot.legend()->setMinors(0);
} else {
plot.legend()->setMajors(10);
plot.legend()->setMinors(0);
}
plot.legend()->setTitleString(describeType(settings->color, false));
} else {
plot.showColorLegend(false);
}
// mesh size
int mx = (maxbinx-minbinx) / settings->xbin;
int my = (maxbiny-minbiny) / settings->ybin;
// mesh MUST be at least 2x2 including 0,0
while (mx < 2) {
maxbinx += settings->xbin;
mx++;
}
while(my < 2) {
maxbiny += settings->ybin;
my++;
}
// add some graph paper so the plot is pretty
if (mx < 4) {
minbinx -= settings->xbin;
mx++;
}
if (my < 4) {
minbiny -= settings->ybin;
my++;
}
// mesh is number of bins PLUS ONE (bug in qwtplot3d)
setMesh(mx + 1, my + 1);
// domain is max values in each axis
setDomain(maxbinx,minbinx,minbiny,maxbiny); // max x/y vals
// set the barsize to a sensible radius (20% space)
// the +setting->xbin bit is to offset the additional mx/my bug
double xr = 0.8 * (((double)settings->xbin/((double)(maxbinx-minbinx)+(settings->xbin))) / 2.0);
double yr = 0.8 * (((double)settings->ybin/((double)(maxbiny-minbiny)+(settings->ybin))) / 2.0);
plot.diag_ = xr < yr ? (xr*(maxbinx-minbinx)) : (yr*(maxbiny-minbiny));
// now update the model - before the chart axis/legends
create();
double xscale, yscale, zscale;
if ((maxbinx-minbinx) >= (maxbiny-minbiny) && (maxbinx-minbinx) >= (maxz-minz)) {
// scale is set off the x-axis
xscale = 1;
yscale = (maxbinx-minbinx)/(maxbiny-minbiny);
zscale = (maxbinx-minbinx)/(maxz-minz);
} else if ((maxbiny-minbiny) >= (maxbinx-minbinx) && (maxbiny-minbiny) >= (maxz-minz)) {
// scale is set off the y-axis
xscale = (maxbiny-minbiny)/(maxbinx-minbinx);
yscale = 1;
zscale = (maxbiny-minbiny)/(maxz-minz);
} else {
// scale is set off the z-axis
xscale = (maxz-minz)/(maxbinx-minbinx);
yscale = (maxz-minz)/(maxbiny-minbiny);
zscale = 1;
}
// must be integers!?
if (xscale < 1) {
double factor = 1 / xscale;
xscale = 1;
yscale *= factor;
zscale *= factor;
}
if (yscale < 1) {
double factor = 1/ yscale;
yscale = 1;
xscale *= factor;
zscale *= factor;
}
if (zscale < 1) {
double factor = 1/ zscale;
zscale = 1;
yscale *= factor;
xscale *=factor;
}
// general plot settings
plot.setScale(xscale, yscale, zscale);
plot.setTitle("");
plot.setTitleFont(font.family(),font.pointSize(), QFont::Bold);
plot.setCoordinateStyle(FRAME);
plot.setMeshLineWidth(1);
plot.setIsolines(10);
plot.setOrtho(false);
plot.setSmoothMesh(true);
// coordinates - labels, gridlines, tic markers etc
if (settings->gridlines == true)
plot.coordinates()->setGridLines(true, true, Qwt3D::BACK | Qwt3D::LEFT | Qwt3D::FLOOR);
else
plot.coordinates()->setGridLines(true, true, 0);
plot.coordinates()->setLineWidth(1);
plot.coordinates()->setNumberFont(font.family(),font.pointSize());
plot.coordinates()->adjustLabels(25);
plot.coordinates()->adjustNumbers(10);
for (unsigned int i=0; i < plot.coordinates()->axes.size(); i++) {
plot.coordinates()->axes[i].setMajors(7);
plot.coordinates()->axes[i].setMinors(5);
plot.coordinates()->axes[i].recalculateTics();
}
plot.coordinates()->setLabelFont(font.family(), font.pointSize(), QFont::Bold);
plot.coordinates()->axes[Z1].setLabelString(describeType(settings->z, true));
plot.coordinates()->axes[Z2].setLabelString(describeType(settings->z, true));
plot.coordinates()->axes[Z3].setLabelString(describeType(settings->z, true));
plot.coordinates()->axes[Z4].setLabelString(describeType(settings->z, true));
plot.coordinates()->axes[X1].setLabelString(describeType(settings->x, true));
plot.coordinates()->axes[X2].setLabelString(describeType(settings->x, true));
plot.coordinates()->axes[X3].setLabelString(describeType(settings->x, true));
plot.coordinates()->axes[X4].setLabelString(describeType(settings->x, true));
plot.coordinates()->axes[Y1].setLabelString(describeType(settings->y, true));
plot.coordinates()->axes[Y2].setLabelString(describeType(settings->y, true));
plot.coordinates()->axes[Y3].setLabelString(describeType(settings->y, true));
plot.coordinates()->axes[Y4].setLabelString(describeType(settings->y, true));
plot.coordinates()->axes[Z1].setTicLength(25.0/xscale, 10.0/xscale);
plot.coordinates()->axes[Z2].setTicLength(25.0/xscale, 10.0/xscale);
plot.coordinates()->axes[Z3].setTicLength(25.0/xscale, 10.0/xscale);
plot.coordinates()->axes[Z4].setTicLength(25.0/xscale, 10.0/xscale);
plot.coordinates()->axes[X1].setTicLength(25.0/yscale, 10.0/yscale);
plot.coordinates()->axes[X2].setTicLength(25.0/yscale, 10.0/yscale);
plot.coordinates()->axes[X3].setTicLength(25.0/yscale, 10.0/yscale);
plot.coordinates()->axes[X4].setTicLength(25.0/yscale, 10.0/yscale);
plot.coordinates()->axes[Y1].setTicLength(25.0/xscale, 10.0/xscale);
plot.coordinates()->axes[Y2].setTicLength(25.0/xscale, 10.0/xscale);
plot.coordinates()->axes[Y3].setTicLength(25.0/xscale, 10.0/xscale);
plot.coordinates()->axes[Y4].setTicLength(25.0/xscale, 10.0/xscale);
// reset the flippin tic orientation, really this library
// is a bit of a pain now!
plot.coordinates()->axes[X1].setTicOrientation(0, -1, 0);
plot.coordinates()->axes[X2].setTicOrientation(0, -1, 0);
plot.coordinates()->axes[X3].setTicOrientation(0, 1, 0);
plot.coordinates()->axes[X4].setTicOrientation(0, 1, 0);
plot.coordinates()->axes[Y1].setTicOrientation( 1, 0, 0);
plot.coordinates()->axes[Y2].setTicOrientation(-1, 0, 0);
plot.coordinates()->axes[Y3].setTicOrientation(-1, 0, 0);
plot.coordinates()->axes[Y4].setTicOrientation( 1, 0, 0);
plot.coordinates()->axes[Z1].setTicOrientation( 1, 0, 0);
plot.coordinates()->axes[Z2].setTicOrientation( 1, 0, 0);
plot.coordinates()->axes[Z3].setTicOrientation(-1, 0, 0);
plot.coordinates()->axes[Z4].setTicOrientation(-1, 0, 0);
// use the cplotmarker colors for the ticks etc
QColor p = GColor(CPLOTMARKER);
plot.coordinates()->axes[Z1].setColor(p.red(), p.green(), p.blue());
plot.coordinates()->axes[Z2].setColor(p.red(), p.green(), p.blue());
plot.coordinates()->axes[Z3].setColor(p.red(), p.green(), p.blue());
plot.coordinates()->axes[Z4].setColor(p.red(), p.green(), p.blue());
plot.coordinates()->axes[X1].setColor(p.red(), p.green(), p.blue());
plot.coordinates()->axes[X2].setColor(p.red(), p.green(), p.blue());
plot.coordinates()->axes[X3].setColor(p.red(), p.green(), p.blue());
plot.coordinates()->axes[X4].setColor(p.red(), p.green(), p.blue());
plot.coordinates()->axes[Y1].setColor(p.red(), p.green(), p.blue());
plot.coordinates()->axes[Y2].setColor(p.red(), p.green(), p.blue());
plot.coordinates()->axes[Y3].setColor(p.red(), p.green(), p.blue());
plot.coordinates()->axes[Y4].setColor(p.red(), p.green(), p.blue());
// now, at last we can draw the axes markers. phew.
plot.coordinates()->axes[Z1].draw();
plot.coordinates()->axes[Z2].draw();
plot.coordinates()->axes[Z3].draw();
plot.coordinates()->axes[X1].draw();
plot.coordinates()->axes[X2].draw();
plot.coordinates()->axes[X3].draw();
plot.coordinates()->axes[Y1].draw();
plot.coordinates()->axes[Y2].draw();
plot.coordinates()->axes[Y3].draw();
// turn off zpane -- causes nasty flashing when left on between plots
plot.zpane = 0;
}
/*----------------------------------------------------------------------
* BASIC MODEL PLOT
* This is the qwt3d plot object
*
* Constructor - initialises an empty plot
* setData - calls the model data provider to setup data
* and then replots via updateData, updateGL.
*
*----------------------------------------------------------------------*/
ModelPlot::ModelPlot(Context *context, QWidget *parent, ModelSettings *settings) :
#if QWT3D_MINOR_VERSION > 2
GridPlot(parent),
#else
SurfacePlot(parent),
#endif
context(context)
{
diag_=0;
currentStyle = STYLE_BAR;
// the color provider returns a color for an xyz
modelDataColor = new ModelDataColor;
if (settings) settings->colorProvider = modelDataColor;
// the data provider returns a z for an x,y
modelDataProvider = new ModelDataProvider(*this, settings);
// qwtplot3d api changes between 0.2.x and 0.3.x
#if QWT3D_MINOR_VERSION < 3
setDataColor(modelDataColor);
#endif
// box style x/y/z
setCoordinateStyle(FRAME);
// start with bar chart
bar = (Bar *) this->setPlotStyle(Bar(this));
//coordinates()->setAutoScale(true);
// add grid lines and antialias them for smoother lines
//coordinates()->setGridLines(true, true);
//coordinates()->setLineSmooth(true);
//setSmoothMesh(true);
// some detailed axes points
for (unsigned int i=0; i < coordinates()->axes.size(); i++) {
coordinates()->axes[i].setMajors(7);
coordinates()->axes[i].setMinors(5);
}
setMeshLineWidth(1);
// put some space between the axes tic labels and the plot
// to make it easier to read
coordinates()->adjustNumbers(25);
// orthogonal view
setOrtho(false);
// no lighting it makes it tricky to read
// when there are LOTS of bars
//blowout();
// set shift zoom etc
resetViewPoint();
// set colors
configChanged(CONFIG_APPEARANCE);
updateData();
updateGL();
}
void
ModelPlot::configChanged(qint32)
{
// setColors bg
QColor rgba = GColor(CPLOTBACKGROUND);
RGBA bg(rgba.red()/255.0, rgba.green()/255.0, rgba.blue()/255.0, 0);
setBackgroundColor(bg);
// labels
QColor irgba = GCColor::invertColor(GColor(CPLOTBACKGROUND));
RGBA fg(irgba.red()/255.0, irgba.green()/255.0, irgba.blue()/255.0, 0);
coordinates()->setLabelColor(fg);
coordinates()->setNumberColor(fg);
// set grid lines
QColor grid = GColor(CPLOTGRID);
RGBA gr(grid.red()/255.0, grid.green()/255.0, grid.blue()/255.0, 255);
coordinates()->setGridLinesColor(gr);
coordinates()->setLineWidth(1);
}
void
ModelPlot::setStyle(int index)
{
if (currentStyle == STYLE_BAR) degrade(bar);
else degrade(water);
switch (index) {
case 0 : // BAR
bar = (Bar*)this->setPlotStyle(Bar(this));
showNormals(false);
updateNormals();
currentStyle = STYLE_BAR;
break;
case 1 : // SURFACE GRID
setPlotStyle(FILLEDMESH);
water = (Water *)addEnrichment(Water(this));
showNormals(false);
updateNormals();
currentStyle = STYLE_GRID;
break;
case 2 : // SURFACE SMOOTH
setPlotStyle(FILLED);
water = (Water *)addEnrichment(Water(this));
showNormals(false);
updateNormals();
currentStyle = STYLE_SURFACE;
break;
case 3 : // DOTS
setPlotStyle(Dot(10,10));
water = (Water *)addEnrichment(Water(this));
showNormals(false);
updateNormals();
currentStyle = STYLE_DOTS;
break;
}
updateData();
updateGL();
}
void
ModelPlot::setData(ModelSettings *settings)
{
delete modelDataProvider;
settings->colorProvider = modelDataColor;
modelDataProvider = new ModelDataProvider(*this, settings);
if (modelDataProvider->mz.count() == 0) {
hide();
} else {
show();
}
//modelDataProvider->assign(this);
//create();
//resetViewPoint();
//updateNormals();
updateData();
updateGL();
}
void
ModelPlot::setFrame(bool frame)
{
if (intervals_ && frame == true) {
intervals_ |= SHOW_FRAME;
} else if (frame== false) {
intervals_ &= ~SHOW_FRAME;
}
updateData();
updateGL();
}
void
ModelPlot::setLegend(bool legend, int coltype)
{
if (legend == true && coltype != MODEL_NONE) {
showColorLegend(true);
} else {
showColorLegend(false);
}
}
void
ModelPlot::setGrid(bool grid)
{
if (grid == true)
coordinates()->setGridLines(true, true, Qwt3D::BACK | Qwt3D::LEFT | Qwt3D::FLOOR);
else
coordinates()->setGridLines(true, true, 0);
updateData();
updateGL();
}
void
ModelPlot::setZPane(int z)
{
//zpane = (modelDataProvider->maxz-modelDataProvider->minz) / 100 * z;
zpane = (modelDataProvider->getMaxz()-modelDataProvider->getMinz()) / 100 * z;
updateData();
updateGL();
}
void
ModelPlot::resetViewPoint()
{
setRotation(45, 0, 30); // seems most pleasing
setShift(0,0,0); // centre so movement feels natural
setViewportShift(0,0);
setZoom(0.8); // zoom in close but leave space for the axis labels
}
/*----------------------------------------------------------------------
* WATER VERTEX ENRICHMENT
*
* THIS IS *NOT* USED IN BAR STYLE. The water enrichment is done by the
* Bar ENRICHMENT INSTEAD. (this is because the alpha values are honored
* amd I cannot work out why).
*
* IF we decide to get rid of surface/grid plots then this enrichment can
* be removed. But maybe someone likes this plot styles. I hate them.
*
*----------------------------------------------------------------------*/
Water::Water()
{
}
Water::Water(ModelPlot *model) : model(model) {}
void Water::drawBegin()
{
// diag has been moved to a global variable set by the data model
// this is because the reference variable below is cached
// and is unreliable!! (i.e. bug).
//diag_ = (plot->hull().maxVertex-plot->hull().minVertex).length() * barsize;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glLineWidth( 0 );
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0,1);
}
void Water::drawEnd()
{
// plot all the nonsense now -- could shade if
// power zones selected on plot (?)
if (model->zpane) {
// qwtplot3d api changes between 0.2.x and 0.3.x
#if QWT3D_MINOR_VERSION > 2
double minx = plot_p->hull().minVertex.x;
double miny = plot_p->hull().minVertex.y;
double maxx = plot_p->hull().maxVertex.x;
double maxy = plot_p->hull().maxVertex.y;
double minz = plot_p->hull().minVertex.z;
#else
double minx = plot->hull().minVertex.x;
double miny = plot->hull().minVertex.y;
double maxx = plot->hull().maxVertex.x;
double maxy = plot->hull().maxVertex.y;
double minz = plot->hull().minVertex.z;
#endif
double z = model->zpane + minz;
// ZPANE SHADING
glColor4f(0.7,0,0,0.4);
glBegin(GL_QUADS);
// top
glColor4d(0.5,0.5,1,1.0);
glVertex3d(minx,miny,z);
glVertex3d(minx,maxy,z);
glVertex3d(maxx,maxy,z);
glVertex3d(maxx,miny,z);
// bottom
glColor4d(0.5,0.5,1,1.0);
glVertex3d(minx,miny,minz);
glVertex3d(minx,maxy,minz);
glVertex3d(maxx,maxy,minz);
glVertex3d(maxx,miny,minz);
// front
glColor4d(0.5,0.5,1,1.0);
glVertex3d(minx,miny,minz);
glVertex3d(minx,miny,z);
glVertex3d(maxx,miny,z);
glVertex3d(maxx,miny,minz);
// back
glColor4d(0.5,0.5,1,1.0);
glVertex3d(minx,maxy,minz);
glVertex3d(minx,maxy,z);
glVertex3d(maxx,maxy,z);
glVertex3d(maxx,maxy,minz);
// left
glColor4d(0.5,0.5,1,1.0);
glVertex3d(minx,miny,minz);
glVertex3d(minx,miny,z);
glVertex3d(minx,maxy,z);
glVertex3d(minx,maxy,minz);
// right
glColor4d(0.5,0.5,1,1.0);
glVertex3d(maxx,miny,minz);
glVertex3d(maxx,miny,z);
glVertex3d(maxx,maxy,z);
glVertex3d(maxx,maxy,minz);
glEnd();
QColor x = GColor(CPLOTMARKER);
glColor3d(x.red(),x.green(),x.blue());
glBegin(GL_LINES);
glVertex3d(minx,miny,z); glVertex3d(minx,maxy, z);
glVertex3d(minx,maxy,z); glVertex3d(maxx,maxy, z);
glVertex3d(maxx,maxy,z); glVertex3d(maxx,miny, z);
glVertex3d(maxx,miny,z); glVertex3d(minx,miny, z);
glEnd();
}
}
// draw the enrichment, called for each xyz triple
void Water::draw(Qwt3D::Triple const& )
{}
/*----------------------------------------------------------------------
* BAR VERTEX ENRICHMENT (From here to bottom of source file)
*
* A vertex enrichment to show bars not a surface
* Code courtesy of the enrichment example in the qwtplot3d library and
* hacked to fixup reloading plots etc (the original was a demo example)
*
* CHANGED TO DRAW TWO BARS - wireframe for MAX and shaded for interval
* or wireframe and shaded MAX depending upon
* whether any intervals have been selected.
*
*----------------------------------------------------------------------*/
Bar::Bar()
{
}
Bar::Bar(ModelPlot *model) : model(model) {}
void Bar::drawBegin()
{
// diag has been moved to a global variable set by the data model
// this is because the reference variable below is cached
// and is unreliable!! (i.e. bug).
//diag_ = (plot->hull().maxVertex-plot->hull().minVertex).length() * barsize;
glLineWidth( 0 );
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1,1);
}
// enrichment pure virtual - we do nothing
void Bar::drawEnd()
{
// plot all the nonsense now -- could shade if
// power zones selected on plot (?)
if (model->zpane) {
// qwtplot3d api changes between 0.2.x and 0.3.x
#if QWT3D_MINOR_VERSION > 2
double minx = plot_p->hull().minVertex.x;
double miny = plot_p->hull().minVertex.y;
double maxx = plot_p->hull().maxVertex.x;
double maxy = plot_p->hull().maxVertex.y;
double minz = plot_p->hull().minVertex.z;
#else
double minx = plot->hull().minVertex.x;
double miny = plot->hull().minVertex.y;
double maxx = plot->hull().maxVertex.x;
double maxy = plot->hull().maxVertex.y;
double minz = plot->hull().minVertex.z;
#endif
double z = model->zpane + minz;
// ZPANE SHADING
glColor3d(0.7,0,0);
glBegin(GL_QUADS);
// top
glColor4d(0.5,0.5,1,0.7);
glVertex3d(minx,miny,z);
glVertex3d(minx,maxy,z);
glVertex3d(maxx,maxy,z);
glVertex3d(maxx,miny,z);
// bottom
glColor4d(0.5,0.5,1,0.7);
glVertex3d(minx,miny,minz);
glVertex3d(minx,maxy,minz);
glVertex3d(maxx,maxy,minz);
glVertex3d(maxx,miny,minz);
// front
glColor4d(0.5,0.5,1,0.7);
glVertex3d(minx,miny,minz);
glVertex3d(minx,miny,z);
glVertex3d(maxx,miny,z);
glVertex3d(maxx,miny,minz);
// back
glColor4d(0.5,0.5,1,0.7);
glVertex3d(minx,maxy,minz);
glVertex3d(minx,maxy,z);
glVertex3d(maxx,maxy,z);
glVertex3d(maxx,maxy,minz);
// left
glColor4d(0.5,0.5,1,0.7);
glVertex3d(minx,miny,minz);
glVertex3d(minx,miny,z);
glVertex3d(minx,maxy,z);
glVertex3d(minx,maxy,minz);
// left
glColor4d(0.5,0.5,1,0.7);
glVertex3d(maxx,miny,minz);
glVertex3d(maxx,miny,z);
glVertex3d(maxx,maxy,z);
glVertex3d(maxx,maxy,minz);
glEnd();
QColor x = GColor(CPLOTMARKER);
glColor3d(x.red(),x.green(),x.blue());
glBegin(GL_LINES);
glVertex3d(minx,miny,z); glVertex3d(minx,maxy, z);
glVertex3d(minx,maxy,z); glVertex3d(maxx,maxy, z);
glVertex3d(maxx,maxy,z); glVertex3d(maxx,miny, z);
glVertex3d(maxx,miny,z); glVertex3d(minx,miny, z);
glEnd();
}
}
// draw the enrichment, called for each xyz triple
void Bar::draw(Qwt3D::Triple const& pos)
{
// GLStateBewarer sb(GL_LINE_SMOOTH, true);
// sb.turnOn();
// qwtplot3d api changes between 0.2.x and 0.3.x
#if QWT3D_MINOR_VERSION > 2
double interval = plot_p->hull().maxVertex.z-plot_p->hull().minVertex.z;
double numlevel = plot_p->hull().minVertex.z + 1 * interval;
GLdouble gminz = plot_p->hull().minVertex.z;
#else
double interval = plot->hull().maxVertex.z-plot->hull().minVertex.z;
double numlevel = plot->hull().minVertex.z + 1 * interval;
GLdouble gminz = plot->hull().minVertex.z;
#endif
interval /=100;
// get the colour for this bar from the plot colour provider (defined above)
RGBA rgbat, rgbab;
// don't plot a thing if it don't mean a thing
if (pos.z == gminz) return;
if (model->intervals_ == 0) {
// just plot using normal colours (one set of bars per x/y)
// qwtplot3d api changes between 0.2.x and 0.3.x
#if QWT3D_MINOR_VERSION > 2
rgbat = (plot_p->dataColor())->rgba(pos);
rgbab = (plot_p->dataColor())->rgba(pos.x, pos.y, gminz);
#else
rgbat = (*plot->dataColor())(pos);
rgbab = (*plot->dataColor())(pos.x, pos.y, gminz);
#endif
} else {
// first bars use max and are see-through
QColor irgba = GCColor::invertColor(GColor(CPLOTBACKGROUND));
RGBA t(irgba.red()/255.0, irgba.green()/255.0, irgba.blue()/255.0, 1);
RGBA b(irgba.red()/255.0, irgba.green()/255.0, irgba.blue()/255.0, 0);
rgbat = t;
rgbab = b;
}
if (model->intervals_ == 0) {
// shade the max bars if not doing intervals
glBegin(GL_QUADS);
glColor4d(rgbab.r,rgbab.g,rgbab.b,rgbab.a);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,gminz);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,gminz);
if (pos.z > numlevel - interval && pos.z < numlevel + interval )
glColor3d(0.7,0,0);
else
glColor4d(rgbat.r,rgbat.g,rgbat.b,rgbat.a);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,pos.z);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,pos.z);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,pos.z);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,pos.z);
glColor4d(rgbab.r,rgbab.g,rgbat.b,rgbab.a);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,gminz);
glColor4d(rgbat.r,rgbat.g,rgbat.b,rgbat.a);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,pos.z);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,pos.z);
glColor4d(rgbab.r,rgbab.g,rgbat.b,rgbab.a);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,gminz);
glColor4d(rgbat.r,rgbat.g,rgbat.b,rgbat.a);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,pos.z);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,pos.z);
glColor4d(rgbab.r,rgbab.g,rgbat.b,rgbab.a);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,gminz);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,gminz);
glColor4d(rgbat.r,rgbat.g,rgbat.b,rgbat.a);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,pos.z);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,pos.z);
glColor4d(rgbab.r,rgbab.g,rgbat.b,rgbab.a);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,gminz);
glColor4d(rgbat.r,rgbat.g,rgbat.b,rgbat.a);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,pos.z);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,pos.z);
glEnd();
}
if (model->intervals_ == 0 || model->intervals_&SHOW_FRAME) {
QColor irgba = GCColor::invertColor(GColor(CPLOTBACKGROUND));
glColor3d(irgba.red()/255.0, irgba.green()/255.0,irgba.blue()/255);
glBegin(GL_LINES);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,gminz); glVertex3d(pos.x+model->diag_,pos.y-model->diag_,gminz);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,pos.z); glVertex3d(pos.x+model->diag_,pos.y-model->diag_,pos.z);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,pos.z); glVertex3d(pos.x+model->diag_,pos.y+model->diag_,pos.z);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,gminz); glVertex3d(pos.x+model->diag_,pos.y+model->diag_,gminz);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,gminz); glVertex3d(pos.x-model->diag_,pos.y+model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,gminz); glVertex3d(pos.x+model->diag_,pos.y+model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,pos.z); glVertex3d(pos.x+model->diag_,pos.y+model->diag_,pos.z);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,pos.z); glVertex3d(pos.x-model->diag_,pos.y+model->diag_,pos.z);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,gminz); glVertex3d(pos.x-model->diag_,pos.y-model->diag_,pos.z);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,gminz); glVertex3d(pos.x+model->diag_,pos.y-model->diag_,pos.z);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,gminz); glVertex3d(pos.x+model->diag_,pos.y+model->diag_,pos.z);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,gminz); glVertex3d(pos.x-model->diag_,pos.y+model->diag_,pos.z);
glEnd();
}
// if we have don't have intervals we're done
if (model->intervals_ == 0) return;
// get the intervals drawn
// just plot using normal colours (one set of bars per x/y)
// qwtplot3d api changes between 0.2.x and 0.3.x
#if QWT3D_MINOR_VERSION > 2
rgbat = (plot_p->dataColor())->rgba(pos);
rgbab = (plot_p->dataColor())->rgba(pos.x, pos.y, gminz);
#else
rgbat = (*plot->dataColor())(pos);
rgbab = (*plot->dataColor())(pos.x, pos.y, gminz);
#endif
// get pos for the interval data
// call the current data provider
// which is a global
double z = model->iz.value(xystring(pos.x,pos.y));
if (z == 0) return;
// do the max bars
glBegin(GL_QUADS);
glColor4d(rgbab.r,rgbab.g,rgbab.b,rgbab.a);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,gminz);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,gminz);
if (z > numlevel - interval && z < numlevel + interval )
glColor3d(0.7,0,0);
else
glColor4d(rgbat.r,rgbat.g,rgbat.b,rgbat.a);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,z);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,z);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,z);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,z);
glColor4d(rgbab.r,rgbab.g,rgbat.b,rgbab.a);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,gminz);
glColor4d(rgbat.r,rgbat.g,rgbat.b,rgbat.a);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,z);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,z);
glColor4d(rgbab.r,rgbab.g,rgbat.b,rgbab.a);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,gminz);
glColor4d(rgbat.r,rgbat.g,rgbat.b,rgbat.a);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,z);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,z);
glColor4d(rgbab.r,rgbab.g,rgbat.b,rgbab.a);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,gminz);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,gminz);
glColor4d(rgbat.r,rgbat.g,rgbat.b,rgbat.a);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,z);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,z);
glColor4d(rgbab.r,rgbab.g,rgbat.b,rgbab.a);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,gminz);
glColor4d(rgbat.r,rgbat.g,rgbat.b,rgbat.a);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,z);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,z);
glEnd();
QColor x = GColor(CPLOTMARKER);
glColor3d(x.red(),x.green(),x.blue());
glBegin(GL_LINES);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,gminz); glVertex3d(pos.x+model->diag_,pos.y-model->diag_,gminz);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,z); glVertex3d(pos.x+model->diag_,pos.y-model->diag_,z);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,z); glVertex3d(pos.x+model->diag_,pos.y+model->diag_,z);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,gminz); glVertex3d(pos.x+model->diag_,pos.y+model->diag_,gminz);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,gminz); glVertex3d(pos.x-model->diag_,pos.y+model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,gminz); glVertex3d(pos.x+model->diag_,pos.y+model->diag_,gminz);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,z); glVertex3d(pos.x+model->diag_,pos.y+model->diag_,z);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,z); glVertex3d(pos.x-model->diag_,pos.y+model->diag_,z);
glVertex3d(pos.x-model->diag_,pos.y-model->diag_,gminz); glVertex3d(pos.x-model->diag_,pos.y-model->diag_,z);
glVertex3d(pos.x+model->diag_,pos.y-model->diag_,gminz); glVertex3d(pos.x+model->diag_,pos.y-model->diag_,z);
glVertex3d(pos.x+model->diag_,pos.y+model->diag_,gminz); glVertex3d(pos.x+model->diag_,pos.y+model->diag_,z);
glVertex3d(pos.x-model->diag_,pos.y+model->diag_,gminz); glVertex3d(pos.x-model->diag_,pos.y+model->diag_,z);
glEnd();
}