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.

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