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.

1557 lines
47KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 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. // tests that some co-ords aren't NaNs
  19. #define JUCE_CHECK_COORDS_ARE_VALID(x, y) \
  20. jassert (x == x && y == y);
  21. //==============================================================================
  22. namespace PathHelpers
  23. {
  24. const float ellipseAngularIncrement = 0.05f;
  25. static String nextToken (String::CharPointerType& t)
  26. {
  27. t = t.findEndOfWhitespace();
  28. String::CharPointerType start (t);
  29. size_t numChars = 0;
  30. while (! (t.isEmpty() || t.isWhitespace()))
  31. {
  32. ++t;
  33. ++numChars;
  34. }
  35. return String (start, numChars);
  36. }
  37. inline double lengthOf (float x1, float y1, float x2, float y2) noexcept
  38. {
  39. return juce_hypot ((double) (x1 - x2), (double) (y1 - y2));
  40. }
  41. }
  42. //==============================================================================
  43. const float Path::lineMarker = 100001.0f;
  44. const float Path::moveMarker = 100002.0f;
  45. const float Path::quadMarker = 100003.0f;
  46. const float Path::cubicMarker = 100004.0f;
  47. const float Path::closeSubPathMarker = 100005.0f;
  48. //==============================================================================
  49. Path::PathBounds::PathBounds() noexcept
  50. : pathXMin (0), pathXMax (0), pathYMin (0), pathYMax (0)
  51. {
  52. }
  53. Rectangle<float> Path::PathBounds::getRectangle() const noexcept
  54. {
  55. return Rectangle<float> (pathXMin, pathYMin, pathXMax - pathXMin, pathYMax - pathYMin);
  56. }
  57. void Path::PathBounds::reset() noexcept
  58. {
  59. pathXMin = pathYMin = pathYMax = pathXMax = 0;
  60. }
  61. void Path::PathBounds::reset (const float x, const float y) noexcept
  62. {
  63. pathXMin = pathXMax = x;
  64. pathYMin = pathYMax = y;
  65. }
  66. void Path::PathBounds::extend (const float x, const float y) noexcept
  67. {
  68. pathXMin = jmin (pathXMin, x);
  69. pathXMax = jmax (pathXMax, x);
  70. pathYMin = jmin (pathYMin, y);
  71. pathYMax = jmax (pathYMax, y);
  72. }
  73. void Path::PathBounds::extend (const float x1, const float y1, const float x2, const float y2) noexcept
  74. {
  75. if (x1 < x2)
  76. {
  77. pathXMin = jmin (pathXMin, x1);
  78. pathXMax = jmax (pathXMax, x2);
  79. }
  80. else
  81. {
  82. pathXMin = jmin (pathXMin, x2);
  83. pathXMax = jmax (pathXMax, x1);
  84. }
  85. if (y1 < y2)
  86. {
  87. pathYMin = jmin (pathYMin, y1);
  88. pathYMax = jmax (pathYMax, y2);
  89. }
  90. else
  91. {
  92. pathYMin = jmin (pathYMin, y2);
  93. pathYMax = jmax (pathYMax, y1);
  94. }
  95. }
  96. //==============================================================================
  97. Path::Path()
  98. : numElements (0), useNonZeroWinding (true)
  99. {
  100. }
  101. Path::~Path()
  102. {
  103. }
  104. Path::Path (const Path& other)
  105. : numElements (other.numElements),
  106. bounds (other.bounds),
  107. useNonZeroWinding (other.useNonZeroWinding)
  108. {
  109. if (numElements > 0)
  110. {
  111. data.setAllocatedSize ((int) numElements);
  112. memcpy (data.elements, other.data.elements, numElements * sizeof (float));
  113. }
  114. }
  115. Path& Path::operator= (const Path& other)
  116. {
  117. if (this != &other)
  118. {
  119. data.ensureAllocatedSize ((int) other.numElements);
  120. numElements = other.numElements;
  121. bounds = other.bounds;
  122. useNonZeroWinding = other.useNonZeroWinding;
  123. if (numElements > 0)
  124. memcpy (data.elements, other.data.elements, numElements * sizeof (float));
  125. }
  126. return *this;
  127. }
  128. #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
  129. Path::Path (Path&& other) noexcept
  130. : data (static_cast <ArrayAllocationBase <float, DummyCriticalSection>&&> (other.data)),
  131. numElements (other.numElements),
  132. bounds (other.bounds),
  133. useNonZeroWinding (other.useNonZeroWinding)
  134. {
  135. }
  136. Path& Path::operator= (Path&& other) noexcept
  137. {
  138. data = static_cast <ArrayAllocationBase <float, DummyCriticalSection>&&> (other.data);
  139. numElements = other.numElements;
  140. bounds = other.bounds;
  141. useNonZeroWinding = other.useNonZeroWinding;
  142. return *this;
  143. }
  144. #endif
  145. bool Path::operator== (const Path& other) const noexcept
  146. {
  147. return ! operator!= (other);
  148. }
  149. bool Path::operator!= (const Path& other) const noexcept
  150. {
  151. if (numElements != other.numElements || useNonZeroWinding != other.useNonZeroWinding)
  152. return true;
  153. for (size_t i = 0; i < numElements; ++i)
  154. if (data.elements[i] != other.data.elements[i])
  155. return true;
  156. return false;
  157. }
  158. void Path::clear() noexcept
  159. {
  160. numElements = 0;
  161. bounds.reset();
  162. }
  163. void Path::swapWithPath (Path& other) noexcept
  164. {
  165. data.swapWith (other.data);
  166. std::swap (numElements, other.numElements);
  167. std::swap (bounds.pathXMin, other.bounds.pathXMin);
  168. std::swap (bounds.pathXMax, other.bounds.pathXMax);
  169. std::swap (bounds.pathYMin, other.bounds.pathYMin);
  170. std::swap (bounds.pathYMax, other.bounds.pathYMax);
  171. std::swap (useNonZeroWinding, other.useNonZeroWinding);
  172. }
  173. //==============================================================================
  174. void Path::setUsingNonZeroWinding (const bool isNonZero) noexcept
  175. {
  176. useNonZeroWinding = isNonZero;
  177. }
  178. void Path::scaleToFit (const float x, const float y, const float w, const float h,
  179. const bool preserveProportions) noexcept
  180. {
  181. applyTransform (getTransformToScaleToFit (x, y, w, h, preserveProportions));
  182. }
  183. //==============================================================================
  184. bool Path::isEmpty() const noexcept
  185. {
  186. size_t i = 0;
  187. while (i < numElements)
  188. {
  189. const float type = data.elements [i++];
  190. if (type == moveMarker)
  191. {
  192. i += 2;
  193. }
  194. else if (type == lineMarker
  195. || type == quadMarker
  196. || type == cubicMarker)
  197. {
  198. return false;
  199. }
  200. }
  201. return true;
  202. }
  203. Rectangle<float> Path::getBounds() const noexcept
  204. {
  205. return bounds.getRectangle();
  206. }
  207. Rectangle<float> Path::getBoundsTransformed (const AffineTransform& transform) const noexcept
  208. {
  209. return getBounds().transformed (transform);
  210. }
  211. //==============================================================================
  212. void Path::startNewSubPath (const float x, const float y)
  213. {
  214. JUCE_CHECK_COORDS_ARE_VALID (x, y);
  215. if (numElements == 0)
  216. bounds.reset (x, y);
  217. else
  218. bounds.extend (x, y);
  219. data.ensureAllocatedSize ((int) numElements + 3);
  220. data.elements [numElements++] = moveMarker;
  221. data.elements [numElements++] = x;
  222. data.elements [numElements++] = y;
  223. }
  224. void Path::startNewSubPath (const Point<float>& start)
  225. {
  226. startNewSubPath (start.x, start.y);
  227. }
  228. void Path::lineTo (const float x, const float y)
  229. {
  230. JUCE_CHECK_COORDS_ARE_VALID (x, y);
  231. if (numElements == 0)
  232. startNewSubPath (0, 0);
  233. data.ensureAllocatedSize ((int) numElements + 3);
  234. data.elements [numElements++] = lineMarker;
  235. data.elements [numElements++] = x;
  236. data.elements [numElements++] = y;
  237. bounds.extend (x, y);
  238. }
  239. void Path::lineTo (const Point<float>& end)
  240. {
  241. lineTo (end.x, end.y);
  242. }
  243. void Path::quadraticTo (const float x1, const float y1,
  244. const float x2, const float y2)
  245. {
  246. JUCE_CHECK_COORDS_ARE_VALID (x1, y1);
  247. JUCE_CHECK_COORDS_ARE_VALID (x2, y2);
  248. if (numElements == 0)
  249. startNewSubPath (0, 0);
  250. data.ensureAllocatedSize ((int) numElements + 5);
  251. data.elements [numElements++] = quadMarker;
  252. data.elements [numElements++] = x1;
  253. data.elements [numElements++] = y1;
  254. data.elements [numElements++] = x2;
  255. data.elements [numElements++] = y2;
  256. bounds.extend (x1, y1, x2, y2);
  257. }
  258. void Path::quadraticTo (const Point<float>& controlPoint,
  259. const Point<float>& endPoint)
  260. {
  261. quadraticTo (controlPoint.x, controlPoint.y,
  262. endPoint.x, endPoint.y);
  263. }
  264. void Path::cubicTo (const float x1, const float y1,
  265. const float x2, const float y2,
  266. const float x3, const float y3)
  267. {
  268. JUCE_CHECK_COORDS_ARE_VALID (x1, y1);
  269. JUCE_CHECK_COORDS_ARE_VALID (x2, y2);
  270. JUCE_CHECK_COORDS_ARE_VALID (x3, y3);
  271. if (numElements == 0)
  272. startNewSubPath (0, 0);
  273. data.ensureAllocatedSize ((int) numElements + 7);
  274. data.elements [numElements++] = cubicMarker;
  275. data.elements [numElements++] = x1;
  276. data.elements [numElements++] = y1;
  277. data.elements [numElements++] = x2;
  278. data.elements [numElements++] = y2;
  279. data.elements [numElements++] = x3;
  280. data.elements [numElements++] = y3;
  281. bounds.extend (x1, y1, x2, y2);
  282. bounds.extend (x3, y3);
  283. }
  284. void Path::cubicTo (const Point<float>& controlPoint1,
  285. const Point<float>& controlPoint2,
  286. const Point<float>& endPoint)
  287. {
  288. cubicTo (controlPoint1.x, controlPoint1.y,
  289. controlPoint2.x, controlPoint2.y,
  290. endPoint.x, endPoint.y);
  291. }
  292. void Path::closeSubPath()
  293. {
  294. if (numElements > 0
  295. && data.elements [numElements - 1] != closeSubPathMarker)
  296. {
  297. data.ensureAllocatedSize ((int) numElements + 1);
  298. data.elements [numElements++] = closeSubPathMarker;
  299. }
  300. }
  301. Point<float> Path::getCurrentPosition() const
  302. {
  303. int i = (int) numElements - 1;
  304. if (i > 0 && data.elements[i] == closeSubPathMarker)
  305. {
  306. while (i >= 0)
  307. {
  308. if (data.elements[i] == moveMarker)
  309. {
  310. i += 2;
  311. break;
  312. }
  313. --i;
  314. }
  315. }
  316. if (i > 0)
  317. return Point<float> (data.elements [i - 1], data.elements [i]);
  318. return Point<float>();
  319. }
  320. void Path::addRectangle (const float x, const float y,
  321. const float w, const float h)
  322. {
  323. float x1 = x, y1 = y, x2 = x + w, y2 = y + h;
  324. if (w < 0) std::swap (x1, x2);
  325. if (h < 0) std::swap (y1, y2);
  326. data.ensureAllocatedSize ((int) numElements + 13);
  327. if (numElements == 0)
  328. {
  329. bounds.pathXMin = x1;
  330. bounds.pathXMax = x2;
  331. bounds.pathYMin = y1;
  332. bounds.pathYMax = y2;
  333. }
  334. else
  335. {
  336. bounds.pathXMin = jmin (bounds.pathXMin, x1);
  337. bounds.pathXMax = jmax (bounds.pathXMax, x2);
  338. bounds.pathYMin = jmin (bounds.pathYMin, y1);
  339. bounds.pathYMax = jmax (bounds.pathYMax, y2);
  340. }
  341. data.elements [numElements++] = moveMarker;
  342. data.elements [numElements++] = x1;
  343. data.elements [numElements++] = y2;
  344. data.elements [numElements++] = lineMarker;
  345. data.elements [numElements++] = x1;
  346. data.elements [numElements++] = y1;
  347. data.elements [numElements++] = lineMarker;
  348. data.elements [numElements++] = x2;
  349. data.elements [numElements++] = y1;
  350. data.elements [numElements++] = lineMarker;
  351. data.elements [numElements++] = x2;
  352. data.elements [numElements++] = y2;
  353. data.elements [numElements++] = closeSubPathMarker;
  354. }
  355. void Path::addRoundedRectangle (const float x, const float y,
  356. const float w, const float h,
  357. float csx,
  358. float csy)
  359. {
  360. csx = jmin (csx, w * 0.5f);
  361. csy = jmin (csy, h * 0.5f);
  362. const float cs45x = csx * 0.45f;
  363. const float cs45y = csy * 0.45f;
  364. const float x2 = x + w;
  365. const float y2 = y + h;
  366. startNewSubPath (x + csx, y);
  367. lineTo (x2 - csx, y);
  368. cubicTo (x2 - cs45x, y, x2, y + cs45y, x2, y + csy);
  369. lineTo (x2, y2 - csy);
  370. cubicTo (x2, y2 - cs45y, x2 - cs45x, y2, x2 - csx, y2);
  371. lineTo (x + csx, y2);
  372. cubicTo (x + cs45x, y2, x, y2 - cs45y, x, y2 - csy);
  373. lineTo (x, y + csy);
  374. cubicTo (x, y + cs45y, x + cs45x, y, x + csx, y);
  375. closeSubPath();
  376. }
  377. void Path::addRoundedRectangle (const float x, const float y,
  378. const float w, const float h,
  379. float cs)
  380. {
  381. addRoundedRectangle (x, y, w, h, cs, cs);
  382. }
  383. void Path::addTriangle (const float x1, const float y1,
  384. const float x2, const float y2,
  385. const float x3, const float y3)
  386. {
  387. startNewSubPath (x1, y1);
  388. lineTo (x2, y2);
  389. lineTo (x3, y3);
  390. closeSubPath();
  391. }
  392. void Path::addQuadrilateral (const float x1, const float y1,
  393. const float x2, const float y2,
  394. const float x3, const float y3,
  395. const float x4, const float y4)
  396. {
  397. startNewSubPath (x1, y1);
  398. lineTo (x2, y2);
  399. lineTo (x3, y3);
  400. lineTo (x4, y4);
  401. closeSubPath();
  402. }
  403. void Path::addEllipse (const float x, const float y,
  404. const float w, const float h)
  405. {
  406. const float hw = w * 0.5f;
  407. const float hw55 = hw * 0.55f;
  408. const float hh = h * 0.5f;
  409. const float hh55 = hh * 0.55f;
  410. const float cx = x + hw;
  411. const float cy = y + hh;
  412. startNewSubPath (cx, cy - hh);
  413. cubicTo (cx + hw55, cy - hh, cx + hw, cy - hh55, cx + hw, cy);
  414. cubicTo (cx + hw, cy + hh55, cx + hw55, cy + hh, cx, cy + hh);
  415. cubicTo (cx - hw55, cy + hh, cx - hw, cy + hh55, cx - hw, cy);
  416. cubicTo (cx - hw, cy - hh55, cx - hw55, cy - hh, cx, cy - hh);
  417. closeSubPath();
  418. }
  419. void Path::addArc (const float x, const float y,
  420. const float w, const float h,
  421. const float fromRadians,
  422. const float toRadians,
  423. const bool startAsNewSubPath)
  424. {
  425. const float radiusX = w / 2.0f;
  426. const float radiusY = h / 2.0f;
  427. addCentredArc (x + radiusX,
  428. y + radiusY,
  429. radiusX, radiusY,
  430. 0.0f,
  431. fromRadians, toRadians,
  432. startAsNewSubPath);
  433. }
  434. void Path::addCentredArc (const float centreX, const float centreY,
  435. const float radiusX, const float radiusY,
  436. const float rotationOfEllipse,
  437. const float fromRadians,
  438. float toRadians,
  439. const bool startAsNewSubPath)
  440. {
  441. if (radiusX > 0.0f && radiusY > 0.0f)
  442. {
  443. const Point<float> centre (centreX, centreY);
  444. const AffineTransform rotation (AffineTransform::rotation (rotationOfEllipse, centreX, centreY));
  445. float angle = fromRadians;
  446. if (startAsNewSubPath)
  447. startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
  448. if (fromRadians < toRadians)
  449. {
  450. if (startAsNewSubPath)
  451. angle += PathHelpers::ellipseAngularIncrement;
  452. while (angle < toRadians)
  453. {
  454. lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
  455. angle += PathHelpers::ellipseAngularIncrement;
  456. }
  457. }
  458. else
  459. {
  460. if (startAsNewSubPath)
  461. angle -= PathHelpers::ellipseAngularIncrement;
  462. while (angle > toRadians)
  463. {
  464. lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
  465. angle -= PathHelpers::ellipseAngularIncrement;
  466. }
  467. }
  468. lineTo (centre.getPointOnCircumference (radiusX, radiusY, toRadians).transformedBy (rotation));
  469. }
  470. }
  471. void Path::addPieSegment (const float x, const float y,
  472. const float width, const float height,
  473. const float fromRadians,
  474. const float toRadians,
  475. const float innerCircleProportionalSize)
  476. {
  477. float radiusX = width * 0.5f;
  478. float radiusY = height * 0.5f;
  479. const Point<float> centre (x + radiusX, y + radiusY);
  480. startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, fromRadians));
  481. addArc (x, y, width, height, fromRadians, toRadians);
  482. if (std::abs (fromRadians - toRadians) > float_Pi * 1.999f)
  483. {
  484. closeSubPath();
  485. if (innerCircleProportionalSize > 0)
  486. {
  487. radiusX *= innerCircleProportionalSize;
  488. radiusY *= innerCircleProportionalSize;
  489. startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, toRadians));
  490. addArc (centre.x - radiusX, centre.y - radiusY, radiusX * 2.0f, radiusY * 2.0f, toRadians, fromRadians);
  491. }
  492. }
  493. else
  494. {
  495. if (innerCircleProportionalSize > 0)
  496. {
  497. radiusX *= innerCircleProportionalSize;
  498. radiusY *= innerCircleProportionalSize;
  499. addArc (centre.x - radiusX, centre.y - radiusY, radiusX * 2.0f, radiusY * 2.0f, toRadians, fromRadians);
  500. }
  501. else
  502. {
  503. lineTo (centre);
  504. }
  505. }
  506. closeSubPath();
  507. }
  508. //==============================================================================
  509. void Path::addLineSegment (const Line<float>& line, float lineThickness)
  510. {
  511. const Line<float> reversed (line.reversed());
  512. lineThickness *= 0.5f;
  513. startNewSubPath (line.getPointAlongLine (0, lineThickness));
  514. lineTo (line.getPointAlongLine (0, -lineThickness));
  515. lineTo (reversed.getPointAlongLine (0, lineThickness));
  516. lineTo (reversed.getPointAlongLine (0, -lineThickness));
  517. closeSubPath();
  518. }
  519. void Path::addArrow (const Line<float>& line, float lineThickness,
  520. float arrowheadWidth, float arrowheadLength)
  521. {
  522. const Line<float> reversed (line.reversed());
  523. lineThickness *= 0.5f;
  524. arrowheadWidth *= 0.5f;
  525. arrowheadLength = jmin (arrowheadLength, 0.8f * line.getLength());
  526. startNewSubPath (line.getPointAlongLine (0, lineThickness));
  527. lineTo (line.getPointAlongLine (0, -lineThickness));
  528. lineTo (reversed.getPointAlongLine (arrowheadLength, lineThickness));
  529. lineTo (reversed.getPointAlongLine (arrowheadLength, arrowheadWidth));
  530. lineTo (line.getEnd());
  531. lineTo (reversed.getPointAlongLine (arrowheadLength, -arrowheadWidth));
  532. lineTo (reversed.getPointAlongLine (arrowheadLength, -lineThickness));
  533. closeSubPath();
  534. }
  535. void Path::addPolygon (const Point<float>& centre, const int numberOfSides,
  536. const float radius, const float startAngle)
  537. {
  538. jassert (numberOfSides > 1); // this would be silly.
  539. if (numberOfSides > 1)
  540. {
  541. const float angleBetweenPoints = float_Pi * 2.0f / numberOfSides;
  542. for (int i = 0; i < numberOfSides; ++i)
  543. {
  544. const float angle = startAngle + i * angleBetweenPoints;
  545. const Point<float> p (centre.getPointOnCircumference (radius, angle));
  546. if (i == 0)
  547. startNewSubPath (p);
  548. else
  549. lineTo (p);
  550. }
  551. closeSubPath();
  552. }
  553. }
  554. void Path::addStar (const Point<float>& centre, const int numberOfPoints,
  555. const float innerRadius, const float outerRadius, const float startAngle)
  556. {
  557. jassert (numberOfPoints > 1); // this would be silly.
  558. if (numberOfPoints > 1)
  559. {
  560. const float angleBetweenPoints = float_Pi * 2.0f / numberOfPoints;
  561. for (int i = 0; i < numberOfPoints; ++i)
  562. {
  563. const float angle = startAngle + i * angleBetweenPoints;
  564. const Point<float> p (centre.getPointOnCircumference (outerRadius, angle));
  565. if (i == 0)
  566. startNewSubPath (p);
  567. else
  568. lineTo (p);
  569. lineTo (centre.getPointOnCircumference (innerRadius, angle + angleBetweenPoints * 0.5f));
  570. }
  571. closeSubPath();
  572. }
  573. }
  574. void Path::addBubble (const Rectangle<float>& bodyArea,
  575. const Rectangle<float>& maximumArea,
  576. const Point<float>& arrowTip,
  577. const float cornerSize,
  578. const float arrowBaseWidth)
  579. {
  580. const float cornerSize2 = 2.0f * cornerSize;
  581. startNewSubPath (bodyArea.getX() + cornerSize, bodyArea.getY());
  582. const float targetLimitX = bodyArea.getX() + cornerSize + arrowBaseWidth;
  583. const float targetLimitW = bodyArea.getWidth() - cornerSize2 - arrowBaseWidth * 2.0f;
  584. const float targetLimitY = bodyArea.getY() + cornerSize + arrowBaseWidth;
  585. const float targetLimitH = bodyArea.getHeight() - cornerSize2 - arrowBaseWidth * 2.0f;
  586. if (Rectangle<float> (targetLimitX, maximumArea.getY(),
  587. targetLimitW, bodyArea.getY() - maximumArea.getY()).contains (arrowTip))
  588. {
  589. lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getY());
  590. lineTo (arrowTip.x, arrowTip.y);
  591. lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getY());
  592. }
  593. lineTo (bodyArea.getRight() - cornerSize, bodyArea.getY());
  594. addArc (bodyArea.getRight() - cornerSize2, bodyArea.getY(), cornerSize2, cornerSize2, 0, float_Pi * 0.5f);
  595. if (Rectangle<float> (bodyArea.getRight(), targetLimitY,
  596. maximumArea.getRight() - bodyArea.getRight(), targetLimitH).contains (arrowTip))
  597. {
  598. lineTo (bodyArea.getRight(), arrowTip.y - arrowBaseWidth);
  599. lineTo (arrowTip.x, arrowTip.y);
  600. lineTo (bodyArea.getRight(), arrowTip.y + arrowBaseWidth);
  601. }
  602. lineTo (bodyArea.getRight(), bodyArea.getBottom() - cornerSize);
  603. addArc (bodyArea.getRight() - cornerSize2, bodyArea.getBottom() - cornerSize2, cornerSize2, cornerSize2, float_Pi * 0.5f, float_Pi);
  604. if (Rectangle<float> (targetLimitX, bodyArea.getBottom(),
  605. targetLimitW, maximumArea.getBottom() - bodyArea.getBottom()).contains (arrowTip))
  606. {
  607. lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getBottom());
  608. lineTo (arrowTip.x, arrowTip.y);
  609. lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getBottom());
  610. }
  611. lineTo (bodyArea.getX() + cornerSize, bodyArea.getBottom());
  612. addArc (bodyArea.getX(), bodyArea.getBottom() - cornerSize2, cornerSize2, cornerSize2, float_Pi, float_Pi * 1.5f);
  613. if (Rectangle<float> (maximumArea.getX(), targetLimitY, bodyArea.getX() - maximumArea.getX(), targetLimitH).contains (arrowTip))
  614. {
  615. lineTo (bodyArea.getX(), arrowTip.y + arrowBaseWidth);
  616. lineTo (arrowTip.x, arrowTip.y);
  617. lineTo (bodyArea.getX(), arrowTip.y - arrowBaseWidth);
  618. }
  619. lineTo (bodyArea.getX(), bodyArea.getY() + cornerSize);
  620. addArc (bodyArea.getX(), bodyArea.getY(), cornerSize2, cornerSize2, float_Pi * 1.5f, float_Pi * 2.0f - 0.05f);
  621. closeSubPath();
  622. }
  623. void Path::addPath (const Path& other)
  624. {
  625. size_t i = 0;
  626. const float* const d = other.data.elements;
  627. while (i < other.numElements)
  628. {
  629. const float type = d[i++];
  630. if (type == moveMarker)
  631. {
  632. startNewSubPath (d[i], d[i + 1]);
  633. i += 2;
  634. }
  635. else if (type == lineMarker)
  636. {
  637. lineTo (d[i], d[i + 1]);
  638. i += 2;
  639. }
  640. else if (type == quadMarker)
  641. {
  642. quadraticTo (d[i], d[i + 1], d[i + 2], d[i + 3]);
  643. i += 4;
  644. }
  645. else if (type == cubicMarker)
  646. {
  647. cubicTo (d[i], d[i + 1], d[i + 2], d[i + 3], d[i + 4], d[i + 5]);
  648. i += 6;
  649. }
  650. else if (type == closeSubPathMarker)
  651. {
  652. closeSubPath();
  653. }
  654. else
  655. {
  656. // something's gone wrong with the element list!
  657. jassertfalse;
  658. }
  659. }
  660. }
  661. void Path::addPath (const Path& other,
  662. const AffineTransform& transformToApply)
  663. {
  664. size_t i = 0;
  665. const float* const d = other.data.elements;
  666. while (i < other.numElements)
  667. {
  668. const float type = d [i++];
  669. if (type == closeSubPathMarker)
  670. {
  671. closeSubPath();
  672. }
  673. else
  674. {
  675. float x = d[i++];
  676. float y = d[i++];
  677. transformToApply.transformPoint (x, y);
  678. if (type == moveMarker)
  679. {
  680. startNewSubPath (x, y);
  681. }
  682. else if (type == lineMarker)
  683. {
  684. lineTo (x, y);
  685. }
  686. else if (type == quadMarker)
  687. {
  688. float x2 = d [i++];
  689. float y2 = d [i++];
  690. transformToApply.transformPoint (x2, y2);
  691. quadraticTo (x, y, x2, y2);
  692. }
  693. else if (type == cubicMarker)
  694. {
  695. float x2 = d [i++];
  696. float y2 = d [i++];
  697. float x3 = d [i++];
  698. float y3 = d [i++];
  699. transformToApply.transformPoints (x2, y2, x3, y3);
  700. cubicTo (x, y, x2, y2, x3, y3);
  701. }
  702. else
  703. {
  704. // something's gone wrong with the element list!
  705. jassertfalse;
  706. }
  707. }
  708. }
  709. }
  710. //==============================================================================
  711. void Path::applyTransform (const AffineTransform& transform) noexcept
  712. {
  713. bounds.reset();
  714. bool firstPoint = true;
  715. float* d = data.elements;
  716. float* const end = d + numElements;
  717. while (d < end)
  718. {
  719. const float type = *d++;
  720. if (type == moveMarker)
  721. {
  722. transform.transformPoint (d[0], d[1]);
  723. if (firstPoint)
  724. {
  725. firstPoint = false;
  726. bounds.reset (d[0], d[1]);
  727. }
  728. else
  729. {
  730. bounds.extend (d[0], d[1]);
  731. }
  732. d += 2;
  733. }
  734. else if (type == lineMarker)
  735. {
  736. transform.transformPoint (d[0], d[1]);
  737. bounds.extend (d[0], d[1]);
  738. d += 2;
  739. }
  740. else if (type == quadMarker)
  741. {
  742. transform.transformPoints (d[0], d[1], d[2], d[3]);
  743. bounds.extend (d[0], d[1], d[2], d[3]);
  744. d += 4;
  745. }
  746. else if (type == cubicMarker)
  747. {
  748. transform.transformPoints (d[0], d[1], d[2], d[3], d[4], d[5]);
  749. bounds.extend (d[0], d[1], d[2], d[3]);
  750. bounds.extend (d[4], d[5]);
  751. d += 6;
  752. }
  753. }
  754. }
  755. //==============================================================================
  756. AffineTransform Path::getTransformToScaleToFit (const float x, const float y,
  757. const float w, const float h,
  758. const bool preserveProportions,
  759. const Justification& justification) const
  760. {
  761. Rectangle<float> bounds (getBounds());
  762. if (preserveProportions)
  763. {
  764. if (w <= 0 || h <= 0 || bounds.isEmpty())
  765. return AffineTransform::identity;
  766. float newW, newH;
  767. const float srcRatio = bounds.getHeight() / bounds.getWidth();
  768. if (srcRatio > h / w)
  769. {
  770. newW = h / srcRatio;
  771. newH = h;
  772. }
  773. else
  774. {
  775. newW = w;
  776. newH = w * srcRatio;
  777. }
  778. float newXCentre = x;
  779. float newYCentre = y;
  780. if (justification.testFlags (Justification::left)) newXCentre += newW * 0.5f;
  781. else if (justification.testFlags (Justification::right)) newXCentre += w - newW * 0.5f;
  782. else newXCentre += w * 0.5f;
  783. if (justification.testFlags (Justification::top)) newYCentre += newH * 0.5f;
  784. else if (justification.testFlags (Justification::bottom)) newYCentre += h - newH * 0.5f;
  785. else newYCentre += h * 0.5f;
  786. return AffineTransform::translation (bounds.getWidth() * -0.5f - bounds.getX(),
  787. bounds.getHeight() * -0.5f - bounds.getY())
  788. .scaled (newW / bounds.getWidth(),
  789. newH / bounds.getHeight())
  790. .translated (newXCentre, newYCentre);
  791. }
  792. else
  793. {
  794. return AffineTransform::translation (-bounds.getX(), -bounds.getY())
  795. .scaled (w / bounds.getWidth(),
  796. h / bounds.getHeight())
  797. .translated (x, y);
  798. }
  799. }
  800. //==============================================================================
  801. bool Path::contains (const float x, const float y, const float tolerance) const
  802. {
  803. if (x <= bounds.pathXMin || x >= bounds.pathXMax
  804. || y <= bounds.pathYMin || y >= bounds.pathYMax)
  805. return false;
  806. PathFlatteningIterator i (*this, AffineTransform::identity, tolerance);
  807. int positiveCrossings = 0;
  808. int negativeCrossings = 0;
  809. while (i.next())
  810. {
  811. if ((i.y1 <= y && i.y2 > y) || (i.y2 <= y && i.y1 > y))
  812. {
  813. const float intersectX = i.x1 + (i.x2 - i.x1) * (y - i.y1) / (i.y2 - i.y1);
  814. if (intersectX <= x)
  815. {
  816. if (i.y1 < i.y2)
  817. ++positiveCrossings;
  818. else
  819. ++negativeCrossings;
  820. }
  821. }
  822. }
  823. return useNonZeroWinding ? (negativeCrossings != positiveCrossings)
  824. : ((negativeCrossings + positiveCrossings) & 1) != 0;
  825. }
  826. bool Path::contains (const Point<float>& point, const float tolerance) const
  827. {
  828. return contains (point.x, point.y, tolerance);
  829. }
  830. bool Path::intersectsLine (const Line<float>& line, const float tolerance)
  831. {
  832. PathFlatteningIterator i (*this, AffineTransform::identity, tolerance);
  833. Point<float> intersection;
  834. while (i.next())
  835. if (line.intersects (Line<float> (i.x1, i.y1, i.x2, i.y2), intersection))
  836. return true;
  837. return false;
  838. }
  839. Line<float> Path::getClippedLine (const Line<float>& line, const bool keepSectionOutsidePath) const
  840. {
  841. Line<float> result (line);
  842. const bool startInside = contains (line.getStart());
  843. const bool endInside = contains (line.getEnd());
  844. if (startInside == endInside)
  845. {
  846. if (keepSectionOutsidePath == startInside)
  847. result = Line<float>();
  848. }
  849. else
  850. {
  851. PathFlatteningIterator i (*this, AffineTransform::identity);
  852. Point<float> intersection;
  853. while (i.next())
  854. {
  855. if (line.intersects (Line<float> (i.x1, i.y1, i.x2, i.y2), intersection))
  856. {
  857. if ((startInside && keepSectionOutsidePath) || (endInside && ! keepSectionOutsidePath))
  858. result.setStart (intersection);
  859. else
  860. result.setEnd (intersection);
  861. }
  862. }
  863. }
  864. return result;
  865. }
  866. float Path::getLength (const AffineTransform& transform) const
  867. {
  868. float length = 0;
  869. PathFlatteningIterator i (*this, transform);
  870. while (i.next())
  871. length += Line<float> (i.x1, i.y1, i.x2, i.y2).getLength();
  872. return length;
  873. }
  874. Point<float> Path::getPointAlongPath (float distanceFromStart, const AffineTransform& transform) const
  875. {
  876. PathFlatteningIterator i (*this, transform);
  877. while (i.next())
  878. {
  879. const Line<float> line (i.x1, i.y1, i.x2, i.y2);
  880. const float lineLength = line.getLength();
  881. if (distanceFromStart <= lineLength)
  882. return line.getPointAlongLine (distanceFromStart);
  883. distanceFromStart -= lineLength;
  884. }
  885. return Point<float> (i.x2, i.y2);
  886. }
  887. float Path::getNearestPoint (const Point<float>& targetPoint, Point<float>& pointOnPath,
  888. const AffineTransform& transform) const
  889. {
  890. PathFlatteningIterator i (*this, transform);
  891. float bestPosition = 0, bestDistance = std::numeric_limits<float>::max();
  892. float length = 0;
  893. Point<float> pointOnLine;
  894. while (i.next())
  895. {
  896. const Line<float> line (i.x1, i.y1, i.x2, i.y2);
  897. const float distance = line.getDistanceFromPoint (targetPoint, pointOnLine);
  898. if (distance < bestDistance)
  899. {
  900. bestDistance = distance;
  901. bestPosition = length + pointOnLine.getDistanceFrom (line.getStart());
  902. pointOnPath = pointOnLine;
  903. }
  904. length += line.getLength();
  905. }
  906. return bestPosition;
  907. }
  908. //==============================================================================
  909. Path Path::createPathWithRoundedCorners (const float cornerRadius) const
  910. {
  911. if (cornerRadius <= 0.01f)
  912. return *this;
  913. size_t indexOfPathStart = 0, indexOfPathStartThis = 0;
  914. size_t n = 0;
  915. bool lastWasLine = false, firstWasLine = false;
  916. Path p;
  917. while (n < numElements)
  918. {
  919. const float type = data.elements [n++];
  920. if (type == moveMarker)
  921. {
  922. indexOfPathStart = p.numElements;
  923. indexOfPathStartThis = n - 1;
  924. const float x = data.elements [n++];
  925. const float y = data.elements [n++];
  926. p.startNewSubPath (x, y);
  927. lastWasLine = false;
  928. firstWasLine = (data.elements [n] == lineMarker);
  929. }
  930. else if (type == lineMarker || type == closeSubPathMarker)
  931. {
  932. float startX = 0, startY = 0, joinX = 0, joinY = 0, endX, endY;
  933. if (type == lineMarker)
  934. {
  935. endX = data.elements [n++];
  936. endY = data.elements [n++];
  937. if (n > 8)
  938. {
  939. startX = data.elements [n - 8];
  940. startY = data.elements [n - 7];
  941. joinX = data.elements [n - 5];
  942. joinY = data.elements [n - 4];
  943. }
  944. }
  945. else
  946. {
  947. endX = data.elements [indexOfPathStartThis + 1];
  948. endY = data.elements [indexOfPathStartThis + 2];
  949. if (n > 6)
  950. {
  951. startX = data.elements [n - 6];
  952. startY = data.elements [n - 5];
  953. joinX = data.elements [n - 3];
  954. joinY = data.elements [n - 2];
  955. }
  956. }
  957. if (lastWasLine)
  958. {
  959. const double len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY);
  960. if (len1 > 0)
  961. {
  962. const double propNeeded = jmin (0.5, cornerRadius / len1);
  963. p.data.elements [p.numElements - 2] = (float) (joinX - (joinX - startX) * propNeeded);
  964. p.data.elements [p.numElements - 1] = (float) (joinY - (joinY - startY) * propNeeded);
  965. }
  966. const double len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY);
  967. if (len2 > 0)
  968. {
  969. const double propNeeded = jmin (0.5, cornerRadius / len2);
  970. p.quadraticTo (joinX, joinY,
  971. (float) (joinX + (endX - joinX) * propNeeded),
  972. (float) (joinY + (endY - joinY) * propNeeded));
  973. }
  974. p.lineTo (endX, endY);
  975. }
  976. else if (type == lineMarker)
  977. {
  978. p.lineTo (endX, endY);
  979. lastWasLine = true;
  980. }
  981. if (type == closeSubPathMarker)
  982. {
  983. if (firstWasLine)
  984. {
  985. startX = data.elements [n - 3];
  986. startY = data.elements [n - 2];
  987. joinX = endX;
  988. joinY = endY;
  989. endX = data.elements [indexOfPathStartThis + 4];
  990. endY = data.elements [indexOfPathStartThis + 5];
  991. const double len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY);
  992. if (len1 > 0)
  993. {
  994. const double propNeeded = jmin (0.5, cornerRadius / len1);
  995. p.data.elements [p.numElements - 2] = (float) (joinX - (joinX - startX) * propNeeded);
  996. p.data.elements [p.numElements - 1] = (float) (joinY - (joinY - startY) * propNeeded);
  997. }
  998. const double len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY);
  999. if (len2 > 0)
  1000. {
  1001. const double propNeeded = jmin (0.5, cornerRadius / len2);
  1002. endX = (float) (joinX + (endX - joinX) * propNeeded);
  1003. endY = (float) (joinY + (endY - joinY) * propNeeded);
  1004. p.quadraticTo (joinX, joinY, endX, endY);
  1005. p.data.elements [indexOfPathStart + 1] = endX;
  1006. p.data.elements [indexOfPathStart + 2] = endY;
  1007. }
  1008. }
  1009. p.closeSubPath();
  1010. }
  1011. }
  1012. else if (type == quadMarker)
  1013. {
  1014. lastWasLine = false;
  1015. const float x1 = data.elements [n++];
  1016. const float y1 = data.elements [n++];
  1017. const float x2 = data.elements [n++];
  1018. const float y2 = data.elements [n++];
  1019. p.quadraticTo (x1, y1, x2, y2);
  1020. }
  1021. else if (type == cubicMarker)
  1022. {
  1023. lastWasLine = false;
  1024. const float x1 = data.elements [n++];
  1025. const float y1 = data.elements [n++];
  1026. const float x2 = data.elements [n++];
  1027. const float y2 = data.elements [n++];
  1028. const float x3 = data.elements [n++];
  1029. const float y3 = data.elements [n++];
  1030. p.cubicTo (x1, y1, x2, y2, x3, y3);
  1031. }
  1032. }
  1033. return p;
  1034. }
  1035. //==============================================================================
  1036. void Path::loadPathFromStream (InputStream& source)
  1037. {
  1038. while (! source.isExhausted())
  1039. {
  1040. switch (source.readByte())
  1041. {
  1042. case 'm':
  1043. {
  1044. const float x = source.readFloat();
  1045. const float y = source.readFloat();
  1046. startNewSubPath (x, y);
  1047. break;
  1048. }
  1049. case 'l':
  1050. {
  1051. const float x = source.readFloat();
  1052. const float y = source.readFloat();
  1053. lineTo (x, y);
  1054. break;
  1055. }
  1056. case 'q':
  1057. {
  1058. const float x1 = source.readFloat();
  1059. const float y1 = source.readFloat();
  1060. const float x2 = source.readFloat();
  1061. const float y2 = source.readFloat();
  1062. quadraticTo (x1, y1, x2, y2);
  1063. break;
  1064. }
  1065. case 'b':
  1066. {
  1067. const float x1 = source.readFloat();
  1068. const float y1 = source.readFloat();
  1069. const float x2 = source.readFloat();
  1070. const float y2 = source.readFloat();
  1071. const float x3 = source.readFloat();
  1072. const float y3 = source.readFloat();
  1073. cubicTo (x1, y1, x2, y2, x3, y3);
  1074. break;
  1075. }
  1076. case 'c':
  1077. closeSubPath();
  1078. break;
  1079. case 'n':
  1080. useNonZeroWinding = true;
  1081. break;
  1082. case 'z':
  1083. useNonZeroWinding = false;
  1084. break;
  1085. case 'e':
  1086. return; // end of path marker
  1087. default:
  1088. jassertfalse; // illegal char in the stream
  1089. break;
  1090. }
  1091. }
  1092. }
  1093. void Path::loadPathFromData (const void* const pathData, const size_t numberOfBytes)
  1094. {
  1095. MemoryInputStream in (pathData, numberOfBytes, false);
  1096. loadPathFromStream (in);
  1097. }
  1098. void Path::writePathToStream (OutputStream& dest) const
  1099. {
  1100. dest.writeByte (useNonZeroWinding ? 'n' : 'z');
  1101. size_t i = 0;
  1102. while (i < numElements)
  1103. {
  1104. const float type = data.elements [i++];
  1105. if (type == moveMarker)
  1106. {
  1107. dest.writeByte ('m');
  1108. dest.writeFloat (data.elements [i++]);
  1109. dest.writeFloat (data.elements [i++]);
  1110. }
  1111. else if (type == lineMarker)
  1112. {
  1113. dest.writeByte ('l');
  1114. dest.writeFloat (data.elements [i++]);
  1115. dest.writeFloat (data.elements [i++]);
  1116. }
  1117. else if (type == quadMarker)
  1118. {
  1119. dest.writeByte ('q');
  1120. dest.writeFloat (data.elements [i++]);
  1121. dest.writeFloat (data.elements [i++]);
  1122. dest.writeFloat (data.elements [i++]);
  1123. dest.writeFloat (data.elements [i++]);
  1124. }
  1125. else if (type == cubicMarker)
  1126. {
  1127. dest.writeByte ('b');
  1128. dest.writeFloat (data.elements [i++]);
  1129. dest.writeFloat (data.elements [i++]);
  1130. dest.writeFloat (data.elements [i++]);
  1131. dest.writeFloat (data.elements [i++]);
  1132. dest.writeFloat (data.elements [i++]);
  1133. dest.writeFloat (data.elements [i++]);
  1134. }
  1135. else if (type == closeSubPathMarker)
  1136. {
  1137. dest.writeByte ('c');
  1138. }
  1139. }
  1140. dest.writeByte ('e'); // marks the end-of-path
  1141. }
  1142. String Path::toString() const
  1143. {
  1144. MemoryOutputStream s (2048);
  1145. if (! useNonZeroWinding)
  1146. s << 'a';
  1147. size_t i = 0;
  1148. float lastMarker = 0.0f;
  1149. while (i < numElements)
  1150. {
  1151. const float marker = data.elements [i++];
  1152. char markerChar = 0;
  1153. int numCoords = 0;
  1154. if (marker == moveMarker)
  1155. {
  1156. markerChar = 'm';
  1157. numCoords = 2;
  1158. }
  1159. else if (marker == lineMarker)
  1160. {
  1161. markerChar = 'l';
  1162. numCoords = 2;
  1163. }
  1164. else if (marker == quadMarker)
  1165. {
  1166. markerChar = 'q';
  1167. numCoords = 4;
  1168. }
  1169. else if (marker == cubicMarker)
  1170. {
  1171. markerChar = 'c';
  1172. numCoords = 6;
  1173. }
  1174. else
  1175. {
  1176. jassert (marker == closeSubPathMarker);
  1177. markerChar = 'z';
  1178. }
  1179. if (marker != lastMarker)
  1180. {
  1181. if (s.getDataSize() != 0)
  1182. s << ' ';
  1183. s << markerChar;
  1184. lastMarker = marker;
  1185. }
  1186. while (--numCoords >= 0 && i < numElements)
  1187. {
  1188. String coord (data.elements [i++], 3);
  1189. while (coord.endsWithChar ('0') && coord != "0")
  1190. coord = coord.dropLastCharacters (1);
  1191. if (coord.endsWithChar ('.'))
  1192. coord = coord.dropLastCharacters (1);
  1193. if (s.getDataSize() != 0)
  1194. s << ' ';
  1195. s << coord;
  1196. }
  1197. }
  1198. return s.toUTF8();
  1199. }
  1200. void Path::restoreFromString (const String& stringVersion)
  1201. {
  1202. clear();
  1203. setUsingNonZeroWinding (true);
  1204. String::CharPointerType t (stringVersion.getCharPointer());
  1205. juce_wchar marker = 'm';
  1206. int numValues = 2;
  1207. float values [6];
  1208. for (;;)
  1209. {
  1210. const String token (PathHelpers::nextToken (t));
  1211. const juce_wchar firstChar = token[0];
  1212. int startNum = 0;
  1213. if (firstChar == 0)
  1214. break;
  1215. if (firstChar == 'm' || firstChar == 'l')
  1216. {
  1217. marker = firstChar;
  1218. numValues = 2;
  1219. }
  1220. else if (firstChar == 'q')
  1221. {
  1222. marker = firstChar;
  1223. numValues = 4;
  1224. }
  1225. else if (firstChar == 'c')
  1226. {
  1227. marker = firstChar;
  1228. numValues = 6;
  1229. }
  1230. else if (firstChar == 'z')
  1231. {
  1232. marker = firstChar;
  1233. numValues = 0;
  1234. }
  1235. else if (firstChar == 'a')
  1236. {
  1237. setUsingNonZeroWinding (false);
  1238. continue;
  1239. }
  1240. else
  1241. {
  1242. ++startNum;
  1243. values [0] = token.getFloatValue();
  1244. }
  1245. for (int i = startNum; i < numValues; ++i)
  1246. values [i] = PathHelpers::nextToken (t).getFloatValue();
  1247. switch (marker)
  1248. {
  1249. case 'm': startNewSubPath (values[0], values[1]); break;
  1250. case 'l': lineTo (values[0], values[1]); break;
  1251. case 'q': quadraticTo (values[0], values[1], values[2], values[3]); break;
  1252. case 'c': cubicTo (values[0], values[1], values[2], values[3], values[4], values[5]); break;
  1253. case 'z': closeSubPath(); break;
  1254. default: jassertfalse; break; // illegal string format?
  1255. }
  1256. }
  1257. }
  1258. //==============================================================================
  1259. Path::Iterator::Iterator (const Path& path_)
  1260. : path (path_),
  1261. index (0)
  1262. {
  1263. }
  1264. Path::Iterator::~Iterator()
  1265. {
  1266. }
  1267. bool Path::Iterator::next()
  1268. {
  1269. const float* const elements = path.data.elements;
  1270. if (index < path.numElements)
  1271. {
  1272. const float type = elements [index++];
  1273. if (type == moveMarker)
  1274. {
  1275. elementType = startNewSubPath;
  1276. x1 = elements [index++];
  1277. y1 = elements [index++];
  1278. }
  1279. else if (type == lineMarker)
  1280. {
  1281. elementType = lineTo;
  1282. x1 = elements [index++];
  1283. y1 = elements [index++];
  1284. }
  1285. else if (type == quadMarker)
  1286. {
  1287. elementType = quadraticTo;
  1288. x1 = elements [index++];
  1289. y1 = elements [index++];
  1290. x2 = elements [index++];
  1291. y2 = elements [index++];
  1292. }
  1293. else if (type == cubicMarker)
  1294. {
  1295. elementType = cubicTo;
  1296. x1 = elements [index++];
  1297. y1 = elements [index++];
  1298. x2 = elements [index++];
  1299. y2 = elements [index++];
  1300. x3 = elements [index++];
  1301. y3 = elements [index++];
  1302. }
  1303. else if (type == closeSubPathMarker)
  1304. {
  1305. elementType = closePath;
  1306. }
  1307. return true;
  1308. }
  1309. return false;
  1310. }
  1311. #undef JUCE_CHECK_COORDS_ARE_VALID