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.

856 lines
28KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../../../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_RelativeCoordinate.h"
  21. #include "../drawables/juce_DrawablePath.h"
  22. #include "../../../io/streams/juce_MemoryOutputStream.h"
  23. //==============================================================================
  24. namespace RelativeCoordinateHelpers
  25. {
  26. static bool isOrigin (const String& name)
  27. {
  28. return name.isEmpty()
  29. || name == RelativeCoordinate::Strings::parentLeft
  30. || name == RelativeCoordinate::Strings::parentTop;
  31. }
  32. static const String getOriginAnchorName (const bool isHorizontal) throw()
  33. {
  34. return isHorizontal ? RelativeCoordinate::Strings::parentLeft
  35. : RelativeCoordinate::Strings::parentTop;
  36. }
  37. static const String getExtentAnchorName (const bool isHorizontal) throw()
  38. {
  39. return isHorizontal ? RelativeCoordinate::Strings::parentRight
  40. : RelativeCoordinate::Strings::parentBottom;
  41. }
  42. static const String getObjectName (const String& fullName)
  43. {
  44. return fullName.upToFirstOccurrenceOf (".", false, false);
  45. }
  46. static const String getEdgeName (const String& fullName)
  47. {
  48. return fullName.fromFirstOccurrenceOf (".", false, false);
  49. }
  50. static const RelativeCoordinate findCoordinate (const String& name, const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
  51. {
  52. return nameFinder != 0 ? nameFinder->findNamedCoordinate (getObjectName (name), getEdgeName (name))
  53. : RelativeCoordinate();
  54. }
  55. //==============================================================================
  56. struct RecursionException : public std::runtime_error
  57. {
  58. RecursionException() : std::runtime_error ("Recursive RelativeCoordinate expression")
  59. {
  60. }
  61. };
  62. //==============================================================================
  63. static void skipWhitespace (const String& s, int& i)
  64. {
  65. while (CharacterFunctions::isWhitespace (s[i]))
  66. ++i;
  67. }
  68. static void skipComma (const String& s, int& i)
  69. {
  70. skipWhitespace (s, i);
  71. if (s[i] == ',')
  72. ++i;
  73. }
  74. static const String readAnchorName (const String& s, int& i)
  75. {
  76. skipWhitespace (s, i);
  77. if (CharacterFunctions::isLetter (s[i]) || s[i] == '_')
  78. {
  79. int start = i;
  80. while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.')
  81. ++i;
  82. return s.substring (start, i);
  83. }
  84. return String::empty;
  85. }
  86. static double readNumber (const String& s, int& i)
  87. {
  88. skipWhitespace (s, i);
  89. int start = i;
  90. if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-')
  91. ++i;
  92. while (CharacterFunctions::isDigit (s[i]) || s[i] == '.')
  93. ++i;
  94. if ((s[i] == 'e' || s[i] == 'E')
  95. && (CharacterFunctions::isDigit (s[i + 1])
  96. || s[i + 1] == '-'
  97. || s[i + 1] == '+'))
  98. {
  99. i += 2;
  100. while (CharacterFunctions::isDigit (s[i]))
  101. ++i;
  102. }
  103. const double value = s.substring (start, i).getDoubleValue();
  104. while (CharacterFunctions::isWhitespace (s[i]) || s[i] == ',')
  105. ++i;
  106. return value;
  107. }
  108. static const RelativeCoordinate readNextCoordinate (const String& s, int& i, const bool isHorizontal)
  109. {
  110. String anchor1 (readAnchorName (s, i));
  111. double value = 0;
  112. if (anchor1.isNotEmpty())
  113. {
  114. skipWhitespace (s, i);
  115. if (s[i] == '+')
  116. value = readNumber (s, ++i);
  117. else if (s[i] == '-')
  118. value = -readNumber (s, ++i);
  119. return RelativeCoordinate (value, anchor1);
  120. }
  121. else
  122. {
  123. value = readNumber (s, i);
  124. skipWhitespace (s, i);
  125. if (s[i] == '%')
  126. {
  127. value /= 100.0;
  128. skipWhitespace (s, ++i);
  129. String anchor2;
  130. if (s[i] == '*')
  131. {
  132. anchor1 = readAnchorName (s, ++i);
  133. if (anchor1.isEmpty())
  134. anchor1 = getOriginAnchorName (isHorizontal);
  135. skipWhitespace (s, i);
  136. if (s[i] == '-' && s[i + 1] == '>')
  137. {
  138. i += 2;
  139. anchor2 = readAnchorName (s, i);
  140. }
  141. else
  142. {
  143. anchor2 = anchor1;
  144. anchor1 = getOriginAnchorName (isHorizontal);
  145. }
  146. }
  147. else
  148. {
  149. anchor1 = getOriginAnchorName (isHorizontal);
  150. anchor2 = getExtentAnchorName (isHorizontal);
  151. }
  152. return RelativeCoordinate (value, anchor1, anchor2);
  153. }
  154. return RelativeCoordinate (value, isHorizontal);
  155. }
  156. }
  157. static const String limitedAccuracyString (const double n)
  158. {
  159. if (! (n < -0.001 || n > 0.001)) // to detect NaN and inf as well as for rounding
  160. return "0";
  161. return String (n, 3).trimCharactersAtEnd ("0").trimCharactersAtEnd (".");
  162. }
  163. }
  164. //==============================================================================
  165. const String RelativeCoordinate::Strings::parent ("parent");
  166. const String RelativeCoordinate::Strings::left ("left");
  167. const String RelativeCoordinate::Strings::right ("right");
  168. const String RelativeCoordinate::Strings::top ("top");
  169. const String RelativeCoordinate::Strings::bottom ("bottom");
  170. const String RelativeCoordinate::Strings::parentLeft ("parent.left");
  171. const String RelativeCoordinate::Strings::parentTop ("parent.top");
  172. const String RelativeCoordinate::Strings::parentRight ("parent.right");
  173. const String RelativeCoordinate::Strings::parentBottom ("parent.bottom");
  174. //==============================================================================
  175. RelativeCoordinate::RelativeCoordinate()
  176. : value (0)
  177. {
  178. }
  179. RelativeCoordinate::RelativeCoordinate (const double absoluteDistanceFromOrigin, const bool horizontal_)
  180. : anchor1 (RelativeCoordinateHelpers::getOriginAnchorName (horizontal_)),
  181. value (absoluteDistanceFromOrigin)
  182. {
  183. }
  184. RelativeCoordinate::RelativeCoordinate (const double absoluteDistance, const String& source)
  185. : anchor1 (source.trim()),
  186. value (absoluteDistance)
  187. {
  188. jassert (anchor1.isNotEmpty());
  189. }
  190. RelativeCoordinate::RelativeCoordinate (const double relativeProportion, const String& pos1, const String& pos2)
  191. : anchor1 (pos1.trim()),
  192. anchor2 (pos2.trim()),
  193. value (relativeProportion)
  194. {
  195. jassert (anchor1.isNotEmpty());
  196. jassert (anchor2.isNotEmpty());
  197. }
  198. RelativeCoordinate::RelativeCoordinate (const String& s, const bool isHorizontal)
  199. : value (0)
  200. {
  201. int i = 0;
  202. *this = RelativeCoordinateHelpers::readNextCoordinate (s, i, isHorizontal);
  203. }
  204. RelativeCoordinate::~RelativeCoordinate()
  205. {
  206. }
  207. bool RelativeCoordinate::operator== (const RelativeCoordinate& other) const throw()
  208. {
  209. return value == other.value && anchor1 == other.anchor1 && anchor2 == other.anchor2;
  210. }
  211. bool RelativeCoordinate::operator!= (const RelativeCoordinate& other) const throw()
  212. {
  213. return ! operator== (other);
  214. }
  215. //==============================================================================
  216. const RelativeCoordinate RelativeCoordinate::getAnchorCoordinate1() const
  217. {
  218. return RelativeCoordinate (0.0, anchor1);
  219. }
  220. const RelativeCoordinate RelativeCoordinate::getAnchorCoordinate2() const
  221. {
  222. return RelativeCoordinate (0.0, anchor2);
  223. }
  224. double RelativeCoordinate::resolveAnchor (const String& anchorName, const NamedCoordinateFinder* nameFinder, int recursionCounter)
  225. {
  226. if (RelativeCoordinateHelpers::isOrigin (anchorName))
  227. return 0.0;
  228. return RelativeCoordinateHelpers::findCoordinate (anchorName, nameFinder).resolve (nameFinder, recursionCounter + 1);
  229. }
  230. double RelativeCoordinate::resolve (const NamedCoordinateFinder* nameFinder, int recursionCounter) const
  231. {
  232. if (recursionCounter > 150)
  233. {
  234. jassertfalse
  235. throw RelativeCoordinateHelpers::RecursionException();
  236. }
  237. const double pos1 = resolveAnchor (anchor1, nameFinder, recursionCounter);
  238. return isProportional() ? pos1 + (resolveAnchor (anchor2, nameFinder, recursionCounter) - pos1) * value
  239. : pos1 + value;
  240. }
  241. double RelativeCoordinate::resolve (const NamedCoordinateFinder* nameFinder) const
  242. {
  243. try
  244. {
  245. return resolve (nameFinder, 0);
  246. }
  247. catch (RelativeCoordinateHelpers::RecursionException&)
  248. {}
  249. return 0.0;
  250. }
  251. bool RelativeCoordinate::isRecursive (const NamedCoordinateFinder* nameFinder) const
  252. {
  253. try
  254. {
  255. (void) resolve (nameFinder, 0);
  256. }
  257. catch (RelativeCoordinateHelpers::RecursionException&)
  258. {
  259. return true;
  260. }
  261. return false;
  262. }
  263. void RelativeCoordinate::moveToAbsolute (double newPos, const NamedCoordinateFinder* nameFinder)
  264. {
  265. try
  266. {
  267. const double pos1 = resolveAnchor (anchor1, nameFinder, 0);
  268. if (isProportional())
  269. {
  270. const double size = resolveAnchor (anchor2, nameFinder, 0) - pos1;
  271. if (size != 0)
  272. value = (newPos - pos1) / size;
  273. }
  274. else
  275. {
  276. value = newPos - pos1;
  277. }
  278. }
  279. catch (RelativeCoordinateHelpers::RecursionException&)
  280. {}
  281. }
  282. void RelativeCoordinate::toggleProportionality (const NamedCoordinateFinder* nameFinder, bool isHorizontal)
  283. {
  284. const double oldValue = resolve (nameFinder);
  285. anchor1 = RelativeCoordinateHelpers::getOriginAnchorName (isHorizontal);
  286. anchor2 = isProportional() ? String::empty
  287. : RelativeCoordinateHelpers::getExtentAnchorName (isHorizontal);
  288. moveToAbsolute (oldValue, nameFinder);
  289. }
  290. bool RelativeCoordinate::references (const String& coordName, const NamedCoordinateFinder* nameFinder) const
  291. {
  292. using namespace RelativeCoordinateHelpers;
  293. if (isOrigin (anchor1) && ! isProportional())
  294. return isOrigin (coordName);
  295. return anchor1 == coordName
  296. || anchor2 == coordName
  297. || findCoordinate (anchor1, nameFinder).references (coordName, nameFinder)
  298. || (isProportional() && findCoordinate (anchor2, nameFinder).references (coordName, nameFinder));
  299. }
  300. bool RelativeCoordinate::isDynamic() const
  301. {
  302. return anchor2.isNotEmpty() || ! RelativeCoordinateHelpers::isOrigin (anchor1);
  303. }
  304. //==============================================================================
  305. const String RelativeCoordinate::toString() const
  306. {
  307. using namespace RelativeCoordinateHelpers;
  308. if (isProportional())
  309. {
  310. const String percent (limitedAccuracyString (value * 100.0));
  311. if (isOrigin (anchor1))
  312. {
  313. if (anchor2 == "parent.right" || anchor2 == "parent.bottom")
  314. return percent + "%";
  315. else
  316. return percent + "% * " + anchor2;
  317. }
  318. else
  319. return percent + "% * " + anchor1 + " -> " + anchor2;
  320. }
  321. else
  322. {
  323. if (isOrigin (anchor1))
  324. return limitedAccuracyString (value);
  325. else if (value > 0)
  326. return anchor1 + " + " + limitedAccuracyString (value);
  327. else if (value < 0)
  328. return anchor1 + " - " + limitedAccuracyString (-value);
  329. else
  330. return anchor1;
  331. }
  332. }
  333. //==============================================================================
  334. const double RelativeCoordinate::getEditableNumber() const
  335. {
  336. return isProportional() ? value * 100.0 : value;
  337. }
  338. void RelativeCoordinate::setEditableNumber (const double newValue)
  339. {
  340. value = isProportional() ? newValue / 100.0 : newValue;
  341. }
  342. //==============================================================================
  343. void RelativeCoordinate::changeAnchor1 (const String& newAnchorName, const NamedCoordinateFinder* nameFinder)
  344. {
  345. jassert (newAnchorName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_."));
  346. const double oldValue = resolve (nameFinder);
  347. anchor1 = newAnchorName;
  348. moveToAbsolute (oldValue, nameFinder);
  349. }
  350. void RelativeCoordinate::changeAnchor2 (const String& newAnchorName, const NamedCoordinateFinder* nameFinder)
  351. {
  352. jassert (isProportional());
  353. jassert (newAnchorName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_."));
  354. const double oldValue = resolve (nameFinder);
  355. anchor2 = newAnchorName;
  356. moveToAbsolute (oldValue, nameFinder);
  357. }
  358. void RelativeCoordinate::renameAnchorIfUsed (const String& oldName, const String& newName, const NamedCoordinateFinder* nameFinder)
  359. {
  360. using namespace RelativeCoordinateHelpers;
  361. jassert (oldName.isNotEmpty());
  362. jassert (newName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_"));
  363. if (newName.isEmpty())
  364. {
  365. if (getObjectName (anchor1) == oldName
  366. || getObjectName (anchor2) == oldName)
  367. {
  368. value = resolve (nameFinder);
  369. anchor1 = String::empty;
  370. anchor2 = String::empty;
  371. }
  372. }
  373. else
  374. {
  375. if (getObjectName (anchor1) == oldName)
  376. anchor1 = newName + "." + getEdgeName (anchor1);
  377. if (getObjectName (anchor2) == oldName)
  378. anchor2 = newName + "." + getEdgeName (anchor2);
  379. }
  380. }
  381. //==============================================================================
  382. RelativePoint::RelativePoint()
  383. : x (0, true), y (0, false)
  384. {
  385. }
  386. RelativePoint::RelativePoint (const Point<float>& absolutePoint)
  387. : x (absolutePoint.getX(), true), y (absolutePoint.getY(), false)
  388. {
  389. }
  390. RelativePoint::RelativePoint (const float x_, const float y_)
  391. : x (x_, true), y (y_, false)
  392. {
  393. }
  394. RelativePoint::RelativePoint (const RelativeCoordinate& x_, const RelativeCoordinate& y_)
  395. : x (x_), y (y_)
  396. {
  397. }
  398. RelativePoint::RelativePoint (const String& s)
  399. {
  400. int i = 0;
  401. x = RelativeCoordinateHelpers::readNextCoordinate (s, i, true);
  402. RelativeCoordinateHelpers::skipComma (s, i);
  403. y = RelativeCoordinateHelpers::readNextCoordinate (s, i, false);
  404. }
  405. bool RelativePoint::operator== (const RelativePoint& other) const throw()
  406. {
  407. return x == other.x && y == other.y;
  408. }
  409. bool RelativePoint::operator!= (const RelativePoint& other) const throw()
  410. {
  411. return ! operator== (other);
  412. }
  413. const Point<float> RelativePoint::resolve (const RelativeCoordinate::NamedCoordinateFinder* nameFinder) const
  414. {
  415. return Point<float> ((float) x.resolve (nameFinder),
  416. (float) y.resolve (nameFinder));
  417. }
  418. void RelativePoint::moveToAbsolute (const Point<float>& newPos, const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
  419. {
  420. x.moveToAbsolute (newPos.getX(), nameFinder);
  421. y.moveToAbsolute (newPos.getY(), nameFinder);
  422. }
  423. const String RelativePoint::toString() const
  424. {
  425. return x.toString() + ", " + y.toString();
  426. }
  427. void RelativePoint::renameAnchorIfUsed (const String& oldName, const String& newName, const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
  428. {
  429. x.renameAnchorIfUsed (oldName, newName, nameFinder);
  430. y.renameAnchorIfUsed (oldName, newName, nameFinder);
  431. }
  432. bool RelativePoint::isDynamic() const
  433. {
  434. return x.isDynamic() || y.isDynamic();
  435. }
  436. //==============================================================================
  437. RelativeRectangle::RelativeRectangle()
  438. {
  439. }
  440. RelativeRectangle::RelativeRectangle (const Rectangle<float>& rect, const String& componentName)
  441. : left (rect.getX(), true),
  442. right (rect.getWidth(), componentName + "." + RelativeCoordinate::Strings::left),
  443. top (rect.getY(), false),
  444. bottom (rect.getHeight(), componentName + "." + RelativeCoordinate::Strings::top)
  445. {
  446. }
  447. RelativeRectangle::RelativeRectangle (const String& s)
  448. {
  449. int i = 0;
  450. left = RelativeCoordinateHelpers::readNextCoordinate (s, i, true);
  451. RelativeCoordinateHelpers::skipComma (s, i);
  452. top = RelativeCoordinateHelpers::readNextCoordinate (s, i, false);
  453. RelativeCoordinateHelpers::skipComma (s, i);
  454. right = RelativeCoordinateHelpers::readNextCoordinate (s, i, true);
  455. RelativeCoordinateHelpers::skipComma (s, i);
  456. bottom = RelativeCoordinateHelpers::readNextCoordinate (s, i, false);
  457. }
  458. bool RelativeRectangle::operator== (const RelativeRectangle& other) const throw()
  459. {
  460. return left == other.left && top == other.top && right == other.right && bottom == other.bottom;
  461. }
  462. bool RelativeRectangle::operator!= (const RelativeRectangle& other) const throw()
  463. {
  464. return ! operator== (other);
  465. }
  466. const Rectangle<float> RelativeRectangle::resolve (const RelativeCoordinate::NamedCoordinateFinder* nameFinder) const
  467. {
  468. const double l = left.resolve (nameFinder);
  469. const double r = right.resolve (nameFinder);
  470. const double t = top.resolve (nameFinder);
  471. const double b = bottom.resolve (nameFinder);
  472. return Rectangle<float> ((float) l, (float) t, (float) (r - l), (float) (b - t));
  473. }
  474. void RelativeRectangle::moveToAbsolute (const Rectangle<float>& newPos, const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
  475. {
  476. left.moveToAbsolute (newPos.getX(), nameFinder);
  477. right.moveToAbsolute (newPos.getRight(), nameFinder);
  478. top.moveToAbsolute (newPos.getY(), nameFinder);
  479. bottom.moveToAbsolute (newPos.getBottom(), nameFinder);
  480. }
  481. const String RelativeRectangle::toString() const
  482. {
  483. return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString();
  484. }
  485. void RelativeRectangle::renameAnchorIfUsed (const String& oldName, const String& newName,
  486. const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
  487. {
  488. left.renameAnchorIfUsed (oldName, newName, nameFinder);
  489. right.renameAnchorIfUsed (oldName, newName, nameFinder);
  490. top.renameAnchorIfUsed (oldName, newName, nameFinder);
  491. bottom.renameAnchorIfUsed (oldName, newName, nameFinder);
  492. }
  493. //==============================================================================
  494. RelativePointPath::RelativePointPath()
  495. : usesNonZeroWinding (true),
  496. containsDynamicPoints (false)
  497. {
  498. }
  499. RelativePointPath::RelativePointPath (const RelativePointPath& other)
  500. : usesNonZeroWinding (true),
  501. containsDynamicPoints (false)
  502. {
  503. ValueTree state (DrawablePath::valueTreeType);
  504. other.writeTo (state, 0);
  505. parse (state);
  506. }
  507. RelativePointPath::RelativePointPath (const ValueTree& drawable)
  508. : usesNonZeroWinding (true),
  509. containsDynamicPoints (false)
  510. {
  511. parse (drawable);
  512. }
  513. RelativePointPath::RelativePointPath (const Path& path)
  514. {
  515. usesNonZeroWinding = path.isUsingNonZeroWinding();
  516. Path::Iterator i (path);
  517. while (i.next())
  518. {
  519. switch (i.elementType)
  520. {
  521. case Path::Iterator::startNewSubPath: elements.add (new StartSubPath (RelativePoint (i.x1, i.y1))); break;
  522. case Path::Iterator::lineTo: elements.add (new LineTo (RelativePoint (i.x1, i.y1))); break;
  523. case Path::Iterator::quadraticTo: elements.add (new QuadraticTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2))); break;
  524. case Path::Iterator::cubicTo: elements.add (new CubicTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2), RelativePoint (i.x3, i.y3))); break;
  525. case Path::Iterator::closePath: elements.add (new CloseSubPath()); break;
  526. default: jassertfalse; break;
  527. }
  528. }
  529. }
  530. void RelativePointPath::writeTo (ValueTree state, UndoManager* undoManager) const
  531. {
  532. DrawablePath::ValueTreeWrapper wrapper (state);
  533. wrapper.setUsesNonZeroWinding (usesNonZeroWinding, undoManager);
  534. ValueTree pathTree (wrapper.getPathState());
  535. pathTree.removeAllChildren (undoManager);
  536. for (int i = 0; i < elements.size(); ++i)
  537. pathTree.addChild (elements.getUnchecked(i)->createTree(), -1, undoManager);
  538. }
  539. void RelativePointPath::parse (const ValueTree& state)
  540. {
  541. DrawablePath::ValueTreeWrapper wrapper (state);
  542. usesNonZeroWinding = wrapper.usesNonZeroWinding();
  543. RelativePoint points[3];
  544. const ValueTree pathTree (wrapper.getPathState());
  545. const int num = pathTree.getNumChildren();
  546. for (int i = 0; i < num; ++i)
  547. {
  548. const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i));
  549. const int numCps = e.getNumControlPoints();
  550. for (int j = 0; j < numCps; ++j)
  551. {
  552. points[j] = e.getControlPoint (j);
  553. containsDynamicPoints = containsDynamicPoints || points[j].isDynamic();
  554. }
  555. const Identifier type (e.getType());
  556. if (type == DrawablePath::ValueTreeWrapper::Element::startSubPathElement)
  557. elements.add (new StartSubPath (points[0]));
  558. else if (type == DrawablePath::ValueTreeWrapper::Element::closeSubPathElement)
  559. elements.add (new CloseSubPath());
  560. else if (type == DrawablePath::ValueTreeWrapper::Element::lineToElement)
  561. elements.add (new LineTo (points[0]));
  562. else if (type == DrawablePath::ValueTreeWrapper::Element::quadraticToElement)
  563. elements.add (new QuadraticTo (points[0], points[1]));
  564. else if (type == DrawablePath::ValueTreeWrapper::Element::cubicToElement)
  565. elements.add (new CubicTo (points[0], points[1], points[2]));
  566. else
  567. jassertfalse;
  568. }
  569. }
  570. RelativePointPath::~RelativePointPath()
  571. {
  572. }
  573. void RelativePointPath::swapWith (RelativePointPath& other) throw()
  574. {
  575. elements.swapWithArray (other.elements);
  576. swapVariables (usesNonZeroWinding, other.usesNonZeroWinding);
  577. }
  578. void RelativePointPath::createPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder)
  579. {
  580. for (int i = 0; i < elements.size(); ++i)
  581. elements.getUnchecked(i)->addToPath (path, coordFinder);
  582. }
  583. bool RelativePointPath::containsAnyDynamicPoints() const
  584. {
  585. return containsDynamicPoints;
  586. }
  587. //==============================================================================
  588. RelativePointPath::ElementBase::ElementBase (const ElementType type_) : type (type_)
  589. {
  590. }
  591. //==============================================================================
  592. RelativePointPath::StartSubPath::StartSubPath (const RelativePoint& pos)
  593. : ElementBase (startSubPathElement), startPos (pos)
  594. {
  595. }
  596. const ValueTree RelativePointPath::StartSubPath::createTree() const
  597. {
  598. ValueTree v (DrawablePath::ValueTreeWrapper::Element::startSubPathElement);
  599. v.setProperty (DrawablePath::ValueTreeWrapper::point1, startPos.toString(), 0);
  600. return v;
  601. }
  602. void RelativePointPath::StartSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const
  603. {
  604. const Point<float> p (startPos.resolve (coordFinder));
  605. path.startNewSubPath (p.getX(), p.getY());
  606. }
  607. RelativePoint* RelativePointPath::StartSubPath::getControlPoints (int& numPoints)
  608. {
  609. numPoints = 1;
  610. return &startPos;
  611. }
  612. //==============================================================================
  613. RelativePointPath::CloseSubPath::CloseSubPath()
  614. : ElementBase (closeSubPathElement)
  615. {
  616. }
  617. const ValueTree RelativePointPath::CloseSubPath::createTree() const
  618. {
  619. return ValueTree (DrawablePath::ValueTreeWrapper::Element::closeSubPathElement);
  620. }
  621. void RelativePointPath::CloseSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder*) const
  622. {
  623. path.closeSubPath();
  624. }
  625. RelativePoint* RelativePointPath::CloseSubPath::getControlPoints (int& numPoints)
  626. {
  627. numPoints = 0;
  628. return 0;
  629. }
  630. //==============================================================================
  631. RelativePointPath::LineTo::LineTo (const RelativePoint& endPoint_)
  632. : ElementBase (lineToElement), endPoint (endPoint_)
  633. {
  634. }
  635. const ValueTree RelativePointPath::LineTo::createTree() const
  636. {
  637. ValueTree v (DrawablePath::ValueTreeWrapper::Element::lineToElement);
  638. v.setProperty (DrawablePath::ValueTreeWrapper::point1, endPoint.toString(), 0);
  639. return v;
  640. }
  641. void RelativePointPath::LineTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const
  642. {
  643. const Point<float> p (endPoint.resolve (coordFinder));
  644. path.lineTo (p.getX(), p.getY());
  645. }
  646. RelativePoint* RelativePointPath::LineTo::getControlPoints (int& numPoints)
  647. {
  648. numPoints = 1;
  649. return &endPoint;
  650. }
  651. //==============================================================================
  652. RelativePointPath::QuadraticTo::QuadraticTo (const RelativePoint& controlPoint, const RelativePoint& endPoint)
  653. : ElementBase (quadraticToElement)
  654. {
  655. controlPoints[0] = controlPoint;
  656. controlPoints[1] = endPoint;
  657. }
  658. const ValueTree RelativePointPath::QuadraticTo::createTree() const
  659. {
  660. ValueTree v (DrawablePath::ValueTreeWrapper::Element::quadraticToElement);
  661. v.setProperty (DrawablePath::ValueTreeWrapper::point1, controlPoints[0].toString(), 0);
  662. v.setProperty (DrawablePath::ValueTreeWrapper::point2, controlPoints[1].toString(), 0);
  663. return v;
  664. }
  665. void RelativePointPath::QuadraticTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const
  666. {
  667. const Point<float> p1 (controlPoints[0].resolve (coordFinder));
  668. const Point<float> p2 (controlPoints[1].resolve (coordFinder));
  669. path.quadraticTo (p1.getX(), p1.getY(), p2.getX(), p2.getY());
  670. }
  671. RelativePoint* RelativePointPath::QuadraticTo::getControlPoints (int& numPoints)
  672. {
  673. numPoints = 2;
  674. return controlPoints;
  675. }
  676. //==============================================================================
  677. RelativePointPath::CubicTo::CubicTo (const RelativePoint& controlPoint1, const RelativePoint& controlPoint2, const RelativePoint& endPoint)
  678. : ElementBase (cubicToElement)
  679. {
  680. controlPoints[0] = controlPoint1;
  681. controlPoints[1] = controlPoint2;
  682. controlPoints[2] = endPoint;
  683. }
  684. const ValueTree RelativePointPath::CubicTo::createTree() const
  685. {
  686. ValueTree v (DrawablePath::ValueTreeWrapper::Element::cubicToElement);
  687. v.setProperty (DrawablePath::ValueTreeWrapper::point1, controlPoints[0].toString(), 0);
  688. v.setProperty (DrawablePath::ValueTreeWrapper::point2, controlPoints[1].toString(), 0);
  689. v.setProperty (DrawablePath::ValueTreeWrapper::point3, controlPoints[2].toString(), 0);
  690. return v;
  691. }
  692. void RelativePointPath::CubicTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const
  693. {
  694. const Point<float> p1 (controlPoints[0].resolve (coordFinder));
  695. const Point<float> p2 (controlPoints[1].resolve (coordFinder));
  696. const Point<float> p3 (controlPoints[2].resolve (coordFinder));
  697. path.cubicTo (p1.getX(), p1.getY(), p2.getX(), p2.getY(), p3.getX(), p3.getY());
  698. }
  699. RelativePoint* RelativePointPath::CubicTo::getControlPoints (int& numPoints)
  700. {
  701. numPoints = 3;
  702. return controlPoints;
  703. }
  704. END_JUCE_NAMESPACE