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.

1571 lines
46KB

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