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.

403 lines
12KB

  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_name;
  79. if (name.isEmpty() == true)
  80. name = "Untitled envelope";
  81. g.drawText(name, 10, 10, getWidth(), getHeight(), Justification::topLeft);
  82. const float linethickness = 1.0f;
  83. for (int i = 0; i < m_envelope->GetNumNodes(); ++i)
  84. {
  85. const envelope_node& pt = m_envelope->GetNodeAtIndex(i);
  86. double xcor = jmap(pt.Time, m_view_start_time, m_view_end_time, 0.0, (double)getWidth());
  87. double ycor = (double)getHeight()-jmap(pt.Value, m_view_start_value, m_view_end_value, 0.0,(double)getHeight());
  88. g.setColour(Colours::white);
  89. if (pt.Status==0)
  90. g.drawRect((float)xcor - 4.0f, (float)ycor - 4.0f, 8.0f, 8.0f, 1.0f);
  91. else g.fillRect((float)xcor - 4.0f, (float)ycor - 4.0f, 8.0f, 8.0f);
  92. m_envelope->resamplePointToLinearSegments(i,0.0, 1.0, 0.0, 1.0, [&g,linethickness,this](double pt_x0, double pt_y0, double pt_x1, double pt_y1)
  93. {
  94. double foo_x0 = jmap<double>(pt_x0, m_view_start_time, m_view_end_time, 0.0, getWidth());
  95. double foo_x1 = jmap<double>(pt_x1, m_view_start_time, m_view_end_time, 0.0, getWidth());
  96. double foo_y0 = (double)getHeight() - jmap<double>(pt_y0, m_view_start_value, m_view_end_value, 0.0, getHeight());
  97. double foo_y1 = (double)getHeight() - jmap<double>(pt_y1, m_view_start_value, m_view_end_value, 0.0, getHeight());
  98. g.setColour(m_env_color);
  99. g.drawLine((float)foo_x0, (float)foo_y0, (float)foo_x1, (float)foo_y1, linethickness);
  100. //g.setColour(Colours::white);
  101. //g.drawLine(foo_x0, foo_y0 - 8.0, foo_x0, foo_y0 + 8.0, 1.0);
  102. }, [this](double xdiff)
  103. {
  104. return std::max((int)(xdiff*getWidth() / 16), 8);
  105. });
  106. /*
  107. envelope_node pt1;
  108. if (i + 1 < m_envelope->GetNumNodes())
  109. {
  110. g.setColour(m_env_color);
  111. pt1 = m_envelope->GetNodeAtIndex(i + 1);
  112. double xcor1 = jmap(pt1.Time, m_view_start_time, m_view_end_time, 0.0, (double)getWidth());
  113. double ycor1 = (double)getHeight() - jmap(pt1.Value, m_view_start_value, m_view_end_value, 0.0, (double)getHeight());
  114. g.drawLine((float)xcor, (float)ycor, (float)xcor1, (float)ycor1, linethickness);
  115. }
  116. if (i == 0 && pt.Time >= 0.0)
  117. {
  118. g.setColour(m_env_color);
  119. g.drawLine(0.0f, (float)ycor, (float)xcor, float(ycor), linethickness);
  120. }
  121. if (i == m_envelope->GetNumNodes()-1 && pt.Time < 1.0)
  122. {
  123. g.setColour(m_env_color);
  124. g.drawLine((float)xcor, (float)ycor, (float)getWidth(), float(ycor), linethickness);
  125. }
  126. */
  127. }
  128. #ifdef ENVELOPEDRAWDERIVATIVE
  129. g.setColour(Colours::green);
  130. //double prevderiv = derivative([this](double xx) { return m_envelope->GetInterpolatedNodeValue(xx); }, 0.0);
  131. for (int i = 0; i < getWidth()/8; ++i)
  132. {
  133. double x = 1.0 / getWidth()*(i*8);
  134. double derv = derivative([this](double xx) { return m_envelope->GetInterpolatedNodeValue(xx); }, x);
  135. double y = getHeight()-jmap<double>(derv, -10.0, 10.0, 0.0, getHeight());
  136. g.fillEllipse(i*8, y, 5.0f, 5.0f);
  137. }
  138. #endif
  139. }
  140. void EnvelopeComponent::changeListenerCallback(ChangeBroadcaster*)
  141. {
  142. repaint();
  143. }
  144. void EnvelopeComponent::timerCallback(int)
  145. {
  146. }
  147. void EnvelopeComponent::set_envelope(std::shared_ptr<breakpoint_envelope> env, String name)
  148. {
  149. m_envelope = env;
  150. m_name = name;
  151. repaint();
  152. }
  153. void EnvelopeComponent::mouseDrag(const MouseEvent& ev)
  154. {
  155. if (m_envelope == nullptr)
  156. return;
  157. if (m_segment_drag_info.first >= 0 && ev.mods.isAltDown())
  158. {
  159. double dist = jmap<double>(ev.getDistanceFromDragStartX(), -300.0, 300.0, -1.0, 1.0);
  160. m_envelope->performRelativeTransformation([dist, this](int index, envelope_node& point)
  161. {
  162. if (index == m_segment_drag_info.first)
  163. {
  164. point.ShapeParam1 += dist;
  165. m_segment_drag_info.second = true;
  166. }
  167. });
  168. repaint();
  169. return;
  170. }
  171. if (m_segment_drag_info.first >= 0)
  172. {
  173. double dist = jmap<double>(ev.getDistanceFromDragStartY(), -getHeight(), getHeight(), -1.0, 1.0);
  174. m_envelope->adjustEnvelopeSegmentValues(m_segment_drag_info.first, -dist);
  175. repaint();
  176. return;
  177. }
  178. if (m_node_to_drag >= 0)
  179. {
  180. //Logger::writeToLog("trying to move pt " + String(m_node_to_drag));
  181. envelope_node& pt = m_envelope->GetNodeAtIndex(m_node_to_drag);
  182. double left_bound = m_view_start_time;
  183. double right_bound = m_view_end_time;
  184. if (m_node_to_drag > 0 )
  185. {
  186. left_bound = m_envelope->GetNodeAtIndex(m_node_to_drag - 1).Time;
  187. }
  188. if (m_node_to_drag < m_envelope->GetNumNodes() - 1)
  189. {
  190. right_bound = m_envelope->GetNodeAtIndex(m_node_to_drag + 1).Time;
  191. }
  192. double normx = jmap((double)ev.x, 0.0, (double)getWidth(), m_view_start_time, m_view_end_time);
  193. double normy = jmap((double)getHeight() - ev.y, 0.0, (double)getHeight(), m_view_start_value, m_view_end_value);
  194. pt.Time=jlimit(left_bound+0.001, right_bound - 0.001, normx);
  195. pt.Value=jlimit(0.0,1.0,normy);
  196. m_last_tip = String(pt.Time, 2) + " " + String(pt.Value, 2);
  197. show_bubble(ev.x, ev.y, pt);
  198. m_node_that_was_dragged = m_node_to_drag;
  199. repaint();
  200. return;
  201. }
  202. }
  203. void EnvelopeComponent::mouseMove(const MouseEvent & ev)
  204. {
  205. if (m_envelope == nullptr)
  206. return;
  207. m_node_to_drag = find_hot_envelope_point(ev.x, ev.y);
  208. if (m_node_to_drag >= 0)
  209. {
  210. if (m_mouse_down == false)
  211. {
  212. show_bubble(ev.x, ev.y, m_envelope->GetNodeAtIndex(m_node_to_drag));
  213. setMouseCursor(MouseCursor::PointingHandCursor);
  214. }
  215. }
  216. else
  217. {
  218. setMouseCursor(MouseCursor::NormalCursor);
  219. m_bubble.setVisible(false);
  220. }
  221. }
  222. void EnvelopeComponent::mouseDown(const MouseEvent & ev)
  223. {
  224. if (m_envelope == nullptr)
  225. return;
  226. if (ev.mods.isRightButtonDown() == true)
  227. {
  228. PopupMenu menu;
  229. menu.addItem(1, "Reset");
  230. menu.addItem(2, "Invert");
  231. int r = menu.show();
  232. if (r == 1)
  233. {
  234. m_cs->enter();
  235. m_envelope->ResetEnvelope();
  236. m_cs->exit();
  237. }
  238. if (r == 2)
  239. {
  240. for (int i = 0; i < m_envelope->GetNumNodes(); ++i)
  241. {
  242. double val = 1.0 - m_envelope->GetNodeAtIndex(i).Value;
  243. m_envelope->GetNodeAtIndex(i).Value = val;
  244. }
  245. }
  246. repaint();
  247. return;
  248. }
  249. m_node_to_drag = find_hot_envelope_point(ev.x, ev.y);
  250. m_mouse_down = true;
  251. m_segment_drag_info = { findHotEnvelopeSegment(ev.x, ev.y, true),false };
  252. if (m_segment_drag_info.first >= 0)
  253. {
  254. m_envelope->beginRelativeTransformation();
  255. return;
  256. }
  257. if (m_node_to_drag >= 0 && ev.mods.isAltDown() == true)
  258. {
  259. if (m_envelope->GetNumNodes() < 2)
  260. {
  261. m_bubble.showAt({ ev.x,ev.y, 0,0 }, AttributedString("Can't remove last node"), 3000, false, false);
  262. return;
  263. }
  264. m_cs->enter();
  265. m_envelope->DeleteNode(m_node_to_drag);
  266. m_cs->exit();
  267. m_node_to_drag = -1;
  268. OnEnvelopeEdited(m_envelope.get());
  269. repaint();
  270. return;
  271. }
  272. if (m_node_to_drag >= 0 && ev.mods.isShiftDown()==true)
  273. {
  274. int oldstatus = m_envelope->GetNodeAtIndex(m_node_to_drag).Status;
  275. if (oldstatus==0)
  276. m_envelope->GetNodeAtIndex(m_node_to_drag).Status=1;
  277. else m_envelope->GetNodeAtIndex(m_node_to_drag).Status=0;
  278. repaint();
  279. return;
  280. }
  281. if (m_node_to_drag == -1)
  282. {
  283. double normx = jmap((double)ev.x, 0.0, (double)getWidth(), m_view_start_time, m_view_end_time);
  284. double normy = jmap((double)getHeight() - ev.y, 0.0, (double)getHeight(), m_view_start_value, m_view_end_value);
  285. m_cs->enter();
  286. m_envelope->AddNode ({ normx,normy, 0.5});
  287. m_envelope->SortNodes();
  288. m_cs->exit();
  289. m_mouse_down = false;
  290. OnEnvelopeEdited(m_envelope.get());
  291. repaint();
  292. }
  293. }
  294. void EnvelopeComponent::mouseUp(const MouseEvent &ev)
  295. {
  296. if (ev.mods == ModifierKeys::noModifiers)
  297. m_bubble.setVisible(false);
  298. if (m_node_that_was_dragged >= 0 || m_segment_drag_info.second==true)
  299. {
  300. OnEnvelopeEdited(m_envelope.get());
  301. }
  302. m_mouse_down = false;
  303. m_node_that_was_dragged = -1;
  304. m_node_to_drag = -1;
  305. if (m_segment_drag_info.second == true)
  306. {
  307. m_segment_drag_info = { -1,false };
  308. m_envelope->endRelativeTransformation();
  309. }
  310. }
  311. bool EnvelopeComponent::keyPressed(const KeyPress & ev)
  312. {
  313. if (ev == KeyPress::deleteKey && m_envelope!=nullptr)
  314. {
  315. m_node_to_drag = -1;
  316. //m_envelope->ClearAllNodes();
  317. m_cs->enter();
  318. m_envelope->removePointsConditionally([](const envelope_node& pt) { return pt.Status == 1; });
  319. if (m_envelope->GetNumNodes()==0)
  320. m_envelope->AddNode({ 0.0,0.5 });
  321. m_cs->exit();
  322. repaint();
  323. OnEnvelopeEdited(m_envelope.get());
  324. return true;
  325. }
  326. return false;
  327. }
  328. int EnvelopeComponent::find_hot_envelope_point(double xcor, double ycor)
  329. {
  330. if (m_envelope == nullptr)
  331. return -1;
  332. for (int i = 0; i < m_envelope->GetNumNodes(); ++i)
  333. {
  334. const envelope_node& pt = m_envelope->GetNodeAtIndex(i);
  335. double ptxcor = jmap(pt.Time, m_view_start_time, m_view_end_time, 0.0, (double)getWidth());
  336. double ptycor = (double)getHeight() - jmap(pt.Value, m_view_start_value, m_view_end_value, 0.0, (double)getHeight());
  337. juce::Rectangle<double> target(ptxcor - 4.0, ptycor - 4.0, 8.0, 8.0);
  338. if (target.contains(xcor, ycor) == true)
  339. {
  340. return i;
  341. }
  342. }
  343. return -1;
  344. }
  345. int EnvelopeComponent::findHotEnvelopeSegment(double xcor, double ycor, bool detectsegment)
  346. {
  347. if (m_envelope == nullptr)
  348. return -1;
  349. for (int i = 0; i < m_envelope->GetNumNodes()-1; ++i)
  350. {
  351. const envelope_node& pt0 = m_envelope->GetNodeAtIndex(i);
  352. const envelope_node& pt1 = m_envelope->GetNodeAtIndex(i+1);
  353. float xcor0 = (float)jmap<double>(pt0.Time, m_view_start_time, m_view_end_time, 0.0, getWidth());
  354. float xcor1 = (float)jmap<double>(pt1.Time, m_view_start_time, m_view_end_time, 0.0, getWidth());
  355. float segwidth = xcor1 - xcor0;
  356. juce::Rectangle<float> segrect(xcor0+8.0f, 0.0f, segwidth-16.0f, (float)getHeight());
  357. if (segrect.contains((float)xcor, (float)ycor))
  358. {
  359. if (detectsegment == false)
  360. return i;
  361. else
  362. {
  363. double normx = jmap<double>(xcor, 0.0, getWidth(), m_view_start_time, m_view_end_time);
  364. double yval = m_envelope->GetInterpolatedNodeValue(normx);
  365. float ycor0 = (float)(getHeight()-jmap<double>(yval, 0.0, 1.0, 0.0, getHeight()));
  366. juce::Rectangle<float> segrect2((float)(xcor - 20), (float)(ycor - 20), 40, 40);
  367. if (segrect2.contains((float)xcor, ycor0))
  368. return i;
  369. }
  370. }
  371. }
  372. return -1;
  373. }