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.

724 lines
25 KiB

/***************************************************************************
qsaxes.h
-------------------
version : 0.1
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. *
* *
***************************************************************************/
#ifndef QSAXES_H
#define QSAXES_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include<math.h>
#include<list.h>
#include<qstring.h>
#include<qptrlist.h>
#include"qsgraphicaldata.h"
#include"qsdrv.h"
#include"qscobject.h"
#include"qsprojection.h"
class QTimer;
class QSPlot;
class QSAxes;
/**
* \brief Simple QSCObject wrapper around QSAxes
*
* QSAxes doesn't inherit QCObject ( multimple inheritance from QObject is not allowed ), instead
* a shadow QSCObject is providen, so use:axes->shadowObject() instead of casting. Notice that
* this object inherits QSCGroup, so all objects added to this group will be bound with this axes -
* it will allow them to to use this axes coordinate system, for example arrow could point at
* some area of a graph. Constructor is a private memeber. You have to create an QSAxes2D or QSAxes3D
* first and use QSAxes::shadowObject() to get a pointer to this object.
*/
class QSCAxesShadow : public QSCGroup {
friend class QSAxes;
Q_OBJECT
public:
/**
* Destructor.
*/
virtual ~QSCAxesShadow();
virtual void setAutoUpdates( bool enabled );
/**
* Can't change parent axes. This method does nothing.
*/
virtual void setParentAxes( QSAxes * );
virtual void setBox( const QSRectf& rect, QSDrv *drv );
virtual QSRectf box( QSDrv *drv );
virtual bool isHit( const QSPt2f &p, QSDrv* drv );
virtual bool isAxesShadow();
virtual int style();
virtual QString name();
virtual void draw( QSDrv *drv, bool blocking, bool transparent );
virtual void paintSkeleton( QPainter *p, double dpi = 72.0 );
virtual void paint( QPainter *p, double dpi = 72.0, bool blocking=true, bool transparent=true );
virtual bool busy() const;
virtual void stop();
virtual void loadStateFromStream( QDataStream& stream, QSObjectFactory *factory );
virtual void saveStateToStream( QDataStream& stream, QSObjectFactory *factory );
public slots:
virtual void forceUpdate();
protected:
/**
* Protected constructor. Use QSAxes::shadowObject() to create QSCAxesShadow object.
*/
QSCAxesShadow( QSAxes *parent );
private slots:
virtual void slot_object_added( QSCObject *object );
virtual void slot_object_removed( QSCObject *object );
virtual void slot_draw_objects( QSDrv *drv, bool, bool );
virtual void slot_update();
void slot_draw_ends();
};
//----------------------------------------------------------------------------------------------------//
/**
* \brief Base graph object
*
* Base class for all axes.Generaly it holds a list of axes and datasets.There can be any number of axes in this
* object. They are avaliable as a simple list without further interpretation. Axes also implements QSCObject ( canvas
* object ) interface, so can be added to the canvas. Warning: It does not inherit QSCObject ( problem
* with multiple inheritance ) directly, you must create such object with shadowObject() method. This object can repaint
* itself using a given painter or driver. It takes much time to repaint a graph if it contains many datapoints,
* so it can be repainted in the background see paint() ( 'blocking' argument ), state(), stop(). sigDrawEnds()
* When it state changes and it needs redrawing it notify it using sigUpdate() .
* @author Kamil Dobkowski
*/
class QSAxes : public QSGraphicalData
{
Q_OBJECT
friend class QSPlot;
Q_PROPERTY( double xPosMM READ xPosMM WRITE setXPosMM )
Q_PROPERTY( double yPosMM READ yPosMM WRITE setYPosMM )
Q_PROPERTY( double widthMM READ widthMM WRITE setWidthMM )
Q_PROPERTY( double heightMM READ heightMM WRITE setHeightMM )
Q_PROPERTY( bool drawInBackground READ drawInBackground WRITE setDrawInBackground )
Q_PROPERTY( bool axesOnly READ axesOnly WRITE setAxesOnly )
Q_PROPERTY( QString background READ background_property WRITE set_background_property )
public:
/**
* Current state.
* @see #state
*/
enum InternalState { Waiting = 0, Busy };
/**
* See mixedToCanvas() , canvasToMixed(). When data point is to be drawn its coordinates are first mapped to
* world coordinate system using QSPlot::defaultAxis() and QSAxis::dataToWorld(). Notice that each dataset can
* use a different set of axes. World coorinate system is a simple (1,1) square for QSAxes2D, and (1,1,1) cube
* for QSAxes3D. Next if point is 'outside' axes ( has coordinates lower than 0.0 or greater than 1.0 ) is
* trown out, see proj() and QSProjection::clipPoint2(). If it is visible its coordinates are mapped to canvas
* ( screen ) coordinates using proj() and QSProjection::world2DToCanvas() and the point is drawn on the screen.
* You can next map canvas coordinates to mm's ( it is a matter of a dpi scale factor ). There are also normCoordinates,
* but they are rarely used.
*/
enum CoordinateSystem { mmCoord, normCoord, worldCoord, dataCoord };
/**
* Element category ( used by QSDrvHitTest ). You can find out which element is currently drawn
* using QSDrv::currentElement(), of course you will be able to call this function only if drawing
* is done in the background. See state() .
*/
enum ElementCategory { GeneralCategory, AxisCategory, GridCategory, DatasetCategory, UnknownCategory };
/**
* Constructor.
*/
QSAxes( QObject * parent, const QSProjection *proj, const char * name=0 );
/**
* Destructor.
*/
virtual ~QSAxes();
/**
* Returns the current state.
* Drawing - means that work is in progress at the moment (.
* using background handler callback ).
* Waiting - work is finished.
* You can stop drawing at any time by calling 'stop()'.
*/
InternalState state() const { return m_internal_state; }
/**
* Stops drawing immediately.
*/
virtual void stop();
/**
* Returns true if a last plot drawing operation was complete
* or false if was stopped before end.
*/
bool complete() const { return m_is_complete; }
//----------------------------------------------------------------//
/**
* Set position on a page in mm. This is not used by this class.
* It is only a useful hint. If you want to set the paint area
* basing on this parameter you must call setCanvasRect instead:
* QSCShadowObject uses this properties to find its size.
* Example: setCanvasRect( calculateCanvasRect(dpi) )
*/
void setPosMM( const QSPt2f& pos_mm );
/**
* Sets x position
*/
void setXPosMM( double x_mm );
/**
* Sets y position
*/
void setYPosMM( double y_mm );
/**
* Sets size of the axes on a page in mm. This is not used by this class
* It is only a useful hint.If you want to set the paint area you must
* call setCanvasRect instead. Example: setCanvasRect( calculateCanvasRect(dpi) )
* QSCShadowObject uses this properties to calculate its size.
*/
void setSizeMM( const QSPt2f& size_mm );
/**
* Sets width in mm.
*/
void setWidthMM( double w_mm );
/**
* Sets height in mm
*/
void setHeightMM( double h_mm );
/**
* Returns position of the axes on the page in mm.
*/
QSPt2f posMM() const { return m_pos_mm; }
/**
* Returns position of the axes on the page in mm.
*/
double xPosMM() const { return m_pos_mm.x; }
/**
* Returns position of the axes on the page in mm.
*/
double yPosMM() const { return m_pos_mm.y; }
/**
* Returns size of the axes in mm.
*/
QSPt2f sizeMM() const { return m_size_mm; }
/**
* Returns size of the axes in mm.
*/
double widthMM() const { return m_size_mm.x; }
/**
* Returns size of the axes in mm.
*/
double heightMM() const { return m_size_mm.y; }
/**
* Calculates canvas area from canvasPosMM() and canvasSizeMM()
*/
QSRectf calculateCanvasRect( double dpi = 72.0 );
/**
* Sets the canvas rectangle to be painted. There is no 'paremetersChanged()'
* or any other notification !
*/
void setCanvasRect( const QSRectf& r );
/**
* Returns canvas position.
* Result valid only during drawing - see @ref #state .
*/
QSRectf canvasRect() const { return QSRectf( m_cpos.x, m_cpos.y, m_csize.x, m_csize.y ); }
/**
* Turn on a spectial mode of fitting to canvas rect. There is no 'paremetersChanged()'
* or any other notification.
*/
void setFitToCanvasRect( bool enabled );
/**
*
*/
bool fitToCanvasRect() const { return m_transformation_rect; }
/**
* This is only a hint. You must pass 'blocking' value each time
* tou call paintPlot or drawPlot
*/
void setDrawInBackground( bool enabled );
/**
* See 'setDrawInBackground()'
*/
bool drawInBackground() const { return m_draw_in_background; }
//-------------------------------------------------------------//
/**
* Draws only axes ( not datasets ) -fast mode.
*/
void setAxesOnly( bool enabled );
/**
* Return an axes only setting.
*/
bool axesOnly() const { return m_axes_only; }
/**
* Sets the background color.
*/
void setBackground( const QSGFill& fill );
/**
* Returns the current background color.
*/
QSGFill background() const;
//------------------------------------------------------------//
/**
* Remembered view contains such parameters as axis min ,axis max, axis scale, axis reversed.
* Up to three different views can be remembered ( index must be in 0-2 ).
*/
virtual void rememberCurrentView( int index );
/**
* Sets the view properties
*/
virtual void setRememberedView( int index );
//------------------------------------------------------------//
/**
* Triggers transformation update after you modified 'setCanvasRect', datasets, axes etc.
* It is called also when redrawing, so there is no need to call it by hand.
*/
virtual void initMappings( QSDrv *drv );
/**
* Canvas coordinates are on-screen pixels coordinates. See 'canvasPos()' and 'canvasSize()'.
* Normalized coordinates maps canvas coordinates to <0,1> rectangle.
* Example: canvasToNormalized(canvasPos()) returns ( 0.0, 0.0 ) point.
* See also 'initMappings()'
*/
double canvasToNormalizedX( double value ) const ;
/**
* See canvasToNormalizedX()
*/
double canvasToNormalizedY( double value ) const ;
/**
* See canvasToNormalizedX()
*/
QSPt2f canvasToNormalized( const QSPt2f& pos ) const;
/**
* See canvasToNormalizedX()
*/
double normalizedXToCanvas( double value ) const ;
/**
* See canvasToNormalizedX()
*/
double normalizedYToCanvas( double value ) const ;
/**
* See canvasToNormalizedX()
*/
QSPt2f normalizedToCanvas( const QSPt2f& pos ) const;
/**
* From data to screen coordinates.
* See also 'initMappings()'
*/
QSPt2f dataToCanvas( const QSPt3f& pos, QSAxis *xAxis, QSAxis *yAxis, QSAxis *zAxis ) const;
/**
* From data to screen coordinates.
* See also 'initMappings()'
*/
QSPt2f dataToCanvas( const QSPt2f& pos, QSAxis *xAxis, QSAxis *yAxis ) const;
/**
* From mixed type coordinates to canvas. Not all combinations of CoordinateSystems are allowed in all cases.
* In in_coords parameter you can describe which coordinate system is used for each point coordinate. See
* CoordinateSystem .
*/
virtual QSPt3f mixedToCanvas( const QSPt3f& pos, CoordinateSystem in_coords[3], double dpi, QSAxis *xAxis, QSAxis *yAxis, QSAxis *zAxis ) const = 0;
/**
* From canvas to mixed coordinates. In out_coords parameter you can request which coordinate system
* should be used for each output coordinate. Not all combinations of CoordinateSystems are allowed in all cases.
* See CoordinateSystem .
*/
virtual QSPt3f canvasToMixed( const QSPt3f& pos, CoordinateSystem out_coords[3], double dpi, QSAxis *xAxis, QSAxis *yAxis, QSAxis *zAxis ) const = 0;
//------------------------------------------------------------//
/**
* Controls if method 'updatePlot()' will be called automatically
* after any parameter ( by set... method ) or data ( setMatrix... ) changes.
*/
void setAutoUpdates( bool enabled );
/**
* Returns the auto-updates setting.
*/
bool autoUpdates() const { return m_auto_updates; }
//----------------------------------------------------------------//
/**
* The same as axisCount()+plotCount().
*/
virtual int childCount() const;
/**
* Returns a child object - it is a joined list of child axes and
* child plots.
*/
virtual QSData *child( int index ) const;
//----------------------------------------------------------------//
/**
* Returns a total number of child plots.
*/
int plotCount() const;
/**
* Adds a new plot to the list. Emits sigChildAdded().
* The newly constructed object should be immediately added
* to the child list.
*/
void plotAdd( QSPlot *p );
/**
* Adds a new plot to the list. Emits sigChildAdded().
* The newly constructed object should be immediately added
* to the child list.
*/
void plotInsert( int beforePos, QSPlot *p );
/**
* Removes a dataset from the dataset list but doesn't delete it.
* Emits sigChildRemoved ( all child datasets are deleted in destructor however ).
*/
void plotRemove( QSPlot *p );
/**
* Removes and deletes a dataset from the dataset list. Emits sigChildRemoved
* It doesn't remove it nor deletes it if it is not found on the child list.
*/
void plotDelete( QSPlot *p );
/**
* Returns a plot at the index 'index'.
* Plots are numbered from 0 ( at the back ) to
* plotCount()-1 ( at the front ).
*/
QSPlot *plot( int index ) const ;
/**
* Returns the current index of the given plot.
*/
int plotIndex( QSPlot *o ) const ;
/**
* Moves the given object to the front. Emits sigChildOrder.
*/
void plotToFront( QSPlot *o );
/**
* Moves the given object to the back. Emits sigChildDOrder.
*/
void plotToBack( QSPlot *o );
/**
* Raises the given object. Emits sigChildOrder.
*/
void plotRaise( QSPlot *o );
/**
* Lowers the given object. Emits sigChildOrder.
*/
void plotLower( QSPlot * o );
/**
* Reorderes the given object. Emits sigChildOrder.
*/
void plotReorder( int position, QSPlot * o );
//----------------------------------------------------------------//
/**
* Returns a total number of child axes.
*/
int axisCount() const;
/**
* Adds a new axis to the list. Emits sigChildAdded().
* The newly constructed axis object should be immediately added
* to the child list.
*/
void axisAdd( QSAxis *axis );
/**
* Adds a new axis to the list. Emits sigChildAdded().
* The newly constructed axis object should be immediately added
* to the child list.
*/
void axisInsert( int position, QSAxis *axis );
/**
* Removes an axis from the axis list but doesn't delete it
* ( all child axes are deleted in destructor however ).
* You can't remove an axis if it is the last axis of the
* given type ( there always must be at least a one X,Y,V,Z axis present ).
* Operation is silently ignored in the case, and function returns false;.
* Emits sigChildRemoved
*/
bool axisRemove( QSAxis *axis );
/**
* Removes and deletes an axis from the axis list
* You can't remove an axis if it is the last axis of the
* given type ( there always must be at least a one X,Y,V,Z axis present ).
* Operation is silently ignored in the case, and function returns false;.
* Emits sigChildRemoved
*/
bool axisDelete( QSAxis *axis );
/**
* Returns axis of a given type. Returned axis is always first axis found
* ( with the lowest index ). axisType has type of QSAxis::AxisType.
*/
QSAxis *axisOfType( int axisType ) const;
/**
* Returns an axis at the index 'index'.
* Axes are numbered from 0 ( at the back ) to
* axisCount()-1 ( at the front ).
*/
QSAxis *axis( int index ) const;
/**
* Returns the current index of the given axis.
*/
int axisIndex( QSAxis *axis ) const ;
/**
* Moves the given axis to the front. Emits sigChildOrder.
*/
void axisToFront( QSAxis *axis );
/**
* Moves the given axis to the back. Emits sigChildDOrder.
*/
void axisToBack( QSAxis *axis );
/**
* Raises the given axis. Emits sigChildOrder.
*/
void axisRaise( QSAxis *axis );
/**
* Lowers the given axis. Emits sigChildOrder.
*/
void axisLower( QSAxis *axis );
/**
* Reorderes the given axis. Emits sigChildOrder.
*/
void axisReorder( int position, QSAxis *axis );
//----------------------------------------------------------------//
//----------------------------------------------------------------//
/**
* Returns a shadow object. It has the same position and size as axes.
* When this position is changed by object->setBox a position and size
* of this object is updated ( see 'posMM()' and 'sizeMM()' ).
* Do not delete it.
*/
QSCAxesShadow *shadowObject();
//----------------------------------------------------------------//
/**
* Info about the given point
*/
virtual QString posInfo( QSPt2f& pos );
//--------------------------------------------------------------------------//
/**
* Paints the plot to the given painter, 'dpi' is used to calculate
* a pixel-height of points and fonts. If blocking is true, this function
* returns after drawing ends ( may take a while ). If blocking is false
* driver is copied using QSDrv::copy(), this function returns immediately,
* drawing is continued in a timer event
*/
virtual void paintPlot( QPainter *p, double dpi=72.0, bool blocking=true, bool transparent=true ) = 0;
/**
* Paints the plot with the given driver, 'drv->dpi' is used to calculate
* a pixel-height of points and fonts. If blocking is true, this function
* returns after drawing ends ( may take a while ). If blocking is false
* driver is copied using QSDrv::copy(), this function returns immediately,
* drawing is continued in a timer event
*/
virtual void drawPlot( QSDrv *drv, bool blocking=true, bool transparent=true ) = 0;
/**
* Paints (fast )simplified version of this object
*/
virtual void paintSkeleton( QPainter *p, double dpi=72.0, bool reallyFast=false );
/**
* Emits sigUpdate
*/
void forceUpdate() { emit sigUpdate(); }
/**
*
*/
QSDrv *run_gDriver() const { return m_drv; }
/**
*
*/
double run_dpi() const { return m_curr_dpi; }
/**
* Returns a projection used by this axis object.
*/
const QSProjection *proj() const { return m_proj; }
//----------------------------------------------------------------//
void set_background_property( const QString& value );
QString background_property() const;
/**
* Restores all axes and datasets.
*/
virtual void loadStateFromStream( QDataStream& stream, QSObjectFactory *factory );
/**
* Saves all axes and datasets.
*/
virtual void saveStateToStream( QDataStream& stream, QSObjectFactory *factory );
signals:
/**
* Emitted after any data change in child plots and an axes object itself.
*/
void sigDataChanged( QSGraphicalData *b, int channel );
/**
*
*/
void sigUpdate();
/**
* After all ranges and transforms are calculated
*/
void sigRangesValid();
/**
* Emitted when draw ends but before graphics is closed.
*/
void sigUserDraw( QSDrv *drv, bool blocking, bool transparent );
/**
* Emitted when draw ends, after graphics has been closed.
*/
void sigDrawEnds();
//----------------------------------------------------------------//
protected:
/**
* Reimplemented. Calls stop().
*/
virtual void dataChanging( QSData *object, int channel = -1 );
/**
* Reimplemented. Emits sigDataChanged() and ( see setAutoUpdates() ) sigUpdate().
*/
virtual void dataChanged( QSData *object, int channel = -1 );
/**
* Reimplemented. Calls stop().
*/
virtual void parametersChanging();
/**
* Reimplemented. Emits ( see setAutoUpdates() ) sigUpdate().
*/
virtual void parametersChanged();
/**
* Starts redrawing process.
*/
virtual void start( QSDrv *drv, bool blocking, bool transparent );
/**
* Called when all ranges are calculated.
* Default implementation emits 'sigRangesValid'
*/
virtual void axisRangesCalculated() { emit sigRangesValid(); }
/**
* This function is called in 'start()' before drawing starts.
* Default implementation does nothing.
*/
virtual void allocRuntimeData() {}
/**
* This function is called in 'stop()' after drawing stops.
* Default implementation does nothing.
*/
virtual void freeRuntimeData() {}
/**
* Draws an axis. It is reimplemented in QSAxes2D and QSAxes3D
*/
virtual void drawAxis( QSAxis */*axis*/ ) {}
/**
* Draws an axis grid. It is reimplemented in QSAxes2D and QSAxes3D
*/
virtual void drawGrid( QSAxis */*axis*/, bool /*major*/ ) {}
/**
* Reimplemented from QObject
*/
virtual void childEvent ( QChildEvent * );
struct margins_t {
int l;
int r;
int t;
int b;
} m_m;
InternalState m_internal_state;
QSGFill m_bckg_fill;
QSPt2f m_csize;
QSPt2f m_cpos;
QSPt2f m_pos_mm;
QSPt2f m_size_mm;
double m_curr_dpi;
QSDrv *m_drv;
bool m_bkg_handler;
bool m_axes_only;
bool m_really_fast;
bool m_blocking;
bool m_transparent;
QSCAxesShadow *m_shadow_object;
bool m_transformation_rect;
bool m_draw_in_background;
const QSProjection *m_proj;
int m_curr_dataset_nr;
private:
struct qsaxes_private_data;
qsaxes_private_data *d;
QTimer *m_timer;
// change to fields
bool m_runtime_data_allocated;
bool m_current_started;
bool m_is_complete;
bool m_auto_updates;
bool m_delete_driver;
double m_almost_zero;
void calculate_auto_ranges();
private slots:
void work_proc();
};
//---------------------------------------------------------------------------------------------------//
/**
* \brief Object which have a parent axes.
*
* Axis or dataset
* @author Kamil Dobkowski
*/
class QSAxesChild : public QSGraphicalData
{
Q_OBJECT
public:
/**
* Constructor. The object should be added immediately to the
* Axes child list.
*/
QSAxesChild( QSAxes *parentAxes, const char *name=0 );
/**
* Destructor
*/
virtual ~QSAxesChild();
/**
* Returns parent axes.
*/
QSAxes *parentAxes() const { return m_axes; }
protected:
QSAxes *m_axes;
};
#endif