View.h 16.3 KB
Newer Older
1 2 3
/*
* KmPlot - a math. function plotter for the KDE-Desktop
*
4
* Copyright (C) 1998, 1999, 2000, 2002  Klaus-Dieter Möller <kd.moeller@t-online.de>
David Saxton's avatar
David Saxton committed
5
*                     2006 David Saxton <david@bluehaze.org>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
*               
* This file is part of the KDE Project.
* KmPlot is part of the KDE-EDU Project.
*
* 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
Dirk Mueller's avatar
Dirk Mueller committed
22
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
23 24 25
*
*/

26 27
#ifndef View_included
#define View_included
28
#undef	 GrayScale
29

30 31
// Qt includes
#include <qpixmap.h>
32
#include <QPointer>
Laurent Montel's avatar
Laurent Montel committed
33 34 35 36
#include <QMouseEvent>
#include <QResizeEvent>
#include <QKeyEvent>
#include <QEvent>
37

38
// KDE includes
39
#include <kactionclasses.h>
40
#include <kdebug.h>
Laurent Montel's avatar
Laurent Montel committed
41
#include <kmenu.h>
42
#include <kprinter.h>
43 44
#include <kpushbutton.h>

45

46 47
// local includes
#include "diagr.h"
48
#include "xparser.h"
49

50
class KMinMax;
51
class KSliderWindow;
David Saxton's avatar
David Saxton committed
52
class MainDlg;
53
class QPaintEvent;
54
class QTime;
55 56 57 58 59 60 61 62 63 64 65
class XParser;


/**
 * For drawing the area of a (Cartesian) plot.
 */
 class IntegralDrawSettings
{
	public:
		IntegralDrawSettings();
		
66
		Plot plot;
67 68 69
		double dmin, dmax;
};

70

Matthias Messmer's avatar
Matthias Messmer committed
71
/**
Matthias Messmer's avatar
Matthias Messmer committed
72
 * @short This class contains the plots. 
Matthias Messmer's avatar
Matthias Messmer committed
73
 *
Matthias Messmer's avatar
Matthias Messmer committed
74
 * It is the central widget of MainDlg.
Matthias Messmer's avatar
Matthias Messmer committed
75
 * @see MainDlg, MainDlg::view
Matthias Messmer's avatar
Matthias Messmer committed
76
 */
