Audacity  3.0.3
EnvelopeEditor.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  EnvelopeEditor.cpp
6 
7  Paul Licameli split this from Envelope.cpp
8 
9 **********************************************************************/
10 
11 
12 #include "EnvelopeEditor.h"
13 
14 
15 
16 #include <wx/dc.h>
17 #include <wx/event.h>
18 
19 #include "AColor.h"
20 #include "Envelope.h"
21 #include "TrackArtist.h"
23 #include "ViewInfo.h"
24 
25 namespace {
26 void DrawPoint(wxDC & dc, const wxRect & r, int x, int y, bool top)
27 {
28  if (y >= 0 && y <= r.height) {
29  wxRect circle(r.x + x, r.y + (top ? y - 1: y - 2), 4, 4);
30  dc.DrawEllipse(circle);
31  }
32 }
33 }
34 
36 (const Envelope &env,
37  TrackPanelDrawingContext &context, const wxRect & r,
38  bool dB, double dBRange,
39  float zoomMin, float zoomMax, bool mirrored)
40 {
41  auto &dc = context.dc;
42  const auto artist = TrackArtist::Get( context );
43  const auto &zoomInfo = *artist->pZoomInfo;
44 
45  bool highlight = false;
46 #ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
47  auto target = dynamic_cast<EnvelopeHandle*>(context.target.get());
48  highlight = target && target->GetEnvelope() == this;
49 #endif
50  wxPen &pen = highlight ? AColor::uglyPen : AColor::envelopePen;
51  dc.SetPen( pen );
52  dc.SetBrush(*wxWHITE_BRUSH);
53 
54  for (int i = 0; i < (int)env.GetNumberOfPoints(); i++) {
55  const double time = env[i].GetT() + env.GetOffset();
56  const wxInt64 position = zoomInfo.TimeToPosition(time);
57  if (position >= 0 && position < r.width) {
58  // Change colour if this is the draggable point...
59  if (i == env.GetDragPoint()) {
60  dc.SetPen( pen );
61  dc.SetBrush(AColor::envelopeBrush);
62  }
63 
64  double v = env[i].GetVal();
65  int x = (int)(position);
66  int y, y2;
67 
68  y = GetWaveYPos(v, zoomMin, zoomMax, r.height, dB,
69  true, dBRange, false);
70  if (!mirrored) {
71  DrawPoint(dc, r, x, y, true);
72  }
73  else {
74  y2 = GetWaveYPos(-v-.000000001, zoomMin, zoomMax, r.height, dB,
75  true, dBRange, false);
76 
77  // This follows the same logic as the envelop drawing in
78  // TrackArt::DrawEnvelope().
79  // TODO: make this calculation into a reusable function.
80  if (y2 - y < 9) {
81  int value = (int)((zoomMax / (zoomMax - zoomMin)) * r.height);
82  y = value - 4;
83  y2 = value + 4;
84  }
85 
86  DrawPoint(dc, r, x, y, true);
87  DrawPoint(dc, r, x, y2, false);
88 
89  // Contour
90  y = GetWaveYPos(v, zoomMin, zoomMax, r.height, dB,
91  false, dBRange, false);
92  y2 = GetWaveYPos(-v-.000000001, zoomMin, zoomMax, r.height, dB,
93  false, dBRange, false);
94  if (y <= y2) {
95  DrawPoint(dc, r, x, y, true);
96  DrawPoint(dc, r, x, y2, false);
97  }
98  }
99 
100  // Change colour back again if was the draggable point.
101  if (i == env.GetDragPoint()) {
102  dc.SetPen( pen );
103  dc.SetBrush(*wxWHITE_BRUSH);
104  }
105  }
106  }
107 }
108 
109 EnvelopeEditor::EnvelopeEditor(Envelope &envelope, bool mirrored)
110  : mEnvelope(envelope)
111  , mMirrored(mirrored)
112  , mContourOffset(-1)
113  // , mInitialVal(-1.0)
114  // , mInitialY(-1)
115  , mUpper(false)
116  , mButton(wxMOUSE_BTN_NONE)
117  , mDirty(false)
118 {
119 }
120 
122 {
123 }
124 
125 namespace
126 {
127 inline int SQR(int x) { return x * x; }
128 }
129 
137 float EnvelopeEditor::ValueOfPixel( int y, int height, bool upper,
138  bool dB, double dBRange,
139  float zoomMin, float zoomMax)
140 {
141  float v = ::ValueOfPixel(y, height, 0 != mContourOffset, dB, dBRange, zoomMin, zoomMax);
142 
143  // MB: this is mostly equivalent to what the old code did, I'm not sure
144  // if anything special is needed for asymmetric ranges
145  if(upper)
146  return mEnvelope.ClampValue(v);
147  else
148  return mEnvelope.ClampValue(-v);
149 }
150 
157 bool EnvelopeEditor::HandleMouseButtonDown(const wxMouseEvent & event, wxRect & r,
158  const ZoomInfo &zoomInfo,
159  bool dB, double dBRange,
160  float zoomMin, float zoomMax)
161 {
162  int ctr = (int)(r.height * zoomMax / (zoomMax - zoomMin));
163  bool upper = !mMirrored || (zoomMin >= 0.0) || (event.m_y - r.y < ctr);
164 
165  int clip_y = event.m_y - r.y;
166  if(clip_y < 0) clip_y = 0; //keeps point in rect r, even if mouse isn't
167  if(clip_y > r.GetBottom()) clip_y = r.GetBottom();
168 
169  int bestNum = -1;
170  int bestDistSqr = 100; // Must be within 10 pixel radius.
171 
172  // Member variables hold state that will be needed in dragging.
173  mButton = event.GetButton();
174  mContourOffset = false;
175 
176  // wxLogDebug(wxT("Y:%i Height:%i Offset:%i"), y, height, mContourOffset );
177  int len = mEnvelope.GetNumberOfPoints();
178 
179  // TODO: extract this into a function FindNearestControlPoint()
180  // TODO: also fix it so that we can drag the last point on an envelope.
181  for (int i = 0; i < len; i++) { //search for control point nearest click
182  const double time = mEnvelope[i].GetT() + mEnvelope.GetOffset();
183  const wxInt64 position = zoomInfo.TimeToPosition(time);
184  if (position >= 0 && position < r.width) {
185 
186  int x = (int)(position);
187  int y[4];
188  int numControlPoints;
189 
190  // Outer control points
191  double value = mEnvelope[i].GetVal();
192  y[0] = GetWaveYPos(value, zoomMin, zoomMax, r.height,
193  dB, true, dBRange, false);
194  y[1] = GetWaveYPos(-value, zoomMin, zoomMax, r.height,
195  dB, true, dBRange, false);
196 
197  // Inner control points(contour)
198  y[2] = GetWaveYPos(value, zoomMin, zoomMax, r.height,
199  dB, false, dBRange, false);
200  y[3] = GetWaveYPos(-value -.00000001, zoomMin, zoomMax,
201  r.height, dB, false, dBRange, false);
202 
203  numControlPoints = 4;
204 
205  if (y[2] > y[3])
206  numControlPoints = 2;
207 
208  if (!mMirrored)
209  numControlPoints = 1;
210 
211  const int deltaXSquared = SQR(x - (event.m_x - r.x));
212  for(int j=0; j<numControlPoints; j++){
213 
214  const int dSqr = deltaXSquared + SQR(y[j] - (event.m_y - r.y));
215  if (dSqr < bestDistSqr) {
216  bestNum = i;
217  bestDistSqr = dSqr;
218  mContourOffset = (bool)(j > 1);
219  }
220  }
221  }
222  }
223 
224  if (bestNum >= 0) {
225  mEnvelope.SetDragPoint(bestNum);
226  }
227  else {
228  // TODO: Extract this into a function CreateNewPoint
229  const double when = zoomInfo.PositionToTime(event.m_x, r.x);
230 
231  // if (when <= 0 || when >= mTrackLen)
232  // return false;
233 
234  const double v = mEnvelope.GetValue( when );
235 
236  int ct = GetWaveYPos( v, zoomMin, zoomMax, r.height, dB,
237  false, dBRange, false) ;
238  int cb = GetWaveYPos( -v-.000000001, zoomMin, zoomMax, r.height, dB,
239  false, dBRange, false) ;
240  if (ct <= cb || !mMirrored) {
241  int t = GetWaveYPos( v, zoomMin, zoomMax, r.height, dB,
242  true, dBRange, false) ;
243  int b = GetWaveYPos( -v, zoomMin, zoomMax, r.height, dB,
244  true, dBRange, false) ;
245 
246  ct = (t + ct) / 2;
247  cb = (b + cb) / 2;
248 
249  if (mMirrored &&
250  (event.m_y - r.y) > ct &&
251  ((event.m_y - r.y) < cb))
252  mContourOffset = true;
253  else
254  mContourOffset = false;
255  }
256 
257  double newVal = ValueOfPixel(clip_y, r.height, upper, dB, dBRange,
258  zoomMin, zoomMax);
259 
261  mDirty = true;
262  }
263 
264  mUpper = upper;
265 
266  // const int dragPoint = mEnvelope.GetDragPoint();
267  // mInitialVal = mEnvelope[dragPoint].GetVal();
268  // mInitialY = event.m_y+mContourOffset;
269 
270  return true;
271 }
272 
273 void EnvelopeEditor::MoveDragPoint(const wxMouseEvent & event, wxRect & r,
274  const ZoomInfo &zoomInfo, bool dB, double dBRange,
275  float zoomMin, float zoomMax)
276 {
277  int clip_y = event.m_y - r.y;
278  if(clip_y < 0) clip_y = 0;
279  if(clip_y > r.height) clip_y = r.height;
280  double newVal = ValueOfPixel(clip_y, r.height, mUpper, dB, dBRange,
281  zoomMin, zoomMax);
282 
283  // We no longer tolerate multiple envelope points at the same t.
284  // epsilon is less than the time offset of a single sample
285  // TODO: However because mTrackEpsilon assumes 200KHz this use
286  // of epsilon is a tad bogus. What we need to do instead is DELETE
287  // a duplicated point on a mouse up.
288  double newWhen = zoomInfo.PositionToTime(event.m_x, r.x) - mEnvelope.GetOffset();
289  mEnvelope.MoveDragPoint(newWhen, newVal);
290 }
291 
292 bool EnvelopeEditor::HandleDragging(const wxMouseEvent & event, wxRect & r,
293  const ZoomInfo &zoomInfo, bool dB, double dBRange,
294  float zoomMin, float zoomMax,
295  float WXUNUSED(eMin), float WXUNUSED(eMax))
296 {
297  mDirty = true;
298 
299  wxRect larger = r;
300  larger.Inflate(10, 10);
301 
302  if (larger.Contains(event.m_x, event.m_y))
303  {
304  // IF we're in the rect THEN we're not deleting this point (anymore).
305  // ...we're dragging it.
306  MoveDragPoint( event, r, zoomInfo, dB, dBRange, zoomMin, zoomMax);
307  return true;
308  }
309 
311  // IF we already know we're deleting THEN no envelope point to update.
312  return false;
313 
314  // Invalidate the point
316  return true;
317 }
318 
319 // Exit dragging mode and delete dragged point if necessary.
321 {
323  mButton = wxMOUSE_BTN_NONE;
324  return true;
325 }
326 
327 // Returns true if parent needs to be redrawn
328 bool EnvelopeEditor::MouseEvent(const wxMouseEvent & event, wxRect & r,
329  const ZoomInfo &zoomInfo, bool dB, double dBRange,
330  float zoomMin, float zoomMax)
331 {
332  if (event.ButtonDown() && mButton == wxMOUSE_BTN_NONE)
333  return HandleMouseButtonDown( event, r, zoomInfo, dB, dBRange,
334  zoomMin, zoomMax);
335  if (event.Dragging() && mEnvelope.GetDragPoint() >= 0)
336  return HandleDragging( event, r, zoomInfo, dB, dBRange,
337  zoomMin, zoomMax);
338  if (event.ButtonUp() && event.GetButton() == mButton)
339  return HandleMouseButtonUp();
340  return false;
341 }
Envelope::GetDragPoint
int GetDragPoint() const
Definition: Envelope.h:221
EnvelopeEditor::mButton
int mButton
Definition: EnvelopeEditor.h:68
EnvelopeEditor::HandleDragging
bool HandleDragging(const wxMouseEvent &event, wxRect &r, const ZoomInfo &zoomInfo, bool dB, double dBRange, float zoomMin=-1.0, float zoomMax=1.0, float eMin=0., float eMax=2.)
Definition: EnvelopeEditor.cpp:292
EnvelopeEditor.h
TrackArtist.h
EnvelopeHandle::GetEnvelope
Envelope * GetEnvelope() const
Definition: EnvelopeHandle.h:59
EnvelopeEditor::ValueOfPixel
float ValueOfPixel(int y, int height, bool upper, bool dB, double dBRange, float zoomMin, float zoomMax)
Definition: EnvelopeEditor.cpp:137
EnvelopeEditor::HandleMouseButtonDown
bool HandleMouseButtonDown(const wxMouseEvent &event, wxRect &r, const ZoomInfo &zoomInfo, bool dB, double dBRange, float zoomMin=-1.0, float zoomMax=1.0)
Definition: EnvelopeEditor.cpp:157
ZoomInfo
Definition: ZoomInfo.h:47
TrackPanelDrawingContext
Definition: TrackPanelDrawingContext.h:22
Envelope
Piecewise linear or piecewise exponential function from double to double.
Definition: Envelope.h:71
EnvelopeEditor::mEnvelope
Envelope & mEnvelope
Definition: EnvelopeEditor.h:58
Envelope::ClampValue
double ClampValue(double value)
Definition: Envelope.h:110
Envelope::MoveDragPoint
void MoveDragPoint(double newWhen, double value)
Definition: Envelope.cpp:183
TrackPanelDrawingContext::dc
wxDC & dc
Definition: TrackPanelDrawingContext.h:23
EnvelopeHandle
Definition: EnvelopeHandle.h:28
EnvelopeEditor::MouseEvent
bool MouseEvent(const wxMouseEvent &event, wxRect &r, const ZoomInfo &zoomInfo, bool dB, double dBRange, float zoomMin=-1.0, float zoomMax=1.0)
Definition: EnvelopeEditor.cpp:328
AColor::envelopePen
static wxPen envelopePen
Definition: AColor.h:104
ZoomInfo::TimeToPosition
wxInt64 TimeToPosition(double time, wxInt64 origin=0, bool ignoreFisheye=false) const
STM: Converts a project time to screen x position.
Definition: ZoomInfo.cpp:49
EnvelopeEditor::MoveDragPoint
void MoveDragPoint(const wxMouseEvent &event, wxRect &r, const ZoomInfo &zoomInfo, bool dB, double dBRange, float zoomMin, float zoomMax)
Definition: EnvelopeEditor.cpp:273
anonymous_namespace{EnvelopeEditor.cpp}::SQR
int SQR(int x)
Definition: EnvelopeEditor.cpp:127
EnvelopeEditor::HandleMouseButtonUp
bool HandleMouseButtonUp()
Definition: EnvelopeEditor.cpp:320
Envelope::GetOffset
double GetOffset() const
Definition: Envelope.h:98
TrackArtist::Get
static TrackArtist * Get(TrackPanelDrawingContext &)
Definition: TrackArtist.cpp:79
Envelope::SetDragPointValid
void SetDragPointValid(bool valid)
Definition: Envelope.cpp:146
EnvelopeEditor::mDirty
bool mDirty
Definition: EnvelopeEditor.h:69
EnvelopeEditor::~EnvelopeEditor
~EnvelopeEditor()
Definition: EnvelopeEditor.cpp:121
AColor::uglyPen
static wxPen uglyPen
Definition: AColor.h:130
Envelope::InsertOrReplace
int InsertOrReplace(double when, double value)
Add a point at a particular absolute time coordinate.
Definition: Envelope.h:181
EnvelopeEditor::DrawPoints
static void DrawPoints(const Envelope &env, TrackPanelDrawingContext &context, const wxRect &r, bool dB, double dBRange, float zoomMin, float zoomMax, bool mirrored)
Definition: EnvelopeEditor.cpp:36
Envelope::ClearDragPoint
void ClearDragPoint()
Definition: Envelope.cpp:209
ViewInfo.h
Envelope::GetDragPointValid
bool GetDragPointValid() const
Definition: Envelope.h:226
EnvelopeEditor::mUpper
bool mUpper
Definition: EnvelopeEditor.h:67
Envelope.h
Envelope::GetValue
double GetValue(double t, double sampleDur=0) const
Get envelope value at time t.
Definition: Envelope.cpp:827
TrackPanelDrawingContext.h
EnvelopeEditor::mMirrored
const bool mMirrored
Definition: EnvelopeEditor.h:59
EnvelopeEditor::mContourOffset
int mContourOffset
Number of pixels contour is from the true envelope.
Definition: EnvelopeEditor.h:62
EnvelopeEditor::EnvelopeEditor
EnvelopeEditor(Envelope &envelope, bool mirrored)
Definition: EnvelopeEditor.cpp:109
Envelope::SetDragPoint
void SetDragPoint(int dragPoint)
Definition: Envelope.cpp:140
GetWaveYPos
int GetWaveYPos(float value, float min, float max, int height, bool dB, bool outer, float dBr, bool clip)
Definition: TrackArtist.cpp:136
TrackPanelDrawingContext::target
UIHandlePtr target
Definition: TrackPanelDrawingContext.h:24
anonymous_namespace{EnvelopeEditor.cpp}::DrawPoint
void DrawPoint(wxDC &dc, const wxRect &r, int x, int y, bool top)
Definition: EnvelopeEditor.cpp:26
AColor.h
AColor::envelopeBrush
static wxBrush envelopeBrush
Definition: AColor.h:106
Envelope::GetNumberOfPoints
size_t GetNumberOfPoints() const
Return number of points.
Definition: Envelope.cpp:693
ZoomInfo::PositionToTime
double PositionToTime(wxInt64 position, wxInt64 origin=0, bool ignoreFisheye=false) const
Definition: ZoomInfo.cpp:39