You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

443 lines
13KB

  1. /*
  2. Copyright (C) 2006-2011 Nasca Octavian Paul
  3. Author: Nasca Octavian Paul
  4. Copyright (C) 2017 Xenakios
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of version 2 of the GNU General Public License
  7. as published by the Free Software Foundation.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License (version 2) for more details.
  12. You should have received a copy of the GNU General Public License (version 2)
  13. along with this program; if not, write to the Free Software Foundation,
  14. Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  15. */
  16. #include "envelope_component.h"
  17. EnvelopeComponent::EnvelopeComponent(CriticalSection* cs) : m_cs(cs)
  18. {
  19. OnEnvelopeEdited = [](breakpoint_envelope*) {};
  20. setWantsKeyboardFocus(true);
  21. ValueFromNormalized = [](double x) { return x; };
  22. TimeFromNormalized = [](double x) { return x; };
  23. addChildComponent(&m_bubble);
  24. setOpaque(true);
  25. }
  26. EnvelopeComponent::~EnvelopeComponent()
  27. {
  28. }
  29. void EnvelopeComponent::show_bubble(int x, int y, const envelope_node& node)
  30. {
  31. double scaledtime = TimeFromNormalized(node.Time);
  32. double scaledvalue = ValueFromNormalized(node.Value);
  33. x -= 50;
  34. if (x < 0)
  35. x = 0;
  36. if (x + 100 > getWidth())
  37. x = getWidth() - 100;
  38. if (y < 0)
  39. y = 0;
  40. if (y + 20 > getHeight())
  41. y = getHeight() - 20;
  42. AttributedString temp(String::formatted("%.2f %.2f", scaledtime, scaledvalue));
  43. temp.setColour(Colours::white);
  44. m_bubble.showAt({ x,y,100,20 }, temp , 5000);
  45. }
  46. void EnvelopeComponent::paint(Graphics& g)
  47. {
  48. if (!EnvelopeUnderlayDraw)
  49. {
  50. g.fillAll(Colours::black);
  51. g.setColour(Colours::white.darker());
  52. juce::Rectangle<int> rect(0, 0, getWidth(), getHeight());
  53. g.setFont(15.0);
  54. }
  55. else
  56. {
  57. g.saveState();
  58. EnvelopeUnderlayDraw(this, g);
  59. g.restoreState();
  60. }
  61. if (m_envelope == nullptr)
  62. {
  63. g.drawText("No envelope set", 10, 10, getWidth(), getHeight(), Justification::centred);
  64. return;
  65. }
  66. if (m_envelope.unique() == true)
  67. {
  68. g.drawText("Envelope is orphaned (may be a bug)", 10, 10, getWidth(), getHeight(), Justification::centred);
  69. return;
  70. }
  71. for (int i = 0; i < 10; ++i)
  72. {
  73. double norm = 1.0 / 10 * i;
  74. double hz = TimeFromNormalized(norm);
  75. int xcor = getWidth() / 10 * i;
  76. g.drawText(String(hz, 1), xcor, getHeight() - 20, 100, 20, Justification::topLeft);
  77. }
  78. String name = m_envelope->GetName();
  79. if (name.isEmpty() == true)
  80. name = "Untitled envelope";
  81. g.drawText(name, 10, 10, getWidth(), getHeight(), Justification::topLeft);
  82. auto draw_env = [this, &g](Colour envcolor, bool drawTransformed, float linethickness)
  83. {
  84. g.setColour(envcolor);
  85. double y0 = 0.0;
  86. if (drawTransformed==false)
  87. y0 = m_envelope->GetInterpolatedNodeValue(0.0);
  88. else y0 = m_envelope->getTransformedValue(0.0);
  89. const int drawstep = 1;
  90. for (int i = 1; i < getWidth(); ++i)
  91. {
  92. double env_x = 1.0 / getWidth()*i;
  93. double y1 = 0.0;
  94. if (drawTransformed==false)
  95. y1 = m_envelope->GetInterpolatedNodeValue(env_x);
  96. else y1 = m_envelope->getTransformedValue(env_x);
  97. double foo_y0 = (double)getHeight() - jmap<double>(y0, m_view_start_value, m_view_end_value, 0.0, getHeight());
  98. double foo_y1 = (double)getHeight() - jmap<double>(y1, m_view_start_value, m_view_end_value, 0.0, getHeight());
  99. g.drawLine((float)i, foo_y0, (float)i + 1, foo_y1, linethickness);
  100. y0 = y1;
  101. }
  102. };
  103. draw_env(m_env_color, false, 1.0f);
  104. draw_env(Colours::aquamarine.darker(), true, 1.0f);
  105. for (int i = 0; i < m_envelope->GetNumNodes(); ++i)
  106. {
  107. const envelope_node& pt = m_envelope->GetNodeAtIndex(i);
  108. double xcor = jmap(pt.Time, m_view_start_time, m_view_end_time, 0.0, (double)getWidth());
  109. double ycor = (double)getHeight() - jmap(pt.Value, m_view_start_value, m_view_end_value, 0.0, (double)getHeight());
  110. g.setColour(Colours::white);
  111. if (pt.Status == 0)
  112. g.drawRect((float)xcor - 4.0f, (float)ycor - 4.0f, 8.0f, 8.0f, 1.0f);
  113. else g.fillRect((float)xcor - 4.0f, (float)ycor - 4.0f, 8.0f, 8.0f);
  114. }
  115. }
  116. void EnvelopeComponent::changeListenerCallback(ChangeBroadcaster*)
  117. {
  118. repaint();
  119. }
  120. void EnvelopeComponent::timerCallback(int)
  121. {
  122. }
  123. void EnvelopeComponent::set_envelope(std::shared_ptr<breakpoint_envelope> env, String name)
  124. {
  125. m_envelope = env;
  126. m_name = name;
  127. repaint();
  128. }
  129. void EnvelopeComponent::mouseDrag(const MouseEvent& ev)
  130. {
  131. if (m_envelope == nullptr)
  132. return;
  133. if (m_segment_drag_info.first >= 0 && ev.mods.isAltDown())
  134. {
  135. double dist = jmap<double>(ev.getDistanceFromDragStartX(), -300.0, 300.0, -1.0, 1.0);
  136. m_envelope->performRelativeTransformation([dist, this](int index, envelope_node& point)
  137. {
  138. if (index == m_segment_drag_info.first)
  139. {
  140. point.ShapeParam1 += dist;
  141. m_segment_drag_info.second = true;
  142. }
  143. });
  144. repaint();
  145. return;
  146. }
  147. if (m_segment_drag_info.first >= 0)
  148. {
  149. double dist = jmap<double>(ev.getDistanceFromDragStartY(), -getHeight(), getHeight(), -1.0, 1.0);
  150. m_envelope->adjustEnvelopeSegmentValues(m_segment_drag_info.first, -dist);
  151. m_envelope->updateMinMaxValues();
  152. repaint();
  153. return;
  154. }
  155. if (m_node_to_drag >= 0)
  156. {
  157. //Logger::writeToLog("trying to move pt " + String(m_node_to_drag));
  158. envelope_node& pt = m_envelope->GetNodeAtIndex(m_node_to_drag);
  159. double left_bound = m_view_start_time;
  160. double right_bound = m_view_end_time;
  161. if (m_node_to_drag > 0 )
  162. {
  163. left_bound = m_envelope->GetNodeAtIndex(m_node_to_drag - 1).Time;
  164. }
  165. if (m_node_to_drag < m_envelope->GetNumNodes() - 1)
  166. {
  167. right_bound = m_envelope->GetNodeAtIndex(m_node_to_drag + 1).Time;
  168. }
  169. double normx = jmap((double)ev.x, 0.0, (double)getWidth(), m_view_start_time, m_view_end_time);
  170. double normy = jmap((double)getHeight() - ev.y, 0.0, (double)getHeight(), m_view_start_value, m_view_end_value);
  171. pt.Time=jlimit(left_bound+0.001, right_bound - 0.001, normx);
  172. pt.Value=jlimit(0.0,1.0,normy);
  173. m_envelope->updateMinMaxValues();
  174. m_last_tip = String(pt.Time, 2) + " " + String(pt.Value, 2);
  175. show_bubble(ev.x, ev.y, pt);
  176. m_node_that_was_dragged = m_node_to_drag;
  177. repaint();
  178. return;
  179. }
  180. }
  181. void EnvelopeComponent::mouseMove(const MouseEvent & ev)
  182. {
  183. if (m_envelope == nullptr)
  184. return;
  185. m_node_to_drag = find_hot_envelope_point(ev.x, ev.y);
  186. if (m_node_to_drag >= 0)
  187. {
  188. if (m_mouse_down == false)
  189. {
  190. show_bubble(ev.x, ev.y, m_envelope->GetNodeAtIndex(m_node_to_drag));
  191. setMouseCursor(MouseCursor::PointingHandCursor);
  192. }
  193. }
  194. else
  195. {
  196. int temp = findHotEnvelopeSegment(ev.x, ev.y, true);
  197. if (temp>=0)
  198. setMouseCursor(MouseCursor::UpDownResizeCursor);
  199. else
  200. setMouseCursor(MouseCursor::NormalCursor);
  201. m_bubble.setVisible(false);
  202. }
  203. }
  204. void EnvelopeComponent::mouseDown(const MouseEvent & ev)
  205. {
  206. if (m_envelope == nullptr)
  207. return;
  208. if (ev.mods.isRightButtonDown() == true)
  209. {
  210. PopupMenu menu;
  211. menu.addItem(1, "Reset");
  212. menu.addItem(2, "Invert");
  213. menu.addItem(3, "Wrap envelope X transform", true, m_envelope->m_transform_wrap_x);
  214. menu.addItem(4, "Envelope Y random linear interpolation", true, m_envelope->m_transform_y_random_linear_interpolation);
  215. int r = menu.show();
  216. if (r == 1)
  217. {
  218. m_cs->enter();
  219. m_envelope->ResetEnvelope();
  220. m_cs->exit();
  221. }
  222. if (r == 2)
  223. {
  224. for (int i = 0; i < m_envelope->GetNumNodes(); ++i)
  225. {
  226. double val = 1.0 - m_envelope->GetNodeAtIndex(i).Value;
  227. m_envelope->GetNodeAtIndex(i).Value = val;
  228. }
  229. }
  230. if (r == 3)
  231. {
  232. toggleBool(m_envelope->m_transform_wrap_x);
  233. }
  234. if (r == 4)
  235. {
  236. toggleBool(m_envelope->m_transform_y_random_linear_interpolation);
  237. }
  238. repaint();
  239. return;
  240. }
  241. m_node_to_drag = find_hot_envelope_point(ev.x, ev.y);
  242. m_mouse_down = true;
  243. m_segment_drag_info = { findHotEnvelopeSegment(ev.x, ev.y, true),false };
  244. if (m_segment_drag_info.first >= 0)
  245. {
  246. m_envelope->beginRelativeTransformation();
  247. return;
  248. }
  249. if (m_node_to_drag >= 0 && ev.mods.isAltDown() == true)
  250. {
  251. if (m_envelope->GetNumNodes() < 2)
  252. {
  253. m_bubble.showAt({ ev.x,ev.y, 0,0 }, AttributedString("Can't remove last node"), 3000, false, false);
  254. return;
  255. }
  256. m_cs->enter();
  257. m_envelope->DeleteNode(m_node_to_drag);
  258. m_cs->exit();
  259. m_envelope->updateMinMaxValues();
  260. m_node_to_drag = -1;
  261. OnEnvelopeEdited(m_envelope.get());
  262. repaint();
  263. return;
  264. }
  265. if (m_node_to_drag >= 0 && ev.mods.isShiftDown()==true)
  266. {
  267. int oldstatus = m_envelope->GetNodeAtIndex(m_node_to_drag).Status;
  268. if (oldstatus==0)
  269. m_envelope->GetNodeAtIndex(m_node_to_drag).Status=1;
  270. else m_envelope->GetNodeAtIndex(m_node_to_drag).Status=0;
  271. repaint();
  272. return;
  273. }
  274. if (m_node_to_drag == -1)
  275. {
  276. double normx = jmap((double)ev.x, 0.0, (double)getWidth(), m_view_start_time, m_view_end_time);
  277. double normy = jmap((double)getHeight() - ev.y, 0.0, (double)getHeight(), m_view_start_value, m_view_end_value);
  278. m_cs->enter();
  279. m_envelope->AddNode ({ normx,normy, 0.5});
  280. m_envelope->SortNodes();
  281. m_cs->exit();
  282. m_envelope->updateMinMaxValues();
  283. m_mouse_down = false;
  284. OnEnvelopeEdited(m_envelope.get());
  285. repaint();
  286. }
  287. }
  288. void EnvelopeComponent::mouseUp(const MouseEvent &ev)
  289. {
  290. if (ev.mods == ModifierKeys::noModifiers)
  291. m_bubble.setVisible(false);
  292. if (m_node_that_was_dragged >= 0 || m_segment_drag_info.second==true)
  293. {
  294. OnEnvelopeEdited(m_envelope.get());
  295. }
  296. m_mouse_down = false;
  297. m_node_that_was_dragged = -1;
  298. m_node_to_drag = -1;
  299. if (m_segment_drag_info.second == true)
  300. {
  301. m_segment_drag_info = { -1,false };
  302. m_envelope->endRelativeTransformation();
  303. }
  304. }
  305. bool EnvelopeComponent::keyPressed(const KeyPress & ev)
  306. {
  307. if (m_envelope == nullptr)
  308. return false;
  309. auto f = [this](auto& env_var, double amt)
  310. {
  311. env_var+=amt;
  312. return true;
  313. };
  314. bool r = false;
  315. if (ev == 'Q')
  316. r = f(m_envelope->m_transform_x_shift,-0.01);
  317. if (ev == 'W')
  318. r = f(m_envelope->m_transform_x_shift,0.01);
  319. if (ev == 'E')
  320. r = f(m_envelope->m_transform_y_shift,0.01);
  321. if (ev == 'D')
  322. r = f(m_envelope->m_transform_y_shift,-0.01);
  323. if (ev == 'R')
  324. r = f(m_envelope->m_transform_y_scale,0.05);
  325. if (ev == 'F')
  326. r = f(m_envelope->m_transform_y_scale,-0.05);
  327. if (ev == 'T')
  328. r = f(m_envelope->m_transform_y_sinus,0.01);
  329. if (ev == 'G')
  330. r = f(m_envelope->m_transform_y_sinus,-0.01);
  331. if (ev == 'Y')
  332. r = f(m_envelope->m_transform_y_tilt,0.02);
  333. if (ev == 'H')
  334. r = f(m_envelope->m_transform_y_tilt,-0.02);
  335. if (ev == 'V')
  336. r = f(m_envelope->m_transform_y_sinus_freq,1.0);
  337. if (ev == 'B')
  338. r = f(m_envelope->m_transform_y_sinus_freq,-1.0);
  339. m_envelope->m_transform_y_sinus_freq = jlimit(1.0,64.0, m_envelope->m_transform_y_sinus_freq);
  340. if (r==true)
  341. {
  342. repaint();
  343. return true;
  344. }
  345. if (ev == 'A')
  346. {
  347. m_envelope->m_transform_x_shift = 0.0;
  348. m_envelope->m_transform_y_scale = 1.0;
  349. m_envelope->m_transform_y_shift = 0.0;
  350. m_envelope->m_transform_y_sinus = 0.0;
  351. repaint();
  352. return true;
  353. }
  354. if (ev == KeyPress::deleteKey)
  355. {
  356. m_node_to_drag = -1;
  357. //m_envelope->ClearAllNodes();
  358. m_cs->enter();
  359. m_envelope->removePointsConditionally([](const envelope_node& pt) { return pt.Status == 1; });
  360. if (m_envelope->GetNumNodes()==0)
  361. m_envelope->AddNode({ 0.0,0.5 });
  362. m_cs->exit();
  363. repaint();
  364. OnEnvelopeEdited(m_envelope.get());
  365. return true;
  366. }
  367. return false;
  368. }
  369. int EnvelopeComponent::find_hot_envelope_point(double xcor, double ycor)
  370. {
  371. if (m_envelope == nullptr)
  372. return -1;
  373. for (int i = 0; i < m_envelope->GetNumNodes(); ++i)
  374. {
  375. const envelope_node& pt = m_envelope->GetNodeAtIndex(i);
  376. double ptxcor = jmap(pt.Time, m_view_start_time, m_view_end_time, 0.0, (double)getWidth());
  377. double ptycor = (double)getHeight() - jmap(pt.Value, m_view_start_value, m_view_end_value, 0.0, (double)getHeight());
  378. juce::Rectangle<double> target(ptxcor - 4.0, ptycor - 4.0, 8.0, 8.0);
  379. if (target.contains(xcor, ycor) == true)
  380. {
  381. return i;
  382. }
  383. }
  384. return -1;
  385. }
  386. int EnvelopeComponent::findHotEnvelopeSegment(double xcor, double ycor, bool detectsegment)
  387. {
  388. if (m_envelope == nullptr)
  389. return -1;
  390. for (int i = 0; i < m_envelope->GetNumNodes()-1; ++i)
  391. {
  392. const envelope_node& pt0 = m_envelope->GetNodeAtIndex(i);
  393. const envelope_node& pt1 = m_envelope->GetNodeAtIndex(i+1);
  394. float xcor0 = (float)jmap<double>(pt0.Time, m_view_start_time, m_view_end_time, 0.0, getWidth());
  395. float xcor1 = (float)jmap<double>(pt1.Time, m_view_start_time, m_view_end_time, 0.0, getWidth());
  396. float segwidth = xcor1 - xcor0;
  397. juce::Rectangle<float> segrect(xcor0+8.0f, 0.0f, segwidth-16.0f, (float)getHeight());
  398. if (segrect.contains((float)xcor, (float)ycor))
  399. {
  400. if (detectsegment == false)
  401. return i;
  402. else
  403. {
  404. double normx = jmap<double>(xcor, 0.0, getWidth(), m_view_start_time, m_view_end_time);
  405. double yval = m_envelope->GetInterpolatedNodeValue(normx);
  406. float ycor0 = (float)(getHeight()-jmap<double>(yval, 0.0, 1.0, 0.0, getHeight()));
  407. juce::Rectangle<float> segrect2((float)(xcor - 20), (float)(ycor - 10), 40, 20);
  408. if (segrect2.contains((float)xcor, ycor0))
  409. return i;
  410. }
  411. }
  412. }
  413. return -1;
  414. }