Audacity 3.2.0
LinearUpdater.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 LinearUpdater.cpp
6
7 Dominic Mazzoni
8 Michael Papadopoulos split from Ruler.cpp
9
10**********************************************************************/
11
12#include "LinearUpdater.h"
13#include "BeatsFormat.h"
14
16{
17 static LinearUpdater instance;
18 return instance;
19}
20
22 wxDC& dc, const Envelope* envelope,
23 UpdateOutputs& allOutputs, const RulerStruct& context) const
24{
25 TickOutputs majorOutputs{
26 allOutputs.majorLabels, allOutputs.bits, allOutputs.box };
27
28 const double mDbMirrorValue = context.mDbMirrorValue;
29 const int mLength = context.mLength;
30
31 const int mLeft = context.mLeft;
32 const int mTop = context.mTop;
33 const int mBottom = context.mBottom;
34 const int mRight = context.mRight;
35 const int mOrientation = context.mOrientation;
36
37 const double mMin = context.mMin;
38 const double mMax = context.mMax;
39 const double mHiddenMin = context.mHiddenMin;
40 const double mHiddenMax = context.mHiddenMax;
41
42 const RulerStruct::Fonts& mFonts = *context.mpFonts;
43 const bool mLabelEdges = context.mLabelEdges;
44
45 // Use the "hidden" min and max to determine the tick size.
46 // That may make a difference with fisheye.
47 // Otherwise you may see the tick size for the whole ruler change
48 // when the fisheye approaches start or end.
49 double UPP = (mHiddenMax - mHiddenMin) / mLength; // Units per pixel
50 TickSizes tickSizes{ UPP, mOrientation, context.mpRulerFormat, false };
51
52 auto TickAtValue =
53 [this, &tickSizes, &dc, &majorOutputs, &mFonts, mOrientation,
54 mMin, mMax, mLength, mRight, mBottom, &context]
55 (double value) -> int {
56 // Make a tick only if the value is strictly between the bounds
57 if (value < std::min(mMin, mMax))
58 return -1;
59 if (value > std::max(mMin, mMax))
60 return -1;
61
62 int mid;
63 if (mpZoomInfo) {
64 // Tick only at zero
65 if (value)
66 return -1;
67 mid = (int)(mpZoomInfo->TimeToPosition(0.0, mLeftOffset));
68 }
69 else
70 mid = (int)(mLength * ((mMin - value) / (mMin - mMax)) + 0.5);
71
72 const int iMaxPos = (mOrientation == wxHORIZONTAL) ? mRight : mBottom - 5;
73 if (mid >= 0 && mid < iMaxPos)
74 Tick(dc, mid, value, tickSizes, mFonts.major, majorOutputs, context);
75 else
76 return -1;
77
78 return mid;
79 };
80
81 if (mDbMirrorValue) {
82 // For dB scale, let the zeroes prevail over the extreme values if
83 // not the same, and let midline prevail over all
84
85 // Do the midline
86 TickAtValue(-mDbMirrorValue);
87
88 // Do the upper zero
89 TickAtValue(0.0);
90
91 // Do the other zero
92 TickAtValue(-2 * mDbMirrorValue);
93 }
94
95 if (!mDbMirrorValue) {
96 // Zero (if it's strictly in the middle somewhere)
97 TickAtValue(0.0);
98 }
99
100 double sign = UPP > 0.0 ? 1.0 : -1.0;
101
102 int nDroppedMinorLabels = 0;
103 // Major and minor ticks
104 for (int jj = 0; jj < 3; ++jj) {
105 const double denom = jj == 0 ? tickSizes.mMajor :
106 jj == 1 ? tickSizes.mMinor : tickSizes.mMinorMinor;
107 tickSizes.tickType = jj == 0 ? RulerFormat::t_major :
109 if (denom == 0) continue;
110 auto font = jj == 0 ? mFonts.major :
111 jj == 1 ? mFonts.minor : mFonts.minorMinor;
112 TickOutputs outputs{
113 (jj == 0 ? allOutputs.majorLabels :
114 jj == 1 ? allOutputs.minorLabels : allOutputs.minorMinorLabels),
115 allOutputs.bits, allOutputs.box
116 };
117 int ii = -1, j = 0;
118 double d, warpedD, nextD;
119
120 double prevTime = 0.0, time = 0.0;
121 if (mpZoomInfo) {
122 j = mpZoomInfo->TimeToPosition(mMin);
123 prevTime = mpZoomInfo->PositionToTime(--j);
124 time = mpZoomInfo->PositionToTime(++j);
125 d = (prevTime + time) / 2.0;
126 }
127 else
128 d = mMin - UPP / 2;
129 if (envelope)
130 warpedD = ComputeWarpedLength(*envelope, 0.0, d);
131 else
132 warpedD = d;
133 // using ints doesn't work, as
134 // this will overflow and be negative at high zoom.
135 double step = floor(sign * warpedD / denom);
136 while (ii <= mLength) {
137 ii++;
138 if (mpZoomInfo)
139 {
140 prevTime = time;
141 time = mpZoomInfo->PositionToTime(++j);
142 nextD = (prevTime + time) / 2.0;
143 // wxASSERT(time >= prevTime);
144 }
145 else
146 nextD = d + UPP;
147 if (envelope)
148 warpedD += ComputeWarpedLength(*envelope, d, nextD);
149 else
150 warpedD = nextD;
151 d = nextD;
152
153 if (floor(sign * warpedD / denom) > step) {
154 step = floor(sign * warpedD / denom);
155 bool ticked = Tick(dc, ii, sign * step * denom, tickSizes,
156 font, outputs, context);
157 bool major = jj == 0;
158 if (!major && !ticked) {
159 nDroppedMinorLabels++;
160 }
161 }
162 }
163 }
164
165 tickSizes.tickType = RulerFormat::t_major;
166
167 // If we've dropped minor labels through overcrowding, then don't show
168 // any of them. We're allowed though to drop ones which correspond to the
169 // major numbers.
170 // TODO: Doesn't play nicely with BeatsFormat, possibly because of dropping values less than 0?
171 // Investigate and come up with a better solution than specifically ignoring BeatsFormat
172
173 if (nDroppedMinorLabels >
174 (allOutputs.majorLabels.size() + (mLabelEdges ? 2 : 0))
175 && !dynamic_cast<const BeatsFormat*>(context.mpRulerFormat)) {
176 // Old code dropped the labels AND their ticks, like so:
177 // mMinorLabels.clear();
178 // Nowadays we just drop the labels.
179 for (auto& label : allOutputs.minorLabels) {
180 label.text = {};
181 label.units = {};
182 }
183 for (auto& label : allOutputs.minorMinorLabels) {
184 label.text = {};
185 label.units = {};
186 }
187 }
188
189 // Left and Right Edges
190 if (mLabelEdges) {
191 Tick(dc, 0, mMin, tickSizes, mFonts.major, majorOutputs, context);
192 Tick(dc, mLength, mMax, tickSizes, mFonts.major, majorOutputs, context);
193 }
194
195 BoxAdjust(allOutputs, context);
196}
197
int min(int a, int b)
TranslatableString label
Definition: TagsEditor.cpp:165
Piecewise linear or piecewise exponential function from double to double.
Definition: Envelope.h:72
double ComputeWarpedLength(const Envelope &env, double t0, double t1) const
bool Tick(wxDC &dc, int pos, double d, const TickSizes &tickSizes, wxFont font, TickOutputs outputs, const RulerStruct &context) const
const ZoomInfo * mpZoomInfo
Definition: LinearUpdater.h:40
void Update(wxDC &dc, const Envelope *envelope, UpdateOutputs &allOutputs, const RulerStruct &context) const override
static const LinearUpdater & Instance()
~LinearUpdater() override
void BoxAdjust(UpdateOutputs &allOutputs, const RulerStruct &context) const
double PositionToTime(int64 position, int64 origin=0, bool ignoreFisheye=false) const
Definition: ZoomInfo.cpp:34
int64 TimeToPosition(double time, int64 origin=0, bool ignoreFisheye=false) const
STM: Converts a project time to screen x position.
Definition: ZoomInfo.cpp:44
std::unique_ptr< Fonts > mpFonts
Definition: RulerUpdater.h:52
double mMax
Definition: RulerUpdater.h:34
double mHiddenMax
Definition: RulerUpdater.h:35
double mDbMirrorValue
Definition: RulerUpdater.h:49
int mOrientation
Definition: RulerUpdater.h:37
bool mLabelEdges
Definition: RulerUpdater.h:39
const RulerFormat * mpRulerFormat
Definition: RulerUpdater.h:41
double mMin
Definition: RulerUpdater.h:32
double mHiddenMin
Definition: RulerUpdater.h:33