77
class View : public QWidget
78
{
79
	Q_OBJECT
80 81 82 83 84
	public:
		/// Contructor sets up the parser, too.
		View( bool readOnly, bool & modified, KMenu * functionPopup, QWidget* parent, KActionCollection *ac );
		void setMinMaxDlg(KMinMax *);
		virtual ~View();
85
	
86 87
		/// There is only one view.
		static View * self() { return m_self; }
88

89 90 91 92 93
		enum PlotMedium
		{
			Screen,
			Printer,
			SVG,
94
			Pixmap
95 96 97 98 99 100 101 102 103 104 105 106 107 108
		};
		/// Reimplemented to draw all stuff to the view.
		void draw( QPaintDevice *, PlotMedium medium );
		
		/// Getting all relevant settings using KConfig XT class Settings.
		void getSettings();
		/// Clears all functions in the parser and gets default settings.
		/// @see getSettings
		void init();
		enum ExtremaType { Minimum, Maximum };
		/**
		* Finding the minimum or maximum value.
		* \return The (x,y) coordinates of the extrema point.
		*/
109
		QPointF findMinMaxValue( const Plot & plot, ExtremaType type, double dmin, double dmax );
110 111 112 113 114 115
		/**
		* Calculates the area between the given plot and the x-axis
		* (from x = \p dmin to x = \p dmax). The area will also be colored in.
		* \return the area.
		*/
		double areaUnderGraph( IntegralDrawSettings settings );
David Saxton's avatar
David Saxton committed
116 117 118 119 120 121 122 123 124
		/// the calculation was cancelled by the user
		bool isCalculationStopped();
		/**
		 * Used in posToString for requesting how the position string is to be
		 * created.
		 */
		enum PositionFormatting
		{
			DecimalFormat,		///< Plain text, using no scientific notation; just decimal expansion.
125
			ScientificFormat	///< Rich text possibly using scientific notation (mult x 10 ^ exp).
David Saxton's avatar
David Saxton committed
126 127 128 129 130 131 132 133 134 135 136
		};
		/**
		 * @return a string for displaying the x or y coordinate in the statusbar.
		 * \param x The number to convert to a string.
		 * \param delta is the amount by which the value varies over one pixel in
		 * the view. This is for choosing an appropriate number of decimals so that
		 * moving the cursor shows a nice change in the string.
		 * \param format How the number should be represented as a string.
		 * \param color If using scientific mode, the color to format the text.
		 */
		QString posToString( double x, double delta, PositionFormatting format, QColor color = Qt::black ) const;
137

David Saxton's avatar
David Saxton committed
138 139 140 141
		/// Slider controlling parameter values
		QPointer<KSliderWindow> m_sliderWindow;
		/// Menu actions for the sliders
		KToggleAction * m_menuSliderAction;
142 143
		/// Menu action for showing function extrema
		KToggleAction * m_showFunctionExtrema;
David Saxton's avatar
David Saxton committed
144 145
		void updateSliders(); /// show only needed sliders
		
146 147 148
		/**
		 * Convert a width in mm to a suitable QPen width for drawing.
		 */
David Saxton's avatar
David Saxton committed
149
		double mmToPenWidth( double width_mm, bool antialias ) const;
150

151 152 153
		/** Current plot range endge. */
		double m_xmin;
		double m_xmax;
David Saxton's avatar
David Saxton committed
154

155
		/// trace mode stuff, must be accessible in KMinMax
156 157 158 159 160 161 162 163 164 165 166
		Plot m_currentPlot;
		/**
		 * Convenience function for calculating the value of \p eq using the
		 * given \p mode
		 */
		double value( const Plot & plot, int eq, double x, bool updateParameter );
		/**
		 * \return the real position of the function (similar to calling
		 * value(), but returns both coordinates).
		 */
		QPointF realValue( const Plot & plot, double x, bool updateParameter );
167 168 169 170 171 172 173 174 175 176
		/**
		 * \return the (signed) curvature (in screen coordinates) of the plot
		 * at \p x (and \p y for implicit functions).
		 */
		double pixelCurvature( const Plot & plot, double x, double y = 0 );
		/**
		 * \return the angle of the normal (in radians) of the plot when viewed
		 * on the screen.
		 */
		double pixelNormal( const Plot & plot, double x, double y = 0 );
David Saxton's avatar
David Saxton committed
177

178 179 180 181 182 183 184 185 186 187 188 189
	public slots:
		/// Called when the user want to cancel the drawing
		void stopDrawing();
		/// A slider window has been closed
		void slidersWindowClosed();
	
		/// Called when the graph should be updated
		void drawPlot();
		///Slots for the three first items in popup menu
		void mnuHide_clicked();
		void mnuRemove_clicked();
		void mnuEdit_clicked();
190
		void showExtrema( bool show );
191
		void animateFunction();
192 193 194 195
		///Slots for the zoom menu
		void mnuZoomIn_clicked();
		void mnuZoomOut_clicked();
		void mnuTrig_clicked();
Fredrik Edemar's avatar
Fredrik Edemar committed
196

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
	protected slots:
		void paintEvent(QPaintEvent *);
		void resizeEvent(QResizeEvent *);
		/// Updating the cross hair.
		void mouseMoveEvent(QMouseEvent *);
		/// Toggles the trace mode if the cursor is near to a plot.
		void mousePressEvent(QMouseEvent *);
		/// when a key is pressed and the graph widget has focus
		void keyPressEvent(QKeyEvent * );
		/// called when a mouse key is released
		void mouseReleaseEvent ( QMouseEvent * e );
		/// Is needed to be reimplement so that the user can stop a preview-drawing
		bool event( QEvent * e );
		/// Restore the mouse cursor when a drawing is finished
		void updateCursor();
		/**
		* Updates csxpos and csypos from the current mouse position.
		* @return whether the crosshair is within the bounds of the diagram.
		*/
		bool updateCrosshairPosition();
	
	signals:
		void setStatusBarText(const QString &);
220
	
221 222 223 224 225 226
	protected:
		/// called when focus is lost
		virtual void focusOutEvent( QFocusEvent * );
		/// called when focus is gained
		virtual void focusInEvent( QFocusEvent * );
	
227
	private:
228 229 230 231
		/**
		 * \return an appropriate value to use in numerical differentiation.
		 */
		double h() const;
232 233 234 235 236
		/**
		* Print out table with additional information. Only for printing.
		*/
		void drawHeaderTable(QPainter *);
		/**
237
		* Draw the function plots (other than implicit).
238 239
		*/
		void plotFunction(Function *ufkt, QPainter*);
240 241 242 243
		/**
		 * Draw an implicit function.
		 */
		void plotImplicit( Function * function, QPainter * );
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
		/**
		 * Draw the extrema points, function names, etc. This needs to be done
		 * after the functions have all been drawn so that the label positioning
		 * knows where the plots have been drawn.
		 */
		void drawFunctionInfo( QPainter * painter );
		/**
		 * Initializes for the drawLabel function, called before drawing has
		 * started.
		 */
		void initDrawLabels();
		/**
		 * Draw text (e.g. showing the value of an extrema point or a function
		 * name) at the given (real) position.
		 */
		void drawLabel( QPainter * painter, const QColor & color, const QPointF & realPos, const QString & text );
260 261
		/**
		 * Used by plotImplicit to draw the plot in the square associated with
262 263
		 * the given point. \p orientation is the edge that the plot trace
		 * originates from.
264
		 */
265
		void plotImplicitInSquare( const Plot & plot, QPainter *, double x, double y, Qt::Orientation orientation );
266 267 268 269
		/**
		* \return whether should draw the pixel from the given line length,
		* according to the given pen style (used in plotfkt).
		*/
270
		bool penShouldDraw( double totalLength, const Plot & plot );
271 272 273 274 275
		/**
		* \return An appropriate pen for drawing the plot. (\p antialias should be
		* set to whether the current painter is using antialiasing - this is for
		* choosing an appropriate pen width).
		*/
276
		QPen penForPlot( const Plot & plot, bool antialias ) const;
277 278
		/// Gets the greek pi symbol.
		void setpi(QString *);
279 280 281 282 283 284 285 286
		/**
		 * Used in findRoot.
		 */
		enum RootAccuracy
		{
			PreciseRoot,	///< Will potential spend a long time finding a root to a high degree of accuracy
			RoughRoot		///< Won't spend too long making a root accurate, giving up quickly if failed to find root
		};
287 288 289 290 291
		/**
		 * Used in trace mode. Attempts to find the root of equation \p eq near
		 * \p x (which is then set to the exact root if found).
		 * \returns whether a root was found.
		 */
292 293 294 295 296 297 298 299 300 301 302
		bool findRoot( double * x, const Plot & plot, RootAccuracy accuracy );
		/**
		 * Equivalent function as above for implicit functions.
		 */
		bool findRoot( double * x, double * y, const Plot & plot, RootAccuracy accuracy );
		/**
		 * For use in the findRoot functions.
		 * \p max_k maximum number of iterations
		 * \p max_f the largest value of y which is deemed a root found
		 */
		void setupFindRoot( const Plot & plot, RootAccuracy accuracy, double * max_k, double * max_f, int * n );
303 304 305 306 307 308 309
		/**
		 * Finds the list of points (in function coordinates) at which the
		 * derivative of the given plot is zero in the range of the currently
		 * viewable segment of the plot.
		 */
		QList<QPointF> findStationaryPoints( const Plot & plot );
		/**
310 311
		 * Find all roots (at which the given plot is zero) in the range
		 * [min,max].
312
		 */
313
		QList<double> findRoots( const Plot & plot, double min, double max, RootAccuracy accuracy );
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
		///return the inverted color
		void invertColor(QColor &, QColor &);
		/// Changes the text in the statusbar
		void setStatusBar(const QString &text, const int id);
		/**
		* \return whether the crosshairs should be shown for the current mouse
		* position, zoom mode, etc.
		*/
		bool shouldShowCrosshairs() const;
		/**
		* Zooms in by amount \p zoomFactor (which will zooming out if less than 1)
		* from clicking at \p mousePos (in widget coordinates).
		*/
		void zoomIn( const QPoint & mousePos, double zoomFactor );
		/**
		* Zooms in from having drawn \p zoomRect (which is in widget coordinates).
		*/
		void zoomIn( const QRect & zoomRect );
		/**
		* Zooms out from havoutg drawn \p zoomRect (which is out widget
		* coordinates).
		*/
		void zoomOut( const QRect & zoomRect );
		/**
		* Translates the view by \p dx, \p dy (in widget coordinates).
		*/
		void translateView( int dx, int dy );
		/**
		* Animates zooming from the current zoom rect to the one given (in real
		* coordinates)
		*/
		void animateZoom( const QRectF & newCoords );
		/**
		* Finds the plot (if any) under the last mouse pos as recorded by
		* updateCrosshairPosition(). This sets csmode, cstype, csparam. If no plot
		* was found, then csmode is set to -1.
		 * \return the function position of the closest plot if one was found.
		*/
		QPointF getPlotUnderMouse();
353 354 355 356
		/**
		* Finds the closest point to \p pos to the given function.
		* \return the parametization (angle or t) that gives the closest point.
		*/
357
		double getClosestPoint( const QPointF & pos, const Plot & plot );
358 359 360 361
		/**
		* Calculates the pixel distance from \p pos to the display point of the
		* given function at \p x.
		*/
362
		double pixelDistance( const QPointF & pos, const Plot & plot, double x, bool updateFunctionParameter );
363 364 365 366 367 368 369 370 371 372 373 374 375 376
		/**
		 * \return an appropriate xmin value for the given function
		 * plotting.
		 */
		double getXmin( Function * function );
		/**
		 * \return an appropriate xmax value for the given function for
		 * plotting.
		 */
		double getXmax( Function * function );
	
		/// for areadrawing
		IntegralDrawSettings m_integralDrawSettings;
		bool m_drawIntegral;
David Saxton's avatar
David Saxton committed
377
	
378 379 380 381 382
		double m_width, m_height;
		float m_scaler;
	
		QPointF m_crosshairPixelCoords;
		QPointF m_crosshairPosition;	///< in real coordinates
383
	
David Saxton's avatar
David Saxton committed
384 385 386 387 388 389 390 391 392 393 394 395 396
		/**
		 * The t- or x- (angle) coordinate of the traced curve - when tracing a
		 * polar or parametric curve.
		 */
		double m_trace_x;
		/**
		 * When tracing a Cartesian plot and the trace position nears the
		 * x-axis, an attempt to find a root will be found. If found, this will
		 * be set to true, and no further attempts will be made at finding a
		 * root. Once the plot position moves away from the x-axis again, this
		 * will be set to false.
		 */
		bool m_haveRoot;
397

398 399
		/// @return whether cspos is in the range of the view or in the custom range for the given \p plot
		bool crosshairPositionValid( Function * plot ) const;
David Saxton's avatar
David Saxton committed
400
	
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
		QRect area;
		QMatrix wm;
	
		double tlgx, tlgy, drskalx, drskaly;
		QString tlgxstr, tlgystr, drskalxstr, drskalystr;
	
		/** @name Plotrange
		* There are 4 predefined plot ranges:
		* @li 0: -8..8
		* @li 1: -5..5
		* @li 2: 0..16
		* @li 3: 0..10
		* @li 4: custom
		*/
		//@{
		///Convert axes range predefinition index to boundaries.
		void getMinMax(int koord, QString &mini, QString &maxi);
		/** Handle predefiend axes ranges.
		*
		* @p koord can have the values 0 to 4 which have the following meanings: 
		*
		* In the last case @p minstr and @p maxstr are evaluated.
		*/
		void coordToMinMax( const int koord, const QString &minStr, const QString &maxStr,
							double &min, double &max );
426 427 428 429 430 431
		//@{
		/** Current plot range endge. */
		double m_ymin;
		double m_ymax;
		//@}
		//@}
432

433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
		void setScaling();
		/// represents the KPrinter option app-kmplot-printtable.
		/// @see KPrinterDlg
		bool m_printHeaderTable;
		/// if stop_calculating is true, the user has canceled drawing of an integral graph
		bool stop_calculating;
		/// the background color of the graph
		QColor m_backgroundColor;
		/// the inverted background color used by the "Fadenkreuz"
		QColor m_invertedBackgroundColor;
		/// pointer to KMinMax
		KMinMax *m_minmax;
		///buffer the current window so all functions don't need to be re-drawed
		QPixmap buffer;
		/// the popup menu
		KMenu *m_popupmenu;
		/// is set to true if an integral is calculated
		bool m_isDrawing;
		///status of the popup menu
		char m_popupmenushown; /// 0==no popup 1==popup 2==popup+trace mode before
		/// true == modifications not saved
		bool &m_modified;
		/// False if KmPlot is started as a program, otherwise true
		bool const m_readonly;
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
		/// For drawing diagram labels
		QFont m_labelFont;
		
		/// Indicate which parts of the diagram have content (e.g. axis or
		/// plots), so that they can be avoided when drawing diagram labels
		static const int LabelGridSize = 50;
		bool m_usedDiagramArea[LabelGridSize][LabelGridSize];
		/**
		 * Marks the given diagram rectangle (in screen coords) as 'used'.
		 */
		void markDiagramAreaUsed( const QRectF & rect );
		/**
		 * Marks the given diagram point (in screen coords) as 'used'.
		 */
		void markDiagramPointUsed( QPointF point );
		/**
		 * \return the m_usedDiagramArea coords for the screen rect.
		 */
		QRect usedDiagramRect( QRectF rect ) const;
		/**
		 * \return the cost of occupying the given rectangle (as in whether it
		 * overlaps other diagram content, etc).
		 */
		int rectCost( const QRectF & rect ) const;
David Saxton's avatar
David Saxton committed
481
	
482 483 484 485 486 487 488 489 490
		enum ZoomMode
		{
			Normal,				///< no zooming
			AnimatingZoom,		///< animating a current zooming
			ZoomIn,				///< zoom in
			ZoomOut,			///< zoom out
			ZoomInDrawing,		///< drawing a rectangle for zooming in
			ZoomOutDrawing,		///< drawing a rectangle for zooming out
			AboutToTranslate,	///< user has clicked on an empty spot, but hasn't moved the mouse yet
491
			Translating		///< dragging the view with the mouse
492 493 494 495 496 497 498 499 500 501 502 503
		};
			
		/// The current editing status
		ZoomMode m_zoomMode;
		/// for zoom-mode
		QPoint m_zoomRectangleStart;
		/// for animating zoom; contains the rectangle (in real coordinates) to draw
		QRectF m_animateZoomRect;
		/// for translating the view via dragging
		QPoint m_prevDragMousePos;
		/// timer that is started when the mouse is pressed
		QTime * m_mousePressTimer;
David Saxton's avatar
David Saxton committed
504
	
505 506 507 508 509 510 511 512 513 514
		QString m_statusbartext1;
		QString m_statusbartext2;
		QString m_statusbartext3;
		QString m_statusbartext4;
		KActionCollection *m_ac;
		
		enum Cursor { CursorWait, CursorBlank, CursorArrow, CursorCross, CursorMagnify, CursorLessen, CursorMove };
		Cursor m_prevCursor;
		
		static View * m_self;
515 516
};

517
#endif // View_included