|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638 |
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2017 - ROLI Ltd.
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 5 End-User License
- Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
- 27th April 2017).
-
- End User License Agreement: www.juce.com/juce-5-licence
- Privacy Policy: www.juce.com/juce-5-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- // tests that some coordinates aren't NaNs
- #define JUCE_CHECK_COORDS_ARE_VALID(x, y) \
- jassert (x == x && y == y);
-
- //==============================================================================
- namespace PathHelpers
- {
- const float ellipseAngularIncrement = 0.05f;
-
- static String nextToken (String::CharPointerType& t)
- {
- t = t.findEndOfWhitespace();
-
- auto start = t;
- size_t numChars = 0;
-
- while (! (t.isEmpty() || t.isWhitespace()))
- {
- ++t;
- ++numChars;
- }
-
- return { start, numChars };
- }
-
- inline double lengthOf (float x1, float y1, float x2, float y2) noexcept
- {
- return juce_hypot ((double) (x1 - x2), (double) (y1 - y2));
- }
- }
-
- //==============================================================================
- const float Path::lineMarker = 100001.0f;
- const float Path::moveMarker = 100002.0f;
- const float Path::quadMarker = 100003.0f;
- const float Path::cubicMarker = 100004.0f;
- const float Path::closeSubPathMarker = 100005.0f;
-
- const float Path::defaultToleranceForTesting = 1.0f;
- const float Path::defaultToleranceForMeasurement = 0.6f;
-
- static inline bool isMarker (float value, float marker) noexcept
- {
- return value == marker;
- }
-
- //==============================================================================
- Path::PathBounds::PathBounds() noexcept
- {
- }
-
- Rectangle<float> Path::PathBounds::getRectangle() const noexcept
- {
- return { pathXMin, pathYMin, pathXMax - pathXMin, pathYMax - pathYMin };
- }
-
- void Path::PathBounds::reset() noexcept
- {
- pathXMin = pathYMin = pathYMax = pathXMax = 0;
- }
-
- void Path::PathBounds::reset (const float x, const float y) noexcept
- {
- pathXMin = pathXMax = x;
- pathYMin = pathYMax = y;
- }
-
- void Path::PathBounds::extend (const float x, const float y) noexcept
- {
- pathXMin = jmin (pathXMin, x);
- pathXMax = jmax (pathXMax, x);
- pathYMin = jmin (pathYMin, y);
- pathYMax = jmax (pathYMax, y);
- }
-
- void Path::PathBounds::extend (const float x1, const float y1, const float x2, const float y2) noexcept
- {
- if (x1 < x2)
- {
- pathXMin = jmin (pathXMin, x1);
- pathXMax = jmax (pathXMax, x2);
- }
- else
- {
- pathXMin = jmin (pathXMin, x2);
- pathXMax = jmax (pathXMax, x1);
- }
-
- if (y1 < y2)
- {
- pathYMin = jmin (pathYMin, y1);
- pathYMax = jmax (pathYMax, y2);
- }
- else
- {
- pathYMin = jmin (pathYMin, y2);
- pathYMax = jmax (pathYMax, y1);
- }
- }
-
- //==============================================================================
- Path::Path()
- {
- }
-
- Path::~Path()
- {
- }
-
- Path::Path (const Path& other)
- : numElements (other.numElements),
- bounds (other.bounds),
- useNonZeroWinding (other.useNonZeroWinding)
- {
- if (numElements > 0)
- {
- data.setAllocatedSize ((int) numElements);
- memcpy (data.elements, other.data.elements, numElements * sizeof (float));
- }
- }
-
- Path& Path::operator= (const Path& other)
- {
- if (this != &other)
- {
- data.ensureAllocatedSize ((int) other.numElements);
-
- numElements = other.numElements;
- bounds = other.bounds;
- useNonZeroWinding = other.useNonZeroWinding;
-
- if (numElements > 0)
- memcpy (data.elements, other.data.elements, numElements * sizeof (float));
- }
-
- return *this;
- }
-
- Path::Path (Path&& other) noexcept
- : data (static_cast<ArrayAllocationBase <float, DummyCriticalSection>&&> (other.data)),
- numElements (other.numElements),
- bounds (other.bounds),
- useNonZeroWinding (other.useNonZeroWinding)
- {
- }
-
- Path& Path::operator= (Path&& other) noexcept
- {
- data = static_cast<ArrayAllocationBase <float, DummyCriticalSection>&&> (other.data);
- numElements = other.numElements;
- bounds = other.bounds;
- useNonZeroWinding = other.useNonZeroWinding;
- return *this;
- }
-
- bool Path::operator== (const Path& other) const noexcept
- {
- return ! operator!= (other);
- }
-
- bool Path::operator!= (const Path& other) const noexcept
- {
- if (numElements != other.numElements || useNonZeroWinding != other.useNonZeroWinding)
- return true;
-
- for (size_t i = 0; i < numElements; ++i)
- if (data.elements[i] != other.data.elements[i])
- return true;
-
- return false;
- }
-
- void Path::clear() noexcept
- {
- numElements = 0;
- bounds.reset();
- }
-
- void Path::swapWithPath (Path& other) noexcept
- {
- data.swapWith (other.data);
- std::swap (numElements, other.numElements);
- std::swap (bounds.pathXMin, other.bounds.pathXMin);
- std::swap (bounds.pathXMax, other.bounds.pathXMax);
- std::swap (bounds.pathYMin, other.bounds.pathYMin);
- std::swap (bounds.pathYMax, other.bounds.pathYMax);
- std::swap (useNonZeroWinding, other.useNonZeroWinding);
- }
-
- //==============================================================================
- void Path::setUsingNonZeroWinding (const bool isNonZero) noexcept
- {
- useNonZeroWinding = isNonZero;
- }
-
- void Path::scaleToFit (float x, float y, float w, float h, bool preserveProportions) noexcept
- {
- applyTransform (getTransformToScaleToFit (x, y, w, h, preserveProportions));
- }
-
- //==============================================================================
- bool Path::isEmpty() const noexcept
- {
- size_t i = 0;
-
- while (i < numElements)
- {
- auto type = data.elements[i++];
-
- if (isMarker (type, moveMarker))
- {
- i += 2;
- }
- else if (isMarker (type, lineMarker)
- || isMarker (type, quadMarker)
- || isMarker (type, cubicMarker))
- {
- return false;
- }
- }
-
- return true;
- }
-
- Rectangle<float> Path::getBounds() const noexcept
- {
- return bounds.getRectangle();
- }
-
- Rectangle<float> Path::getBoundsTransformed (const AffineTransform& transform) const noexcept
- {
- return getBounds().transformedBy (transform);
- }
-
- //==============================================================================
- void Path::preallocateSpace (int numExtraCoordsToMakeSpaceFor)
- {
- data.ensureAllocatedSize ((int) numElements + numExtraCoordsToMakeSpaceFor);
- }
-
- void Path::startNewSubPath (const float x, const float y)
- {
- JUCE_CHECK_COORDS_ARE_VALID (x, y);
-
- if (numElements == 0)
- bounds.reset (x, y);
- else
- bounds.extend (x, y);
-
- preallocateSpace (3);
-
- data.elements[numElements++] = moveMarker;
- data.elements[numElements++] = x;
- data.elements[numElements++] = y;
- }
-
- void Path::startNewSubPath (Point<float> start)
- {
- startNewSubPath (start.x, start.y);
- }
-
- void Path::lineTo (const float x, const float y)
- {
- JUCE_CHECK_COORDS_ARE_VALID (x, y);
-
- if (numElements == 0)
- startNewSubPath (0, 0);
-
- preallocateSpace (3);
-
- data.elements[numElements++] = lineMarker;
- data.elements[numElements++] = x;
- data.elements[numElements++] = y;
-
- bounds.extend (x, y);
- }
-
- void Path::lineTo (Point<float> end)
- {
- lineTo (end.x, end.y);
- }
-
- void Path::quadraticTo (const float x1, const float y1,
- const float x2, const float y2)
- {
- JUCE_CHECK_COORDS_ARE_VALID (x1, y1);
- JUCE_CHECK_COORDS_ARE_VALID (x2, y2);
-
- if (numElements == 0)
- startNewSubPath (0, 0);
-
- preallocateSpace (5);
-
- data.elements[numElements++] = quadMarker;
- data.elements[numElements++] = x1;
- data.elements[numElements++] = y1;
- data.elements[numElements++] = x2;
- data.elements[numElements++] = y2;
-
- bounds.extend (x1, y1, x2, y2);
- }
-
- void Path::quadraticTo (Point<float> controlPoint, Point<float> endPoint)
- {
- quadraticTo (controlPoint.x, controlPoint.y,
- endPoint.x, endPoint.y);
- }
-
- void Path::cubicTo (const float x1, const float y1,
- const float x2, const float y2,
- const float x3, const float y3)
- {
- JUCE_CHECK_COORDS_ARE_VALID (x1, y1);
- JUCE_CHECK_COORDS_ARE_VALID (x2, y2);
- JUCE_CHECK_COORDS_ARE_VALID (x3, y3);
-
- if (numElements == 0)
- startNewSubPath (0, 0);
-
- preallocateSpace (7);
-
- data.elements[numElements++] = cubicMarker;
- data.elements[numElements++] = x1;
- data.elements[numElements++] = y1;
- data.elements[numElements++] = x2;
- data.elements[numElements++] = y2;
- data.elements[numElements++] = x3;
- data.elements[numElements++] = y3;
-
- bounds.extend (x1, y1, x2, y2);
- bounds.extend (x3, y3);
- }
-
- void Path::cubicTo (Point<float> controlPoint1,
- Point<float> controlPoint2,
- Point<float> endPoint)
- {
- cubicTo (controlPoint1.x, controlPoint1.y,
- controlPoint2.x, controlPoint2.y,
- endPoint.x, endPoint.y);
- }
-
- void Path::closeSubPath()
- {
- if (numElements > 0 && ! isMarker (data.elements[numElements - 1], closeSubPathMarker))
- {
- preallocateSpace (1);
- data.elements[numElements++] = closeSubPathMarker;
- }
- }
-
- Point<float> Path::getCurrentPosition() const
- {
- int i = (int) numElements - 1;
-
- if (i > 0 && isMarker (data.elements[i], closeSubPathMarker))
- {
- while (i >= 0)
- {
- if (isMarker (data.elements[i], moveMarker))
- {
- i += 2;
- break;
- }
-
- --i;
- }
- }
-
- if (i > 0)
- return { data.elements[i - 1], data.elements[i] };
-
- return {};
- }
-
- void Path::addRectangle (const float x, const float y,
- const float w, const float h)
- {
- float x1 = x, y1 = y, x2 = x + w, y2 = y + h;
-
- if (w < 0) std::swap (x1, x2);
- if (h < 0) std::swap (y1, y2);
-
- preallocateSpace (13);
-
- if (numElements == 0)
- {
- bounds.pathXMin = x1;
- bounds.pathXMax = x2;
- bounds.pathYMin = y1;
- bounds.pathYMax = y2;
- }
- else
- {
- bounds.pathXMin = jmin (bounds.pathXMin, x1);
- bounds.pathXMax = jmax (bounds.pathXMax, x2);
- bounds.pathYMin = jmin (bounds.pathYMin, y1);
- bounds.pathYMax = jmax (bounds.pathYMax, y2);
- }
-
- data.elements[numElements++] = moveMarker;
- data.elements[numElements++] = x1;
- data.elements[numElements++] = y2;
- data.elements[numElements++] = lineMarker;
- data.elements[numElements++] = x1;
- data.elements[numElements++] = y1;
- data.elements[numElements++] = lineMarker;
- data.elements[numElements++] = x2;
- data.elements[numElements++] = y1;
- data.elements[numElements++] = lineMarker;
- data.elements[numElements++] = x2;
- data.elements[numElements++] = y2;
- data.elements[numElements++] = closeSubPathMarker;
- }
-
- void Path::addRoundedRectangle (float x, float y, float w, float h, float csx, float csy)
- {
- addRoundedRectangle (x, y, w, h, csx, csy, true, true, true, true);
- }
-
- void Path::addRoundedRectangle (const float x, const float y, const float w, const float h,
- float csx, float csy,
- const bool curveTopLeft, const bool curveTopRight,
- const bool curveBottomLeft, const bool curveBottomRight)
- {
- csx = jmin (csx, w * 0.5f);
- csy = jmin (csy, h * 0.5f);
- auto cs45x = csx * 0.45f;
- auto cs45y = csy * 0.45f;
- auto x2 = x + w;
- auto y2 = y + h;
-
- if (curveTopLeft)
- {
- startNewSubPath (x, y + csy);
- cubicTo (x, y + cs45y, x + cs45x, y, x + csx, y);
- }
- else
- {
- startNewSubPath (x, y);
- }
-
- if (curveTopRight)
- {
- lineTo (x2 - csx, y);
- cubicTo (x2 - cs45x, y, x2, y + cs45y, x2, y + csy);
- }
- else
- {
- lineTo (x2, y);
- }
-
- if (curveBottomRight)
- {
- lineTo (x2, y2 - csy);
- cubicTo (x2, y2 - cs45y, x2 - cs45x, y2, x2 - csx, y2);
- }
- else
- {
- lineTo (x2, y2);
- }
-
- if (curveBottomLeft)
- {
- lineTo (x + csx, y2);
- cubicTo (x + cs45x, y2, x, y2 - cs45y, x, y2 - csy);
- }
- else
- {
- lineTo (x, y2);
- }
-
- closeSubPath();
- }
-
- void Path::addRoundedRectangle (float x, float y, float w, float h, float cs)
- {
- addRoundedRectangle (x, y, w, h, cs, cs);
- }
-
- void Path::addTriangle (float x1, float y1,
- float x2, float y2,
- float x3, float y3)
- {
- addTriangle (Point<float> (x1, y1),
- Point<float> (x2, y2),
- Point<float> (x3, y3));
- }
-
- void Path::addTriangle (Point<float> p1, Point<float> p2, Point<float> p3)
- {
- startNewSubPath (p1);
- lineTo (p2);
- lineTo (p3);
- closeSubPath();
- }
-
- void Path::addQuadrilateral (const float x1, const float y1,
- const float x2, const float y2,
- const float x3, const float y3,
- const float x4, const float y4)
- {
- startNewSubPath (x1, y1);
- lineTo (x2, y2);
- lineTo (x3, y3);
- lineTo (x4, y4);
- closeSubPath();
- }
-
- void Path::addEllipse (float x, float y, float w, float h)
- {
- addEllipse (Rectangle<float> (x, y, w, h));
- }
-
- void Path::addEllipse (Rectangle<float> area)
- {
- auto hw = area.getWidth() * 0.5f;
- auto hw55 = hw * 0.55f;
- auto hh = area.getHeight() * 0.5f;
- auto hh55 = hh * 0.55f;
- auto cx = area.getX() + hw;
- auto cy = area.getY() + hh;
-
- startNewSubPath (cx, cy - hh);
- cubicTo (cx + hw55, cy - hh, cx + hw, cy - hh55, cx + hw, cy);
- cubicTo (cx + hw, cy + hh55, cx + hw55, cy + hh, cx, cy + hh);
- cubicTo (cx - hw55, cy + hh, cx - hw, cy + hh55, cx - hw, cy);
- cubicTo (cx - hw, cy - hh55, cx - hw55, cy - hh, cx, cy - hh);
- closeSubPath();
- }
-
- void Path::addArc (const float x, const float y,
- const float w, const float h,
- const float fromRadians,
- const float toRadians,
- const bool startAsNewSubPath)
- {
- auto radiusX = w / 2.0f;
- auto radiusY = h / 2.0f;
-
- addCentredArc (x + radiusX,
- y + radiusY,
- radiusX, radiusY,
- 0.0f,
- fromRadians, toRadians,
- startAsNewSubPath);
- }
-
- void Path::addCentredArc (const float centreX, const float centreY,
- const float radiusX, const float radiusY,
- const float rotationOfEllipse,
- const float fromRadians,
- float toRadians,
- const bool startAsNewSubPath)
- {
- if (radiusX > 0.0f && radiusY > 0.0f)
- {
- const Point<float> centre (centreX, centreY);
- auto rotation = AffineTransform::rotation (rotationOfEllipse, centreX, centreY);
- auto angle = fromRadians;
-
- if (startAsNewSubPath)
- startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
-
- if (fromRadians < toRadians)
- {
- if (startAsNewSubPath)
- angle += PathHelpers::ellipseAngularIncrement;
-
- while (angle < toRadians)
- {
- lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
- angle += PathHelpers::ellipseAngularIncrement;
- }
- }
- else
- {
- if (startAsNewSubPath)
- angle -= PathHelpers::ellipseAngularIncrement;
-
- while (angle > toRadians)
- {
- lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
- angle -= PathHelpers::ellipseAngularIncrement;
- }
- }
-
- lineTo (centre.getPointOnCircumference (radiusX, radiusY, toRadians).transformedBy (rotation));
- }
- }
-
- void Path::addPieSegment (const float x, const float y,
- const float width, const float height,
- const float fromRadians,
- const float toRadians,
- const float innerCircleProportionalSize)
- {
- float radiusX = width * 0.5f;
- float radiusY = height * 0.5f;
- const Point<float> centre (x + radiusX, y + radiusY);
-
- startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, fromRadians));
- addArc (x, y, width, height, fromRadians, toRadians);
-
- if (std::abs (fromRadians - toRadians) > float_Pi * 1.999f)
- {
- closeSubPath();
-
- if (innerCircleProportionalSize > 0)
- {
- radiusX *= innerCircleProportionalSize;
- radiusY *= innerCircleProportionalSize;
-
- startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, toRadians));
- addArc (centre.x - radiusX, centre.y - radiusY, radiusX * 2.0f, radiusY * 2.0f, toRadians, fromRadians);
- }
- }
- else
- {
- if (innerCircleProportionalSize > 0)
- {
- radiusX *= innerCircleProportionalSize;
- radiusY *= innerCircleProportionalSize;
-
- addArc (centre.x - radiusX, centre.y - radiusY, radiusX * 2.0f, radiusY * 2.0f, toRadians, fromRadians);
- }
- else
- {
- lineTo (centre);
- }
- }
-
- closeSubPath();
- }
-
- void Path::addPieSegment (Rectangle<float> segmentBounds,
- const float fromRadians,
- const float toRadians,
- const float innerCircleProportionalSize)
- {
- addPieSegment (segmentBounds.getX(),
- segmentBounds.getY(),
- segmentBounds.getWidth(),
- segmentBounds.getHeight(),
- fromRadians,
- toRadians,
- innerCircleProportionalSize);
- }
-
- //==============================================================================
- void Path::addLineSegment (Line<float> line, float lineThickness)
- {
- auto reversed = line.reversed();
- lineThickness *= 0.5f;
-
- startNewSubPath (line.getPointAlongLine (0, lineThickness));
- lineTo (line.getPointAlongLine (0, -lineThickness));
- lineTo (reversed.getPointAlongLine (0, lineThickness));
- lineTo (reversed.getPointAlongLine (0, -lineThickness));
- closeSubPath();
- }
-
- void Path::addArrow (Line<float> line, float lineThickness,
- float arrowheadWidth, float arrowheadLength)
- {
- auto reversed = line.reversed();
- lineThickness *= 0.5f;
- arrowheadWidth *= 0.5f;
- arrowheadLength = jmin (arrowheadLength, 0.8f * line.getLength());
-
- startNewSubPath (line.getPointAlongLine (0, lineThickness));
- lineTo (line.getPointAlongLine (0, -lineThickness));
- lineTo (reversed.getPointAlongLine (arrowheadLength, lineThickness));
- lineTo (reversed.getPointAlongLine (arrowheadLength, arrowheadWidth));
- lineTo (line.getEnd());
- lineTo (reversed.getPointAlongLine (arrowheadLength, -arrowheadWidth));
- lineTo (reversed.getPointAlongLine (arrowheadLength, -lineThickness));
- closeSubPath();
- }
-
- void Path::addPolygon (Point<float> centre, int numberOfSides,
- float radius, float startAngle)
- {
- jassert (numberOfSides > 1); // this would be silly.
-
- if (numberOfSides > 1)
- {
- auto angleBetweenPoints = float_Pi * 2.0f / numberOfSides;
-
- for (int i = 0; i < numberOfSides; ++i)
- {
- auto angle = startAngle + i * angleBetweenPoints;
- auto p = centre.getPointOnCircumference (radius, angle);
-
- if (i == 0)
- startNewSubPath (p);
- else
- lineTo (p);
- }
-
- closeSubPath();
- }
- }
-
- void Path::addStar (Point<float> centre, int numberOfPoints, float innerRadius,
- float outerRadius, float startAngle)
- {
- jassert (numberOfPoints > 1); // this would be silly.
-
- if (numberOfPoints > 1)
- {
- auto angleBetweenPoints = float_Pi * 2.0f / numberOfPoints;
-
- for (int i = 0; i < numberOfPoints; ++i)
- {
- auto angle = startAngle + i * angleBetweenPoints;
- auto p = centre.getPointOnCircumference (outerRadius, angle);
-
- if (i == 0)
- startNewSubPath (p);
- else
- lineTo (p);
-
- lineTo (centre.getPointOnCircumference (innerRadius, angle + angleBetweenPoints * 0.5f));
- }
-
- closeSubPath();
- }
- }
-
- void Path::addBubble (Rectangle<float> bodyArea,
- Rectangle<float> maximumArea,
- Point<float> arrowTip,
- const float cornerSize,
- const float arrowBaseWidth)
- {
- auto halfW = bodyArea.getWidth() / 2.0f;
- auto halfH = bodyArea.getHeight() / 2.0f;
- auto cornerSizeW = jmin (cornerSize, halfW);
- auto cornerSizeH = jmin (cornerSize, halfH);
- auto cornerSizeW2 = 2.0f * cornerSizeW;
- auto cornerSizeH2 = 2.0f * cornerSizeH;
-
- startNewSubPath (bodyArea.getX() + cornerSizeW, bodyArea.getY());
-
- const Rectangle<float> targetLimit (bodyArea.reduced (jmin (halfW - 1.0f, cornerSizeW + arrowBaseWidth),
- jmin (halfH - 1.0f, cornerSizeH + arrowBaseWidth)));
-
- if (Rectangle<float> (targetLimit.getX(), maximumArea.getY(),
- targetLimit.getWidth(), bodyArea.getY() - maximumArea.getY()).contains (arrowTip))
- {
- lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getY());
- lineTo (arrowTip.x, arrowTip.y);
- lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getY());
- }
-
- lineTo (bodyArea.getRight() - cornerSizeW, bodyArea.getY());
- addArc (bodyArea.getRight() - cornerSizeW2, bodyArea.getY(), cornerSizeW2, cornerSizeH2, 0, float_Pi * 0.5f);
-
- if (Rectangle<float> (bodyArea.getRight(), targetLimit.getY(),
- maximumArea.getRight() - bodyArea.getRight(), targetLimit.getHeight()).contains (arrowTip))
- {
- lineTo (bodyArea.getRight(), arrowTip.y - arrowBaseWidth);
- lineTo (arrowTip.x, arrowTip.y);
- lineTo (bodyArea.getRight(), arrowTip.y + arrowBaseWidth);
- }
-
- lineTo (bodyArea.getRight(), bodyArea.getBottom() - cornerSizeH);
- addArc (bodyArea.getRight() - cornerSizeW2, bodyArea.getBottom() - cornerSizeH2, cornerSizeW2, cornerSizeH2, float_Pi * 0.5f, float_Pi);
-
- if (Rectangle<float> (targetLimit.getX(), bodyArea.getBottom(),
- targetLimit.getWidth(), maximumArea.getBottom() - bodyArea.getBottom()).contains (arrowTip))
- {
- lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getBottom());
- lineTo (arrowTip.x, arrowTip.y);
- lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getBottom());
- }
-
- lineTo (bodyArea.getX() + cornerSizeW, bodyArea.getBottom());
- addArc (bodyArea.getX(), bodyArea.getBottom() - cornerSizeH2, cornerSizeW2, cornerSizeH2, float_Pi, float_Pi * 1.5f);
-
- if (Rectangle<float> (maximumArea.getX(), targetLimit.getY(),
- bodyArea.getX() - maximumArea.getX(), targetLimit.getHeight()).contains (arrowTip))
- {
- lineTo (bodyArea.getX(), arrowTip.y + arrowBaseWidth);
- lineTo (arrowTip.x, arrowTip.y);
- lineTo (bodyArea.getX(), arrowTip.y - arrowBaseWidth);
- }
-
- lineTo (bodyArea.getX(), bodyArea.getY() + cornerSizeH);
- addArc (bodyArea.getX(), bodyArea.getY(), cornerSizeW2, cornerSizeH2, float_Pi * 1.5f, float_Pi * 2.0f - 0.05f);
-
- closeSubPath();
- }
-
- void Path::addPath (const Path& other)
- {
- size_t i = 0;
- const float* d = other.data.elements;
-
- while (i < other.numElements)
- {
- auto type = d[i++];
-
- if (isMarker (type, moveMarker))
- {
- startNewSubPath (d[i], d[i + 1]);
- i += 2;
- }
- else if (isMarker (type, lineMarker))
- {
- lineTo (d[i], d[i + 1]);
- i += 2;
- }
- else if (isMarker (type, quadMarker))
- {
- quadraticTo (d[i], d[i + 1], d[i + 2], d[i + 3]);
- i += 4;
- }
- else if (isMarker (type, cubicMarker))
- {
- cubicTo (d[i], d[i + 1], d[i + 2], d[i + 3], d[i + 4], d[i + 5]);
- i += 6;
- }
- else if (isMarker (type, closeSubPathMarker))
- {
- closeSubPath();
- }
- else
- {
- // something's gone wrong with the element list!
- jassertfalse;
- }
- }
- }
-
- void Path::addPath (const Path& other,
- const AffineTransform& transformToApply)
- {
- size_t i = 0;
- const float* d = other.data.elements;
-
- while (i < other.numElements)
- {
- auto type = d[i++];
-
- if (isMarker (type, closeSubPathMarker))
- {
- closeSubPath();
- }
- else
- {
- auto x = d[i++];
- auto y = d[i++];
- transformToApply.transformPoint (x, y);
-
- if (isMarker (type, moveMarker))
- {
- startNewSubPath (x, y);
- }
- else if (isMarker (type, lineMarker))
- {
- lineTo (x, y);
- }
- else if (isMarker (type, quadMarker))
- {
- auto x2 = d[i++];
- auto y2 = d[i++];
- transformToApply.transformPoint (x2, y2);
-
- quadraticTo (x, y, x2, y2);
- }
- else if (isMarker (type, cubicMarker))
- {
- auto x2 = d[i++];
- auto y2 = d[i++];
- auto x3 = d[i++];
- auto y3 = d[i++];
- transformToApply.transformPoints (x2, y2, x3, y3);
-
- cubicTo (x, y, x2, y2, x3, y3);
- }
- else
- {
- // something's gone wrong with the element list!
- jassertfalse;
- }
- }
- }
- }
-
- //==============================================================================
- void Path::applyTransform (const AffineTransform& transform) noexcept
- {
- bounds.reset();
- bool firstPoint = true;
- float* d = data.elements;
- auto* end = d + numElements;
-
- while (d < end)
- {
- auto type = *d++;
-
- if (isMarker (type, moveMarker))
- {
- transform.transformPoint (d[0], d[1]);
-
- if (firstPoint)
- {
- firstPoint = false;
- bounds.reset (d[0], d[1]);
- }
- else
- {
- bounds.extend (d[0], d[1]);
- }
-
- d += 2;
- }
- else if (isMarker (type, lineMarker))
- {
- transform.transformPoint (d[0], d[1]);
- bounds.extend (d[0], d[1]);
- d += 2;
- }
- else if (isMarker (type, quadMarker))
- {
- transform.transformPoints (d[0], d[1], d[2], d[3]);
- bounds.extend (d[0], d[1], d[2], d[3]);
- d += 4;
- }
- else if (isMarker (type, cubicMarker))
- {
- transform.transformPoints (d[0], d[1], d[2], d[3], d[4], d[5]);
- bounds.extend (d[0], d[1], d[2], d[3]);
- bounds.extend (d[4], d[5]);
- d += 6;
- }
- }
- }
-
-
- //==============================================================================
- AffineTransform Path::getTransformToScaleToFit (Rectangle<float> area, bool preserveProportions,
- Justification justification) const
- {
- return getTransformToScaleToFit (area.getX(), area.getY(), area.getWidth(), area.getHeight(),
- preserveProportions, justification);
- }
-
- AffineTransform Path::getTransformToScaleToFit (const float x, const float y,
- const float w, const float h,
- const bool preserveProportions,
- Justification justification) const
- {
- auto boundsRect = getBounds();
-
- if (preserveProportions)
- {
- if (w <= 0 || h <= 0 || boundsRect.isEmpty())
- return AffineTransform();
-
- float newW, newH;
- auto srcRatio = boundsRect.getHeight() / boundsRect.getWidth();
-
- if (srcRatio > h / w)
- {
- newW = h / srcRatio;
- newH = h;
- }
- else
- {
- newW = w;
- newH = w * srcRatio;
- }
-
- auto newXCentre = x;
- auto newYCentre = y;
-
- if (justification.testFlags (Justification::left)) newXCentre += newW * 0.5f;
- else if (justification.testFlags (Justification::right)) newXCentre += w - newW * 0.5f;
- else newXCentre += w * 0.5f;
-
- if (justification.testFlags (Justification::top)) newYCentre += newH * 0.5f;
- else if (justification.testFlags (Justification::bottom)) newYCentre += h - newH * 0.5f;
- else newYCentre += h * 0.5f;
-
- return AffineTransform::translation (boundsRect.getWidth() * -0.5f - boundsRect.getX(),
- boundsRect.getHeight() * -0.5f - boundsRect.getY())
- .scaled (newW / boundsRect.getWidth(),
- newH / boundsRect.getHeight())
- .translated (newXCentre, newYCentre);
- }
- else
- {
- return AffineTransform::translation (-boundsRect.getX(), -boundsRect.getY())
- .scaled (w / boundsRect.getWidth(),
- h / boundsRect.getHeight())
- .translated (x, y);
- }
- }
-
- //==============================================================================
- bool Path::contains (const float x, const float y, const float tolerance) const
- {
- if (x <= bounds.pathXMin || x >= bounds.pathXMax
- || y <= bounds.pathYMin || y >= bounds.pathYMax)
- return false;
-
- PathFlatteningIterator i (*this, AffineTransform(), tolerance);
-
- int positiveCrossings = 0;
- int negativeCrossings = 0;
-
- while (i.next())
- {
- if ((i.y1 <= y && i.y2 > y) || (i.y2 <= y && i.y1 > y))
- {
- auto intersectX = i.x1 + (i.x2 - i.x1) * (y - i.y1) / (i.y2 - i.y1);
-
- if (intersectX <= x)
- {
- if (i.y1 < i.y2)
- ++positiveCrossings;
- else
- ++negativeCrossings;
- }
- }
- }
-
- return useNonZeroWinding ? (negativeCrossings != positiveCrossings)
- : ((negativeCrossings + positiveCrossings) & 1) != 0;
- }
-
- bool Path::contains (Point<float> point, const float tolerance) const
- {
- return contains (point.x, point.y, tolerance);
- }
-
- bool Path::intersectsLine (Line<float> line, const float tolerance)
- {
- PathFlatteningIterator i (*this, AffineTransform(), tolerance);
- Point<float> intersection;
-
- while (i.next())
- if (line.intersects (Line<float> (i.x1, i.y1, i.x2, i.y2), intersection))
- return true;
-
- return false;
- }
-
- Line<float> Path::getClippedLine (Line<float> line, const bool keepSectionOutsidePath) const
- {
- Line<float> result (line);
- const bool startInside = contains (line.getStart());
- const bool endInside = contains (line.getEnd());
-
- if (startInside == endInside)
- {
- if (keepSectionOutsidePath == startInside)
- result = Line<float>();
- }
- else
- {
- PathFlatteningIterator i (*this, AffineTransform());
- Point<float> intersection;
-
- while (i.next())
- {
- if (line.intersects ({ i.x1, i.y1, i.x2, i.y2 }, intersection))
- {
- if ((startInside && keepSectionOutsidePath) || (endInside && ! keepSectionOutsidePath))
- result.setStart (intersection);
- else
- result.setEnd (intersection);
- }
- }
- }
-
- return result;
- }
-
- float Path::getLength (const AffineTransform& transform, float tolerance) const
- {
- float length = 0;
- PathFlatteningIterator i (*this, transform, tolerance);
-
- while (i.next())
- length += Line<float> (i.x1, i.y1, i.x2, i.y2).getLength();
-
- return length;
- }
-
- Point<float> Path::getPointAlongPath (float distanceFromStart,
- const AffineTransform& transform,
- float tolerance) const
- {
- PathFlatteningIterator i (*this, transform, tolerance);
-
- while (i.next())
- {
- const Line<float> line (i.x1, i.y1, i.x2, i.y2);
- auto lineLength = line.getLength();
-
- if (distanceFromStart <= lineLength)
- return line.getPointAlongLine (distanceFromStart);
-
- distanceFromStart -= lineLength;
- }
-
- return { i.x2, i.y2 };
- }
-
- float Path::getNearestPoint (Point<float> targetPoint, Point<float>& pointOnPath,
- const AffineTransform& transform,
- float tolerance) const
- {
- PathFlatteningIterator i (*this, transform, tolerance);
- float bestPosition = 0, bestDistance = std::numeric_limits<float>::max();
- float length = 0;
- Point<float> pointOnLine;
-
- while (i.next())
- {
- const Line<float> line (i.x1, i.y1, i.x2, i.y2);
- auto distance = line.getDistanceFromPoint (targetPoint, pointOnLine);
-
- if (distance < bestDistance)
- {
- bestDistance = distance;
- bestPosition = length + pointOnLine.getDistanceFrom (line.getStart());
- pointOnPath = pointOnLine;
- }
-
- length += line.getLength();
- }
-
- return bestPosition;
- }
-
- //==============================================================================
- Path Path::createPathWithRoundedCorners (const float cornerRadius) const
- {
- if (cornerRadius <= 0.01f)
- return *this;
-
- Path p;
- size_t indexOfPathStart = 0, indexOfPathStartThis = 0;
- size_t n = 0;
- bool lastWasLine = false, firstWasLine = false;
-
- while (n < numElements)
- {
- auto type = data.elements[n++];
-
- if (isMarker (type, moveMarker))
- {
- indexOfPathStart = p.numElements;
- indexOfPathStartThis = n - 1;
- auto x = data.elements[n++];
- auto y = data.elements[n++];
- p.startNewSubPath (x, y);
- lastWasLine = false;
- firstWasLine = (isMarker (data.elements[n], lineMarker));
- }
- else if (isMarker (type, lineMarker) || isMarker (type, closeSubPathMarker))
- {
- float startX = 0, startY = 0, joinX = 0, joinY = 0, endX, endY;
-
- if (isMarker (type, lineMarker))
- {
- endX = data.elements[n++];
- endY = data.elements[n++];
-
- if (n > 8)
- {
- startX = data.elements[n - 8];
- startY = data.elements[n - 7];
- joinX = data.elements[n - 5];
- joinY = data.elements[n - 4];
- }
- }
- else
- {
- endX = data.elements[indexOfPathStartThis + 1];
- endY = data.elements[indexOfPathStartThis + 2];
-
- if (n > 6)
- {
- startX = data.elements[n - 6];
- startY = data.elements[n - 5];
- joinX = data.elements[n - 3];
- joinY = data.elements[n - 2];
- }
- }
-
- if (lastWasLine)
- {
- auto len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY);
-
- if (len1 > 0)
- {
- auto propNeeded = jmin (0.5, cornerRadius / len1);
-
- p.data.elements[p.numElements - 2] = (float) (joinX - (joinX - startX) * propNeeded);
- p.data.elements[p.numElements - 1] = (float) (joinY - (joinY - startY) * propNeeded);
- }
-
- auto len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY);
-
- if (len2 > 0)
- {
- auto propNeeded = jmin (0.5, cornerRadius / len2);
-
- p.quadraticTo (joinX, joinY,
- (float) (joinX + (endX - joinX) * propNeeded),
- (float) (joinY + (endY - joinY) * propNeeded));
- }
-
- p.lineTo (endX, endY);
- }
- else if (isMarker (type, lineMarker))
- {
- p.lineTo (endX, endY);
- lastWasLine = true;
- }
-
- if (isMarker (type, closeSubPathMarker))
- {
- if (firstWasLine)
- {
- startX = data.elements[n - 3];
- startY = data.elements[n - 2];
- joinX = endX;
- joinY = endY;
- endX = data.elements[indexOfPathStartThis + 4];
- endY = data.elements[indexOfPathStartThis + 5];
-
- auto len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY);
-
- if (len1 > 0)
- {
- auto propNeeded = jmin (0.5, cornerRadius / len1);
-
- p.data.elements[p.numElements - 2] = (float) (joinX - (joinX - startX) * propNeeded);
- p.data.elements[p.numElements - 1] = (float) (joinY - (joinY - startY) * propNeeded);
- }
-
- auto len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY);
-
- if (len2 > 0)
- {
- auto propNeeded = jmin (0.5, cornerRadius / len2);
-
- endX = (float) (joinX + (endX - joinX) * propNeeded);
- endY = (float) (joinY + (endY - joinY) * propNeeded);
-
- p.quadraticTo (joinX, joinY, endX, endY);
-
- p.data.elements[indexOfPathStart + 1] = endX;
- p.data.elements[indexOfPathStart + 2] = endY;
- }
- }
-
- p.closeSubPath();
- }
- }
- else if (isMarker (type, quadMarker))
- {
- lastWasLine = false;
- auto x1 = data.elements[n++];
- auto y1 = data.elements[n++];
- auto x2 = data.elements[n++];
- auto y2 = data.elements[n++];
- p.quadraticTo (x1, y1, x2, y2);
- }
- else if (isMarker (type, cubicMarker))
- {
- lastWasLine = false;
- auto x1 = data.elements[n++];
- auto y1 = data.elements[n++];
- auto x2 = data.elements[n++];
- auto y2 = data.elements[n++];
- auto x3 = data.elements[n++];
- auto y3 = data.elements[n++];
- p.cubicTo (x1, y1, x2, y2, x3, y3);
- }
- }
-
- return p;
- }
-
- //==============================================================================
- void Path::loadPathFromStream (InputStream& source)
- {
- while (! source.isExhausted())
- {
- switch (source.readByte())
- {
- case 'm':
- {
- auto x = source.readFloat();
- auto y = source.readFloat();
- startNewSubPath (x, y);
- break;
- }
-
- case 'l':
- {
- auto x = source.readFloat();
- auto y = source.readFloat();
- lineTo (x, y);
- break;
- }
-
- case 'q':
- {
- auto x1 = source.readFloat();
- auto y1 = source.readFloat();
- auto x2 = source.readFloat();
- auto y2 = source.readFloat();
- quadraticTo (x1, y1, x2, y2);
- break;
- }
-
- case 'b':
- {
- auto x1 = source.readFloat();
- auto y1 = source.readFloat();
- auto x2 = source.readFloat();
- auto y2 = source.readFloat();
- auto x3 = source.readFloat();
- auto y3 = source.readFloat();
- cubicTo (x1, y1, x2, y2, x3, y3);
- break;
- }
-
- case 'c':
- closeSubPath();
- break;
-
- case 'n':
- useNonZeroWinding = true;
- break;
-
- case 'z':
- useNonZeroWinding = false;
- break;
-
- case 'e':
- return; // end of path marker
-
- default:
- jassertfalse; // illegal char in the stream
- break;
- }
- }
- }
-
- void Path::loadPathFromData (const void* const pathData, const size_t numberOfBytes)
- {
- MemoryInputStream in (pathData, numberOfBytes, false);
- loadPathFromStream (in);
- }
-
- void Path::writePathToStream (OutputStream& dest) const
- {
- dest.writeByte (useNonZeroWinding ? 'n' : 'z');
-
- for (size_t i = 0; i < numElements;)
- {
- auto type = data.elements[i++];
-
- if (isMarker (type, moveMarker))
- {
- dest.writeByte ('m');
- dest.writeFloat (data.elements[i++]);
- dest.writeFloat (data.elements[i++]);
- }
- else if (isMarker (type, lineMarker))
- {
- dest.writeByte ('l');
- dest.writeFloat (data.elements[i++]);
- dest.writeFloat (data.elements[i++]);
- }
- else if (isMarker (type, quadMarker))
- {
- dest.writeByte ('q');
- dest.writeFloat (data.elements[i++]);
- dest.writeFloat (data.elements[i++]);
- dest.writeFloat (data.elements[i++]);
- dest.writeFloat (data.elements[i++]);
- }
- else if (isMarker (type, cubicMarker))
- {
- dest.writeByte ('b');
- dest.writeFloat (data.elements[i++]);
- dest.writeFloat (data.elements[i++]);
- dest.writeFloat (data.elements[i++]);
- dest.writeFloat (data.elements[i++]);
- dest.writeFloat (data.elements[i++]);
- dest.writeFloat (data.elements[i++]);
- }
- else if (isMarker (type, closeSubPathMarker))
- {
- dest.writeByte ('c');
- }
- }
-
- dest.writeByte ('e'); // marks the end-of-path
- }
-
- String Path::toString() const
- {
- MemoryOutputStream s (2048);
- if (! useNonZeroWinding)
- s << 'a';
-
- size_t i = 0;
- float lastMarker = 0.0f;
-
- while (i < numElements)
- {
- auto type = data.elements[i++];
- char markerChar = 0;
- int numCoords = 0;
-
- if (isMarker (type, moveMarker))
- {
- markerChar = 'm';
- numCoords = 2;
- }
- else if (isMarker (type, lineMarker))
- {
- markerChar = 'l';
- numCoords = 2;
- }
- else if (isMarker (type, quadMarker))
- {
- markerChar = 'q';
- numCoords = 4;
- }
- else if (isMarker (type, cubicMarker))
- {
- markerChar = 'c';
- numCoords = 6;
- }
- else
- {
- jassert (isMarker (type, closeSubPathMarker));
- markerChar = 'z';
- }
-
- if (! isMarker (type, lastMarker))
- {
- if (s.getDataSize() != 0)
- s << ' ';
-
- s << markerChar;
- lastMarker = type;
- }
-
- while (--numCoords >= 0 && i < numElements)
- {
- String coord (data.elements[i++], 3);
-
- while (coord.endsWithChar ('0') && coord != "0")
- coord = coord.dropLastCharacters (1);
-
- if (coord.endsWithChar ('.'))
- coord = coord.dropLastCharacters (1);
-
- if (s.getDataSize() != 0)
- s << ' ';
-
- s << coord;
- }
- }
-
- return s.toUTF8();
- }
-
- void Path::restoreFromString (StringRef stringVersion)
- {
- clear();
- setUsingNonZeroWinding (true);
-
- auto t = stringVersion.text;
- juce_wchar marker = 'm';
- int numValues = 2;
- float values[6];
-
- for (;;)
- {
- auto token = PathHelpers::nextToken (t);
- auto firstChar = token[0];
- int startNum = 0;
-
- if (firstChar == 0)
- break;
-
- if (firstChar == 'm' || firstChar == 'l')
- {
- marker = firstChar;
- numValues = 2;
- }
- else if (firstChar == 'q')
- {
- marker = firstChar;
- numValues = 4;
- }
- else if (firstChar == 'c')
- {
- marker = firstChar;
- numValues = 6;
- }
- else if (firstChar == 'z')
- {
- marker = firstChar;
- numValues = 0;
- }
- else if (firstChar == 'a')
- {
- setUsingNonZeroWinding (false);
- continue;
- }
- else
- {
- ++startNum;
- values [0] = token.getFloatValue();
- }
-
- for (int i = startNum; i < numValues; ++i)
- values [i] = PathHelpers::nextToken (t).getFloatValue();
-
- switch (marker)
- {
- case 'm': startNewSubPath (values[0], values[1]); break;
- case 'l': lineTo (values[0], values[1]); break;
- case 'q': quadraticTo (values[0], values[1], values[2], values[3]); break;
- case 'c': cubicTo (values[0], values[1], values[2], values[3], values[4], values[5]); break;
- case 'z': closeSubPath(); break;
- default: jassertfalse; break; // illegal string format?
- }
- }
- }
-
- //==============================================================================
- Path::Iterator::Iterator (const Path& p) noexcept
- : elementType (startNewSubPath), path (p)
- {
- }
-
- Path::Iterator::~Iterator() noexcept
- {
- }
-
- bool Path::Iterator::next() noexcept
- {
- const float* elements = path.data.elements;
-
- if (index < path.numElements)
- {
- auto type = elements[index++];
-
- if (isMarker (type, moveMarker))
- {
- elementType = startNewSubPath;
- x1 = elements[index++];
- y1 = elements[index++];
- }
- else if (isMarker (type, lineMarker))
- {
- elementType = lineTo;
- x1 = elements[index++];
- y1 = elements[index++];
- }
- else if (isMarker (type, quadMarker))
- {
- elementType = quadraticTo;
- x1 = elements[index++];
- y1 = elements[index++];
- x2 = elements[index++];
- y2 = elements[index++];
- }
- else if (isMarker (type, cubicMarker))
- {
- elementType = cubicTo;
- x1 = elements[index++];
- y1 = elements[index++];
- x2 = elements[index++];
- y2 = elements[index++];
- x3 = elements[index++];
- y3 = elements[index++];
- }
- else if (isMarker (type, closeSubPathMarker))
- {
- elementType = closePath;
- }
-
- return true;
- }
-
- return false;
- }
-
- #undef JUCE_CHECK_COORDS_ARE_VALID
-
- } // namespace juce
|