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.

438 lines
13KB

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