You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

677 lines
19 KiB

/***************************************************************************
qscurve.cpp
-------------------
begin : 01-January-2000
copyright : (C) 2000 by Kamil Dobkowski
email : kamildobk@poczta.onet.pl
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include "qscurve.h"
#include <assert.h>
#define SET_PROPERTY( property, new_value ) if ((property)!=(new_value)) { parametersChanging(); (property)=(new_value); parametersChanged(); }
struct curve_runtime_data {
int stage;
int pk;
QSAxis *xaxis;
QSAxis *yaxis;
int cpass;
int tpass;
bool ipass;
QSMatrix *sx;
QSMatrix *sy;
QSMatrix *dx;
QSMatrix *dy;
int xw;
int yw;
bool delete_po;
bool shasx;
bool shasdx;
bool shasdy;
QSSegment *po;
QSPt2f zero;
QSPt2f curr_pos;
QSPt2f curr_delta;
QSGFill curr_fill;
QSGLine curr_line;
QSGPoint curr_point;
QSGLine curr_err_line;
QSGArrow curr_arrow_1;
QSGArrow curr_arrow_2;
QSGLine curr_x_line;
QSGLine curr_y_line;
};
//-------------------------------------------------------------//
QSCurve::QSCurve(QSAxes * parent, const char * name)
:QSPlot2D(parent,name)
{
assert( parent );
d = NULL;
m_title_str = tr("Untitled curve");
m_evalid = false;
m_type = Lines;
m_po = NULL;
m_xmin = 0.0;
m_xmax = 0.0;
m_ymin = 0.0;
m_ymax = 0.0;
m_zero.set(0.0,0.0);
m_pdelta.set(0.0,0.0);
m_fdelta.set(0.0,0.0);
#define CHANNELS_NUM 11
#define FILLS_NUM 1
#define FONTS_NUM 0
#define LINES_NUM 4
#define POINTS_NUM 1
initChannelTable( CHANNELS_NUM );
initAttributeTables( FONTS_NUM, FILLS_NUM, LINES_NUM, POINTS_NUM );
m_settings.lines[ErrorLine] = QSGLine::invisibleLine;
m_settings.lines[XLine] = QSGLine::invisibleLine;
m_settings.lines[YLine] = QSGLine::invisibleLine;
m_settings.fills[BaseFill] = QSGFill::transparentFill;
}
//-------------------------------------------------------------//
QSCurve::~QSCurve()
{
delete m_po;
}
//-------------------------------------------------------------//
void QSCurve::dataChanged( int channel )
{
m_evalid = false;
QSPlot2D::dataChanged( channel );
}
//-------------------------------------------------------------//
void QSCurve::allocRuntimeData()
{
QSPlot2D::allocRuntimeData();
d = new curve_runtime_data();
d->po = NULL;
d->stage = 0;
d->delete_po = false;
d->pk = 0;
d->sx = matrix( XVector );
d->sy = matrix( YVector );
d->dx = matrix( DXVector );
d->dy = matrix( DYVector );
d->xw = matrixRows( XVector );
d->yw = matrixRows( YVector );
d->shasx = ( matrixRows(XVector) == d->yw );
d->shasdx = ( matrixRows(DXVector) == d->yw );
d->shasdy = ( matrixRows(DYVector) == d->yw );
d->xaxis = defaultAxis(QSAxis::XAxisType);
d->yaxis = defaultAxis(QSAxis::YAxisType);
}
//-------------------------------------------------------------//
void QSCurve::freeRuntimeData()
{
if ( d->delete_po ) delete d->po;
delete d; d = NULL;
QSPlot2D::freeRuntimeData();
}
//-------------------------------------------------------------//
void QSCurve::get_values( int index )
{
d->curr_pos.x = d->shasx ? d->sx->value(index,0) : index;
d->curr_pos.y = d->sy->value(index,0);
d->curr_delta.x = m_fdelta.x + ( d->shasdx ? d->dx->value(index,0) : 0.0 ) + m_pdelta.x*d->curr_pos.x/100.0;
d->curr_delta.y = m_fdelta.y + ( d->shasdy ? d->dy->value(index,0) : 0.0 ) + m_pdelta.y*d->curr_pos.y/100.0;
}
//-------------------------------------------------------------//
void QSCurve::get_attributes( int index )
{
d->curr_point = pointFromData( matrix(PointStyles), index, 0, m_settings.points[PointMark] );
d->curr_fill = fillFromData( matrix(FillStyles), index, 0, m_settings.fills[BaseFill] );
d->curr_line = lineFromData( matrix(LineStyles), index, 0, m_settings.lines[BaseLine] );
d->curr_arrow_1 = arrowFromData( matrix(ArrowStyles), index, 0, arrow1() );
d->curr_arrow_2 = arrowFromData( matrix(ArrowStyles), index, 2, arrow2() );
d->curr_err_line = lineFromData( matrix(ErrorLineStyles), index, 0, m_settings.lines[ErrorLine] );
d->curr_x_line = lineFromData( matrix(XLineStyles), index, 0, m_settings.lines[XLine] );
d->curr_y_line = lineFromData( matrix(YLineStyles), index, 0, m_settings.lines[YLine] );
}
//-------------------------------------------------------------//
bool QSCurve::getAxisRange( QSAxis *axis, double& min, double& max )
{
allocRuntimeData();
if ( d->yw == 0 ) { freeRuntimeData(); return false; }
if ( !m_evalid ) {
d->pk = 0;
// if we should care about +/-dx or only +dx
bool dminus = (line(ErrorLine).style != QSGLine::Invisible || m_type == Ribbon);
while ( d->pk<d->yw ) {
get_values( d->pk++ );
QSPt2f p1 = dminus ? d->curr_pos-d->curr_delta : d->curr_pos;
QSPt2f p2 = d->curr_pos+d->curr_delta;
if ( m_xmin > p1.x || d->pk == 1 ) m_xmin = p1.x;
if ( m_xmax < p1.x || d->pk == 1 ) m_xmax = p1.x;
if ( m_xmin > p2.x ) m_xmin = p2.x;
if ( m_xmax < p2.x ) m_xmax = p2.x;
if ( m_ymin > p1.y || d->pk == 1 ) m_ymin = p1.y;
if ( m_ymax < p1.y || d->pk == 1 ) m_ymax = p1.y;
if ( m_ymin > p2.y ) m_ymin = p2.y;
if ( m_ymax < p2.y ) m_ymax = p2.y;
}
m_evalid = true;
}
if ( axis == d->xaxis ) { min = m_xmin; max = m_xmax; }
else if ( axis == d->yaxis ) { min = m_ymin; max = m_ymax; }
else { freeRuntimeData(); return false; }
freeRuntimeData();
return true;
}
//-------------------------------------------------------------//
bool QSCurve::start()
{
QSPlot2D::start();
d->stage = 0;
d->pk = 0;
d->zero = dataToWorld(m_zero);
d->stage = StartDrawLines;
return true;
}
//-------------------------------------------------------------//
bool QSCurve::step()
{
switch( d->stage ) {
case StartDrawLines: start_draw_lines(); break;
case DrawLines: draw_lines(); break;
case StartDrawSeries: start_draw_series(); break;
case DrawSeries: draw_series(); break;
case StartDrawErrorbars: start_draw_errorbars(); break;
case DrawErrorbars: draw_errorbars(); break;
case StartDrawPointMarks: start_draw_pointmarks(); break;
case DrawPointMarks: draw_pointmarks(); break;
default: return false;
}
return true;
}
//-------------------------------------------------------------//
void QSCurve::end()
{
QSPlot2D::end();
}
//-------------------------------------------------------------//
void QSCurve::start_draw_lines()
{
d->pk=0;
if ( line(XLine).style == QSGLine::Invisible &&
line(YLine).style == QSGLine::Invisible &&
!isChannel(XLineStyles) &&
!isChannel(YLineStyles) ) {
d->stage = StartDrawSeries;
} else {
d->stage = DrawLines;
}
}
//-------------------------------------------------------------//
void QSCurve::draw_lines()
{
QSPt2f p1;
QSPt2f p2;
int curr_step = 0;
while( d->pk<d->yw ) {
get_values( d->pk ); get_attributes( d->pk ); d->pk ++;
QSPt2f pos = dataToWorld(d->curr_pos);
// horizontal line
p1.set( d->zero.x, pos.y );
p2.set( pos.x, pos.y );
m_drv->setLine( d->curr_x_line );
m_drv->drawLine2(p1,p2);
// vertical line
p1.set( pos.x, d->zero.y );
p2.set( pos.x, pos.y );
m_drv->setLine( d->curr_y_line );
m_drv->drawLine2(p1,p2);
if ( ++curr_step > work_steps && m_bkg_handler ) return;
}
d->stage = StartDrawSeries;
}
//-------------------------------------------------------------//
void QSCurve::create_po()
{
d->delete_po = true;
switch( m_type ) {
case Lines: d->po = new QSSLines(); break;
case Area: d->po = new QSSPolys(QSSPolys::Area); break;
case Ribbon: d->po = new QSSPolys(QSSPolys::Ribbon); break;
case Bars: d->po = new QSSBars(); break;
case Vectors: d->po = new QSSFigures(QSSFigures::Vectors); break;
case Flux: d->po = new QSSFigures(QSSFigures::Flux); break;
case Rectangles: d->po = new QSSFigures(QSSFigures::Rectangles); break;
case Ellipses: d->po = new QSSFigures(QSSFigures::Ellipses); break;
case LeftStairs: d->po = new QSSStairs(QSSStairs::Left ); break;
case MiddleStairs:d->po = new QSSStairs(QSSStairs::Middle ); break;
case RightStairs: d->po = new QSSStairs(QSSStairs::Right ); break;
default: d->po = m_po; d->delete_po = false; break;
}
}
//-------------------------------------------------------------//
void QSCurve::start_draw_series()
{
create_po();
d->pk = 0;
d->ipass = false;
d->tpass = d->po->startDraw( this );
if ( d->tpass > 0 ) {
d->cpass = 0;
d->ipass = true;
d->stage = DrawSeries;
d->po->initPass( 0 );
}
}
//-------------------------------------------------------------//
void QSCurve::draw_series()
{
int curr_step = 0;
while( d->ipass && d->pk<d->yw ) {
get_values( d->pk ); get_attributes( d->pk );
d->po->drawSegment( d->pk,
d->curr_pos,
d->curr_delta,
d->curr_line,
d->curr_fill,
d->curr_arrow_1,
d->curr_arrow_2 );
d->pk++;
if ( ++curr_step > work_steps && m_bkg_handler ) return;
}
d->po->endPass();
d->pk = 0;
d->cpass = d->cpass ++;
if ( d->cpass >= d->tpass ) {
// stop drawing
d->po->stopDraw();
if ( d->delete_po ) { delete d->po; d->po = NULL; }
d->stage = StartDrawErrorbars;
} else {
// start new pass
d->po->initPass(d->cpass);
}
}
//-------------------------------------------------------------//
void QSCurve::start_draw_errorbars()
{
d->pk = 0;
if ( line(ErrorLine).style == QSGLine::Invisible && !isChannel(ErrorLineStyles) ) {
d->stage = StartDrawPointMarks;
} else {
d->stage = DrawErrorbars;
}
}
//-------------------------------------------------------------//
void QSCurve::draw_errorbars()
{
int curr_step = 0;
while( d->pk<d->yw ) {
get_values( d->pk ); get_attributes( d->pk ); d->pk++;
// vertical ( y ) error bar
m_drv->setLine( d->curr_err_line );
m_drv->drawArrow2( dataToWorld(QSPt2f(d->curr_pos.x,d->curr_pos.y-d->curr_delta.y)),
dataToWorld(QSPt2f(d->curr_pos.x,d->curr_pos.y+d->curr_delta.y)),
d->curr_arrow_2,
d->curr_arrow_2 );
// horizontal ( x ) error bar
m_drv->setLine( d->curr_err_line );
m_drv->drawArrow2( dataToWorld(QSPt2f(d->curr_pos.x-d->curr_delta.x,d->curr_pos.y)),
dataToWorld(QSPt2f(d->curr_pos.x+d->curr_delta.x,d->curr_pos.y)),
d->curr_arrow_1,
d->curr_arrow_1 );
if ( ++curr_step > work_steps && m_bkg_handler ) return;
}
d->pk = 0;
d->stage = StartDrawPointMarks;
}
//-------------------------------------------------------------//
void QSCurve::start_draw_pointmarks()
{
d->pk = 0;
if ( point(PointMark).style == QSGPoint::Invisible && !isChannel(PointStyles) ) {
d->stage = Stop;
} else {
d->stage = DrawPointMarks;
}
}
//-------------------------------------------------------------//
void QSCurve::draw_pointmarks()
{
int curr_step = 0;
while( d->pk<d->yw ) {
get_values( d->pk ); get_attributes( d->pk ); d->pk++;
m_drv->drawPoint2( dataToWorld(d->curr_pos), d->curr_point );
if ( ++curr_step > work_steps && m_bkg_handler ) return;
}
d->pk = 0; d->stage = Stop;
}
//-------------------------------------------------------------//
void QSCurve::setType( int type )
{
if ( type >= Lines && type <= User )
if ( m_type != type ) {
parametersChanging();
m_type = (SeriesType )type;
m_evalid = false;
parametersChanged();
}
}
//-------------------------------------------------------------//
void QSCurve::setPercentDelta( const QSPt2f& new_delta )
{
if ( m_pdelta != new_delta ) {
parametersChanging();
m_pdelta = new_delta;
m_evalid = false;
parametersChanged();
}
}
//-------------------------------------------------------------//
void QSCurve::setPercentDX( double value )
{
if ( m_pdelta.x != value ) {
parametersChanging();
m_pdelta.x = value;
m_evalid = false;
parametersChanged();
}
}
//-------------------------------------------------------------//
void QSCurve::setPercentDY( double value )
{
if ( m_pdelta.y != value ) {
parametersChanging();
m_pdelta.y = value;
m_evalid = false;
parametersChanged();
}
}
//-------------------------------------------------------------//
void QSCurve::setFixedDelta( const QSPt2f& new_delta )
{
if ( m_fdelta != new_delta ) {
parametersChanging();
m_fdelta = new_delta;
m_evalid = false;
parametersChanged();
}
}
//-------------------------------------------------------------//
void QSCurve::setFixedDX( double value )
{
if ( m_fdelta.x != value ) {
parametersChanging();
m_fdelta.x = value;
m_evalid = false;
parametersChanged();
}
}
//-------------------------------------------------------------//
void QSCurve::setFixedDY( double value )
{
if ( m_fdelta.y != value ) {
parametersChanging();
m_fdelta.y = value;
m_evalid = false;
parametersChanged();
}
}
//-------------------------------------------------------------//
void QSCurve::setZeroPoint( const QSPt2f& new_zero )
{
SET_PROPERTY( m_zero, new_zero );
}
//-------------------------------------------------------------//
void QSCurve::setZeroLevelX( double x )
{
SET_PROPERTY( m_zero.x, x );
}
//-------------------------------------------------------------//
void QSCurve::setZeroLevelY( double y )
{
SET_PROPERTY( m_zero.y, y );
}
//-------------------------------------------------------------//
void QSCurve::setPObject( QSSegment *new_po )
{
if ( m_po != new_po ) {
parametersChanging();
delete m_po; m_po = new_po;
parametersChanged();
}
}
//-------------------------------------------------------------//
void QSCurve::setArrow1( const QSGArrow& astyle )
{
SET_PROPERTY( m_arrow1, astyle );
}
//-------------------------------------------------------------//
void QSCurve::setArrow2( const QSGArrow& astyle )
{
SET_PROPERTY( m_arrow2, astyle );
}
//-------------------------------------------------------------//
QString QSCurve::posInfo( QSPt2f& pos )
{
if ( m_busy ) return QString::null;
QSPt2f p;
double dp = 100.0;
QString result = QString::null;
allocRuntimeData();
while( d->pk<d->yw ) {
get_values( d->pk );
QSPt2f p1 = m_axes->dataToCanvas(d->curr_pos,d->xaxis,d->yaxis);
double d1 = (p1.x-pos.x)*(p1.x-pos.x)+(p1.y-pos.y)*(p1.y-pos.y);
if ( d1 < 5.0*5.0 && d1 < dp ) {
dp = d1;
p = p1;
result = QString(tr("Index ")) + QString::number(d->pk) + "\n";
result += QString(tr("X = ")) + QString::number(d->curr_pos.x) + "\n";
result += QString(tr("Y = ")) + QString::number(d->curr_pos.y) + "\n";
if ( d->shasdx ) result += QString(tr("DX = ")) + QString::number(d->dx->value(0,d->pk)) + "\n";
if ( d->shasdy ) result += QString(tr("DY = ")) + QString::number(d->dy->value(0,d->pk)) + "\n";
result += QString(tr("Delta DX = ")) + QString::number(d->curr_delta.x) + "\n";
result += QString(tr("Delta DY = ")) + QString::number(d->curr_delta.y) + "\n";
if ( d->curr_delta.x ) result += QString(tr("X-delta = ")) + QString::number(d->curr_pos.x-d->curr_delta.x) + "\n";
if ( d->curr_delta.x ) result += QString(tr("X+delta = ")) + QString::number(d->curr_pos.x+d->curr_delta.x) + "\n";
if ( d->curr_delta.y ) result += QString(tr("Y-delta = ")) + QString::number(d->curr_pos.y-d->curr_delta.y) + "\n";
if ( d->curr_delta.y ) result += QString(tr("Y+delta = ")) + QString::number(d->curr_pos.y+d->curr_delta.y) + "\n";
}
d->pk++;
}
freeRuntimeData();
if ( result != QString::null ) pos.set( p.x, p.y );
return result;
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
#define BOX_SIZE 20
#define BOX_SPACE 5
//-------------------------------------------------------------//
QSPt2f QSCurve::legendItemSize( QSDrv *drv )
{
double boxSize = drv->toPixels(BOX_SIZE);
double boxSpace = drv->toPixels(BOX_SPACE);
QSPt2f tsize = drv->textSize( title() );
return QSPt2f(boxSize+boxSpace+tsize.x,QMAX(tsize.y,boxSize));
}
//-------------------------------------------------------------//
void QSCurve::drawLegendItem( const QSPt2f& pos, QSDrv *drv )
{
double boxSize = drv->toPixels(BOX_SIZE);
double boxSpace = drv->toPixels(BOX_SPACE);
QSPt2f tsize = drv->textSize( title() );
double height = QMAX(tsize.y,boxSize);
QSPt2f bpos( pos.x, pos.y+(height-boxSize)/2.0 );
QSPt2f tpos( pos.x+boxSpace+boxSize, pos.y+(height-tsize.y)/2.0 );
drv->drawText( tpos, title(), AlignLeft | AlignTop );
drv->setLine(line(BaseLine));
if ( fill(BaseFill).style != QSGFill::Transparent ) {
drv->setFill( fill(BaseFill) );
drv->drawRect( bpos, QSPt2f(bpos.x+boxSize,bpos.y+boxSize) );
} else {
drv->drawLine( QSPt2f(pos.x,pos.y+height/2.0), QSPt2f(pos.x+boxSize,pos.y+height/2.0) );
}
if ( point(PointMark).style != QSGPoint::Invisible ) {
drv->drawPoint( QSPt2f(pos.x+boxSize/2.0,pos.y+height/2.0), point(PointMark) );
}
}
//-------------------------------------------------------------//
void QSCurve::loadStateFromStream( QDataStream& stream, QSObjectFactory *factory )
{
QSPlot2D::loadStateFromStream( stream, factory );
}
//-------------------------------------------------------------//
void QSCurve::saveStateToStream( QDataStream& stream, QSObjectFactory *factory )
{
QSPlot2D::saveStateToStream( stream, factory );
}
//-------------------------------------------------------------//
QString QSCurve::channelVariable( int channel ) const
{
switch( channel ) {
case XVector: return "x";
case YVector: return "y";
case DXVector: return "dx";
case DYVector: return "dy";
case LineStyles: return "line";
case FillStyles: return "fill";
case PointStyles: return "point";
case ArrowStyles: return "arrow";
case ErrorLineStyles: return "eline";
case XLineStyles: return "xline";
case YLineStyles: return "yline";
}
return QString::null;
}
//-------------------------------------------------------------//
QSCurve::ColumnType QSCurve::columnType( int channel, int column ) const
{
}
//-------------------------------------------------------------//