Audacity 3.2.0
Snap.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 Snap.cpp
6
7 Dominic Mazzoni
8
9**********************************************************************/
10
11
12#include "Snap.h"
13
14#include <algorithm>
15#include <cstdlib>
16
17#include "Project.h"
19#include "ProjectRate.h"
20#include "ProjectSnap.h"
21#include "Track.h"
22#include "ZoomInfo.h"
23
24inline bool operator < (SnapPoint s1, SnapPoint s2)
25{
26 return s1.t < s2.t;
27}
28
30 SnapPointArray candidates,
31 const ZoomInfo &zoomInfo,
32 bool noTimeSnap,
33 int pixelTolerance)
34: mProject{ &project }
35, mZoomInfo{ &zoomInfo }
36, mPixelTolerance{ pixelTolerance }
37, mNoTimeSnap{ noTimeSnap }
38, mCandidates{ move( candidates ) }
39, mSnapPoints{}
40{
41 Reinit();
42}
43
44namespace {
46 SnapPointArray candidates, const TrackList &tracks )
47{
48 for (const auto track : tracks)
49 for (const auto &interval : track->Intervals()) {
50 candidates.emplace_back(interval->Start(), track);
51 if (interval->Start() != interval->End())
52 candidates.emplace_back(interval->End(), track);
53 }
54 return move(candidates);
55}
56}
57
59 const TrackList &tracks,
60 const ZoomInfo &zoomInfo,
61 SnapPointArray candidates,
62 bool noTimeSnap,
63 int pixelTolerance)
65 // Add candidates to given ones by default rules,
66 // then delegate to other ctor
67 FindCandidates( move(candidates), tracks ),
68 zoomInfo, noTimeSnap, pixelTolerance }
69{
70}
71
73{
74}
75
77{
78 const auto &formats = ProjectNumericFormats::Get(*mProject);
79 const auto &settings = ProjectSnap::Get( *mProject );
80
81 auto snapTo = settings.GetSnapTo();
82 auto snapMode = settings.GetSnapMode();
83
84 auto rate = ProjectRate::Get(*mProject).GetRate();
85 auto format = formats.GetSelectionFormat();
86
87 // No need to reinit if these are still the same
88 if (snapTo == mSnapTo && rate == mRate && format == mFormat)
89 {
90 return;
91 }
92
93 // Save NEW settings
94 mSnapTo = snapTo;
95 mRate = rate;
97
98 mSnapPoints.clear();
99
100 // Grab time-snapping prefs (unless otherwise requested)
102
103 // Add a SnapPoint at t=0
104 mSnapPoints.push_back(SnapPoint{});
105
106 // Adjust and filter the candidate points
107 for (const auto &candidate : mCandidates)
108 CondListAdd( candidate.t, candidate.track );
109
110 // Sort all by time
111 std::sort(mSnapPoints.begin(), mSnapPoints.end());
112}
113
114// Adds to mSnapPoints, filtering by TimeConverter
115void SnapManager::CondListAdd(double t, const Track *track)
116{
117 if (!mSnapToTime || ProjectSnap::Get(*mProject).SnapTime(t).time == t)
118 {
119 mSnapPoints.push_back(SnapPoint { t, track });
120 }
121}
122
123// Return the time of the SnapPoint at a given index
124double SnapManager::Get(size_t index)
125{
126 return mSnapPoints[index].t;
127}
128
129// Returns the difference in time between t and the point at a given index
130wxInt64 SnapManager::PixelDiff(double t, size_t index)
131{
132 return std::abs(mZoomInfo->TimeToPosition(t, 0) -
133 mZoomInfo->TimeToPosition(Get(index), 0));
134}
135
136// Find the index where this SnapPoint should go in
137// sorted order, between i0 (inclusive) and i1 (exclusive).
138size_t SnapManager::Find(double t, size_t i0, size_t i1)
139{
140 if (i1 <= i0 + 1)
141 {
142 return i0;
143 }
144
145 size_t half = (i0 + i1) / 2;
146
147 if (t < Get(half))
148 {
149 return Find(t, i0, half);
150 }
151
152 return Find(t, half, i1);
153}
154
155// Find the SnapPoint nearest to time t
156size_t SnapManager::Find(double t)
157{
158 size_t cnt = mSnapPoints.size();
159 size_t index = Find(t, 0, cnt);
160
161 // At this point, either index is the closest, or the next one
162 // to the right is. Keep moving to the right until we get a
163 // different value
164 size_t next = index + 1;
165 while (next + 1 < cnt && Get(next) == Get(index))
166 {
167 next++;
168 }
169
170 // Now return whichever one is closer to time t
171 if (next < cnt && PixelDiff(t, next) < PixelDiff(t, index))
172 {
173 return next;
174 }
175
176 return index;
177}
178
179// Helper: performs snap-to-points for Snap(). Returns true if a snap happened.
181 double t,
182 bool rightEdge,
183 double *outT)
184{
185 *outT = t;
186
187 size_t cnt = mSnapPoints.size();
188 if (cnt == 0)
189 {
190 return false;
191 }
192
193 // Find the nearest SnapPoint
194 size_t index = Find(t);
195
196 // If it's too far away, just give up now
197 if (PixelDiff(t, index) >= mPixelTolerance)
198 {
199 return false;
200 }
201
202 // Otherwise, search left and right for all of the points
203 // within the allowed range.
204 size_t left = index;
205 size_t right = index;
206 size_t i;
207
208 while (left > 0 && PixelDiff(t, left - 1) < mPixelTolerance)
209 {
210 left--;
211 }
212
213 while (right < cnt - 1 && PixelDiff(t, right + 1) < mPixelTolerance)
214 {
215 right++;
216 }
217
218 if (left == index && right == index)
219 {
220 // Awesome, there's only one point that matches!
221 *outT = Get(index);
222 return true;
223 }
224
225 size_t indexInThisTrack = 0;
226 size_t countInThisTrack = 0;
227 for (i = left; i <= right; ++i)
228 {
229 if (mSnapPoints[i].track == currentTrack)
230 {
231 indexInThisTrack = i;
232 countInThisTrack++;
233 }
234 }
235
236 if (countInThisTrack == 1)
237 {
238 // Cool, only one of the points is in the same track, so
239 // we'll use that one.
240 *outT = Get(indexInThisTrack);
241 return true;
242 }
243
244 if (Get(right) - Get(left) < mEpsilon)
245 {
246 // OK, they're basically the same point
247 if (rightEdge)
248 {
249 *outT = Get(right); // Return rightmost
250 }
251 else {
252 *outT = Get(left); // Return leftmost
253 }
254 return true;
255 }
256
257 // None of the points matched, bummer.
258 return false;
259}
260
262(Track *currentTrack, double t, bool rightEdge)
263{
264
265 SnapResults results;
266 // Check to see if we need to reinitialize
267 Reinit();
268
269 results.timeSnappedTime = results.outTime = t;
270 results.outCoord = mZoomInfo->TimeToPosition(t);
271
272 // First snap to points in mSnapPoints
273 results.snappedPoint =
274 SnapToPoints(currentTrack, t, rightEdge, &results.outTime);
275
276 if (mSnapToTime) {
277 // Find where it would snap time to the grid
278 results.timeSnappedTime = ProjectSnap::Get(*mProject).SnapTime(t).time;
279 }
280
281 results.snappedTime = false;
282 if (mSnapToTime)
283 {
284 if (results.snappedPoint)
285 {
286 // Since mSnapPoints only contains points on the grid, we're done
287 results.snappedTime = true;
288 }
289 else
290 {
291 results.outTime = results.timeSnappedTime;
292 results.snappedTime = true;
293 }
294 }
295
296 if (results.Snapped())
297 results.outCoord = mZoomInfo->TimeToPosition(results.outTime);
298
299 return results;
300}
an object holding per-project preferred sample rate
bool operator<(SnapPoint s1, SnapPoint s2)
Definition: Snap.cpp:24
std::vector< SnapPoint > SnapPointArray
Definition: Snap.h:43
const auto tracks
const auto project
declares abstract base class Track, TrackList, and iterators over TrackList
static Settings & settings()
Definition: TrackInfo.cpp:51
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static ProjectNumericFormats & Get(AudacityProject &project)
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
double GetRate() const
Definition: ProjectRate.cpp:53
SnapResult SnapTime(double time) const
Definition: ProjectSnap.cpp:77
static ProjectSnap & Get(AudacityProject &project)
Definition: ProjectSnap.cpp:27
size_t Find(double t, size_t i0, size_t i1)
Definition: Snap.cpp:138
bool SnapToPoints(Track *currentTrack, double t, bool rightEdge, double *outT)
Definition: Snap.cpp:180
~SnapManager()
Definition: Snap.cpp:72
Identifier mSnapTo
Definition: Snap.h:109
double mEpsilon
Two time points closer than this are considered the same.
Definition: Snap.h:102
double mRate
Definition: Snap.h:110
wxInt64 PixelDiff(double t, size_t index)
Definition: Snap.cpp:130
NumericFormatID mFormat
Definition: Snap.h:111
double Get(size_t index)
Definition: Snap.cpp:124
void CondListAdd(double t, const Track *track)
Definition: Snap.cpp:115
const AudacityProject * mProject
Definition: Snap.h:96
SnapResults Snap(Track *currentTrack, double t, bool rightEdge)
Definition: Snap.cpp:262
SnapManager(const AudacityProject &project, SnapPointArray candidates, const ZoomInfo &zoomInfo, bool noTimeSnap=false, int pixelTolerance=kPixelTolerance)
Construct only for specified candidate points.
Definition: Snap.cpp:29
void Reinit()
Definition: Snap.cpp:76
bool mSnapToTime
Definition: Snap.h:107
SnapPointArray mCandidates
Definition: Snap.h:103
SnapPointArray mSnapPoints
Definition: Snap.h:104
bool mNoTimeSnap
Definition: Snap.h:99
int mPixelTolerance
Definition: Snap.h:98
const ZoomInfo * mZoomInfo
Definition: Snap.h:97
Definition: Snap.h:31
double t
Definition: Snap.h:39
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:110
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:850
int64 TimeToPosition(double time, int64 origin=0, bool ignoreFisheye=false) const
STM: Converts a project time to screen x position.
Definition: ZoomInfo.cpp:44
SnapPointArray FindCandidates(SnapPointArray candidates, const TrackList &tracks)
Definition: Snap.cpp:45
double time
Snapped time.
Definition: SnapUtils.h:47
bool snappedTime
Definition: Snap.h:50
double outTime
Definition: Snap.h:47
double timeSnappedTime
Definition: Snap.h:46
wxInt64 outCoord
Definition: Snap.h:48
bool Snapped() const
Definition: Snap.h:52
bool snappedPoint
Definition: Snap.h:49