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.

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