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.

1602 lines
47KB

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