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.

1587 lines
48KB

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