mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 08:38:45 +00:00
... change storage format to .INI files (which is QTs cross-system format) ... differentiate between System, Global and Athlete specific settings ... store the Global Settings in the AthleteDirectory (root) ... store the Athlete specific Settings in the Athletes Names subdir /config ... migrate existing Settings from current location into new formats "on-the-fly"
1472 lines
53 KiB
C++
1472 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) {
|
|
int red = (double)255 * (double)((val-min)/(max-min));
|
|
if (red < 0 || red > 255) red = 127;
|
|
cHSV.setHsv(red, 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 {
|
|
double val;
|
|
int i;
|
|
// just add 100 colors graded from min to max
|
|
for (i=0, val=min; i<100; i++, val += ((max-min)/100)) {
|
|
|
|
int red = (double)255 * (double)((val-min)/(max-min));
|
|
if (red < 0 || red > 255) red = 127;
|
|
cHSV.setHsv(red, 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();
|
|
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();
|
|
}
|