/*========================================================================= Program: Visualization Toolkit Module: vtkAxis.h Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ /** * @class vtkAxis * @brief takes care of drawing 2D axes * * * The vtkAxis is drawn in screen coordinates. It is usually one of the last * elements of a chart to be drawn. It renders the axis label, tick marks and * tick labels. * The tick marks and labels span the range of values between * \a Minimum and \a Maximum. * The \a Minimum and \a Maximum values are not allowed to extend beyond the * \a MinimumLimit and \a MaximumLimit values, respectively. * * Note that many other chart elements (e.g., vtkPlotPoints) refer to * vtkAxis instances to determine how to scale raw data for presentation. * In particular, care must be taken with logarithmic scaling. * The axis Minimum, Maximum, and Limit values are stored both unscaled * and scaled (with log(x) applied when GetLogScaleActive() returns true). * User interfaces will most likely present the unscaled values as they * represent the values provided by the user. * Other chart elements may need the scaled values in order to draw * in the same coordinate system. * * Just because LogScale is set to true does not guarantee that the axis * will use logarithmic scaling -- the Minimum and Maximum values for the * axis must both lie to the same side of origin (and not include the origin). * Also, this switch from linear- to log-scaling may occur during a rendering * pass if autoscaling is enabled. * Because the log and pow functions are not invertible and the axis itself * decides when to switch between them without offering any external class * managing the axis a chance to save the old values, it saves * old Limit values in NonLogUnscaled{Min,Max}Limit so that behavior is * consistent when LogScale is changed from false to true and back again. */ #ifndef vtkAxis_h #define vtkAxis_h #include "vtkChartsCoreModule.h" // For export macro #include "vtkContextItem.h" #include "vtkRect.h" // For bounding rect #include "vtkSmartPointer.h" // For vtkSmartPointer #include "vtkStdString.h" // For vtkStdString ivars #include "vtkVector.h" // For position variables class vtkContext2D; class vtkPen; class vtkFloatArray; class vtkDoubleArray; class vtkStringArray; class vtkTextProperty; class VTKCHARTSCORE_EXPORT vtkAxis : public vtkContextItem { public: vtkTypeMacro(vtkAxis, vtkContextItem); void PrintSelf(ostream& os, vtkIndent indent) override; /** * Enumeration of the axis locations in a conventional XY chart. Other * layouts are possible. */ enum Location { LEFT = 0, BOTTOM, RIGHT, TOP, PARALLEL }; enum { TICK_SIMPLE = 0, TICK_WILKINSON_EXTENDED }; /** * Creates a 2D Chart object. */ static vtkAxis* New(); //@{ /** * Get/set the position of the axis (LEFT, BOTTOM, RIGHT, TOP, PARALLEL). */ virtual void SetPosition(int position); vtkGetMacro(Position, int); //@} //@{ /** * Set point 1 of the axis (in pixels), this is usually the origin. */ void SetPoint1(const vtkVector2f& pos); void SetPoint1(float x, float y); //@} //@{ /** * Get point 1 of the axis (in pixels), this is usually the origin. */ vtkGetVector2Macro(Point1, float); vtkVector2f GetPosition1(); //@} //@{ /** * Set point 2 of the axis (in pixels), this is usually the terminus. */ void SetPoint2(const vtkVector2f& pos); void SetPoint2(float x, float y); //@} //@{ /** * Get point 2 of the axis (in pixels), this is usually the terminus. */ vtkGetVector2Macro(Point2, float); vtkVector2f GetPosition2(); //@} /** * Set the number of tick marks for this axis. Default is -1, which leads to * automatic calculation of nicely spaced tick marks. */ virtual void SetNumberOfTicks(int numberOfTicks); //@{ /** * Get the number of tick marks for this axis. */ vtkGetMacro(NumberOfTicks, int); //@} //@{ /** * Get/set the length of tick marks (in pixels). */ vtkSetMacro(TickLength, float); vtkGetMacro(TickLength, float); //@} //@{ /** * Get the vtkTextProperty that governs how the axis labels are displayed. * Note that the alignment properties are not used. */ vtkGetObjectMacro(LabelProperties, vtkTextProperty); //@} /** * Set the logical minimum value of the axis, in plot coordinates. * If LogScaleActive is true (not just LogScale), then this * sets the minimum base-10 exponent. */ virtual void SetMinimum(double minimum); //@{ /** * Get the logical minimum value of the axis, in plot coordinates. * If LogScaleActive is true (not just LogScale), then this * returns the minimum base-10 exponent. */ vtkGetMacro(Minimum, double); //@} /** * Set the logical maximum value of the axis, in plot coordinates. * If LogScaleActive is true (not just LogScale), then this * sets the maximum base-10 exponent. */ virtual void SetMaximum(double maximum); //@{ /** * Get the logical maximum value of the axis, in plot coordinates. * If LogScaleActive is true (not just LogScale), then this * returns the maximum base-10 exponent. */ vtkGetMacro(Maximum, double); //@} /** * Set the logical, unscaled minimum value of the axis, in plot coordinates. * Use this instead of SetMinimum() if you wish to provide the actual minimum * instead of log10(the minimum) as part of the axis scale. */ virtual void SetUnscaledMinimum(double minimum); //@{ /** * Get the logical minimum value of the axis, in plot coordinates. */ vtkGetMacro(UnscaledMinimum, double); //@} /** * Set the logical maximum value of the axis, in plot coordinates. */ virtual void SetUnscaledMaximum(double maximum); //@{ /** * Get the logical maximum value of the axis, in plot coordinates. */ vtkGetMacro(UnscaledMaximum, double); //@} //@{ /** * Set the logical range of the axis, in plot coordinates. * The unscaled range will always be in the same coordinate system of * the data being plotted, regardless of whether LogScale is true or false. * When calling SetRange() and LogScale is true, the range must be specified * in logarithmic coordinates. * Using SetUnscaledRange(), you may ignore the value of LogScale. */ virtual void SetRange(double minimum, double maximum); virtual void SetRange(double range[2]); virtual void SetUnscaledRange(double minimum, double maximum); virtual void SetUnscaledRange(double range[2]); //@} //@{ /** * Get the logical range of the axis, in plot coordinates. * The unscaled range will always be in the same coordinate system of * the data being plotted, regardless of whether LogScale is true or false. * Calling GetRange() when LogScale is true will return the log10({min, max}). */ virtual void GetRange(double* range); virtual void GetUnscaledRange(double* range); //@} /** * Set the logical lowest possible value for \a Minimum, in plot coordinates. */ virtual void SetMinimumLimit(double lowest); //@{ /** * Get the logical lowest possible value for \a Minimum, in plot coordinates. */ vtkGetMacro(MinimumLimit, double); //@} /** * Set the logical highest possible value for \a Maximum, in plot coordinates. */ virtual void SetMaximumLimit(double highest); //@{ /** * Get the logical highest possible value for \a Maximum, in plot coordinates. */ vtkGetMacro(MaximumLimit, double); //@} /** * Set the logical lowest possible value for \a Minimum, in plot coordinates. */ virtual void SetUnscaledMinimumLimit(double lowest); //@{ /** * Get the logical lowest possible value for \a Minimum, in plot coordinates. */ vtkGetMacro(UnscaledMinimumLimit, double); //@} /** * Set the logical highest possible value for \a Maximum, in plot coordinates. */ virtual void SetUnscaledMaximumLimit(double highest); //@{ /** * Get the logical highest possible value for \a Maximum, in plot coordinates. */ vtkGetMacro(UnscaledMaximumLimit, double); //@} //@{ /** * Get the margins of the axis, in pixels. */ vtkGetVector2Macro(Margins, int); //@} //@{ /** * Set the margins of the axis, in pixels. */ vtkSetVector2Macro(Margins, int); //@} //@{ /** * Get/set the title text of the axis. */ virtual void SetTitle(const vtkStdString& title); virtual vtkStdString GetTitle(); //@} //@{ /** * Get the vtkTextProperty that governs how the axis title is displayed. */ vtkGetObjectMacro(TitleProperties, vtkTextProperty); //@} //@{ /** * Get whether the axis is using a log scale. * This will always be false when LogScale is false. * It is only true when LogScale is true and the \a UnscaledRange * does not cross or include the origin (zero). * The limits (\a MinimumLimit, \a MaximumLimit, and their * unscaled counterparts) do not prevent LogScaleActive from becoming * true; they are adjusted if they cross or include the origin * and the original limits are preserved for when LogScaleActive * becomes false again. */ vtkGetMacro(LogScaleActive, bool); //@} //@{ /** * Get/set whether the axis should attempt to use a log scale. * The default is false. * \sa{LogScaleActive}. */ vtkGetMacro(LogScale, bool); virtual void SetLogScale(bool logScale); vtkBooleanMacro(LogScale, bool); //@} //@{ /** * Get/set whether the axis grid lines should be drawn, default is true. */ vtkSetMacro(GridVisible, bool); vtkGetMacro(GridVisible, bool); //@} //@{ /** * Get/set whether the axis labels should be visible. */ vtkSetMacro(LabelsVisible, bool); vtkGetMacro(LabelsVisible, bool); //@} //@{ /** * Get/set whether the labels for the range should be visible. */ vtkSetMacro(RangeLabelsVisible, bool); vtkGetMacro(RangeLabelsVisible, bool); //@} //@{ /** * Get/set the offset (in pixels) of the label text position from the axis */ vtkSetMacro(LabelOffset, float); vtkGetMacro(LabelOffset, float); //@} //@{ /** * Get/set whether the tick marks should be visible. */ vtkSetMacro(TicksVisible, bool); vtkGetMacro(TicksVisible, bool); //@} //@{ /** * Get/set whether the axis line should be visible. */ vtkSetMacro(AxisVisible, bool); vtkGetMacro(AxisVisible, bool); //@} //@{ /** * Get/set whether the axis title should be visible. */ vtkSetMacro(TitleVisible, bool); vtkGetMacro(TitleVisible, bool); //@} //@{ /** * Get/set the numerical precision to use, default is 2. This is ignored * when Notation is STANDARD_NOTATION or PRINTF_NOTATION. */ virtual void SetPrecision(int precision); vtkGetMacro(Precision, int); //@} /** * Enumeration of the axis notations available. */ enum { STANDARD_NOTATION = 0, SCIENTIFIC_NOTATION, FIXED_NOTATION, PRINTF_NOTATION }; //@{ /** * Get/Set the printf-style format string used when TickLabelAlgorithm is * TICK_SIMPLE and Notation is PRINTF_NOTATION. The default is "%g". */ virtual void SetLabelFormat(const std::string& fmt); vtkGetMacro(LabelFormat, std::string); //@} //@{ /** * Get/Set the printf-style format string used for range labels. * This format is always used regardless of TickLabelAlgorithm and * Notation. Default is "%g". */ vtkSetMacro(RangeLabelFormat, std::string); vtkGetMacro(RangeLabelFormat, std::string); //@} //@{ /** * Get/set the numerical notation, standard, scientific, fixed, or a * printf-style format string. * \sa SetPrecision SetLabelFormat */ virtual void SetNotation(int notation); vtkGetMacro(Notation, int); //@} /** * Enumeration of the axis behaviors. */ enum { AUTO = 0, // Automatically scale the axis to view all data that is visible. FIXED, // Use a fixed axis range and make no attempt to rescale. CUSTOM // Deprecated, use the tick label settings instead. }; //@{ /** * Get/set the behavior of the axis (auto or fixed). The default is 0 (auto). */ vtkSetMacro(Behavior, int); vtkGetMacro(Behavior, int); //@} //@{ /** * Get a pointer to the vtkPen object that controls the way this axis is drawn. */ vtkGetObjectMacro(Pen, vtkPen); //@} //@{ /** * Get a pointer to the vtkPen object that controls the way this axis is drawn. */ vtkGetObjectMacro(GridPen, vtkPen); //@} //@{ /** * Get/set the tick label algorithm that is used to calculate the min, max * and tick spacing. There are currently two algoriths, vtkAxis::TICK_SIMPLE * is the default and uses a simple algorithm. The second option is * vtkAxis::TICK_WILKINSON which uses an extended Wilkinson algorithm to find * the optimal range, spacing and font parameters. */ vtkSetMacro(TickLabelAlgorithm, int); vtkGetMacro(TickLabelAlgorithm, int); //@} //@{ /** * Get/set the scaling factor used for the axis, this defaults to 1.0 (no * scaling), and is used to coordinate scaling with the plots, charts, etc. */ vtkSetMacro(ScalingFactor, double); vtkGetMacro(ScalingFactor, double); vtkSetMacro(Shift, double); vtkGetMacro(Shift, double); //@} /** * Update the geometry of the axis. Takes care of setting up the tick mark * locations etc. Should be called by the scene before rendering. */ void Update() override; /** * Paint event for the axis, called whenever the axis needs to be drawn. */ bool Paint(vtkContext2D* painter) override; /** * Use this function to autoscale the axes after setting the minimum and * maximum values. This will cause the axes to select the nicest numbers * that enclose the minimum and maximum values, and to select an appropriate * number of tick marks. */ virtual void AutoScale(); /** * Recalculate the spacing of the tick marks - typically useful to do after * scaling the axis. */ virtual void RecalculateTickSpacing(); /** * An array with the positions of the tick marks along the axis line. * The positions are specified in the plot coordinates of the axis. */ virtual vtkDoubleArray* GetTickPositions(); /** * An array with the positions of the tick marks along the axis line. * The positions are specified in scene coordinates. */ virtual vtkFloatArray* GetTickScenePositions(); /** * A string array containing the tick labels for the axis. */ virtual vtkStringArray* GetTickLabels(); /** * Set the tick positions, and optionally custom tick labels. If the labels * and positions are null then automatic tick labels will be assigned. If * only positions are supplied then appropriate labels will be generated * according to the axis settings. If positions and labels are supplied they * must be of the same length. Returns true on success, false on failure. */ virtual bool SetCustomTickPositions(vtkDoubleArray* positions, vtkStringArray* labels = nullptr); /** * Request the space the axes require to be drawn. This is returned as a * vtkRectf, with the corner being the offset from Point1, and the width/ * height being the total width/height required by the axis. In order to * ensure the numbers are correct, Update() should be called on the axis. */ vtkRectf GetBoundingRect(vtkContext2D* painter); /** * Return a "nice number", often defined as 1, 2 or 5. If roundUp is true then * the nice number will be rounded up, false it is rounded down. The supplied * number should be between 0.0 and 9.9. */ static double NiceNumber(double number, bool roundUp); /** * Static function to calculate "nice" minimum, maximum, and tick spacing * values. */ static double NiceMinMax(double& min, double& max, float pixelRange, float tickPixelSpacing); /** * Generate a single label using the current settings when TickLabelAlgorithm * is TICK_SIMPLE. */ virtual vtkStdString GenerateSimpleLabel(double val); protected: vtkAxis(); ~vtkAxis() override; /** * Update whether log scaling will be used for layout and rendering. * Log scaling is only active when LogScaling is true and the closed, * unscaled range does not contain the origin. * The boolean parameter determines whether the minimum and maximum values * are set from their unscaled counterparts. */ void UpdateLogScaleActive(bool updateMinMaxFromUnscaled); /** * Calculate and assign nice labels/logical label positions. */ virtual void GenerateTickLabels(double min, double max); /** * Generate tick labels from the supplied double array of tick positions. */ virtual void GenerateTickLabels(); virtual void GenerateLabelFormat(int notation, double n); /** * Generate label using a printf-style format string. */ virtual vtkStdString GenerateSprintfLabel(double value, const std::string& format); /** * Calculate the next "nicest" numbers above and below the current minimum. * \return the "nice" spacing of the numbers. */ double CalculateNiceMinMax(double& min, double& max); /** * Return a tick mark for a logarithmic axis. * If roundUp is true then the upper tick mark is returned. * Otherwise the lower tick mark is returned. * Tick marks will be: ... 0.1 0.2 .. 0.9 1 2 .. 9 10 20 .. 90 100 ... * Parameter nicevalue will be set to true if tick mark is in: * ... 0.1 0.2 0.5 1 2 5 10 20 50 100 ... * Parameter order is set to the detected order of magnitude of the number. */ double LogScaleTickMark(double number, bool roundUp, bool& niceValue, int& order); /** * Generate logarithmically-spaced tick marks with linear-style labels. * This is for the case when log scaling is active, but the axis min and max * span less than an order of magnitude. * In this case, the most significant digit that varies is identified and * ticks generated for each value that digit may take on. If that results * in only 2 tick marks, the next-most-significant digit is varied. * If more than 20 tick marks would result, the stride for the varying digit * is increased. */ virtual void GenerateLogSpacedLinearTicks(int order, double min, double max); /** * Generate tick marks for logarithmic scale for specific order of magnitude. * Mark generation is limited by parameters min and max. * Tick marks will be: ... 0.1 0.2 .. 0.9 1 2 .. 9 10 20 .. 90 100 ... * Tick labels will be: ... 0.1 0.2 0.5 1 2 5 10 20 50 100 ... * If Parameter detaillabels is disabled tick labels will be: * ... 0.1 1 10 100 ... * If min/max is not in between 1.0 and 9.0 defaults will be used. * If min and max do not differ 1 defaults will be used. */ void GenerateLogScaleTickMarks( int order, double min = 1.0, double max = 9.0, bool detailLabels = true); int Position; // The position of the axis (LEFT, BOTTOM, RIGHT, TOP) float* Point1; // The position of point 1 (usually the origin) float* Point2; // The position of point 2 (usually the terminus) vtkVector2f Position1, Position2; double TickInterval; // Interval between tick marks in plot space int NumberOfTicks; // The number of tick marks to draw float TickLength; // The length of the tick marks vtkTextProperty* LabelProperties; // Text properties for the labels. double Minimum; // Minimum value of the axis double Maximum; // Maximum values of the axis double MinimumLimit; // Lowest possible value for Minimum double MaximumLimit; // Highest possible value for Maximum double UnscaledMinimum; // UnscaledMinimum value of the axis double UnscaledMaximum; // UnscaledMaximum values of the axis double UnscaledMinimumLimit; // Lowest possible value for UnscaledMinimum double UnscaledMaximumLimit; // Highest possible value for UnscaledMaximum double NonLogUnscaledMinLimit; // Saved UnscaledMinimumLimit (when !LogActive) double NonLogUnscaledMaxLimit; // Saved UnscaledMinimumLimit (when !LogActive) int Margins[2]; // Horizontal/vertical margins for the axis vtkStdString Title; // The text label drawn on the axis vtkTextProperty* TitleProperties; // Text properties for the axis title bool LogScale; // *Should* the axis use a log scale? bool LogScaleActive; // *Is* the axis using a log scale? bool GridVisible; // Whether the grid for the axis should be drawn bool LabelsVisible; // Should the axis labels be visible bool RangeLabelsVisible; // Should range labels be visible? float LabelOffset; // Offset of label from the tick mark bool TicksVisible; // Should the tick marks be visible. bool AxisVisible; // Should the axis line be visible. bool TitleVisible; // Should the title be visible. int Precision; // Numerical precision to use, defaults to 2. int Notation; // The notation to use (standard, scientific, mixed) std::string LabelFormat; // The printf-style format string used for labels. std::string RangeLabelFormat; // The printf-style format string used for range labels. int Behavior; // The behaviour of the axis (auto, fixed, custom). float MaxLabel[2]; // The widest/tallest axis label. bool TitleAppended; // Track if the title is updated when the label formats // are changed in the Extended Axis Labeling algorithm //@{ /** * Scaling factor used on this axis, this is used to accurately render very * small/large numbers accurately by converting the underlying range by the * scaling factor. */ double ScalingFactor; double Shift; //@} /** * Are we using custom tick labels, or should the axis generate them? */ bool CustomTickLabels; /** * This object stores the vtkPen that controls how the axis is drawn. */ vtkPen* Pen; /** * This object stores the vtkPen that controls how the grid lines are drawn. */ vtkPen* GridPen; /** * Position of tick marks in screen coordinates */ vtkSmartPointer TickPositions; /** * Position of tick marks in screen coordinates */ vtkSmartPointer TickScenePositions; /** * The labels for the tick marks */ vtkSmartPointer TickLabels; /** * Hint as to whether a nice min/max was set, otherwise labels may not be * present at the top/bottom of the axis. */ bool UsingNiceMinMax; /** * Mark the tick labels as dirty when the min/max value is changed. */ bool TickMarksDirty; /** * Flag to indicate that the axis has been resized. */ bool Resized; /** * The algorithm being used to tick label placement. */ int TickLabelAlgorithm; /** * The point cache is marked dirty until it has been initialized. */ vtkTimeStamp BuildTime; private: vtkAxis(const vtkAxis&) = delete; void operator=(const vtkAxis&) = delete; /** * Return true if the value is in range, false otherwise. */ bool InRange(double value); }; #endif // vtkAxis_h