The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

498 lines
19KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. struct BlankCanvas : public AnimatedContent
  14. {
  15. String getName() const override { return "Blank Canvas"; }
  16. void reset() override {}
  17. void handleTouch (Point<float>) override {}
  18. void generateCanvas (Graphics&, SharedCanvasDescription&, Rectangle<float>) override {}
  19. };
  20. //==============================================================================
  21. struct GridLines : public AnimatedContent
  22. {
  23. String getName() const override { return "Grid Lines"; }
  24. void reset() override {}
  25. void handleTouch (Point<float>) override {}
  26. void generateCanvas (Graphics& g, SharedCanvasDescription& canvas, Rectangle<float>) override
  27. {
  28. auto limits = canvas.getLimits();
  29. float lineThickness = 0.1f;
  30. g.setColour (Colours::blue);
  31. g.drawRect (canvas.getLimits(), lineThickness);
  32. for (float y = limits.getY(); y < limits.getBottom(); y += 2.0f)
  33. g.drawLine (limits.getX(), y, limits.getRight(), y, lineThickness);
  34. for (float x = limits.getX(); x < limits.getRight(); x += 2.0f)
  35. g.drawLine (x, limits.getY(), x, limits.getBottom(), lineThickness);
  36. g.setColour (Colours::darkred);
  37. g.drawLine (limits.getX(), limits.getCentreY(), limits.getRight(), limits.getCentreY(), lineThickness);
  38. g.drawLine (limits.getCentreX(), limits.getY(), limits.getCentreX(), limits.getBottom(), lineThickness);
  39. g.setColour (Colours::lightgrey);
  40. g.drawLine (limits.getX(), limits.getY(), limits.getRight(), limits.getBottom(), lineThickness);
  41. g.drawLine (limits.getX(), limits.getBottom(), limits.getRight(), limits.getY(), lineThickness);
  42. }
  43. };
  44. //==============================================================================
  45. struct BackgroundLogo : public AnimatedContent
  46. {
  47. BackgroundLogo()
  48. {
  49. static const char logoData[] = R"blahblah(
  50. <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
  51. viewBox="0 0 239.2 239.2" enable-background="new 0 0 239.2 239.2" xml:space="preserve">
  52. <path fill="#6CC04A" d="M118.8,201.3c-44.6,0-81-36.3-81-81s36.3-81,81-81s81,36.3,81,81S163.4,201.3,118.8,201.3z M118.8,44.8c-41.7,0-75.6,33.9-75.6,75.6s33.9,75.6,75.6,75.6s75.6-33.9,75.6-75.6S160.4,44.8,118.8,44.8z"/>
  53. <path fill="#3B5CAD" d="M182.6,117.6c1.4,0,2.7-0.5,3.7-1.5c1.1-1.1,1.6-2.5,1.4-4c-1.5-12.7-6.5-24.7-14.4-34.8c-1-1.2-2.3-1.9-3.8-1.9c-1.3,0-2.6,0.5-3.6,1.5l-39,39c-0.6,0.6-0.2,1.6,0.7,1.6L182.6,117.6z"/>
  54. <path fill="#E73E51" d="M169.5,165.2L169.5,165.2c1.5,0,2.8-0.7,3.8-1.9c7.9-10.1,12.9-22.1,14.4-34.8c0.2-1.5-0.3-2.9-1.4-4c-1-1-2.3-1.5-3.7-1.5l-55,0c-0.9,0-1.3,1-0.7,1.6l39,39C166.9,164.7,168.2,165.2,169.5,165.2z"/>
  55. <path fill="#E67E3C" d="M122.9,188L122.9,188c1,1,2.5,1.5,4,1.3c12.7-1.5,24.8-6.5,34.8-14.4c1.2-0.9,1.8-2.3,1.9-3.8c0-1.4-0.6-2.7-1.6-3.7l-38.9-38.9c-0.6-0.6-1.6-0.2-1.6,0.7l0,55.2C121.4,185.8,122,187,122.9,188z"/>
  56. <path fill="#F0E049" d="M68,75.4c-1.5,0-2.8,0.7-3.8,1.9c-7.9,10.1-12.9,22.1-14.4,34.8c-0.2,1.5,0.3,2.9,1.4,4c1,1,2.3,1.5,3.7,1.5l55,0c0.9,0,1.3-1,0.7-1.6l-39-39C70.6,76,69.3,75.4,68,75.4z"/>
  57. <path fill="#D5D755" d="M114.6,52.7c-1-1-2.5-1.5-4-1.3c-12.7,1.5-24.8,6.5-34.8,14.4c-1.2,0.9-1.8,2.3-1.9,3.8c0,1.4,0.6,2.7,1.6,3.7l38.9,38.9c0.6,0.6,1.6,0.2,1.6-0.7l0-55.2C116.1,54.9,115.5,53.6,114.6,52.7z"/>
  58. <path fill="#9CB6D3" d="M163.7,69.6c0-1.5-0.7-2.8-1.9-3.8c-10.1-7.9-22.1-12.9-34.8-14.4c-1.5-0.2-2.9,0.3-4,1.4c-1,1-1.5,2.3-1.5,3.7l0,55c0,0.9,1,1.3,1.6,0.7l39-39C163.1,72.1,163.7,70.9,163.7,69.6z"/>
  59. <path fill="#F5BD47" d="M109.9,123l-55,0c-1.4,0-2.7,0.5-3.7,1.5c-1.1,1.1-1.6,2.5-1.4,4c1.5,12.7,6.5,24.7,14.4,34.8c1,1.2,2.3,1.9,3.8,1.9c1.3,0,2.6-0.5,3.5-1.5c0,0,0,0,0,0l39-39C111.2,124,110.8,123,109.9,123z"/>
  60. <path fill="#F19F53" d="M114.4,128.5l-38.9,38.9c-1,1-1.6,2.3-1.6,3.7c0,1.5,0.7,2.9,1.9,3.8c10,7.9,22.1,12.9,34.8,14.4c1.6,0.2,3-0.3,4-1.3c0.9-0.9,1.4-2.2,1.4-3.6c0,0,0,0,0,0l0-55.2C116.1,128.3,115,127.9,114.4,128.5z"/>
  61. </svg>
  62. )blahblah";
  63. logo = Drawable::createFromSVG (*parseXML (logoData));
  64. }
  65. String getName() const override { return "Background Image"; }
  66. void reset() override {}
  67. void handleTouch (Point<float>) override {}
  68. void generateCanvas (Graphics& g, SharedCanvasDescription& canvas, Rectangle<float> activeArea) override
  69. {
  70. logo->drawWithin (g, canvas.getLimits().reduced (3.0f), RectanglePlacement (RectanglePlacement::centred), 0.6f);
  71. }
  72. std::unique_ptr<Drawable> logo;
  73. };
  74. //==============================================================================
  75. struct FlockDemo : public BackgroundLogo
  76. {
  77. String getName() const override { return "Flock"; }
  78. void setNumBirds (int numBirds)
  79. {
  80. BackgroundLogo::reset();
  81. birds.clear();
  82. for (int i = numBirds; --i >= 0;)
  83. birds.add ({});
  84. centreOfGravity = {};
  85. lastGravityMove = {};
  86. fakeMouseTouchLengthToRun = 0;
  87. fakeMouseTouchPosition = {};
  88. fakeMouseTouchVelocity = {};
  89. }
  90. void reset() override
  91. {
  92. BackgroundLogo::reset();
  93. setNumBirds (100);
  94. }
  95. void generateCanvas (Graphics& g, SharedCanvasDescription& canvas, Rectangle<float> activeArea) override
  96. {
  97. BackgroundLogo::generateCanvas (g, canvas, activeArea);
  98. if (Time::getCurrentTime() > lastGravityMove + RelativeTime::seconds (0.5))
  99. {
  100. if (fakeMouseTouchLengthToRun > 0)
  101. {
  102. --fakeMouseTouchLengthToRun;
  103. fakeMouseTouchPosition += fakeMouseTouchVelocity;
  104. centreOfGravity = fakeMouseTouchPosition;
  105. }
  106. else
  107. {
  108. centreOfGravity = {};
  109. if (rng.nextInt (300) == 2 && canvas.clients.size() > 0)
  110. {
  111. fakeMouseTouchLengthToRun = 50;
  112. fakeMouseTouchPosition = canvas.clients.getReference (rng.nextInt (canvas.clients.size())).centre;
  113. fakeMouseTouchVelocity = { rng.nextFloat() * 0.3f - 0.15f,
  114. rng.nextFloat() * 0.3f - 0.15f };
  115. }
  116. }
  117. }
  118. g.setColour (Colours::white.withAlpha (0.2f));
  119. if (! centreOfGravity.isOrigin())
  120. g.fillEllipse (centreOfGravity.getX() - 1.0f, centreOfGravity.getY() - 1.0f, 2.0f, 2.0f);
  121. for (int i = 0; i < birds.size(); ++i)
  122. for (int j = i + 1; j < birds.size(); ++j)
  123. attractBirds (birds.getReference(i), birds.getReference(j));
  124. for (auto& b : birds)
  125. {
  126. if (! centreOfGravity.isOrigin())
  127. b.move (centreOfGravity, 0.4f);
  128. b.update();
  129. b.draw (g);
  130. b.bounceOffEdges (canvas.getLimits().expanded (1.0f));
  131. }
  132. for (int i = rings.size(); --i >= 0;)
  133. {
  134. if (rings.getReference(i).update())
  135. rings.getReference(i).draw (g);
  136. else
  137. rings.remove (i);
  138. }
  139. }
  140. bool isRingNear (Point<float> p) const
  141. {
  142. for (auto& r : rings)
  143. if (r.centre.getDistanceFrom (p) < 1.0f)
  144. return true;
  145. return false;
  146. }
  147. void handleTouch (Point<float> position) override
  148. {
  149. lastGravityMove = Time::getCurrentTime();
  150. centreOfGravity = position;
  151. fakeMouseTouchLengthToRun = 0;
  152. if (! isRingNear (position))
  153. rings.add ({ position, 1.0f, 0.5f });
  154. }
  155. //==============================================================================
  156. struct Bird
  157. {
  158. Bird()
  159. {
  160. Random randGen;
  161. pos.x = randGen.nextFloat() * 10.0f - 5.0f;
  162. pos.y = randGen.nextFloat() * 10.0f - 5.0f;
  163. velocity.x = randGen.nextFloat() * 0.001f;
  164. velocity.y = randGen.nextFloat() * 0.001f;
  165. colour = Colour::fromHSV (randGen.nextFloat(), 0.2f, 0.9f, randGen.nextFloat() * 0.4f + 0.2f);
  166. shape.addTriangle (0.0f, 0.0f, -0.3f, 1.0f, 0.3f, 1.0f);
  167. shape = shape.createPathWithRoundedCorners (0.2f);
  168. shape.applyTransform (AffineTransform::scale (randGen.nextFloat() + 1.0f));
  169. }
  170. Point<float> pos, velocity, acc;
  171. Colour colour;
  172. Path shape;
  173. void move (Point<float> target, float strength)
  174. {
  175. auto r = target - pos;
  176. float rSquared = jmax (0.1f, (r.x * r.x) + (r.y * r.y));
  177. if (rSquared > 1.0f)
  178. velocity += (r * strength / rSquared);
  179. acc = {};
  180. }
  181. void accelerate (Point<float> acceleration)
  182. {
  183. acc += acceleration;
  184. }
  185. void bounceOffEdges (Rectangle<float> limits)
  186. {
  187. if (pos.x < limits.getX()) { velocity.x = std::abs (velocity.x); acc = {}; }
  188. if (pos.x > limits.getRight()) { velocity.x = -std::abs (velocity.x); acc = {}; }
  189. if (pos.y < limits.getY()) { velocity.y = std::abs (velocity.y); acc = {}; }
  190. if (pos.y > limits.getBottom()) { velocity.y = -std::abs (velocity.y); acc = {}; }
  191. }
  192. void update()
  193. {
  194. velocity += acc;
  195. float length = velocity.getDistanceFromOrigin();
  196. const float maxSpeed = 0.5f;
  197. if (length > maxSpeed)
  198. velocity = getVectorWithLength (velocity, maxSpeed);
  199. pos += velocity;
  200. }
  201. void draw (Graphics& g)
  202. {
  203. g.setColour (colour);
  204. g.fillPath (shape, AffineTransform::rotation (Point<float>().getAngleToPoint (velocity)).translated (pos));
  205. }
  206. };
  207. static Point<float> getVectorWithLength (Point<float> v, float newLength)
  208. {
  209. return v * (newLength / v.getDistanceFromOrigin());
  210. }
  211. static void attractBirds (Bird& b1, Bird& b2)
  212. {
  213. auto delta = b1.pos - b2.pos;
  214. const float zoneRadius = 10.0f;
  215. const float low = 0.4f;
  216. const float high = 0.65f;
  217. const float strength = 0.01f;
  218. const float distanceSquared = (delta.x * delta.x) * (delta.y * delta.y);
  219. if (distanceSquared < zoneRadius * zoneRadius && distanceSquared > 0.01f)
  220. {
  221. float proportion = distanceSquared / (zoneRadius * zoneRadius);
  222. if (proportion < low)
  223. {
  224. const float F = (low / proportion - 1.0f) * strength * 0.003f;
  225. delta = getVectorWithLength (delta, F);
  226. b1.accelerate (delta);
  227. b2.accelerate (-delta);
  228. }
  229. else if (proportion < high)
  230. {
  231. const float regionSize = high - low;
  232. const float adjustedProportion = (proportion - low) / regionSize;
  233. const float F = (0.5f - std::cos (adjustedProportion * MathConstants<float>::twoPi) * 0.5f + 0.5f) * strength;
  234. b1.accelerate (getVectorWithLength (b2.velocity, F));
  235. b2.accelerate (getVectorWithLength (b1.velocity, F));
  236. }
  237. else
  238. {
  239. const float regionSize = 1.0f - high;
  240. const float adjustedProportion = (proportion - high) / regionSize;
  241. const float F = (0.5f - std::cos (adjustedProportion * MathConstants<float>::twoPi) * 0.5f + 0.5f) * strength;
  242. delta = getVectorWithLength (delta, F);
  243. b1.accelerate (-delta);
  244. b2.accelerate (delta);
  245. }
  246. }
  247. }
  248. Random rng;
  249. Array<Bird> birds;
  250. Point<float> centreOfGravity;
  251. Time lastGravityMove;
  252. int fakeMouseTouchLengthToRun = 0;
  253. Point<float> fakeMouseTouchPosition, fakeMouseTouchVelocity;
  254. //==============================================================================
  255. struct Ring
  256. {
  257. Point<float> centre;
  258. float diameter, opacity;
  259. bool update()
  260. {
  261. diameter += 0.7f;
  262. opacity -= 0.01f;
  263. return opacity > 0;
  264. }
  265. void draw (Graphics& g)
  266. {
  267. const float thickness = 0.2f;
  268. auto r = Rectangle<float> (diameter, diameter).withCentre (centre);
  269. Path p;
  270. p.addEllipse (r);
  271. p.addEllipse (r.reduced (thickness));
  272. p.setUsingNonZeroWinding (false);
  273. g.setColour (Colours::white.withAlpha (opacity));
  274. g.fillPath (p);
  275. }
  276. };
  277. Array<Ring> rings;
  278. };
  279. //==============================================================================
  280. struct FlockWithText : public FlockDemo
  281. {
  282. FlockWithText()
  283. {
  284. messages.add ("JUCE is our cross-platform C++ framework\n\n"
  285. "In this demo, the same C++ app is running natively on NUMDEVICES devices,\n"
  286. "which are sharing their graphic state via the network");
  287. messages.add ("No other libraries were needed to create this demo.\n"
  288. "JUCE provides thousands of classes for cross-platform GUI,\n"
  289. "audio, networking, data-structures and many other common tasks");
  290. messages.add ("As well as a code library, JUCE provides tools for managing\n"
  291. "cross-platform projects that are built with Xcode,\n"
  292. "Visual Studio, Android Studio, GCC and other compilers");
  293. messages.add ("JUCE can be used to build desktop or mobile apps, and also\n"
  294. "audio plug-ins in the VST2, VST3, AudioUnit, AAX and RTAS formats");
  295. }
  296. String getName() const override { return "Flock with text"; }
  297. void reset() override
  298. {
  299. FlockDemo::reset();
  300. currentMessage = 0;
  301. currentMessageStart = {};
  302. clientIndex = 0;
  303. }
  304. void generateCanvas (Graphics& g, SharedCanvasDescription& canvas, Rectangle<float> activeArea) override
  305. {
  306. FlockDemo::generateCanvas (g, canvas, activeArea);
  307. const float textSize = 0.5f; // inches
  308. const float textBlockWidth = 20.0f; // inches
  309. tick();
  310. Graphics::ScopedSaveState ss (g);
  311. const float scale = 20.0f; // scaled to allow the fonts to use more reasonable sizes
  312. g.addTransform (AffineTransform::scale (1.0f / scale));
  313. String text = String (messages[currentMessage]).replace ("NUMDEVICES", String (canvas.clients.size()));
  314. AttributedString as;
  315. as.append (text, Font (textSize * scale), Colour (0x80ffffff).withMultipliedAlpha (alpha));
  316. as.setJustification (Justification::centred);
  317. auto middle = canvas.clients[clientIndex % canvas.clients.size()].centre * scale;
  318. as.draw (g, Rectangle<float> (textBlockWidth * scale, textBlockWidth * scale).withCentre (middle));
  319. }
  320. void tick()
  321. {
  322. const double displayTimeSeconds = 5.0;
  323. const double fadeTimeSeconds = 1.0;
  324. Time now = Time::getCurrentTime();
  325. const double secondsSinceStart = (now - currentMessageStart).inSeconds();
  326. if (secondsSinceStart > displayTimeSeconds)
  327. {
  328. currentMessageStart = now;
  329. currentMessage = (currentMessage + 1) % messages.size();
  330. ++clientIndex;
  331. alpha = 0;
  332. }
  333. else if (secondsSinceStart > displayTimeSeconds - fadeTimeSeconds)
  334. {
  335. alpha = (float) jlimit (0.0, 1.0, (displayTimeSeconds - secondsSinceStart) / fadeTimeSeconds);
  336. }
  337. else if (secondsSinceStart < fadeTimeSeconds)
  338. {
  339. alpha = (float) jlimit (0.0, 1.0, secondsSinceStart / fadeTimeSeconds);
  340. }
  341. }
  342. StringArray messages;
  343. int currentMessage = 0, clientIndex = 0;
  344. float alpha = 0;
  345. Point<float> centre;
  346. Time currentMessageStart;
  347. };
  348. //==============================================================================
  349. struct SmallFlock : public FlockDemo
  350. {
  351. String getName() const override { return "Small Flock"; }
  352. void reset() override
  353. {
  354. setNumBirds (20);
  355. }
  356. };
  357. //==============================================================================
  358. struct BigFlock : public FlockDemo
  359. {
  360. String getName() const override { return "Big Flock"; }
  361. void reset() override
  362. {
  363. setNumBirds (200);
  364. }
  365. };
  366. //==============================================================================
  367. template <int numHorizontalLogos>
  368. struct MultiLogo : public BackgroundLogo
  369. {
  370. String getName() const override { return "Multi-Logo " + String ((int) numHorizontalLogos); }
  371. void generateCanvas (Graphics& g, SharedCanvasDescription& canvas, Rectangle<float> activeArea) override
  372. {
  373. float indent = 0.5f;
  374. float logoSize = canvas.getLimits().getWidth() / numHorizontalLogos;
  375. auto limits = canvas.getLimits();
  376. for (float x = limits.getX(); x < limits.getRight(); x += logoSize)
  377. {
  378. for (float y = limits.getY(); y < limits.getBottom(); y += logoSize)
  379. {
  380. logo->drawWithin (g, Rectangle<float> (x, y, logoSize, logoSize).reduced (indent),
  381. RectanglePlacement (RectanglePlacement::centred), 0.5f);
  382. }
  383. }
  384. }
  385. };
  386. //==============================================================================
  387. void createAllDemos (OwnedArray<AnimatedContent>& demos)
  388. {
  389. demos.add (new FlockDemo());
  390. demos.add (new FlockWithText());
  391. demos.add (new SmallFlock());
  392. demos.add (new BigFlock());
  393. demos.add (new BackgroundLogo());
  394. demos.add (new MultiLogo<5>());
  395. demos.add (new MultiLogo<10>());
  396. demos.add (new GridLines());
  397. demos.add (new BlankCanvas());
  398. }