migrating to the latest JUCE version

This commit is contained in:
2022-11-04 23:11:33 +01:00
committed by Nikolai Rodionov
parent 4257a0f8ba
commit faf8f18333
2796 changed files with 888518 additions and 784244 deletions

View File

@ -1,280 +1,280 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
{
AffineTransform::AffineTransform (float m00, float m01, float m02,
float m10, float m11, float m12) noexcept
: mat00 (m00), mat01 (m01), mat02 (m02),
mat10 (m10), mat11 (m11), mat12 (m12)
{
}
bool AffineTransform::operator== (const AffineTransform& other) const noexcept
{
return mat00 == other.mat00
&& mat01 == other.mat01
&& mat02 == other.mat02
&& mat10 == other.mat10
&& mat11 == other.mat11
&& mat12 == other.mat12;
}
bool AffineTransform::operator!= (const AffineTransform& other) const noexcept
{
return ! operator== (other);
}
//==============================================================================
bool AffineTransform::isIdentity() const noexcept
{
return mat01 == 0.0f
&& mat02 == 0.0f
&& mat10 == 0.0f
&& mat12 == 0.0f
&& mat00 == 1.0f
&& mat11 == 1.0f;
}
const AffineTransform AffineTransform::identity (1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
//==============================================================================
AffineTransform AffineTransform::followedBy (const AffineTransform& other) const noexcept
{
return { other.mat00 * mat00 + other.mat01 * mat10,
other.mat00 * mat01 + other.mat01 * mat11,
other.mat00 * mat02 + other.mat01 * mat12 + other.mat02,
other.mat10 * mat00 + other.mat11 * mat10,
other.mat10 * mat01 + other.mat11 * mat11,
other.mat10 * mat02 + other.mat11 * mat12 + other.mat12 };
}
AffineTransform AffineTransform::translated (float dx, float dy) const noexcept
{
return { mat00, mat01, mat02 + dx,
mat10, mat11, mat12 + dy };
}
AffineTransform AffineTransform::translation (float dx, float dy) noexcept
{
return { 1.0f, 0.0f, dx,
0.0f, 1.0f, dy };
}
AffineTransform AffineTransform::withAbsoluteTranslation (float tx, float ty) const noexcept
{
return { mat00, mat01, tx,
mat10, mat11, ty };
}
AffineTransform AffineTransform::rotated (float rad) const noexcept
{
auto cosRad = std::cos (rad);
auto sinRad = std::sin (rad);
return { cosRad * mat00 - sinRad * mat10,
cosRad * mat01 - sinRad * mat11,
cosRad * mat02 - sinRad * mat12,
sinRad * mat00 + cosRad * mat10,
sinRad * mat01 + cosRad * mat11,
sinRad * mat02 + cosRad * mat12 };
}
AffineTransform AffineTransform::rotation (float rad) noexcept
{
auto cosRad = std::cos (rad);
auto sinRad = std::sin (rad);
return { cosRad, -sinRad, 0,
sinRad, cosRad, 0 };
}
AffineTransform AffineTransform::rotation (float rad, float pivotX, float pivotY) noexcept
{
auto cosRad = std::cos (rad);
auto sinRad = std::sin (rad);
return { cosRad, -sinRad, -cosRad * pivotX + sinRad * pivotY + pivotX,
sinRad, cosRad, -sinRad * pivotX + -cosRad * pivotY + pivotY };
}
AffineTransform AffineTransform::rotated (float angle, float pivotX, float pivotY) const noexcept
{
return followedBy (rotation (angle, pivotX, pivotY));
}
AffineTransform AffineTransform::scaled (float factorX, float factorY) const noexcept
{
return { factorX * mat00, factorX * mat01, factorX * mat02,
factorY * mat10, factorY * mat11, factorY * mat12 };
}
AffineTransform AffineTransform::scaled (float factor) const noexcept
{
return { factor * mat00, factor * mat01, factor * mat02,
factor * mat10, factor * mat11, factor * mat12 };
}
AffineTransform AffineTransform::scale (float factorX, float factorY) noexcept
{
return { factorX, 0, 0, 0, factorY, 0 };
}
AffineTransform AffineTransform::scale (float factor) noexcept
{
return { factor, 0, 0, 0, factor, 0 };
}
AffineTransform AffineTransform::scaled (float factorX, float factorY,
float pivotX, float pivotY) const noexcept
{
return { factorX * mat00, factorX * mat01, factorX * mat02 + pivotX * (1.0f - factorX),
factorY * mat10, factorY * mat11, factorY * mat12 + pivotY * (1.0f - factorY) };
}
AffineTransform AffineTransform::scale (float factorX, float factorY,
float pivotX, float pivotY) noexcept
{
return { factorX, 0, pivotX * (1.0f - factorX),
0, factorY, pivotY * (1.0f - factorY) };
}
AffineTransform AffineTransform::shear (float shearX, float shearY) noexcept
{
return { 1.0f, shearX, 0,
shearY, 1.0f, 0 };
}
AffineTransform AffineTransform::sheared (float shearX, float shearY) const noexcept
{
return { mat00 + shearX * mat10,
mat01 + shearX * mat11,
mat02 + shearX * mat12,
mat10 + shearY * mat00,
mat11 + shearY * mat01,
mat12 + shearY * mat02 };
}
AffineTransform AffineTransform::verticalFlip (float height) noexcept
{
return { 1.0f, 0.0f, 0.0f,
0.0f, -1.0f, height };
}
AffineTransform AffineTransform::inverted() const noexcept
{
double determinant = getDeterminant();
if (! approximatelyEqual (determinant, 0.0))
{
determinant = 1.0 / determinant;
auto dst00 = (float) ( mat11 * determinant);
auto dst10 = (float) (-mat10 * determinant);
auto dst01 = (float) (-mat01 * determinant);
auto dst11 = (float) ( mat00 * determinant);
return { dst00, dst01, -mat02 * dst00 - mat12 * dst01,
dst10, dst11, -mat02 * dst10 - mat12 * dst11 };
}
// singularity..
return *this;
}
bool AffineTransform::isSingularity() const noexcept
{
return (mat00 * mat11 - mat10 * mat01) == 0.0f;
}
AffineTransform AffineTransform::fromTargetPoints (float x00, float y00,
float x10, float y10,
float x01, float y01) noexcept
{
return { x10 - x00, x01 - x00, x00,
y10 - y00, y01 - y00, y00 };
}
AffineTransform AffineTransform::fromTargetPoints (float sx1, float sy1, float tx1, float ty1,
float sx2, float sy2, float tx2, float ty2,
float sx3, float sy3, float tx3, float ty3) noexcept
{
return fromTargetPoints (sx1, sy1, sx2, sy2, sx3, sy3)
.inverted()
.followedBy (fromTargetPoints (tx1, ty1, tx2, ty2, tx3, ty3));
}
bool AffineTransform::isOnlyTranslation() const noexcept
{
return mat01 == 0.0f
&& mat10 == 0.0f
&& mat00 == 1.0f
&& mat11 == 1.0f;
}
float AffineTransform::getDeterminant() const noexcept
{
return (mat00 * mat11) - (mat01 * mat10);
}
float AffineTransform::getScaleFactor() const noexcept
{
return (std::abs (mat00) + std::abs (mat11)) / 2.0f;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class AffineTransformTests : public UnitTest
{
public:
AffineTransformTests()
: UnitTest ("AffineTransform", UnitTestCategories::maths)
{}
void runTest() override
{
beginTest ("Determinant");
{
constexpr float scale1 = 1.5f, scale2 = 1.3f;
auto transform = AffineTransform::scale (scale1)
.followedBy (AffineTransform::rotation (degreesToRadians (72.0f)))
.followedBy (AffineTransform::translation (100.0f, 20.0f))
.followedBy (AffineTransform::scale (scale2));
expect (approximatelyEqual (std::sqrt (std::abs (transform.getDeterminant())), scale1 * scale2));
}
}
};
static AffineTransformTests timeTests;
#endif
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-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
{
AffineTransform::AffineTransform (float m00, float m01, float m02,
float m10, float m11, float m12) noexcept
: mat00 (m00), mat01 (m01), mat02 (m02),
mat10 (m10), mat11 (m11), mat12 (m12)
{
}
bool AffineTransform::operator== (const AffineTransform& other) const noexcept
{
return mat00 == other.mat00
&& mat01 == other.mat01
&& mat02 == other.mat02
&& mat10 == other.mat10
&& mat11 == other.mat11
&& mat12 == other.mat12;
}
bool AffineTransform::operator!= (const AffineTransform& other) const noexcept
{
return ! operator== (other);
}
//==============================================================================
bool AffineTransform::isIdentity() const noexcept
{
return mat01 == 0.0f
&& mat02 == 0.0f
&& mat10 == 0.0f
&& mat12 == 0.0f
&& mat00 == 1.0f
&& mat11 == 1.0f;
}
const AffineTransform AffineTransform::identity (1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
//==============================================================================
AffineTransform AffineTransform::followedBy (const AffineTransform& other) const noexcept
{
return { other.mat00 * mat00 + other.mat01 * mat10,
other.mat00 * mat01 + other.mat01 * mat11,
other.mat00 * mat02 + other.mat01 * mat12 + other.mat02,
other.mat10 * mat00 + other.mat11 * mat10,
other.mat10 * mat01 + other.mat11 * mat11,
other.mat10 * mat02 + other.mat11 * mat12 + other.mat12 };
}
AffineTransform AffineTransform::translated (float dx, float dy) const noexcept
{
return { mat00, mat01, mat02 + dx,
mat10, mat11, mat12 + dy };
}
AffineTransform AffineTransform::translation (float dx, float dy) noexcept
{
return { 1.0f, 0.0f, dx,
0.0f, 1.0f, dy };
}
AffineTransform AffineTransform::withAbsoluteTranslation (float tx, float ty) const noexcept
{
return { mat00, mat01, tx,
mat10, mat11, ty };
}
AffineTransform AffineTransform::rotated (float rad) const noexcept
{
auto cosRad = std::cos (rad);
auto sinRad = std::sin (rad);
return { cosRad * mat00 - sinRad * mat10,
cosRad * mat01 - sinRad * mat11,
cosRad * mat02 - sinRad * mat12,
sinRad * mat00 + cosRad * mat10,
sinRad * mat01 + cosRad * mat11,
sinRad * mat02 + cosRad * mat12 };
}
AffineTransform AffineTransform::rotation (float rad) noexcept
{
auto cosRad = std::cos (rad);
auto sinRad = std::sin (rad);
return { cosRad, -sinRad, 0,
sinRad, cosRad, 0 };
}
AffineTransform AffineTransform::rotation (float rad, float pivotX, float pivotY) noexcept
{
auto cosRad = std::cos (rad);
auto sinRad = std::sin (rad);
return { cosRad, -sinRad, -cosRad * pivotX + sinRad * pivotY + pivotX,
sinRad, cosRad, -sinRad * pivotX + -cosRad * pivotY + pivotY };
}
AffineTransform AffineTransform::rotated (float angle, float pivotX, float pivotY) const noexcept
{
return followedBy (rotation (angle, pivotX, pivotY));
}
AffineTransform AffineTransform::scaled (float factorX, float factorY) const noexcept
{
return { factorX * mat00, factorX * mat01, factorX * mat02,
factorY * mat10, factorY * mat11, factorY * mat12 };
}
AffineTransform AffineTransform::scaled (float factor) const noexcept
{
return { factor * mat00, factor * mat01, factor * mat02,
factor * mat10, factor * mat11, factor * mat12 };
}
AffineTransform AffineTransform::scale (float factorX, float factorY) noexcept
{
return { factorX, 0, 0, 0, factorY, 0 };
}
AffineTransform AffineTransform::scale (float factor) noexcept
{
return { factor, 0, 0, 0, factor, 0 };
}
AffineTransform AffineTransform::scaled (float factorX, float factorY,
float pivotX, float pivotY) const noexcept
{
return { factorX * mat00, factorX * mat01, factorX * mat02 + pivotX * (1.0f - factorX),
factorY * mat10, factorY * mat11, factorY * mat12 + pivotY * (1.0f - factorY) };
}
AffineTransform AffineTransform::scale (float factorX, float factorY,
float pivotX, float pivotY) noexcept
{
return { factorX, 0, pivotX * (1.0f - factorX),
0, factorY, pivotY * (1.0f - factorY) };
}
AffineTransform AffineTransform::shear (float shearX, float shearY) noexcept
{
return { 1.0f, shearX, 0,
shearY, 1.0f, 0 };
}
AffineTransform AffineTransform::sheared (float shearX, float shearY) const noexcept
{
return { mat00 + shearX * mat10,
mat01 + shearX * mat11,
mat02 + shearX * mat12,
mat10 + shearY * mat00,
mat11 + shearY * mat01,
mat12 + shearY * mat02 };
}
AffineTransform AffineTransform::verticalFlip (float height) noexcept
{
return { 1.0f, 0.0f, 0.0f,
0.0f, -1.0f, height };
}
AffineTransform AffineTransform::inverted() const noexcept
{
double determinant = getDeterminant();
if (! approximatelyEqual (determinant, 0.0))
{
determinant = 1.0 / determinant;
auto dst00 = (float) ( mat11 * determinant);
auto dst10 = (float) (-mat10 * determinant);
auto dst01 = (float) (-mat01 * determinant);
auto dst11 = (float) ( mat00 * determinant);
return { dst00, dst01, -mat02 * dst00 - mat12 * dst01,
dst10, dst11, -mat02 * dst10 - mat12 * dst11 };
}
// singularity..
return *this;
}
bool AffineTransform::isSingularity() const noexcept
{
return (mat00 * mat11 - mat10 * mat01) == 0.0f;
}
AffineTransform AffineTransform::fromTargetPoints (float x00, float y00,
float x10, float y10,
float x01, float y01) noexcept
{
return { x10 - x00, x01 - x00, x00,
y10 - y00, y01 - y00, y00 };
}
AffineTransform AffineTransform::fromTargetPoints (float sx1, float sy1, float tx1, float ty1,
float sx2, float sy2, float tx2, float ty2,
float sx3, float sy3, float tx3, float ty3) noexcept
{
return fromTargetPoints (sx1, sy1, sx2, sy2, sx3, sy3)
.inverted()
.followedBy (fromTargetPoints (tx1, ty1, tx2, ty2, tx3, ty3));
}
bool AffineTransform::isOnlyTranslation() const noexcept
{
return mat01 == 0.0f
&& mat10 == 0.0f
&& mat00 == 1.0f
&& mat11 == 1.0f;
}
float AffineTransform::getDeterminant() const noexcept
{
return (mat00 * mat11) - (mat01 * mat10);
}
float AffineTransform::getScaleFactor() const noexcept
{
return (std::abs (mat00) + std::abs (mat11)) / 2.0f;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class AffineTransformTests : public UnitTest
{
public:
AffineTransformTests()
: UnitTest ("AffineTransform", UnitTestCategories::maths)
{}
void runTest() override
{
beginTest ("Determinant");
{
constexpr float scale1 = 1.5f, scale2 = 1.3f;
auto transform = AffineTransform::scale (scale1)
.followedBy (AffineTransform::rotation (degreesToRadians (72.0f)))
.followedBy (AffineTransform::translation (100.0f, 20.0f))
.followedBy (AffineTransform::scale (scale2));
expect (approximatelyEqual (std::sqrt (std::abs (transform.getDeterminant())), scale1 * scale2));
}
}
};
static AffineTransformTests timeTests;
#endif
} // namespace juce

View File

@ -1,303 +1,303 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
Represents a 2D affine-transformation matrix.
An affine transformation is a transformation such as a rotation, scale, shear,
resize or translation.
These are used for various 2D transformation tasks, e.g. with Path objects.
@see Path, Point, Line
@tags{Graphics}
*/
class JUCE_API AffineTransform final
{
public:
//==============================================================================
/** Creates an identity transform. */
AffineTransform() = default;
/** Creates a copy of another transform. */
AffineTransform (const AffineTransform&) = default;
/** Creates a transform from a set of raw matrix values.
The resulting matrix is:
(mat00 mat01 mat02)
(mat10 mat11 mat12)
( 0 0 1 )
*/
AffineTransform (float mat00, float mat01, float mat02,
float mat10, float mat11, float mat12) noexcept;
/** Copies from another AffineTransform object */
AffineTransform& operator= (const AffineTransform&) = default;
/** Compares two transforms. */
bool operator== (const AffineTransform& other) const noexcept;
/** Compares two transforms. */
bool operator!= (const AffineTransform& other) const noexcept;
//==============================================================================
/** Transforms a 2D coordinate using this matrix. */
template <typename ValueType>
void transformPoint (ValueType& x, ValueType& y) const noexcept
{
auto oldX = x;
x = static_cast<ValueType> (mat00 * oldX + mat01 * y + mat02);
y = static_cast<ValueType> (mat10 * oldX + mat11 * y + mat12);
}
/** Transforms two 2D coordinates using this matrix.
This is just a shortcut for calling transformPoint() on each of these pairs of
coordinates in turn. (And putting all the calculations into one function hopefully
also gives the compiler a bit more scope for pipelining it).
*/
template <typename ValueType>
void transformPoints (ValueType& x1, ValueType& y1,
ValueType& x2, ValueType& y2) const noexcept
{
auto oldX1 = x1, oldX2 = x2;
x1 = static_cast<ValueType> (mat00 * oldX1 + mat01 * y1 + mat02);
y1 = static_cast<ValueType> (mat10 * oldX1 + mat11 * y1 + mat12);
x2 = static_cast<ValueType> (mat00 * oldX2 + mat01 * y2 + mat02);
y2 = static_cast<ValueType> (mat10 * oldX2 + mat11 * y2 + mat12);
}
/** Transforms three 2D coordinates using this matrix.
This is just a shortcut for calling transformPoint() on each of these pairs of
coordinates in turn. (And putting all the calculations into one function hopefully
also gives the compiler a bit more scope for pipelining it).
*/
template <typename ValueType>
void transformPoints (ValueType& x1, ValueType& y1,
ValueType& x2, ValueType& y2,
ValueType& x3, ValueType& y3) const noexcept
{
auto oldX1 = x1, oldX2 = x2, oldX3 = x3;
x1 = static_cast<ValueType> (mat00 * oldX1 + mat01 * y1 + mat02);
y1 = static_cast<ValueType> (mat10 * oldX1 + mat11 * y1 + mat12);
x2 = static_cast<ValueType> (mat00 * oldX2 + mat01 * y2 + mat02);
y2 = static_cast<ValueType> (mat10 * oldX2 + mat11 * y2 + mat12);
x3 = static_cast<ValueType> (mat00 * oldX3 + mat01 * y3 + mat02);
y3 = static_cast<ValueType> (mat10 * oldX3 + mat11 * y3 + mat12);
}
//==============================================================================
/** Returns a new transform which is the same as this one followed by a translation. */
AffineTransform translated (float deltaX,
float deltaY) const noexcept;
/** Returns a new transform which is the same as this one followed by a translation. */
template <typename PointType>
AffineTransform translated (PointType delta) const noexcept
{
return translated ((float) delta.x, (float) delta.y);
}
/** Returns a new transform which is a translation. */
static AffineTransform translation (float deltaX,
float deltaY) noexcept;
/** Returns a new transform which is a translation. */
template <typename PointType>
static AffineTransform translation (PointType delta) noexcept
{
return translation ((float) delta.x, (float) delta.y);
}
/** Returns a copy of this transform with the specified translation matrix values. */
AffineTransform withAbsoluteTranslation (float translationX,
float translationY) const noexcept;
/** Returns a transform which is the same as this one followed by a rotation.
The rotation is specified by a number of radians to rotate clockwise, centred around
the origin (0, 0).
*/
AffineTransform rotated (float angleInRadians) const noexcept;
/** Returns a transform which is the same as this one followed by a rotation about a given point.
The rotation is specified by a number of radians to rotate clockwise, centred around
the coordinates passed in.
*/
AffineTransform rotated (float angleInRadians,
float pivotX,
float pivotY) const noexcept;
/** Returns a new transform which is a rotation about (0, 0). */
static AffineTransform rotation (float angleInRadians) noexcept;
/** Returns a new transform which is a rotation about a given point. */
static AffineTransform rotation (float angleInRadians,
float pivotX,
float pivotY) noexcept;
/** Returns a transform which is the same as this one followed by a re-scaling.
The scaling is centred around the origin (0, 0).
*/
AffineTransform scaled (float factorX,
float factorY) const noexcept;
/** Returns a transform which is the same as this one followed by a re-scaling.
The scaling is centred around the origin (0, 0).
*/
AffineTransform scaled (float factor) const noexcept;
/** Returns a transform which is the same as this one followed by a re-scaling.
The scaling is centred around the origin provided.
*/
AffineTransform scaled (float factorX, float factorY,
float pivotX, float pivotY) const noexcept;
/** Returns a new transform which is a re-scale about the origin. */
static AffineTransform scale (float factorX,
float factorY) noexcept;
/** Returns a new transform which is a re-scale about the origin. */
static AffineTransform scale (float factor) noexcept;
/** Returns a new transform which is a re-scale centred around the point provided. */
static AffineTransform scale (float factorX, float factorY,
float pivotX, float pivotY) noexcept;
/** Returns a transform which is the same as this one followed by a shear.
The shear is centred around the origin (0, 0).
*/
AffineTransform sheared (float shearX, float shearY) const noexcept;
/** Returns a shear transform, centred around the origin (0, 0). */
static AffineTransform shear (float shearX, float shearY) noexcept;
/** Returns a transform that will flip coordinates vertically within a window of the given height.
This is handy for converting between upside-down coordinate systems such as OpenGL or CoreGraphics.
*/
static AffineTransform verticalFlip (float height) noexcept;
/** Returns a matrix which is the inverse operation of this one.
Some matrices don't have an inverse - in this case, the method will just return
an identity transform.
*/
AffineTransform inverted() const noexcept;
/** Returns the transform that will map three known points onto three coordinates
that are supplied.
This returns the transform that will transform (0, 0) into (x00, y00),
(1, 0) to (x10, y10), and (0, 1) to (x01, y01).
*/
static AffineTransform fromTargetPoints (float x00, float y00,
float x10, float y10,
float x01, float y01) noexcept;
/** Returns the transform that will map three specified points onto three target points. */
static AffineTransform fromTargetPoints (float sourceX1, float sourceY1, float targetX1, float targetY1,
float sourceX2, float sourceY2, float targetX2, float targetY2,
float sourceX3, float sourceY3, float targetX3, float targetY3) noexcept;
/** Returns the transform that will map three specified points onto three target points. */
template <typename PointType>
static AffineTransform fromTargetPoints (PointType source1, PointType target1,
PointType source2, PointType target2,
PointType source3, PointType target3) noexcept
{
return fromTargetPoints (source1.x, source1.y, target1.x, target1.y,
source2.x, source2.y, target2.x, target2.y,
source3.x, source3.y, target3.x, target3.y);
}
//==============================================================================
/** Returns the result of concatenating another transformation after this one. */
AffineTransform followedBy (const AffineTransform& other) const noexcept;
/** Returns true if this transform has no effect on points. */
bool isIdentity() const noexcept;
/** Returns true if this transform maps to a singularity - i.e. if it has no inverse. */
bool isSingularity() const noexcept;
/** Returns true if the transform only translates, and doesn't scale or rotate the
points. */
bool isOnlyTranslation() const noexcept;
/** If this transform is only a translation, this returns the X offset.
@see isOnlyTranslation
*/
float getTranslationX() const noexcept { return mat02; }
/** If this transform is only a translation, this returns the X offset.
@see isOnlyTranslation
*/
float getTranslationY() const noexcept { return mat12; }
/** Returns the determinant of the transform. */
float getDeterminant() const noexcept;
//==============================================================================
#ifndef DOXYGEN
/** This method has been deprecated.
You can calculate the scale factor using:
@code
std::sqrt (std::abs (AffineTransform::getDeterminant()))
@endcode
This method produces incorrect values for transforms containing rotations.
Returns the approximate scale factor by which lengths will be transformed.
Obviously a length may be scaled by entirely different amounts depending on its
direction, so this is only appropriate as a rough guide.
*/
[[deprecated ("This method produces incorrect values for transforms containing rotations. "
"See the method docs for a code example on how to calculate the correct scale factor.")]]
float getScaleFactor() const noexcept;
[[deprecated ("If you need an identity transform, just use AffineTransform() or {}.")]]
static const AffineTransform identity;
#endif
//==============================================================================
/* The transform matrix is:
(mat00 mat01 mat02)
(mat10 mat11 mat12)
( 0 0 1 )
*/
float mat00 { 1.0f }, mat01 { 0.0f }, mat02 { 0.0f };
float mat10 { 0.0f }, mat11 { 1.0f }, mat12 { 0.0f };
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
Represents a 2D affine-transformation matrix.
An affine transformation is a transformation such as a rotation, scale, shear,
resize or translation.
These are used for various 2D transformation tasks, e.g. with Path objects.
@see Path, Point, Line
@tags{Graphics}
*/
class JUCE_API AffineTransform final
{
public:
//==============================================================================
/** Creates an identity transform. */
AffineTransform() = default;
/** Creates a copy of another transform. */
AffineTransform (const AffineTransform&) = default;
/** Creates a transform from a set of raw matrix values.
The resulting matrix is:
(mat00 mat01 mat02)
(mat10 mat11 mat12)
( 0 0 1 )
*/
AffineTransform (float mat00, float mat01, float mat02,
float mat10, float mat11, float mat12) noexcept;
/** Copies from another AffineTransform object */
AffineTransform& operator= (const AffineTransform&) = default;
/** Compares two transforms. */
bool operator== (const AffineTransform& other) const noexcept;
/** Compares two transforms. */
bool operator!= (const AffineTransform& other) const noexcept;
//==============================================================================
/** Transforms a 2D coordinate using this matrix. */
template <typename ValueType>
void transformPoint (ValueType& x, ValueType& y) const noexcept
{
auto oldX = x;
x = static_cast<ValueType> (mat00 * oldX + mat01 * y + mat02);
y = static_cast<ValueType> (mat10 * oldX + mat11 * y + mat12);
}
/** Transforms two 2D coordinates using this matrix.
This is just a shortcut for calling transformPoint() on each of these pairs of
coordinates in turn. (And putting all the calculations into one function hopefully
also gives the compiler a bit more scope for pipelining it).
*/
template <typename ValueType>
void transformPoints (ValueType& x1, ValueType& y1,
ValueType& x2, ValueType& y2) const noexcept
{
auto oldX1 = x1, oldX2 = x2;
x1 = static_cast<ValueType> (mat00 * oldX1 + mat01 * y1 + mat02);
y1 = static_cast<ValueType> (mat10 * oldX1 + mat11 * y1 + mat12);
x2 = static_cast<ValueType> (mat00 * oldX2 + mat01 * y2 + mat02);
y2 = static_cast<ValueType> (mat10 * oldX2 + mat11 * y2 + mat12);
}
/** Transforms three 2D coordinates using this matrix.
This is just a shortcut for calling transformPoint() on each of these pairs of
coordinates in turn. (And putting all the calculations into one function hopefully
also gives the compiler a bit more scope for pipelining it).
*/
template <typename ValueType>
void transformPoints (ValueType& x1, ValueType& y1,
ValueType& x2, ValueType& y2,
ValueType& x3, ValueType& y3) const noexcept
{
auto oldX1 = x1, oldX2 = x2, oldX3 = x3;
x1 = static_cast<ValueType> (mat00 * oldX1 + mat01 * y1 + mat02);
y1 = static_cast<ValueType> (mat10 * oldX1 + mat11 * y1 + mat12);
x2 = static_cast<ValueType> (mat00 * oldX2 + mat01 * y2 + mat02);
y2 = static_cast<ValueType> (mat10 * oldX2 + mat11 * y2 + mat12);
x3 = static_cast<ValueType> (mat00 * oldX3 + mat01 * y3 + mat02);
y3 = static_cast<ValueType> (mat10 * oldX3 + mat11 * y3 + mat12);
}
//==============================================================================
/** Returns a new transform which is the same as this one followed by a translation. */
AffineTransform translated (float deltaX,
float deltaY) const noexcept;
/** Returns a new transform which is the same as this one followed by a translation. */
template <typename PointType>
AffineTransform translated (PointType delta) const noexcept
{
return translated ((float) delta.x, (float) delta.y);
}
/** Returns a new transform which is a translation. */
static AffineTransform translation (float deltaX,
float deltaY) noexcept;
/** Returns a new transform which is a translation. */
template <typename PointType>
static AffineTransform translation (PointType delta) noexcept
{
return translation ((float) delta.x, (float) delta.y);
}
/** Returns a copy of this transform with the specified translation matrix values. */
AffineTransform withAbsoluteTranslation (float translationX,
float translationY) const noexcept;
/** Returns a transform which is the same as this one followed by a rotation.
The rotation is specified by a number of radians to rotate clockwise, centred around
the origin (0, 0).
*/
AffineTransform rotated (float angleInRadians) const noexcept;
/** Returns a transform which is the same as this one followed by a rotation about a given point.
The rotation is specified by a number of radians to rotate clockwise, centred around
the coordinates passed in.
*/
AffineTransform rotated (float angleInRadians,
float pivotX,
float pivotY) const noexcept;
/** Returns a new transform which is a rotation about (0, 0). */
static AffineTransform rotation (float angleInRadians) noexcept;
/** Returns a new transform which is a rotation about a given point. */
static AffineTransform rotation (float angleInRadians,
float pivotX,
float pivotY) noexcept;
/** Returns a transform which is the same as this one followed by a re-scaling.
The scaling is centred around the origin (0, 0).
*/
AffineTransform scaled (float factorX,
float factorY) const noexcept;
/** Returns a transform which is the same as this one followed by a re-scaling.
The scaling is centred around the origin (0, 0).
*/
AffineTransform scaled (float factor) const noexcept;
/** Returns a transform which is the same as this one followed by a re-scaling.
The scaling is centred around the origin provided.
*/
AffineTransform scaled (float factorX, float factorY,
float pivotX, float pivotY) const noexcept;
/** Returns a new transform which is a re-scale about the origin. */
static AffineTransform scale (float factorX,
float factorY) noexcept;
/** Returns a new transform which is a re-scale about the origin. */
static AffineTransform scale (float factor) noexcept;
/** Returns a new transform which is a re-scale centred around the point provided. */
static AffineTransform scale (float factorX, float factorY,
float pivotX, float pivotY) noexcept;
/** Returns a transform which is the same as this one followed by a shear.
The shear is centred around the origin (0, 0).
*/
AffineTransform sheared (float shearX, float shearY) const noexcept;
/** Returns a shear transform, centred around the origin (0, 0). */
static AffineTransform shear (float shearX, float shearY) noexcept;
/** Returns a transform that will flip coordinates vertically within a window of the given height.
This is handy for converting between upside-down coordinate systems such as OpenGL or CoreGraphics.
*/
static AffineTransform verticalFlip (float height) noexcept;
/** Returns a matrix which is the inverse operation of this one.
Some matrices don't have an inverse - in this case, the method will just return
an identity transform.
*/
AffineTransform inverted() const noexcept;
/** Returns the transform that will map three known points onto three coordinates
that are supplied.
This returns the transform that will transform (0, 0) into (x00, y00),
(1, 0) to (x10, y10), and (0, 1) to (x01, y01).
*/
static AffineTransform fromTargetPoints (float x00, float y00,
float x10, float y10,
float x01, float y01) noexcept;
/** Returns the transform that will map three specified points onto three target points. */
static AffineTransform fromTargetPoints (float sourceX1, float sourceY1, float targetX1, float targetY1,
float sourceX2, float sourceY2, float targetX2, float targetY2,
float sourceX3, float sourceY3, float targetX3, float targetY3) noexcept;
/** Returns the transform that will map three specified points onto three target points. */
template <typename PointType>
static AffineTransform fromTargetPoints (PointType source1, PointType target1,
PointType source2, PointType target2,
PointType source3, PointType target3) noexcept
{
return fromTargetPoints (source1.x, source1.y, target1.x, target1.y,
source2.x, source2.y, target2.x, target2.y,
source3.x, source3.y, target3.x, target3.y);
}
//==============================================================================
/** Returns the result of concatenating another transformation after this one. */
AffineTransform followedBy (const AffineTransform& other) const noexcept;
/** Returns true if this transform has no effect on points. */
bool isIdentity() const noexcept;
/** Returns true if this transform maps to a singularity - i.e. if it has no inverse. */
bool isSingularity() const noexcept;
/** Returns true if the transform only translates, and doesn't scale or rotate the
points. */
bool isOnlyTranslation() const noexcept;
/** If this transform is only a translation, this returns the X offset.
@see isOnlyTranslation
*/
float getTranslationX() const noexcept { return mat02; }
/** If this transform is only a translation, this returns the X offset.
@see isOnlyTranslation
*/
float getTranslationY() const noexcept { return mat12; }
/** Returns the determinant of the transform. */
float getDeterminant() const noexcept;
//==============================================================================
#ifndef DOXYGEN
/** This method has been deprecated.
You can calculate the scale factor using:
@code
std::sqrt (std::abs (AffineTransform::getDeterminant()))
@endcode
This method produces incorrect values for transforms containing rotations.
Returns the approximate scale factor by which lengths will be transformed.
Obviously a length may be scaled by entirely different amounts depending on its
direction, so this is only appropriate as a rough guide.
*/
[[deprecated ("This method produces incorrect values for transforms containing rotations. "
"See the method docs for a code example on how to calculate the correct scale factor.")]]
float getScaleFactor() const noexcept;
[[deprecated ("If you need an identity transform, just use AffineTransform() or {}.")]]
static const AffineTransform identity;
#endif
//==============================================================================
/* The transform matrix is:
(mat00 mat01 mat02)
(mat10 mat11 mat12)
( 0 0 1 )
*/
float mat00 { 1.0f }, mat01 { 0.0f }, mat02 { 0.0f };
float mat10 { 0.0f }, mat11 { 1.0f }, mat12 { 0.0f };
};
} // namespace juce

View File

@ -1,148 +1,167 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
Specifies a set of gaps to be left around the sides of a rectangle.
This is basically the size of the spaces at the top, bottom, left and right of
a rectangle. It's used by various component classes to specify borders.
@see Rectangle
@tags{Graphics}
*/
template <typename ValueType>
class BorderSize
{
public:
//==============================================================================
/** Creates a null border.
All sizes are left as 0.
*/
BorderSize() = default;
/** Creates a copy of another border. */
BorderSize (const BorderSize&) = default;
/** Creates a border with the given gaps. */
BorderSize (ValueType topGap, ValueType leftGap, ValueType bottomGap, ValueType rightGap) noexcept
: top (topGap), left (leftGap), bottom (bottomGap), right (rightGap)
{
}
/** Creates a border with the given gap on all sides. */
explicit BorderSize (ValueType allGaps) noexcept
: top (allGaps), left (allGaps), bottom (allGaps), right (allGaps)
{
}
//==============================================================================
/** Returns the gap that should be left at the top of the region. */
ValueType getTop() const noexcept { return top; }
/** Returns the gap that should be left at the left of the region. */
ValueType getLeft() const noexcept { return left; }
/** Returns the gap that should be left at the bottom of the region. */
ValueType getBottom() const noexcept { return bottom; }
/** Returns the gap that should be left at the right of the region. */
ValueType getRight() const noexcept { return right; }
/** Returns the sum of the top and bottom gaps. */
ValueType getTopAndBottom() const noexcept { return top + bottom; }
/** Returns the sum of the left and right gaps. */
ValueType getLeftAndRight() const noexcept { return left + right; }
/** Returns true if this border has no thickness along any edge. */
bool isEmpty() const noexcept { return left + right + top + bottom == ValueType(); }
//==============================================================================
/** Changes the top gap. */
void setTop (ValueType newTopGap) noexcept { top = newTopGap; }
/** Changes the left gap. */
void setLeft (ValueType newLeftGap) noexcept { left = newLeftGap; }
/** Changes the bottom gap. */
void setBottom (ValueType newBottomGap) noexcept { bottom = newBottomGap; }
/** Changes the right gap. */
void setRight (ValueType newRightGap) noexcept { right = newRightGap; }
//==============================================================================
/** Returns a rectangle with these borders removed from it. */
Rectangle<ValueType> subtractedFrom (const Rectangle<ValueType>& original) const noexcept
{
return Rectangle<ValueType> (original.getX() + left,
original.getY() + top,
original.getWidth() - (left + right),
original.getHeight() - (top + bottom));
}
/** Removes this border from a given rectangle. */
void subtractFrom (Rectangle<ValueType>& rectangle) const noexcept
{
rectangle = subtractedFrom (rectangle);
}
/** Returns a rectangle with these borders added around it. */
Rectangle<ValueType> addedTo (const Rectangle<ValueType>& original) const noexcept
{
return Rectangle<ValueType> (original.getX() - left,
original.getY() - top,
original.getWidth() + (left + right),
original.getHeight() + (top + bottom));
}
/** Adds this border around a given rectangle. */
void addTo (Rectangle<ValueType>& rectangle) const noexcept
{
rectangle = addedTo (rectangle);
}
//==============================================================================
bool operator== (const BorderSize& other) const noexcept
{
return top == other.top && left == other.left && bottom == other.bottom && right == other.right;
}
bool operator!= (const BorderSize& other) const noexcept
{
return ! operator== (other);
}
private:
//==============================================================================
ValueType top{}, left{}, bottom{}, right{};
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
Specifies a set of gaps to be left around the sides of a rectangle.
This is basically the size of the spaces at the top, bottom, left and right of
a rectangle. It's used by various component classes to specify borders.
@see Rectangle
@tags{Graphics}
*/
template <typename ValueType>
class BorderSize
{
auto tie() const { return std::tie (top, left, bottom, right); }
public:
//==============================================================================
/** Creates a null border.
All sizes are left as 0.
*/
BorderSize() = default;
/** Creates a border with the given gaps. */
BorderSize (ValueType topGap, ValueType leftGap, ValueType bottomGap, ValueType rightGap) noexcept
: top (topGap), left (leftGap), bottom (bottomGap), right (rightGap)
{
}
/** Creates a border with the given gap on all sides. */
explicit BorderSize (ValueType allGaps) noexcept
: top (allGaps), left (allGaps), bottom (allGaps), right (allGaps)
{
}
//==============================================================================
/** Returns the gap that should be left at the top of the region. */
ValueType getTop() const noexcept { return top; }
/** Returns the gap that should be left at the left of the region. */
ValueType getLeft() const noexcept { return left; }
/** Returns the gap that should be left at the bottom of the region. */
ValueType getBottom() const noexcept { return bottom; }
/** Returns the gap that should be left at the right of the region. */
ValueType getRight() const noexcept { return right; }
/** Returns the sum of the top and bottom gaps. */
ValueType getTopAndBottom() const noexcept { return top + bottom; }
/** Returns the sum of the left and right gaps. */
ValueType getLeftAndRight() const noexcept { return left + right; }
/** Returns true if this border has no thickness along any edge. */
bool isEmpty() const noexcept { return left + right + top + bottom == ValueType(); }
//==============================================================================
/** Changes the top gap. */
void setTop (ValueType newTopGap) noexcept { top = newTopGap; }
/** Changes the left gap. */
void setLeft (ValueType newLeftGap) noexcept { left = newLeftGap; }
/** Changes the bottom gap. */
void setBottom (ValueType newBottomGap) noexcept { bottom = newBottomGap; }
/** Changes the right gap. */
void setRight (ValueType newRightGap) noexcept { right = newRightGap; }
//==============================================================================
/** Returns a rectangle with these borders removed from it. */
Rectangle<ValueType> subtractedFrom (const Rectangle<ValueType>& original) const noexcept
{
return { original.getX() + left,
original.getY() + top,
original.getWidth() - (left + right),
original.getHeight() - (top + bottom) };
}
/** Removes this border from a given rectangle. */
void subtractFrom (Rectangle<ValueType>& rectangle) const noexcept
{
rectangle = subtractedFrom (rectangle);
}
/** Returns a rectangle with these borders added around it. */
Rectangle<ValueType> addedTo (const Rectangle<ValueType>& original) const noexcept
{
return { original.getX() - left,
original.getY() - top,
original.getWidth() + (left + right),
original.getHeight() + (top + bottom) };
}
/** Adds this border around a given rectangle. */
void addTo (Rectangle<ValueType>& rectangle) const noexcept
{
rectangle = addedTo (rectangle);
}
/** Removes this border from another border. */
BorderSize<ValueType> subtractedFrom (const BorderSize<ValueType>& other) const noexcept
{
return { other.top - top,
other.left - left,
other.bottom - bottom,
other.right - right };
}
/** Adds this border to another border. */
BorderSize<ValueType> addedTo (const BorderSize<ValueType>& other) const noexcept
{
return { other.top + top,
other.left + left,
other.bottom + bottom,
other.right + right };
}
/** Multiplies each member of the border by a scalar. */
template <typename ScalarType>
BorderSize<ValueType> multipliedBy (ScalarType scalar) const noexcept
{
return { static_cast<ValueType> (scalar * top),
static_cast<ValueType> (scalar * left),
static_cast<ValueType> (scalar * bottom),
static_cast<ValueType> (scalar * right) };
}
//==============================================================================
bool operator== (const BorderSize& other) const noexcept { return tie() == other.tie(); }
bool operator!= (const BorderSize& other) const noexcept { return tie() != other.tie(); }
private:
//==============================================================================
ValueType top{}, left{}, bottom{}, right{};
};
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -1,226 +1,226 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
A table of horizontal scan-line segments - used for rasterising Paths.
@see Path, Graphics
@tags{Graphics}
*/
class JUCE_API EdgeTable
{
public:
//==============================================================================
/** Creates an edge table containing a path.
A table is created with a fixed vertical range, and only sections of the path
which lie within this range will be added to the table.
@param clipLimits only the region of the path that lies within this area will be added
@param pathToAdd the path to add to the table
@param transform a transform to apply to the path being added
*/
EdgeTable (Rectangle<int> clipLimits,
const Path& pathToAdd,
const AffineTransform& transform);
/** Creates an edge table containing a rectangle. */
explicit EdgeTable (Rectangle<int> rectangleToAdd);
/** Creates an edge table containing a rectangle. */
explicit EdgeTable (Rectangle<float> rectangleToAdd);
/** Creates an edge table containing a rectangle list. */
explicit EdgeTable (const RectangleList<int>& rectanglesToAdd);
/** Creates an edge table containing a rectangle list. */
explicit EdgeTable (const RectangleList<float>& rectanglesToAdd);
/** Creates a copy of another edge table. */
EdgeTable (const EdgeTable&);
/** Copies from another edge table. */
EdgeTable& operator= (const EdgeTable&);
/** Destructor. */
~EdgeTable();
//==============================================================================
void clipToRectangle (Rectangle<int> r);
void excludeRectangle (Rectangle<int> r);
void clipToEdgeTable (const EdgeTable&);
void clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels);
bool isEmpty() noexcept;
const Rectangle<int>& getMaximumBounds() const noexcept { return bounds; }
void translate (float dx, int dy) noexcept;
/** Scales all the alpha-levels in the table by the given multiplier. */
void multiplyLevels (float factor);
/** Reduces the amount of space the table has allocated.
This will shrink the table down to use as little memory as possible - useful for
read-only tables that get stored and re-used for rendering.
*/
void optimiseTable();
//==============================================================================
/** Iterates the lines in the table, for rendering.
This function will iterate each line in the table, and call a user-defined class
to render each pixel or continuous line of pixels that the table contains.
@param iterationCallback this templated class must contain the following methods:
@code
inline void setEdgeTableYPos (int y);
inline void handleEdgeTablePixel (int x, int alphaLevel) const;
inline void handleEdgeTablePixelFull (int x) const;
inline void handleEdgeTableLine (int x, int width, int alphaLevel) const;
inline void handleEdgeTableLineFull (int x, int width) const;
@endcode
(these don't necessarily have to be 'const', but it might help it go faster)
*/
template <class EdgeTableIterationCallback>
void iterate (EdgeTableIterationCallback& iterationCallback) const noexcept
{
const int* lineStart = table;
for (int y = 0; y < bounds.getHeight(); ++y)
{
const int* line = lineStart;
lineStart += lineStrideElements;
int numPoints = line[0];
if (--numPoints > 0)
{
int x = *++line;
jassert ((x / scale) >= bounds.getX() && (x / scale) < bounds.getRight());
int levelAccumulator = 0;
iterationCallback.setEdgeTableYPos (bounds.getY() + y);
while (--numPoints >= 0)
{
const int level = *++line;
jassert (isPositiveAndBelow (level, scale));
const int endX = *++line;
jassert (endX >= x);
const int endOfRun = (endX / scale);
if (endOfRun == (x / scale))
{
// small segment within the same pixel, so just save it for the next
// time round..
levelAccumulator += (endX - x) * level;
}
else
{
// plot the fist pixel of this segment, including any accumulated
// levels from smaller segments that haven't been drawn yet
levelAccumulator += (0x100 - (x & 0xff)) * level;
levelAccumulator /= scale;
x /= scale;
if (levelAccumulator > 0)
{
if (levelAccumulator >= 255)
iterationCallback.handleEdgeTablePixelFull (x);
else
iterationCallback.handleEdgeTablePixel (x, levelAccumulator);
}
// if there's a run of similar pixels, do it all in one go..
if (level > 0)
{
jassert (endOfRun <= bounds.getRight());
const int numPix = endOfRun - ++x;
if (numPix > 0)
iterationCallback.handleEdgeTableLine (x, numPix, level);
}
// save the bit at the end to be drawn next time round the loop.
levelAccumulator = (endX & 0xff) * level;
}
x = endX;
}
levelAccumulator /= scale;
if (levelAccumulator > 0)
{
x /= scale;
jassert (x >= bounds.getX() && x < bounds.getRight());
if (levelAccumulator >= 255)
iterationCallback.handleEdgeTablePixelFull (x);
else
iterationCallback.handleEdgeTablePixel (x, levelAccumulator);
}
}
}
}
private:
//==============================================================================
static constexpr auto defaultEdgesPerLine = 32;
static constexpr auto scale = 256;
//==============================================================================
// table line format: number of points; point0 x, point0 levelDelta, point1 x, point1 levelDelta, etc
struct LineItem
{
int x, level;
bool operator< (const LineItem& other) const noexcept { return x < other.x; }
};
HeapBlock<int> table;
Rectangle<int> bounds;
int maxEdgesPerLine, lineStrideElements;
bool needToCheckEmptiness = true;
void allocate();
void clearLineSizes() noexcept;
void addEdgePoint (int x, int y, int winding);
void addEdgePointPair (int x1, int x2, int y, int winding);
void remapTableForNumEdges (int newNumEdgesPerLine);
void remapWithExtraSpace (int numPointsNeeded);
void intersectWithEdgeTableLine (int y, const int* otherLine);
void clipEdgeTableLineToRange (int* line, int x1, int x2) noexcept;
void sanitiseLevels (bool useNonZeroWinding) noexcept;
static void copyEdgeTableData (int* dest, int destLineStride, const int* src, int srcLineStride, int numLines) noexcept;
JUCE_LEAK_DETECTOR (EdgeTable)
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
A table of horizontal scan-line segments - used for rasterising Paths.
@see Path, Graphics
@tags{Graphics}
*/
class JUCE_API EdgeTable
{
public:
//==============================================================================
/** Creates an edge table containing a path.
A table is created with a fixed vertical range, and only sections of the path
which lie within this range will be added to the table.
@param clipLimits only the region of the path that lies within this area will be added
@param pathToAdd the path to add to the table
@param transform a transform to apply to the path being added
*/
EdgeTable (Rectangle<int> clipLimits,
const Path& pathToAdd,
const AffineTransform& transform);
/** Creates an edge table containing a rectangle. */
explicit EdgeTable (Rectangle<int> rectangleToAdd);
/** Creates an edge table containing a rectangle. */
explicit EdgeTable (Rectangle<float> rectangleToAdd);
/** Creates an edge table containing a rectangle list. */
explicit EdgeTable (const RectangleList<int>& rectanglesToAdd);
/** Creates an edge table containing a rectangle list. */
explicit EdgeTable (const RectangleList<float>& rectanglesToAdd);
/** Creates a copy of another edge table. */
EdgeTable (const EdgeTable&);
/** Copies from another edge table. */
EdgeTable& operator= (const EdgeTable&);
/** Destructor. */
~EdgeTable();
//==============================================================================
void clipToRectangle (Rectangle<int> r);
void excludeRectangle (Rectangle<int> r);
void clipToEdgeTable (const EdgeTable&);
void clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels);
bool isEmpty() noexcept;
const Rectangle<int>& getMaximumBounds() const noexcept { return bounds; }
void translate (float dx, int dy) noexcept;
/** Scales all the alpha-levels in the table by the given multiplier. */
void multiplyLevels (float factor);
/** Reduces the amount of space the table has allocated.
This will shrink the table down to use as little memory as possible - useful for
read-only tables that get stored and re-used for rendering.
*/
void optimiseTable();
//==============================================================================
/** Iterates the lines in the table, for rendering.
This function will iterate each line in the table, and call a user-defined class
to render each pixel or continuous line of pixels that the table contains.
@param iterationCallback this templated class must contain the following methods:
@code
inline void setEdgeTableYPos (int y);
inline void handleEdgeTablePixel (int x, int alphaLevel) const;
inline void handleEdgeTablePixelFull (int x) const;
inline void handleEdgeTableLine (int x, int width, int alphaLevel) const;
inline void handleEdgeTableLineFull (int x, int width) const;
@endcode
(these don't necessarily have to be 'const', but it might help it go faster)
*/
template <class EdgeTableIterationCallback>
void iterate (EdgeTableIterationCallback& iterationCallback) const noexcept
{
const int* lineStart = table;
for (int y = 0; y < bounds.getHeight(); ++y)
{
const int* line = lineStart;
lineStart += lineStrideElements;
int numPoints = line[0];
if (--numPoints > 0)
{
int x = *++line;
jassert ((x / scale) >= bounds.getX() && (x / scale) < bounds.getRight());
int levelAccumulator = 0;
iterationCallback.setEdgeTableYPos (bounds.getY() + y);
while (--numPoints >= 0)
{
const int level = *++line;
jassert (isPositiveAndBelow (level, scale));
const int endX = *++line;
jassert (endX >= x);
const int endOfRun = (endX / scale);
if (endOfRun == (x / scale))
{
// small segment within the same pixel, so just save it for the next
// time round..
levelAccumulator += (endX - x) * level;
}
else
{
// plot the fist pixel of this segment, including any accumulated
// levels from smaller segments that haven't been drawn yet
levelAccumulator += (0x100 - (x & 0xff)) * level;
levelAccumulator /= scale;
x /= scale;
if (levelAccumulator > 0)
{
if (levelAccumulator >= 255)
iterationCallback.handleEdgeTablePixelFull (x);
else
iterationCallback.handleEdgeTablePixel (x, levelAccumulator);
}
// if there's a run of similar pixels, do it all in one go..
if (level > 0)
{
jassert (endOfRun <= bounds.getRight());
const int numPix = endOfRun - ++x;
if (numPix > 0)
iterationCallback.handleEdgeTableLine (x, numPix, level);
}
// save the bit at the end to be drawn next time round the loop.
levelAccumulator = (endX & 0xff) * level;
}
x = endX;
}
levelAccumulator /= scale;
if (levelAccumulator > 0)
{
x /= scale;
jassert (x >= bounds.getX() && x < bounds.getRight());
if (levelAccumulator >= 255)
iterationCallback.handleEdgeTablePixelFull (x);
else
iterationCallback.handleEdgeTablePixel (x, levelAccumulator);
}
}
}
}
private:
//==============================================================================
static constexpr auto defaultEdgesPerLine = 32;
static constexpr auto scale = 256;
//==============================================================================
// table line format: number of points; point0 x, point0 levelDelta, point1 x, point1 levelDelta, etc
struct LineItem
{
int x, level;
bool operator< (const LineItem& other) const noexcept { return x < other.x; }
};
HeapBlock<int> table;
Rectangle<int> bounds;
int maxEdgesPerLine, lineStrideElements;
bool needToCheckEmptiness = true;
void allocate();
void clearLineSizes() noexcept;
void addEdgePoint (int x, int y, int winding);
void addEdgePointPair (int x1, int x2, int y, int winding);
void remapTableForNumEdges (int newNumEdgesPerLine);
void remapWithExtraSpace (int numPointsNeeded);
void intersectWithEdgeTableLine (int y, const int* otherLine);
void clipEdgeTableLineToRange (int* line, int x1, int x2) noexcept;
void sanitiseLevels (bool useNonZeroWinding) noexcept;
static void copyEdgeTableData (int* dest, int destLineStride, const int* src, int srcLineStride, int numLines) noexcept;
JUCE_LEAK_DETECTOR (EdgeTable)
};
} // namespace juce

View File

@ -1,421 +1,442 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
Represents a line.
This class contains a bunch of useful methods for various geometric
tasks.
The ValueType template parameter should be a primitive type - float or double
are what it's designed for. Integer types will work in a basic way, but some methods
that perform mathematical operations may not compile, or they may not produce
sensible results.
@see Point, Rectangle, Path, Graphics::drawLine
@tags{Graphics}
*/
template <typename ValueType>
class Line
{
public:
//==============================================================================
/** Creates a line, using (0, 0) as its start and end points. */
Line() = default;
/** Creates a copy of another line. */
Line (const Line&) = default;
/** Creates a line based on the coordinates of its start and end points. */
Line (ValueType startX, ValueType startY, ValueType endX, ValueType endY) noexcept
: start (startX, startY), end (endX, endY)
{
}
/** Creates a line from its start and end points. */
Line (Point<ValueType> startPoint, Point<ValueType> endPoint) noexcept
: start (startPoint), end (endPoint)
{
}
/** Copies a line from another one. */
Line& operator= (const Line&) = default;
/** Destructor. */
~Line() = default;
//==============================================================================
/** Returns the x coordinate of the line's start point. */
inline ValueType getStartX() const noexcept { return start.x; }
/** Returns the y coordinate of the line's start point. */
inline ValueType getStartY() const noexcept { return start.y; }
/** Returns the x coordinate of the line's end point. */
inline ValueType getEndX() const noexcept { return end.x; }
/** Returns the y coordinate of the line's end point. */
inline ValueType getEndY() const noexcept { return end.y; }
/** Returns the line's start point. */
inline Point<ValueType> getStart() const noexcept { return start; }
/** Returns the line's end point. */
inline Point<ValueType> getEnd() const noexcept { return end; }
/** Changes this line's start point */
void setStart (ValueType newStartX, ValueType newStartY) noexcept { start.setXY (newStartX, newStartY); }
/** Changes this line's end point */
void setEnd (ValueType newEndX, ValueType newEndY) noexcept { end.setXY (newEndX, newEndY); }
/** Changes this line's start point */
void setStart (const Point<ValueType> newStart) noexcept { start = newStart; }
/** Changes this line's end point */
void setEnd (const Point<ValueType> newEnd) noexcept { end = newEnd; }
/** Returns a line that is the same as this one, but with the start and end reversed, */
Line reversed() const noexcept { return { end, start }; }
/** Applies an affine transform to the line's start and end points. */
void applyTransform (const AffineTransform& transform) noexcept
{
start.applyTransform (transform);
end.applyTransform (transform);
}
//==============================================================================
/** Returns the length of the line. */
ValueType getLength() const noexcept { return start.getDistanceFrom (end); }
/** Returns the length of the line. */
ValueType getLengthSquared() const noexcept { return start.getDistanceSquaredFrom (end); }
/** Returns true if the line's start and end x coordinates are the same. */
bool isVertical() const noexcept { return start.x == end.x; }
/** Returns true if the line's start and end y coordinates are the same. */
bool isHorizontal() const noexcept { return start.y == end.y; }
/** Returns the line's angle.
This value is the number of radians clockwise from the 12 o'clock direction,
where the line's start point is considered to be at the centre.
*/
typename Point<ValueType>::FloatType getAngle() const noexcept { return start.getAngleToPoint (end); }
/** Creates a line from a start point, length and angle.
This angle is the number of radians clockwise from the 12 o'clock direction,
where the line's start point is considered to be at the centre.
*/
static Line fromStartAndAngle (Point<ValueType> startPoint, ValueType length, ValueType angle) noexcept
{
return { startPoint, startPoint.getPointOnCircumference (length, angle) };
}
//==============================================================================
/** Casts this line to float coordinates. */
Line<float> toFloat() const noexcept { return { start.toFloat(), end.toFloat() }; }
/** Casts this line to double coordinates. */
Line<double> toDouble() const noexcept { return { start.toDouble(), end.toDouble() }; }
//==============================================================================
/** Compares two lines. */
bool operator== (Line other) const noexcept { return start == other.start && end == other.end; }
/** Compares two lines. */
bool operator!= (Line other) const noexcept { return start != other.start || end != other.end; }
//==============================================================================
/** Finds the intersection between two lines.
@param line the line to intersect with
@returns the point at which the lines intersect, even if this lies beyond the end of the lines
*/
Point<ValueType> getIntersection (Line line) const noexcept
{
Point<ValueType> p;
findIntersection (start, end, line.start, line.end, p);
return p;
}
/** Finds the intersection between two lines.
@param line the other line
@param intersection the position of the point where the lines meet (or
where they would meet if they were infinitely long)
the intersection (if the lines intersect). If the lines
are parallel, this will just be set to the position
of one of the line's endpoints.
@returns true if the line segments intersect; false if they don't. Even if they
don't intersect, the intersection coordinates returned will still
be valid
*/
bool intersects (Line line, Point<ValueType>& intersection) const noexcept
{
return findIntersection (start, end, line.start, line.end, intersection);
}
/** Returns true if this line intersects another. */
bool intersects (Line other) const noexcept
{
Point<ValueType> ignored;
return findIntersection (start, end, other.start, other.end, ignored);
}
//==============================================================================
/** Returns the location of the point which is a given distance along this line.
@param distanceFromStart the distance to move along the line from its
start point. This value can be negative or longer
than the line itself
@see getPointAlongLineProportionally
*/
Point<ValueType> getPointAlongLine (ValueType distanceFromStart) const noexcept
{
return start + (end - start) * (distanceFromStart / getLength());
}
/** Returns a point which is a certain distance along and to the side of this line.
This effectively moves a given distance along the line, then another distance
perpendicularly to this, and returns the resulting position.
@param distanceFromStart the distance to move along the line from its
start point. This value can be negative or longer
than the line itself
@param perpendicularDistance how far to move sideways from the line. If you're
looking along the line from its start towards its
end, then a positive value here will move to the
right, negative value move to the left.
*/
Point<ValueType> getPointAlongLine (ValueType distanceFromStart,
ValueType perpendicularDistance) const noexcept
{
auto delta = end - start;
auto length = juce_hypot ((double) delta.x,
(double) delta.y);
if (length <= 0)
return start;
return { start.x + static_cast<ValueType> ((delta.x * distanceFromStart - delta.y * perpendicularDistance) / length),
start.y + static_cast<ValueType> ((delta.y * distanceFromStart + delta.x * perpendicularDistance) / length) };
}
/** Returns the location of the point which is a given distance along this line
proportional to the line's length.
@param proportionOfLength the distance to move along the line from its
start point, in multiples of the line's length.
So a value of 0.0 will return the line's start point
and a value of 1.0 will return its end point. (This value
can be negative or greater than 1.0).
@see getPointAlongLine
*/
Point<ValueType> getPointAlongLineProportionally (typename Point<ValueType>::FloatType proportionOfLength) const noexcept
{
return start + (end - start) * proportionOfLength;
}
/** Returns the smallest distance between this line segment and a given point.
So if the point is close to the line, this will return the perpendicular
distance from the line; if the point is a long way beyond one of the line's
end-point's, it'll return the straight-line distance to the nearest end-point.
pointOnLine receives the position of the point that is found.
@returns the point's distance from the line
@see getPositionAlongLineOfNearestPoint
*/
ValueType getDistanceFromPoint (Point<ValueType> targetPoint,
Point<ValueType>& pointOnLine) const noexcept
{
auto delta = end - start;
auto length = delta.x * delta.x + delta.y * delta.y;
if (length > 0)
{
auto prop = ((targetPoint.x - start.x) * delta.x
+ (targetPoint.y - start.y) * delta.y) / (double) length;
if (prop >= 0 && prop <= 1.0)
{
pointOnLine = start + delta * prop;
return targetPoint.getDistanceFrom (pointOnLine);
}
}
auto fromStart = targetPoint.getDistanceFrom (start);
auto fromEnd = targetPoint.getDistanceFrom (end);
if (fromStart < fromEnd)
{
pointOnLine = start;
return fromStart;
}
pointOnLine = end;
return fromEnd;
}
/** Finds the point on this line which is nearest to a given point, and
returns its position as a proportional position along the line.
@returns a value 0 to 1.0 which is the distance along this line from the
line's start to the point which is nearest to the point passed-in. To
turn this number into a position, use getPointAlongLineProportionally().
@see getDistanceFromPoint, getPointAlongLineProportionally
*/
ValueType findNearestProportionalPositionTo (Point<ValueType> point) const noexcept
{
auto delta = end - start;
auto length = delta.x * delta.x + delta.y * delta.y;
return length <= 0 ? 0
: jlimit (ValueType(), static_cast<ValueType> (1),
static_cast<ValueType> ((((point.x - start.x) * delta.x
+ (point.y - start.y) * delta.y) / length)));
}
/** Finds the point on this line which is nearest to a given point.
@see getDistanceFromPoint, findNearestProportionalPositionTo
*/
Point<ValueType> findNearestPointTo (Point<ValueType> point) const noexcept
{
return getPointAlongLineProportionally (findNearestProportionalPositionTo (point));
}
/** Returns true if the given point lies above this line.
The return value is true if the point's y coordinate is less than the y
coordinate of this line at the given x (assuming the line extends infinitely
in both directions).
*/
bool isPointAbove (Point<ValueType> point) const noexcept
{
return start.x != end.x
&& point.y < ((end.y - start.y) * (point.x - start.x)) / (end.x - start.x) + start.y;
}
//==============================================================================
/** Returns a shortened copy of this line.
This will chop off part of the start of this line by a certain amount, (leaving the
end-point the same), and return the new line.
*/
Line withShortenedStart (ValueType distanceToShortenBy) const noexcept
{
return { getPointAlongLine (jmin (distanceToShortenBy, getLength())), end };
}
/** Returns a shortened copy of this line.
This will chop off part of the end of this line by a certain amount, (leaving the
start-point the same), and return the new line.
*/
Line withShortenedEnd (ValueType distanceToShortenBy) const noexcept
{
auto length = getLength();
return { start, getPointAlongLine (length - jmin (distanceToShortenBy, length)) };
}
private:
//==============================================================================
Point<ValueType> start, end;
static bool isZeroToOne (ValueType v) noexcept { return v >= 0 && v <= static_cast<ValueType> (1); }
static bool findIntersection (const Point<ValueType> p1, const Point<ValueType> p2,
const Point<ValueType> p3, const Point<ValueType> p4,
Point<ValueType>& intersection) noexcept
{
if (p2 == p3)
{
intersection = p2;
return true;
}
auto d1 = p2 - p1;
auto d2 = p4 - p3;
auto divisor = d1.x * d2.y - d2.x * d1.y;
if (divisor == 0)
{
if (! (d1.isOrigin() || d2.isOrigin()))
{
if (d1.y == 0 && d2.y != 0)
{
auto along = (p1.y - p3.y) / d2.y;
intersection = p1.withX (p3.x + along * d2.x);
return isZeroToOne (along);
}
if (d2.y == 0 && d1.y != 0)
{
auto along = (p3.y - p1.y) / d1.y;
intersection = p3.withX (p1.x + along * d1.x);
return isZeroToOne (along);
}
if (d1.x == 0 && d2.x != 0)
{
auto along = (p1.x - p3.x) / d2.x;
intersection = p1.withY (p3.y + along * d2.y);
return isZeroToOne (along);
}
if (d2.x == 0 && d1.x != 0)
{
auto along = (p3.x - p1.x) / d1.x;
intersection = p3.withY (p1.y + along * d1.y);
return isZeroToOne (along);
}
}
intersection = (p2 + p3) / static_cast<ValueType> (2);
return false;
}
auto along1 = ((p1.y - p3.y) * d2.x - (p1.x - p3.x) * d2.y) / divisor;
intersection = p1 + d1 * along1;
if (! isZeroToOne (along1))
return false;
auto along2 = ((p1.y - p3.y) * d1.x - (p1.x - p3.x) * d1.y) / divisor;
return isZeroToOne (along2);
}
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
Represents a line.
This class contains a bunch of useful methods for various geometric
tasks.
The ValueType template parameter should be a primitive type - float or double
are what it's designed for. Integer types will work in a basic way, but some methods
that perform mathematical operations may not compile, or they may not produce
sensible results.
@see Point, Rectangle, Path, Graphics::drawLine
@tags{Graphics}
*/
template <typename ValueType>
class Line
{
public:
//==============================================================================
/** Creates a line, using (0, 0) as its start and end points. */
Line() = default;
/** Creates a copy of another line. */
Line (const Line&) = default;
/** Creates a line based on the coordinates of its start and end points. */
Line (ValueType startX, ValueType startY, ValueType endX, ValueType endY) noexcept
: start (startX, startY), end (endX, endY)
{
}
/** Creates a line from its start and end points. */
Line (Point<ValueType> startPoint, Point<ValueType> endPoint) noexcept
: start (startPoint), end (endPoint)
{
}
/** Copies a line from another one. */
Line& operator= (const Line&) = default;
/** Destructor. */
~Line() = default;
//==============================================================================
/** Returns the x coordinate of the line's start point. */
inline ValueType getStartX() const noexcept { return start.x; }
/** Returns the y coordinate of the line's start point. */
inline ValueType getStartY() const noexcept { return start.y; }
/** Returns the x coordinate of the line's end point. */
inline ValueType getEndX() const noexcept { return end.x; }
/** Returns the y coordinate of the line's end point. */
inline ValueType getEndY() const noexcept { return end.y; }
/** Returns the line's start point. */
inline Point<ValueType> getStart() const noexcept { return start; }
/** Returns the line's end point. */
inline Point<ValueType> getEnd() const noexcept { return end; }
/** Changes this line's start point */
void setStart (ValueType newStartX, ValueType newStartY) noexcept { start.setXY (newStartX, newStartY); }
/** Changes this line's end point */
void setEnd (ValueType newEndX, ValueType newEndY) noexcept { end.setXY (newEndX, newEndY); }
/** Changes this line's start point */
void setStart (const Point<ValueType> newStart) noexcept { start = newStart; }
/** Changes this line's end point */
void setEnd (const Point<ValueType> newEnd) noexcept { end = newEnd; }
/** Returns a line that is the same as this one, but with the start and end reversed, */
Line reversed() const noexcept { return { end, start }; }
/** Applies an affine transform to the line's start and end points. */
void applyTransform (const AffineTransform& transform) noexcept
{
start.applyTransform (transform);
end.applyTransform (transform);
}
//==============================================================================
/** Returns the length of the line. */
ValueType getLength() const noexcept { return start.getDistanceFrom (end); }
/** Returns the length of the line. */
ValueType getLengthSquared() const noexcept { return start.getDistanceSquaredFrom (end); }
/** Returns true if the line's start and end x coordinates are the same. */
bool isVertical() const noexcept { return start.x == end.x; }
/** Returns true if the line's start and end y coordinates are the same. */
bool isHorizontal() const noexcept { return start.y == end.y; }
/** Returns the line's angle.
This value is the number of radians clockwise from the 12 o'clock direction,
where the line's start point is considered to be at the centre.
*/
typename Point<ValueType>::FloatType getAngle() const noexcept { return start.getAngleToPoint (end); }
/** Creates a line from a start point, length and angle.
This angle is the number of radians clockwise from the 12 o'clock direction,
where the line's start point is considered to be at the centre.
*/
static Line fromStartAndAngle (Point<ValueType> startPoint, ValueType length, ValueType angle) noexcept
{
return { startPoint, startPoint.getPointOnCircumference (length, angle) };
}
//==============================================================================
/** Casts this line to float coordinates. */
Line<float> toFloat() const noexcept { return { start.toFloat(), end.toFloat() }; }
/** Casts this line to double coordinates. */
Line<double> toDouble() const noexcept { return { start.toDouble(), end.toDouble() }; }
//==============================================================================
/** Compares two lines. */
bool operator== (Line other) const noexcept { return start == other.start && end == other.end; }
/** Compares two lines. */
bool operator!= (Line other) const noexcept { return start != other.start || end != other.end; }
//==============================================================================
/** Finds the intersection between two lines.
@param line the line to intersect with
@returns the point at which the lines intersect, even if this lies beyond the end of the lines
*/
Point<ValueType> getIntersection (Line line) const noexcept
{
Point<ValueType> p;
findIntersection (start, end, line.start, line.end, p);
return p;
}
/** Finds the intersection between two lines.
@param line the other line
@param intersection the position of the point where the lines meet (or
where they would meet if they were infinitely long)
the intersection (if the lines intersect). If the lines
are parallel, this will just be set to the position
of one of the line's endpoints.
@returns true if the line segments intersect; false if they don't. Even if they
don't intersect, the intersection coordinates returned will still
be valid
*/
bool intersects (Line line, Point<ValueType>& intersection) const noexcept
{
return findIntersection (start, end, line.start, line.end, intersection);
}
/** Returns true if this line intersects another. */
bool intersects (Line other) const noexcept
{
Point<ValueType> ignored;
return findIntersection (start, end, other.start, other.end, ignored);
}
//==============================================================================
/** Returns the location of the point which is a given distance along this line.
@param distanceFromStart the distance to move along the line from its
start point. This value can be negative or longer
than the line itself
@see getPointAlongLineProportionally
*/
Point<ValueType> getPointAlongLine (ValueType distanceFromStart) const noexcept
{
const auto length = getLength();
return length == 0 ? start : start + (end - start) * (distanceFromStart / length);
}
/** Returns a point which is a certain distance along and to the side of this line.
This effectively moves a given distance along the line, then another distance
perpendicularly to this, and returns the resulting position.
@param distanceFromStart the distance to move along the line from its
start point. This value can be negative or longer
than the line itself
@param perpendicularDistance how far to move sideways from the line. If you're
looking along the line from its start towards its
end, then a positive value here will move to the
right, negative value move to the left.
*/
Point<ValueType> getPointAlongLine (ValueType distanceFromStart,
ValueType perpendicularDistance) const noexcept
{
auto delta = end - start;
auto length = juce_hypot ((double) delta.x,
(double) delta.y);
if (length <= 0)
return start;
return { start.x + static_cast<ValueType> ((delta.x * distanceFromStart - delta.y * perpendicularDistance) / length),
start.y + static_cast<ValueType> ((delta.y * distanceFromStart + delta.x * perpendicularDistance) / length) };
}
/** Returns the location of the point which is a given distance along this line
proportional to the line's length.
@param proportionOfLength the distance to move along the line from its
start point, in multiples of the line's length.
So a value of 0.0 will return the line's start point
and a value of 1.0 will return its end point. (This value
can be negative or greater than 1.0).
@see getPointAlongLine
*/
Point<ValueType> getPointAlongLineProportionally (typename Point<ValueType>::FloatType proportionOfLength) const noexcept
{
return start + (end - start) * proportionOfLength;
}
/** Returns the smallest distance between this line segment and a given point.
So if the point is close to the line, this will return the perpendicular
distance from the line; if the point is a long way beyond one of the line's
end-point's, it'll return the straight-line distance to the nearest end-point.
pointOnLine receives the position of the point that is found.
@returns the point's distance from the line
@see getPositionAlongLineOfNearestPoint
*/
ValueType getDistanceFromPoint (Point<ValueType> targetPoint,
Point<ValueType>& pointOnLine) const noexcept
{
auto delta = end - start;
auto length = delta.x * delta.x + delta.y * delta.y;
if (length > 0)
{
auto prop = ((targetPoint.x - start.x) * delta.x
+ (targetPoint.y - start.y) * delta.y) / (double) length;
if (prop >= 0 && prop <= 1.0)
{
pointOnLine = start + delta * prop;
return targetPoint.getDistanceFrom (pointOnLine);
}
}
auto fromStart = targetPoint.getDistanceFrom (start);
auto fromEnd = targetPoint.getDistanceFrom (end);
if (fromStart < fromEnd)
{
pointOnLine = start;
return fromStart;
}
pointOnLine = end;
return fromEnd;
}
/** Finds the point on this line which is nearest to a given point, and
returns its position as a proportional position along the line.
@returns a value 0 to 1.0 which is the distance along this line from the
line's start to the point which is nearest to the point passed-in. To
turn this number into a position, use getPointAlongLineProportionally().
@see getDistanceFromPoint, getPointAlongLineProportionally
*/
ValueType findNearestProportionalPositionTo (Point<ValueType> point) const noexcept
{
auto delta = end - start;
auto length = delta.x * delta.x + delta.y * delta.y;
return length <= 0 ? 0
: jlimit (ValueType(), static_cast<ValueType> (1),
static_cast<ValueType> ((((point.x - start.x) * delta.x
+ (point.y - start.y) * delta.y) / length)));
}
/** Finds the point on this line which is nearest to a given point.
@see getDistanceFromPoint, findNearestProportionalPositionTo
*/
Point<ValueType> findNearestPointTo (Point<ValueType> point) const noexcept
{
return getPointAlongLineProportionally (findNearestProportionalPositionTo (point));
}
/** Returns true if the given point lies above this line.
The return value is true if the point's y coordinate is less than the y
coordinate of this line at the given x (assuming the line extends infinitely
in both directions).
*/
bool isPointAbove (Point<ValueType> point) const noexcept
{
return start.x != end.x
&& point.y < ((end.y - start.y) * (point.x - start.x)) / (end.x - start.x) + start.y;
}
/** Returns a lengthened copy of this line.
This will extend the line by a certain amount by moving the start away from the end
(leaving the end-point the same), and return the new line.
*/
Line withLengthenedStart (ValueType distanceToLengthenBy) const noexcept
{
return withShortenedStart (-distanceToLengthenBy);
}
//==============================================================================
/** Returns a shortened copy of this line.
This will chop off part of the start of this line by a certain amount, (leaving the
end-point the same), and return the new line.
*/
Line withShortenedStart (ValueType distanceToShortenBy) const noexcept
{
return { getPointAlongLine (jmin (distanceToShortenBy, getLength())), end };
}
/** Returns a lengthened copy of this line.
This will extend the line by a certain amount by moving the end away from the start
(leaving the start-point the same), and return the new line.
*/
Line withLengthenedEnd (ValueType distanceToLengthenBy) const noexcept
{
return withShortenedEnd (-distanceToLengthenBy);
}
/** Returns a shortened copy of this line.
This will chop off part of the end of this line by a certain amount, (leaving the
start-point the same), and return the new line.
*/
Line withShortenedEnd (ValueType distanceToShortenBy) const noexcept
{
auto length = getLength();
return { start, getPointAlongLine (length - jmin (distanceToShortenBy, length)) };
}
private:
//==============================================================================
Point<ValueType> start, end;
static bool isZeroToOne (ValueType v) noexcept { return v >= 0 && v <= static_cast<ValueType> (1); }
static bool findIntersection (const Point<ValueType> p1, const Point<ValueType> p2,
const Point<ValueType> p3, const Point<ValueType> p4,
Point<ValueType>& intersection) noexcept
{
if (p2 == p3)
{
intersection = p2;
return true;
}
auto d1 = p2 - p1;
auto d2 = p4 - p3;
auto divisor = d1.x * d2.y - d2.x * d1.y;
if (divisor == 0)
{
if (! (d1.isOrigin() || d2.isOrigin()))
{
if (d1.y == 0 && d2.y != 0)
{
auto along = (p1.y - p3.y) / d2.y;
intersection = p1.withX (p3.x + along * d2.x);
return isZeroToOne (along);
}
if (d2.y == 0 && d1.y != 0)
{
auto along = (p3.y - p1.y) / d1.y;
intersection = p3.withX (p1.x + along * d1.x);
return isZeroToOne (along);
}
if (d1.x == 0 && d2.x != 0)
{
auto along = (p1.x - p3.x) / d2.x;
intersection = p1.withY (p3.y + along * d2.y);
return isZeroToOne (along);
}
if (d2.x == 0 && d1.x != 0)
{
auto along = (p3.x - p1.x) / d1.x;
intersection = p3.withY (p1.y + along * d1.y);
return isZeroToOne (along);
}
}
intersection = (p2 + p3) / static_cast<ValueType> (2);
return false;
}
auto along1 = ((p1.y - p3.y) * d2.x - (p1.x - p3.x) * d2.y) / divisor;
intersection = p1 + d1 * along1;
if (! isZeroToOne (along1))
return false;
auto along2 = ((p1.y - p3.y) * d1.x - (p1.x - p3.x) * d1.y) / divisor;
return isZeroToOne (along2);
}
};
} // namespace juce

View File

@ -1,185 +1,185 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
Represents a parallelogram that is defined by 3 points.
@see Rectangle, Point, Line
@tags{Graphics}
*/
template <typename ValueType>
class Parallelogram
{
public:
//==============================================================================
/** Creates a parallelogram with zero size at the origin.
*/
Parallelogram() = default;
/** Creates a copy of another parallelogram. */
Parallelogram (const Parallelogram&) = default;
/** Creates a parallelogram based on 3 points. */
Parallelogram (Point<ValueType> topLeftPosition,
Point<ValueType> topRightPosition,
Point<ValueType> bottomLeftPosition) noexcept
: topLeft (topLeftPosition), topRight (topRightPosition), bottomLeft (bottomLeftPosition)
{
}
/** Creates a parallelogram from a rectangle. */
Parallelogram (Rectangle<ValueType> rectangle) noexcept
: topLeft (rectangle.getTopLeft()),
topRight (rectangle.getTopRight()),
bottomLeft (rectangle.getBottomLeft())
{
}
Parallelogram& operator= (const Parallelogram&) = default;
/** Destructor. */
~Parallelogram() = default;
//==============================================================================
/** Returns true if the parallelogram has a width or height of more than zero. */
bool isEmpty() const noexcept { return topLeft != topRight || topLeft != bottomLeft; }
/** Returns true if the parallelogram's coordinates are all finite numbers, i.e. not NaN or infinity. */
inline bool isFinite() const noexcept { return topLeft.isFinite() && topRight.isFinite() && bottomLeft.isFinite(); }
/** Returns the width of the parallelogram (i.e. the straight-line distance between the top-left and top-right. */
inline ValueType getWidth() const noexcept { return Line<ValueType> (topLeft, topRight).getLength(); }
/** Returns the height of the parallelogram (i.e. the straight-line distance between the top-left and bottom-left. */
inline ValueType getHeight() const noexcept { return Line<ValueType> (topLeft, bottomLeft).getLength(); }
//==============================================================================
/** Returns the parallelogram's top-left position as a Point. */
Point<ValueType> getTopLeft() const noexcept { return topLeft; }
/** Returns the parallelogram's top-right position as a Point. */
Point<ValueType> getTopRight() const noexcept { return topRight; }
/** Returns the parallelogram's bottom-left position as a Point. */
Point<ValueType> getBottomLeft() const noexcept { return bottomLeft; }
/** Returns the parallelogram's bottom-right position as a Point. */
Point<ValueType> getBottomRight() const noexcept { return topRight + (bottomLeft - topLeft); }
//==============================================================================
/** Returns true if the two parallelograms are identical. */
bool operator== (const Parallelogram& other) const noexcept { return topLeft == other.topLeft && topRight == other.topRight && bottomLeft == other.bottomLeft; }
/** Returns true if the two parallelograms are not identical. */
bool operator!= (const Parallelogram& other) const noexcept { return ! operator== (other); }
//==============================================================================
/** Returns a parallelogram which is the same as this one moved by a given amount. */
Parallelogram operator+ (Point<ValueType> deltaPosition) const noexcept
{
auto p = *this;
p += deltaPosition;
return p;
}
/** Moves this parallelogram by a given amount. */
Parallelogram& operator+= (Point<ValueType> deltaPosition) noexcept
{
topLeft += deltaPosition;
topRight += deltaPosition;
bottomLeft += deltaPosition;
return *this;
}
/** Returns a parallelogram which is the same as this one moved by a given amount. */
Parallelogram operator- (Point<ValueType> deltaPosition) const noexcept
{
return operator+ (-deltaPosition);
}
/** Moves this parallelogram by a given amount. */
Parallelogram& operator-= (Point<ValueType> deltaPosition) noexcept
{
return operator-= (-deltaPosition);
}
/** Returns a parallelogram that has been scaled by the given amount, centred around the origin. */
template <typename PointOrScalarType>
Parallelogram operator* (PointOrScalarType scaleFactor) const noexcept
{
auto p = *this;
p *= scaleFactor;
return p;
}
/** Scales this parallelogram by the given amount, centred around the origin. */
template <typename PointOrScalarType>
Parallelogram operator*= (PointOrScalarType scaleFactor) noexcept
{
topLeft *= scaleFactor;
topRight *= scaleFactor;
bottomLeft *= scaleFactor;
return *this;
}
//==============================================================================
/** Returns a point within this parallelogram, specified as proportional coordinates.
The relative X and Y values should be between 0 and 1, where 0 is the left or
top of this parallelogram, and 1 is the right or bottom. (Out-of-bounds values
will return a point outside the parallelogram).
*/
Point<ValueType> getRelativePoint (Point<ValueType> relativePosition) const noexcept
{
return topLeft
+ (topRight - topLeft) * relativePosition.x
+ (bottomLeft - topLeft) * relativePosition.y;
}
/** Returns a transformed version of the parallelogram. */
Parallelogram transformedBy (const AffineTransform& transform) const noexcept
{
auto p = *this;
transform.transformPoints (p.topLeft.x, p.topLeft.y,
p.topRight.x, p.topRight.y,
p.bottomLeft.x, p.bottomLeft.y);
return p;
}
/** Returns the smallest rectangle that encloses this parallelogram. */
Rectangle<ValueType> getBoundingBox() const noexcept
{
const Point<ValueType> points[] = { topLeft, topRight, bottomLeft, getBottomRight() };
return Rectangle<ValueType>::findAreaContainingPoints (points, 4);
}
Point<ValueType> topLeft, topRight, bottomLeft;
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
Represents a parallelogram that is defined by 3 points.
@see Rectangle, Point, Line
@tags{Graphics}
*/
template <typename ValueType>
class Parallelogram
{
public:
//==============================================================================
/** Creates a parallelogram with zero size at the origin.
*/
Parallelogram() = default;
/** Creates a copy of another parallelogram. */
Parallelogram (const Parallelogram&) = default;
/** Creates a parallelogram based on 3 points. */
Parallelogram (Point<ValueType> topLeftPosition,
Point<ValueType> topRightPosition,
Point<ValueType> bottomLeftPosition) noexcept
: topLeft (topLeftPosition), topRight (topRightPosition), bottomLeft (bottomLeftPosition)
{
}
/** Creates a parallelogram from a rectangle. */
Parallelogram (Rectangle<ValueType> rectangle) noexcept
: topLeft (rectangle.getTopLeft()),
topRight (rectangle.getTopRight()),
bottomLeft (rectangle.getBottomLeft())
{
}
Parallelogram& operator= (const Parallelogram&) = default;
/** Destructor. */
~Parallelogram() = default;
//==============================================================================
/** Returns true if the parallelogram has a width or height of more than zero. */
bool isEmpty() const noexcept { return topLeft != topRight || topLeft != bottomLeft; }
/** Returns true if the parallelogram's coordinates are all finite numbers, i.e. not NaN or infinity. */
inline bool isFinite() const noexcept { return topLeft.isFinite() && topRight.isFinite() && bottomLeft.isFinite(); }
/** Returns the width of the parallelogram (i.e. the straight-line distance between the top-left and top-right. */
inline ValueType getWidth() const noexcept { return Line<ValueType> (topLeft, topRight).getLength(); }
/** Returns the height of the parallelogram (i.e. the straight-line distance between the top-left and bottom-left. */
inline ValueType getHeight() const noexcept { return Line<ValueType> (topLeft, bottomLeft).getLength(); }
//==============================================================================
/** Returns the parallelogram's top-left position as a Point. */
Point<ValueType> getTopLeft() const noexcept { return topLeft; }
/** Returns the parallelogram's top-right position as a Point. */
Point<ValueType> getTopRight() const noexcept { return topRight; }
/** Returns the parallelogram's bottom-left position as a Point. */
Point<ValueType> getBottomLeft() const noexcept { return bottomLeft; }
/** Returns the parallelogram's bottom-right position as a Point. */
Point<ValueType> getBottomRight() const noexcept { return topRight + (bottomLeft - topLeft); }
//==============================================================================
/** Returns true if the two parallelograms are identical. */
bool operator== (const Parallelogram& other) const noexcept { return topLeft == other.topLeft && topRight == other.topRight && bottomLeft == other.bottomLeft; }
/** Returns true if the two parallelograms are not identical. */
bool operator!= (const Parallelogram& other) const noexcept { return ! operator== (other); }
//==============================================================================
/** Returns a parallelogram which is the same as this one moved by a given amount. */
Parallelogram operator+ (Point<ValueType> deltaPosition) const noexcept
{
auto p = *this;
p += deltaPosition;
return p;
}
/** Moves this parallelogram by a given amount. */
Parallelogram& operator+= (Point<ValueType> deltaPosition) noexcept
{
topLeft += deltaPosition;
topRight += deltaPosition;
bottomLeft += deltaPosition;
return *this;
}
/** Returns a parallelogram which is the same as this one moved by a given amount. */
Parallelogram operator- (Point<ValueType> deltaPosition) const noexcept
{
return operator+ (-deltaPosition);
}
/** Moves this parallelogram by a given amount. */
Parallelogram& operator-= (Point<ValueType> deltaPosition) noexcept
{
return operator-= (-deltaPosition);
}
/** Returns a parallelogram that has been scaled by the given amount, centred around the origin. */
template <typename PointOrScalarType>
Parallelogram operator* (PointOrScalarType scaleFactor) const noexcept
{
auto p = *this;
p *= scaleFactor;
return p;
}
/** Scales this parallelogram by the given amount, centred around the origin. */
template <typename PointOrScalarType>
Parallelogram operator*= (PointOrScalarType scaleFactor) noexcept
{
topLeft *= scaleFactor;
topRight *= scaleFactor;
bottomLeft *= scaleFactor;
return *this;
}
//==============================================================================
/** Returns a point within this parallelogram, specified as proportional coordinates.
The relative X and Y values should be between 0 and 1, where 0 is the left or
top of this parallelogram, and 1 is the right or bottom. (Out-of-bounds values
will return a point outside the parallelogram).
*/
Point<ValueType> getRelativePoint (Point<ValueType> relativePosition) const noexcept
{
return topLeft
+ (topRight - topLeft) * relativePosition.x
+ (bottomLeft - topLeft) * relativePosition.y;
}
/** Returns a transformed version of the parallelogram. */
Parallelogram transformedBy (const AffineTransform& transform) const noexcept
{
auto p = *this;
transform.transformPoints (p.topLeft.x, p.topLeft.y,
p.topRight.x, p.topRight.y,
p.bottomLeft.x, p.bottomLeft.y);
return p;
}
/** Returns the smallest rectangle that encloses this parallelogram. */
Rectangle<ValueType> getBoundingBox() const noexcept
{
const Point<ValueType> points[] = { topLeft, topRight, bottomLeft, getBottomRight() };
return Rectangle<ValueType>::findAreaContainingPoints (points, 4);
}
Point<ValueType> topLeft, topRight, bottomLeft;
};
} // namespace juce

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,295 +1,295 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
{
#if JUCE_MSVC && JUCE_DEBUG
#pragma optimize ("t", on)
#endif
//==============================================================================
PathFlatteningIterator::PathFlatteningIterator (const Path& pathToUse,
const AffineTransform& t,
float tolerance)
: x2 (0),
y2 (0),
closesSubPath (false),
subPathIndex (-1),
path (pathToUse),
transform (t),
source (path.data.begin()),
toleranceSquared (tolerance * tolerance),
isIdentityTransform (t.isIdentity())
{
stackPos = stackBase;
}
PathFlatteningIterator::~PathFlatteningIterator()
{
}
bool PathFlatteningIterator::isLastInSubpath() const noexcept
{
return stackPos == stackBase.get()
&& (source == path.data.end() || isMarker (*source, Path::moveMarker));
}
bool PathFlatteningIterator::next()
{
x1 = x2;
y1 = y2;
float x3 = 0;
float y3 = 0;
float x4 = 0;
float y4 = 0;
for (;;)
{
float type;
if (stackPos == stackBase.get())
{
if (source == path.data.end())
return false;
type = *source++;
if (! isMarker (type, Path::closeSubPathMarker))
{
x2 = *source++;
y2 = *source++;
if (isMarker (type, Path::quadMarker))
{
x3 = *source++;
y3 = *source++;
if (! isIdentityTransform)
transform.transformPoints (x2, y2, x3, y3);
}
else if (isMarker (type, Path::cubicMarker))
{
x3 = *source++;
y3 = *source++;
x4 = *source++;
y4 = *source++;
if (! isIdentityTransform)
transform.transformPoints (x2, y2, x3, y3, x4, y4);
}
else
{
if (! isIdentityTransform)
transform.transformPoint (x2, y2);
}
}
}
else
{
type = *--stackPos;
if (! isMarker (type, Path::closeSubPathMarker))
{
x2 = *--stackPos;
y2 = *--stackPos;
if (isMarker (type, Path::quadMarker))
{
x3 = *--stackPos;
y3 = *--stackPos;
}
else if (isMarker (type, Path::cubicMarker))
{
x3 = *--stackPos;
y3 = *--stackPos;
x4 = *--stackPos;
y4 = *--stackPos;
}
}
}
if (isMarker (type, Path::lineMarker))
{
++subPathIndex;
closesSubPath = stackPos == stackBase.get()
&& source != path.data.end()
&& *source == Path::closeSubPathMarker
&& x2 == subPathCloseX
&& y2 == subPathCloseY;
return true;
}
if (isMarker (type, Path::quadMarker))
{
const size_t offset = (size_t) (stackPos - stackBase);
if (offset >= stackSize - 10)
{
stackSize <<= 1;
stackBase.realloc (stackSize);
stackPos = stackBase + offset;
}
auto m1x = (x1 + x2) * 0.5f;
auto m1y = (y1 + y2) * 0.5f;
auto m2x = (x2 + x3) * 0.5f;
auto m2y = (y2 + y3) * 0.5f;
auto m3x = (m1x + m2x) * 0.5f;
auto m3y = (m1y + m2y) * 0.5f;
auto errorX = m3x - x2;
auto errorY = m3y - y2;
auto outsideTolerance = errorX * errorX + errorY * errorY > toleranceSquared;
auto canBeSubdivided = (m3x != m1x && m3x != m2x)
|| (m3y != m1y && m3y != m2y);
if (outsideTolerance && canBeSubdivided)
{
*stackPos++ = y3;
*stackPos++ = x3;
*stackPos++ = m2y;
*stackPos++ = m2x;
*stackPos++ = Path::quadMarker;
*stackPos++ = m3y;
*stackPos++ = m3x;
*stackPos++ = m1y;
*stackPos++ = m1x;
*stackPos++ = Path::quadMarker;
}
else
{
*stackPos++ = y3;
*stackPos++ = x3;
*stackPos++ = Path::lineMarker;
*stackPos++ = m3y;
*stackPos++ = m3x;
*stackPos++ = Path::lineMarker;
}
jassert (stackPos < stackBase + stackSize);
}
else if (isMarker (type, Path::cubicMarker))
{
const size_t offset = (size_t) (stackPos - stackBase);
if (offset >= stackSize - 16)
{
stackSize <<= 1;
stackBase.realloc (stackSize);
stackPos = stackBase + offset;
}
auto m1x = (x1 + x2) * 0.5f;
auto m1y = (y1 + y2) * 0.5f;
auto m2x = (x3 + x2) * 0.5f;
auto m2y = (y3 + y2) * 0.5f;
auto m3x = (x3 + x4) * 0.5f;
auto m3y = (y3 + y4) * 0.5f;
auto m4x = (m1x + m2x) * 0.5f;
auto m4y = (m1y + m2y) * 0.5f;
auto m5x = (m3x + m2x) * 0.5f;
auto m5y = (m3y + m2y) * 0.5f;
auto error1X = m4x - x2;
auto error1Y = m4y - y2;
auto error2X = m5x - x3;
auto error2Y = m5y - y3;
auto outsideTolerance = error1X * error1X + error1Y * error1Y > toleranceSquared
|| error2X * error2X + error2Y * error2Y > toleranceSquared;
auto canBeSubdivided = (m4x != m1x && m4x != m2x)
|| (m4y != m1y && m4y != m2y)
|| (m5x != m3x && m5x != m2x)
|| (m5y != m3y && m5y != m2y);
if (outsideTolerance && canBeSubdivided)
{
*stackPos++ = y4;
*stackPos++ = x4;
*stackPos++ = m3y;
*stackPos++ = m3x;
*stackPos++ = m5y;
*stackPos++ = m5x;
*stackPos++ = Path::cubicMarker;
*stackPos++ = (m4y + m5y) * 0.5f;
*stackPos++ = (m4x + m5x) * 0.5f;
*stackPos++ = m4y;
*stackPos++ = m4x;
*stackPos++ = m1y;
*stackPos++ = m1x;
*stackPos++ = Path::cubicMarker;
}
else
{
*stackPos++ = y4;
*stackPos++ = x4;
*stackPos++ = Path::lineMarker;
*stackPos++ = m5y;
*stackPos++ = m5x;
*stackPos++ = Path::lineMarker;
*stackPos++ = m4y;
*stackPos++ = m4x;
*stackPos++ = Path::lineMarker;
}
}
else if (isMarker (type, Path::closeSubPathMarker))
{
if (x2 != subPathCloseX || y2 != subPathCloseY)
{
x1 = x2;
y1 = y2;
x2 = subPathCloseX;
y2 = subPathCloseY;
closesSubPath = true;
return true;
}
}
else
{
jassert (isMarker (type, Path::moveMarker));
subPathIndex = -1;
subPathCloseX = x1 = x2;
subPathCloseY = y1 = y2;
}
}
}
#if JUCE_MSVC && JUCE_DEBUG
#pragma optimize ("", on) // resets optimisations to the project defaults
#endif
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-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
{
#if JUCE_MSVC && JUCE_DEBUG
#pragma optimize ("t", on)
#endif
//==============================================================================
PathFlatteningIterator::PathFlatteningIterator (const Path& pathToUse,
const AffineTransform& t,
float tolerance)
: x2 (0),
y2 (0),
closesSubPath (false),
subPathIndex (-1),
path (pathToUse),
transform (t),
source (path.data.begin()),
toleranceSquared (tolerance * tolerance),
isIdentityTransform (t.isIdentity())
{
stackPos = stackBase;
}
PathFlatteningIterator::~PathFlatteningIterator()
{
}
bool PathFlatteningIterator::isLastInSubpath() const noexcept
{
return stackPos == stackBase.get()
&& (source == path.data.end() || isMarker (*source, Path::moveMarker));
}
bool PathFlatteningIterator::next()
{
x1 = x2;
y1 = y2;
float x3 = 0;
float y3 = 0;
float x4 = 0;
float y4 = 0;
for (;;)
{
float type;
if (stackPos == stackBase.get())
{
if (source == path.data.end())
return false;
type = *source++;
if (! isMarker (type, Path::closeSubPathMarker))
{
x2 = *source++;
y2 = *source++;
if (isMarker (type, Path::quadMarker))
{
x3 = *source++;
y3 = *source++;
if (! isIdentityTransform)
transform.transformPoints (x2, y2, x3, y3);
}
else if (isMarker (type, Path::cubicMarker))
{
x3 = *source++;
y3 = *source++;
x4 = *source++;
y4 = *source++;
if (! isIdentityTransform)
transform.transformPoints (x2, y2, x3, y3, x4, y4);
}
else
{
if (! isIdentityTransform)
transform.transformPoint (x2, y2);
}
}
}
else
{
type = *--stackPos;
if (! isMarker (type, Path::closeSubPathMarker))
{
x2 = *--stackPos;
y2 = *--stackPos;
if (isMarker (type, Path::quadMarker))
{
x3 = *--stackPos;
y3 = *--stackPos;
}
else if (isMarker (type, Path::cubicMarker))
{
x3 = *--stackPos;
y3 = *--stackPos;
x4 = *--stackPos;
y4 = *--stackPos;
}
}
}
if (isMarker (type, Path::lineMarker))
{
++subPathIndex;
closesSubPath = stackPos == stackBase.get()
&& source != path.data.end()
&& *source == Path::closeSubPathMarker
&& x2 == subPathCloseX
&& y2 == subPathCloseY;
return true;
}
if (isMarker (type, Path::quadMarker))
{
const size_t offset = (size_t) (stackPos - stackBase);
if (offset >= stackSize - 10)
{
stackSize <<= 1;
stackBase.realloc (stackSize);
stackPos = stackBase + offset;
}
auto m1x = (x1 + x2) * 0.5f;
auto m1y = (y1 + y2) * 0.5f;
auto m2x = (x2 + x3) * 0.5f;
auto m2y = (y2 + y3) * 0.5f;
auto m3x = (m1x + m2x) * 0.5f;
auto m3y = (m1y + m2y) * 0.5f;
auto errorX = m3x - x2;
auto errorY = m3y - y2;
auto outsideTolerance = errorX * errorX + errorY * errorY > toleranceSquared;
auto canBeSubdivided = (m3x != m1x && m3x != m2x)
|| (m3y != m1y && m3y != m2y);
if (outsideTolerance && canBeSubdivided)
{
*stackPos++ = y3;
*stackPos++ = x3;
*stackPos++ = m2y;
*stackPos++ = m2x;
*stackPos++ = Path::quadMarker;
*stackPos++ = m3y;
*stackPos++ = m3x;
*stackPos++ = m1y;
*stackPos++ = m1x;
*stackPos++ = Path::quadMarker;
}
else
{
*stackPos++ = y3;
*stackPos++ = x3;
*stackPos++ = Path::lineMarker;
*stackPos++ = m3y;
*stackPos++ = m3x;
*stackPos++ = Path::lineMarker;
}
jassert (stackPos < stackBase + stackSize);
}
else if (isMarker (type, Path::cubicMarker))
{
const size_t offset = (size_t) (stackPos - stackBase);
if (offset >= stackSize - 16)
{
stackSize <<= 1;
stackBase.realloc (stackSize);
stackPos = stackBase + offset;
}
auto m1x = (x1 + x2) * 0.5f;
auto m1y = (y1 + y2) * 0.5f;
auto m2x = (x3 + x2) * 0.5f;
auto m2y = (y3 + y2) * 0.5f;
auto m3x = (x3 + x4) * 0.5f;
auto m3y = (y3 + y4) * 0.5f;
auto m4x = (m1x + m2x) * 0.5f;
auto m4y = (m1y + m2y) * 0.5f;
auto m5x = (m3x + m2x) * 0.5f;
auto m5y = (m3y + m2y) * 0.5f;
auto error1X = m4x - x2;
auto error1Y = m4y - y2;
auto error2X = m5x - x3;
auto error2Y = m5y - y3;
auto outsideTolerance = error1X * error1X + error1Y * error1Y > toleranceSquared
|| error2X * error2X + error2Y * error2Y > toleranceSquared;
auto canBeSubdivided = (m4x != m1x && m4x != m2x)
|| (m4y != m1y && m4y != m2y)
|| (m5x != m3x && m5x != m2x)
|| (m5y != m3y && m5y != m2y);
if (outsideTolerance && canBeSubdivided)
{
*stackPos++ = y4;
*stackPos++ = x4;
*stackPos++ = m3y;
*stackPos++ = m3x;
*stackPos++ = m5y;
*stackPos++ = m5x;
*stackPos++ = Path::cubicMarker;
*stackPos++ = (m4y + m5y) * 0.5f;
*stackPos++ = (m4x + m5x) * 0.5f;
*stackPos++ = m4y;
*stackPos++ = m4x;
*stackPos++ = m1y;
*stackPos++ = m1x;
*stackPos++ = Path::cubicMarker;
}
else
{
*stackPos++ = y4;
*stackPos++ = x4;
*stackPos++ = Path::lineMarker;
*stackPos++ = m5y;
*stackPos++ = m5x;
*stackPos++ = Path::lineMarker;
*stackPos++ = m4y;
*stackPos++ = m4x;
*stackPos++ = Path::lineMarker;
}
}
else if (isMarker (type, Path::closeSubPathMarker))
{
if (x2 != subPathCloseX || y2 != subPathCloseY)
{
x1 = x2;
y1 = y2;
x2 = subPathCloseX;
y2 = subPathCloseY;
closesSubPath = true;
return true;
}
}
else
{
jassert (isMarker (type, Path::moveMarker));
subPathIndex = -1;
subPathCloseX = x1 = x2;
subPathCloseY = y1 = y2;
}
}
}
#if JUCE_MSVC && JUCE_DEBUG
#pragma optimize ("", on) // resets optimisations to the project defaults
#endif
} // namespace juce

View File

@ -1,111 +1,111 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
Flattens a Path object into a series of straight-line sections.
Use one of these to iterate through a Path object, and it will convert
all the curves into line sections so it's easy to render or perform
geometric operations on.
@see Path
@tags{Graphics}
*/
class JUCE_API PathFlatteningIterator final
{
public:
//==============================================================================
/** Creates a PathFlatteningIterator.
After creation, use the next() method to initialise the fields in the
object with the first line's position.
@param path the path to iterate along
@param transform a transform to apply to each point in the path being iterated
@param tolerance the amount by which the curves are allowed to deviate from the lines
into which they are being broken down - a higher tolerance contains
less lines, so can be generated faster, but will be less smooth.
*/
PathFlatteningIterator (const Path& path,
const AffineTransform& transform = AffineTransform(),
float tolerance = Path::defaultToleranceForMeasurement);
/** Destructor. */
~PathFlatteningIterator();
//==============================================================================
/** Fetches the next line segment from the path.
This will update the member variables x1, y1, x2, y2, subPathIndex and closesSubPath
so that they describe the new line segment.
@returns false when there are no more lines to fetch.
*/
bool next();
float x1; /**< The x position of the start of the current line segment. */
float y1; /**< The y position of the start of the current line segment. */
float x2; /**< The x position of the end of the current line segment. */
float y2; /**< The y position of the end of the current line segment. */
/** Indicates whether the current line segment is closing a sub-path.
If the current line is the one that connects the end of a sub-path
back to the start again, this will be true.
*/
bool closesSubPath;
/** The index of the current line within the current sub-path.
E.g. you can use this to see whether the line is the first one in the
subpath by seeing if it's 0.
*/
int subPathIndex;
/** Returns true if the current segment is the last in the current sub-path. */
bool isLastInSubpath() const noexcept;
private:
//==============================================================================
const Path& path;
const AffineTransform transform;
const float* source;
const float toleranceSquared;
float subPathCloseX = 0, subPathCloseY = 0;
const bool isIdentityTransform;
HeapBlock<float> stackBase { 32 };
float* stackPos;
size_t stackSize = 32;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PathFlatteningIterator)
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
Flattens a Path object into a series of straight-line sections.
Use one of these to iterate through a Path object, and it will convert
all the curves into line sections so it's easy to render or perform
geometric operations on.
@see Path
@tags{Graphics}
*/
class JUCE_API PathFlatteningIterator final
{
public:
//==============================================================================
/** Creates a PathFlatteningIterator.
After creation, use the next() method to initialise the fields in the
object with the first line's position.
@param path the path to iterate along
@param transform a transform to apply to each point in the path being iterated
@param tolerance the amount by which the curves are allowed to deviate from the lines
into which they are being broken down - a higher tolerance contains
less lines, so can be generated faster, but will be less smooth.
*/
PathFlatteningIterator (const Path& path,
const AffineTransform& transform = AffineTransform(),
float tolerance = Path::defaultToleranceForMeasurement);
/** Destructor. */
~PathFlatteningIterator();
//==============================================================================
/** Fetches the next line segment from the path.
This will update the member variables x1, y1, x2, y2, subPathIndex and closesSubPath
so that they describe the new line segment.
@returns false when there are no more lines to fetch.
*/
bool next();
float x1; /**< The x position of the start of the current line segment. */
float y1; /**< The y position of the start of the current line segment. */
float x2; /**< The x position of the end of the current line segment. */
float y2; /**< The y position of the end of the current line segment. */
/** Indicates whether the current line segment is closing a sub-path.
If the current line is the one that connects the end of a sub-path
back to the start again, this will be true.
*/
bool closesSubPath;
/** The index of the current line within the current sub-path.
E.g. you can use this to see whether the line is the first one in the
subpath by seeing if it's 0.
*/
int subPathIndex;
/** Returns true if the current segment is the last in the current sub-path. */
bool isLastInSubpath() const noexcept;
private:
//==============================================================================
const Path& path;
const AffineTransform transform;
const float* source;
const float toleranceSquared;
float subPathCloseX = 0, subPathCloseY = 0;
const bool isIdentityTransform;
HeapBlock<float> stackBase { 32 };
float* stackPos;
size_t stackSize = 32;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PathFlatteningIterator)
};
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -1,206 +1,206 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
Describes a type of stroke used to render a solid outline along a path.
A PathStrokeType object can be used directly to create the shape of an outline
around a path, and is used by Graphics::strokePath to specify the type of
stroke to draw.
@see Path, Graphics::strokePath
@tags{Graphics}
*/
class JUCE_API PathStrokeType
{
public:
//==============================================================================
/** The type of shape to use for the corners between two adjacent line segments. */
enum JointStyle
{
mitered, /**< Indicates that corners should be drawn with sharp joints.
Note that for angles that curve back on themselves, drawing a
mitre could require extending the point too far away from the
path, so a mitre limit is imposed and any corners that exceed it
are drawn as bevelled instead. */
curved, /**< Indicates that corners should be drawn as rounded-off. */
beveled /**< Indicates that corners should be drawn with a line flattening their
outside edge. */
};
/** The type shape to use for the ends of lines. */
enum EndCapStyle
{
butt, /**< Ends of lines are flat and don't extend beyond the end point. */
square, /**< Ends of lines are flat, but stick out beyond the end point for half
the thickness of the stroke. */
rounded /**< Ends of lines are rounded-off with a circular shape. */
};
//==============================================================================
/** Creates a stroke type with a given line-width, and default joint/end styles. */
explicit PathStrokeType (float strokeThickness) noexcept;
/** Creates a stroke type.
@param strokeThickness the width of the line to use
@param jointStyle the type of joints to use for corners
@param endStyle the type of end-caps to use for the ends of open paths.
*/
PathStrokeType (float strokeThickness,
JointStyle jointStyle,
EndCapStyle endStyle = butt) noexcept;
/** Creates a copy of another stroke type. */
PathStrokeType (const PathStrokeType&) noexcept;
/** Copies another stroke onto this one. */
PathStrokeType& operator= (const PathStrokeType&) noexcept;
/** Destructor. */
~PathStrokeType() noexcept;
//==============================================================================
/** Applies this stroke type to a path and returns the resultant stroke as another Path.
@param destPath the resultant stroked outline shape will be copied into this path.
Note that it's ok for the source and destination Paths to be
the same object, so you can easily turn a path into a stroked version
of itself.
@param sourcePath the path to use as the source
@param transform an optional transform to apply to the points from the source path
as they are being used
@param extraAccuracy if this is greater than 1.0, it will subdivide the path to
a higher resolution, which improves the quality if you'll later want
to enlarge the stroked path. So for example, if you're planning on drawing
the stroke at 3x the size that you're creating it, you should set this to 3.
@see createDashedStroke
*/
void createStrokedPath (Path& destPath,
const Path& sourcePath,
const AffineTransform& transform = AffineTransform(),
float extraAccuracy = 1.0f) const;
//==============================================================================
/** Applies this stroke type to a path, creating a dashed line.
This is similar to createStrokedPath, but uses the array passed in to
break the stroke up into a series of dashes.
@param destPath the resultant stroked outline shape will be copied into this path.
Note that it's ok for the source and destination Paths to be
the same object, so you can easily turn a path into a stroked version
of itself.
@param sourcePath the path to use as the source
@param dashLengths An array of alternating on/off lengths. E.g. { 2, 3, 4, 5 } will create
a line of length 2, then skip a length of 3, then add a line of length 4,
skip 5, and keep repeating this pattern.
@param numDashLengths The number of lengths in the dashLengths array. This should really be
an even number, otherwise the pattern will get out of step as it
repeats.
@param transform an optional transform to apply to the points from the source path
as they are being used
@param extraAccuracy if this is greater than 1.0, it will subdivide the path to
a higher resolution, which improves the quality if you'll later want
to enlarge the stroked path. So for example, if you're planning on drawing
the stroke at 3x the size that you're creating it, you should set this to 3.
*/
void createDashedStroke (Path& destPath,
const Path& sourcePath,
const float* dashLengths,
int numDashLengths,
const AffineTransform& transform = AffineTransform(),
float extraAccuracy = 1.0f) const;
//==============================================================================
/** Applies this stroke type to a path and returns the resultant stroke as another Path.
@param destPath the resultant stroked outline shape will be copied into this path.
Note that it's ok for the source and destination Paths to be
the same object, so you can easily turn a path into a stroked version
of itself.
@param sourcePath the path to use as the source
@param arrowheadStartWidth the width of the arrowhead at the start of the path
@param arrowheadStartLength the length of the arrowhead at the start of the path
@param arrowheadEndWidth the width of the arrowhead at the end of the path
@param arrowheadEndLength the length of the arrowhead at the end of the path
@param transform an optional transform to apply to the points from the source path
as they are being used
@param extraAccuracy if this is greater than 1.0, it will subdivide the path to
a higher resolution, which improves the quality if you'll later want
to enlarge the stroked path. So for example, if you're planning on drawing
the stroke at 3x the size that you're creating it, you should set this to 3.
@see createDashedStroke
*/
void createStrokeWithArrowheads (Path& destPath,
const Path& sourcePath,
float arrowheadStartWidth, float arrowheadStartLength,
float arrowheadEndWidth, float arrowheadEndLength,
const AffineTransform& transform = AffineTransform(),
float extraAccuracy = 1.0f) const;
//==============================================================================
/** Returns the stroke thickness. */
float getStrokeThickness() const noexcept { return thickness; }
/** Sets the stroke thickness. */
void setStrokeThickness (float newThickness) noexcept { thickness = newThickness; }
/** Returns the joint style. */
JointStyle getJointStyle() const noexcept { return jointStyle; }
/** Sets the joint style. */
void setJointStyle (JointStyle newStyle) noexcept { jointStyle = newStyle; }
/** Returns the end-cap style. */
EndCapStyle getEndStyle() const noexcept { return endStyle; }
/** Sets the end-cap style. */
void setEndStyle (EndCapStyle newStyle) noexcept { endStyle = newStyle; }
//==============================================================================
/** Compares the stroke thickness, joint and end styles of two stroke types. */
bool operator== (const PathStrokeType&) const noexcept;
/** Compares the stroke thickness, joint and end styles of two stroke types. */
bool operator!= (const PathStrokeType&) const noexcept;
private:
//==============================================================================
float thickness;
JointStyle jointStyle;
EndCapStyle endStyle;
JUCE_LEAK_DETECTOR (PathStrokeType)
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
Describes a type of stroke used to render a solid outline along a path.
A PathStrokeType object can be used directly to create the shape of an outline
around a path, and is used by Graphics::strokePath to specify the type of
stroke to draw.
@see Path, Graphics::strokePath
@tags{Graphics}
*/
class JUCE_API PathStrokeType
{
public:
//==============================================================================
/** The type of shape to use for the corners between two adjacent line segments. */
enum JointStyle
{
mitered, /**< Indicates that corners should be drawn with sharp joints.
Note that for angles that curve back on themselves, drawing a
mitre could require extending the point too far away from the
path, so a mitre limit is imposed and any corners that exceed it
are drawn as bevelled instead. */
curved, /**< Indicates that corners should be drawn as rounded-off. */
beveled /**< Indicates that corners should be drawn with a line flattening their
outside edge. */
};
/** The type shape to use for the ends of lines. */
enum EndCapStyle
{
butt, /**< Ends of lines are flat and don't extend beyond the end point. */
square, /**< Ends of lines are flat, but stick out beyond the end point for half
the thickness of the stroke. */
rounded /**< Ends of lines are rounded-off with a circular shape. */
};
//==============================================================================
/** Creates a stroke type with a given line-width, and default joint/end styles. */
explicit PathStrokeType (float strokeThickness) noexcept;
/** Creates a stroke type.
@param strokeThickness the width of the line to use
@param jointStyle the type of joints to use for corners
@param endStyle the type of end-caps to use for the ends of open paths.
*/
PathStrokeType (float strokeThickness,
JointStyle jointStyle,
EndCapStyle endStyle = butt) noexcept;
/** Creates a copy of another stroke type. */
PathStrokeType (const PathStrokeType&) noexcept;
/** Copies another stroke onto this one. */
PathStrokeType& operator= (const PathStrokeType&) noexcept;
/** Destructor. */
~PathStrokeType() noexcept;
//==============================================================================
/** Applies this stroke type to a path and returns the resultant stroke as another Path.
@param destPath the resultant stroked outline shape will be copied into this path.
Note that it's ok for the source and destination Paths to be
the same object, so you can easily turn a path into a stroked version
of itself.
@param sourcePath the path to use as the source
@param transform an optional transform to apply to the points from the source path
as they are being used
@param extraAccuracy if this is greater than 1.0, it will subdivide the path to
a higher resolution, which improves the quality if you'll later want
to enlarge the stroked path. So for example, if you're planning on drawing
the stroke at 3x the size that you're creating it, you should set this to 3.
@see createDashedStroke
*/
void createStrokedPath (Path& destPath,
const Path& sourcePath,
const AffineTransform& transform = AffineTransform(),
float extraAccuracy = 1.0f) const;
//==============================================================================
/** Applies this stroke type to a path, creating a dashed line.
This is similar to createStrokedPath, but uses the array passed in to
break the stroke up into a series of dashes.
@param destPath the resultant stroked outline shape will be copied into this path.
Note that it's ok for the source and destination Paths to be
the same object, so you can easily turn a path into a stroked version
of itself.
@param sourcePath the path to use as the source
@param dashLengths An array of alternating on/off lengths. E.g. { 2, 3, 4, 5 } will create
a line of length 2, then skip a length of 3, then add a line of length 4,
skip 5, and keep repeating this pattern.
@param numDashLengths The number of lengths in the dashLengths array. This should really be
an even number, otherwise the pattern will get out of step as it
repeats.
@param transform an optional transform to apply to the points from the source path
as they are being used
@param extraAccuracy if this is greater than 1.0, it will subdivide the path to
a higher resolution, which improves the quality if you'll later want
to enlarge the stroked path. So for example, if you're planning on drawing
the stroke at 3x the size that you're creating it, you should set this to 3.
*/
void createDashedStroke (Path& destPath,
const Path& sourcePath,
const float* dashLengths,
int numDashLengths,
const AffineTransform& transform = AffineTransform(),
float extraAccuracy = 1.0f) const;
//==============================================================================
/** Applies this stroke type to a path and returns the resultant stroke as another Path.
@param destPath the resultant stroked outline shape will be copied into this path.
Note that it's ok for the source and destination Paths to be
the same object, so you can easily turn a path into a stroked version
of itself.
@param sourcePath the path to use as the source
@param arrowheadStartWidth the width of the arrowhead at the start of the path
@param arrowheadStartLength the length of the arrowhead at the start of the path
@param arrowheadEndWidth the width of the arrowhead at the end of the path
@param arrowheadEndLength the length of the arrowhead at the end of the path
@param transform an optional transform to apply to the points from the source path
as they are being used
@param extraAccuracy if this is greater than 1.0, it will subdivide the path to
a higher resolution, which improves the quality if you'll later want
to enlarge the stroked path. So for example, if you're planning on drawing
the stroke at 3x the size that you're creating it, you should set this to 3.
@see createDashedStroke
*/
void createStrokeWithArrowheads (Path& destPath,
const Path& sourcePath,
float arrowheadStartWidth, float arrowheadStartLength,
float arrowheadEndWidth, float arrowheadEndLength,
const AffineTransform& transform = AffineTransform(),
float extraAccuracy = 1.0f) const;
//==============================================================================
/** Returns the stroke thickness. */
float getStrokeThickness() const noexcept { return thickness; }
/** Sets the stroke thickness. */
void setStrokeThickness (float newThickness) noexcept { thickness = newThickness; }
/** Returns the joint style. */
JointStyle getJointStyle() const noexcept { return jointStyle; }
/** Sets the joint style. */
void setJointStyle (JointStyle newStyle) noexcept { jointStyle = newStyle; }
/** Returns the end-cap style. */
EndCapStyle getEndStyle() const noexcept { return endStyle; }
/** Sets the end-cap style. */
void setEndStyle (EndCapStyle newStyle) noexcept { endStyle = newStyle; }
//==============================================================================
/** Compares the stroke thickness, joint and end styles of two stroke types. */
bool operator== (const PathStrokeType&) const noexcept;
/** Compares the stroke thickness, joint and end styles of two stroke types. */
bool operator!= (const PathStrokeType&) const noexcept;
private:
//==============================================================================
float thickness;
JointStyle jointStyle;
EndCapStyle endStyle;
JUCE_LEAK_DETECTOR (PathStrokeType)
};
} // namespace juce

View File

@ -1,254 +1,254 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
A pair of (x, y) coordinates.
The ValueType template should be a primitive type such as int, float, double,
rather than a class.
@see Line, Path, AffineTransform
@tags{Graphics}
*/
template <typename ValueType>
class Point
{
public:
/** Creates a point at the origin */
constexpr Point() = default;
/** Creates a copy of another point. */
constexpr Point (const Point&) = default;
/** Creates a point from an (x, y) position. */
constexpr Point (ValueType initialX, ValueType initialY) noexcept : x (initialX), y (initialY) {}
//==============================================================================
/** Copies this point from another one. */
Point& operator= (const Point&) = default;
constexpr inline bool operator== (Point other) const noexcept { return x == other.x && y == other.y; }
constexpr inline bool operator!= (Point other) const noexcept { return x != other.x || y != other.y; }
/** Returns true if the point is (0, 0). */
constexpr bool isOrigin() const noexcept { return x == ValueType() && y == ValueType(); }
/** Returns true if the coordinates are finite values. */
constexpr inline bool isFinite() const noexcept { return juce_isfinite(x) && juce_isfinite(y); }
/** Returns the point's x coordinate. */
constexpr inline ValueType getX() const noexcept { return x; }
/** Returns the point's y coordinate. */
constexpr inline ValueType getY() const noexcept { return y; }
/** Sets the point's x coordinate. */
inline void setX (ValueType newX) noexcept { x = newX; }
/** Sets the point's y coordinate. */
inline void setY (ValueType newY) noexcept { y = newY; }
/** Returns a point which has the same Y position as this one, but a new X. */
constexpr Point withX (ValueType newX) const noexcept { return Point (newX, y); }
/** Returns a point which has the same X position as this one, but a new Y. */
constexpr Point withY (ValueType newY) const noexcept { return Point (x, newY); }
/** Changes the point's x and y coordinates. */
void setXY (ValueType newX, ValueType newY) noexcept { x = newX; y = newY; }
/** Adds a pair of coordinates to this value. */
void addXY (ValueType xToAdd, ValueType yToAdd) noexcept { x += xToAdd; y += yToAdd; }
//==============================================================================
/** Returns a point with a given offset from this one. */
constexpr Point translated (ValueType deltaX, ValueType deltaY) const noexcept { return Point (x + deltaX, y + deltaY); }
/** Adds two points together */
constexpr Point operator+ (Point other) const noexcept { return Point (x + other.x, y + other.y); }
/** Adds another point's coordinates to this one */
Point& operator+= (Point other) noexcept { x += other.x; y += other.y; return *this; }
/** Subtracts one points from another */
constexpr Point operator- (Point other) const noexcept { return Point (x - other.x, y - other.y); }
/** Subtracts another point's coordinates to this one */
Point& operator-= (Point other) noexcept { x -= other.x; y -= other.y; return *this; }
/** Multiplies two points together */
template <typename OtherType>
constexpr Point operator* (Point<OtherType> other) const noexcept { return Point ((ValueType) (x * other.x), (ValueType) (y * other.y)); }
/** Multiplies another point's coordinates to this one */
template <typename OtherType>
Point& operator*= (Point<OtherType> other) noexcept { *this = *this * other; return *this; }
/** Divides one point by another */
template <typename OtherType>
constexpr Point operator/ (Point<OtherType> other) const noexcept { return Point ((ValueType) (x / other.x), (ValueType) (y / other.y)); }
/** Divides this point's coordinates by another */
template <typename OtherType>
Point& operator/= (Point<OtherType> other) noexcept { *this = *this / other; return *this; }
/** Returns a point whose coordinates are multiplied by a given scalar value. */
template <typename OtherType>
constexpr Point operator* (OtherType multiplier) const noexcept
{
using CommonType = typename std::common_type<ValueType, OtherType>::type;
return Point ((ValueType) ((CommonType) x * (CommonType) multiplier),
(ValueType) ((CommonType) y * (CommonType) multiplier));
}
/** Returns a point whose coordinates are divided by a given scalar value. */
template <typename OtherType>
constexpr Point operator/ (OtherType divisor) const noexcept
{
using CommonType = typename std::common_type<ValueType, OtherType>::type;
return Point ((ValueType) ((CommonType) x / (CommonType) divisor),
(ValueType) ((CommonType) y / (CommonType) divisor));
}
/** Multiplies the point's coordinates by a scalar value. */
template <typename FloatType>
Point& operator*= (FloatType multiplier) noexcept { x = (ValueType) (x * multiplier); y = (ValueType) (y * multiplier); return *this; }
/** Divides the point's coordinates by a scalar value. */
template <typename FloatType>
Point& operator/= (FloatType divisor) noexcept { x = (ValueType) (x / divisor); y = (ValueType) (y / divisor); return *this; }
/** Returns the inverse of this point. */
constexpr Point operator-() const noexcept { return Point (-x, -y); }
//==============================================================================
/** This type will be double if the Point's type is double, otherwise it will be float. */
using FloatType = typename TypeHelpers::SmallestFloatType<ValueType>::type;
//==============================================================================
/** Returns the straight-line distance between this point and the origin. */
ValueType getDistanceFromOrigin() const noexcept { return juce_hypot (x, y); }
/** Returns the straight-line distance between this point and another one. */
ValueType getDistanceFrom (Point other) const noexcept { return juce_hypot (x - other.x, y - other.y); }
/** Returns the square of the straight-line distance between this point and the origin. */
constexpr ValueType getDistanceSquaredFromOrigin() const noexcept { return x * x + y * y; }
/** Returns the square of the straight-line distance between this point and another one. */
constexpr ValueType getDistanceSquaredFrom (Point other) const noexcept { return (*this - other).getDistanceSquaredFromOrigin(); }
/** Returns the angle from this point to another one.
Taking this point to be the centre of a circle, and the other point being a position on
the circumference, the return value is the number of radians clockwise from the 12 o'clock
direction.
So 12 o'clock = 0, 3 o'clock = Pi/2, 6 o'clock = Pi, 9 o'clock = -Pi/2
*/
FloatType getAngleToPoint (Point other) const noexcept
{
return static_cast<FloatType> (std::atan2 (static_cast<FloatType> (other.x - x),
static_cast<FloatType> (y - other.y)));
}
/** Returns the point that would be reached by rotating this point clockwise
about the origin by the specified angle.
*/
Point rotatedAboutOrigin (ValueType angleRadians) const noexcept
{
return Point (x * std::cos (angleRadians) - y * std::sin (angleRadians),
x * std::sin (angleRadians) + y * std::cos (angleRadians));
}
/** Taking this point to be the centre of a circle, this returns a point on its circumference.
@param radius the radius of the circle.
@param angle the angle of the point, in radians clockwise from the 12 o'clock position.
*/
Point<FloatType> getPointOnCircumference (float radius, float angle) const noexcept
{
return Point<FloatType> (static_cast<FloatType> (x + radius * std::sin (angle)),
static_cast<FloatType> (y - radius * std::cos (angle)));
}
/** Taking this point to be the centre of an ellipse, this returns a point on its circumference.
@param radiusX the horizontal radius of the circle.
@param radiusY the vertical radius of the circle.
@param angle the angle of the point, in radians clockwise from the 12 o'clock position.
*/
Point<FloatType> getPointOnCircumference (float radiusX, float radiusY, float angle) const noexcept
{
return Point<FloatType> (static_cast<FloatType> (x + radiusX * std::sin (angle)),
static_cast<FloatType> (y - radiusY * std::cos (angle)));
}
/** Returns the dot-product of two points (x1 * x2 + y1 * y2). */
constexpr FloatType getDotProduct (Point other) const noexcept { return x * other.x + y * other.y; }
//==============================================================================
/** Uses a transform to change the point's coordinates.
This will only compile if ValueType = float!
@see AffineTransform::transformPoint
*/
void applyTransform (const AffineTransform& transform) noexcept { transform.transformPoint (x, y); }
/** Returns the position of this point, if it is transformed by a given AffineTransform. */
Point transformedBy (const AffineTransform& transform) const noexcept
{
return Point (static_cast<ValueType> (transform.mat00 * (float) x + transform.mat01 * (float) y + transform.mat02),
static_cast<ValueType> (transform.mat10 * (float) x + transform.mat11 * (float) y + transform.mat12));
}
//==============================================================================
/** Casts this point to a Point<int> object. */
constexpr Point<int> toInt() const noexcept { return Point<int> (static_cast<int> (x), static_cast<int> (y)); }
/** Casts this point to a Point<float> object. */
constexpr Point<float> toFloat() const noexcept { return Point<float> (static_cast<float> (x), static_cast<float> (y)); }
/** Casts this point to a Point<double> object. */
constexpr Point<double> toDouble() const noexcept { return Point<double> (static_cast<double> (x), static_cast<double> (y)); }
/** Casts this point to a Point<int> object using roundToInt() to convert the values. */
constexpr Point<int> roundToInt() const noexcept { return Point<int> (juce::roundToInt (x), juce::roundToInt (y)); }
/** Returns the point as a string in the form "x, y". */
String toString() const { return String (x) + ", " + String (y); }
//==============================================================================
ValueType x{}; /**< The point's X coordinate. */
ValueType y{}; /**< The point's Y coordinate. */
};
/** Multiplies the point's coordinates by a scalar value. */
template <typename ValueType>
Point<ValueType> operator* (ValueType value, Point<ValueType> p) noexcept { return p * value; }
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-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
{
//==============================================================================
/**
A pair of (x, y) coordinates.
The ValueType template should be a primitive type such as int, float, double,
rather than a class.
@see Line, Path, AffineTransform
@tags{Graphics}
*/
template <typename ValueType>
class Point
{
public:
/** Creates a point at the origin */
constexpr Point() = default;
/** Creates a copy of another point. */
constexpr Point (const Point&) = default;
/** Creates a point from an (x, y) position. */
constexpr Point (ValueType initialX, ValueType initialY) noexcept : x (initialX), y (initialY) {}
//==============================================================================
/** Copies this point from another one. */
Point& operator= (const Point&) = default;
constexpr inline bool operator== (Point other) const noexcept { return x == other.x && y == other.y; }
constexpr inline bool operator!= (Point other) const noexcept { return x != other.x || y != other.y; }
/** Returns true if the point is (0, 0). */
constexpr bool isOrigin() const noexcept { return x == ValueType() && y == ValueType(); }
/** Returns true if the coordinates are finite values. */
constexpr inline bool isFinite() const noexcept { return juce_isfinite(x) && juce_isfinite(y); }
/** Returns the point's x coordinate. */
constexpr inline ValueType getX() const noexcept { return x; }
/** Returns the point's y coordinate. */
constexpr inline ValueType getY() const noexcept { return y; }
/** Sets the point's x coordinate. */
inline void setX (ValueType newX) noexcept { x = newX; }
/** Sets the point's y coordinate. */
inline void setY (ValueType newY) noexcept { y = newY; }
/** Returns a point which has the same Y position as this one, but a new X. */
constexpr Point withX (ValueType newX) const noexcept { return Point (newX, y); }
/** Returns a point which has the same X position as this one, but a new Y. */
constexpr Point withY (ValueType newY) const noexcept { return Point (x, newY); }
/** Changes the point's x and y coordinates. */
void setXY (ValueType newX, ValueType newY) noexcept { x = newX; y = newY; }
/** Adds a pair of coordinates to this value. */
void addXY (ValueType xToAdd, ValueType yToAdd) noexcept { x += xToAdd; y += yToAdd; }
//==============================================================================
/** Returns a point with a given offset from this one. */
constexpr Point translated (ValueType deltaX, ValueType deltaY) const noexcept { return Point (x + deltaX, y + deltaY); }
/** Adds two points together */
constexpr Point operator+ (Point other) const noexcept { return Point (x + other.x, y + other.y); }
/** Adds another point's coordinates to this one */
Point& operator+= (Point other) noexcept { x += other.x; y += other.y; return *this; }
/** Subtracts one points from another */
constexpr Point operator- (Point other) const noexcept { return Point (x - other.x, y - other.y); }
/** Subtracts another point's coordinates to this one */
Point& operator-= (Point other) noexcept { x -= other.x; y -= other.y; return *this; }
/** Multiplies two points together */
template <typename OtherType>
constexpr Point operator* (Point<OtherType> other) const noexcept { return Point ((ValueType) (x * other.x), (ValueType) (y * other.y)); }
/** Multiplies another point's coordinates to this one */
template <typename OtherType>
Point& operator*= (Point<OtherType> other) noexcept { *this = *this * other; return *this; }
/** Divides one point by another */
template <typename OtherType>
constexpr Point operator/ (Point<OtherType> other) const noexcept { return Point ((ValueType) (x / other.x), (ValueType) (y / other.y)); }
/** Divides this point's coordinates by another */
template <typename OtherType>
Point& operator/= (Point<OtherType> other) noexcept { *this = *this / other; return *this; }
/** Returns a point whose coordinates are multiplied by a given scalar value. */
template <typename OtherType>
constexpr Point operator* (OtherType multiplier) const noexcept
{
using CommonType = typename std::common_type<ValueType, OtherType>::type;
return Point ((ValueType) ((CommonType) x * (CommonType) multiplier),
(ValueType) ((CommonType) y * (CommonType) multiplier));
}
/** Returns a point whose coordinates are divided by a given scalar value. */
template <typename OtherType>
constexpr Point operator/ (OtherType divisor) const noexcept
{
using CommonType = typename std::common_type<ValueType, OtherType>::type;
return Point ((ValueType) ((CommonType) x / (CommonType) divisor),
(ValueType) ((CommonType) y / (CommonType) divisor));
}
/** Multiplies the point's coordinates by a scalar value. */
template <typename FloatType>
Point& operator*= (FloatType multiplier) noexcept { x = (ValueType) (x * multiplier); y = (ValueType) (y * multiplier); return *this; }
/** Divides the point's coordinates by a scalar value. */
template <typename FloatType>
Point& operator/= (FloatType divisor) noexcept { x = (ValueType) (x / divisor); y = (ValueType) (y / divisor); return *this; }
/** Returns the inverse of this point. */
constexpr Point operator-() const noexcept { return Point (-x, -y); }
//==============================================================================
/** This type will be double if the Point's type is double, otherwise it will be float. */
using FloatType = typename TypeHelpers::SmallestFloatType<ValueType>::type;
//==============================================================================
/** Returns the straight-line distance between this point and the origin. */
ValueType getDistanceFromOrigin() const noexcept { return juce_hypot (x, y); }
/** Returns the straight-line distance between this point and another one. */
ValueType getDistanceFrom (Point other) const noexcept { return juce_hypot (x - other.x, y - other.y); }
/** Returns the square of the straight-line distance between this point and the origin. */
constexpr ValueType getDistanceSquaredFromOrigin() const noexcept { return x * x + y * y; }
/** Returns the square of the straight-line distance between this point and another one. */
constexpr ValueType getDistanceSquaredFrom (Point other) const noexcept { return (*this - other).getDistanceSquaredFromOrigin(); }
/** Returns the angle from this point to another one.
Taking this point to be the centre of a circle, and the other point being a position on
the circumference, the return value is the number of radians clockwise from the 12 o'clock
direction.
So 12 o'clock = 0, 3 o'clock = Pi/2, 6 o'clock = Pi, 9 o'clock = -Pi/2
*/
FloatType getAngleToPoint (Point other) const noexcept
{
return static_cast<FloatType> (std::atan2 (static_cast<FloatType> (other.x - x),
static_cast<FloatType> (y - other.y)));
}
/** Returns the point that would be reached by rotating this point clockwise
about the origin by the specified angle.
*/
Point rotatedAboutOrigin (ValueType angleRadians) const noexcept
{
return Point (x * std::cos (angleRadians) - y * std::sin (angleRadians),
x * std::sin (angleRadians) + y * std::cos (angleRadians));
}
/** Taking this point to be the centre of a circle, this returns a point on its circumference.
@param radius the radius of the circle.
@param angle the angle of the point, in radians clockwise from the 12 o'clock position.
*/
Point<FloatType> getPointOnCircumference (float radius, float angle) const noexcept
{
return Point<FloatType> (static_cast<FloatType> (x + radius * std::sin (angle)),
static_cast<FloatType> (y - radius * std::cos (angle)));
}
/** Taking this point to be the centre of an ellipse, this returns a point on its circumference.
@param radiusX the horizontal radius of the circle.
@param radiusY the vertical radius of the circle.
@param angle the angle of the point, in radians clockwise from the 12 o'clock position.
*/
Point<FloatType> getPointOnCircumference (float radiusX, float radiusY, float angle) const noexcept
{
return Point<FloatType> (static_cast<FloatType> (x + radiusX * std::sin (angle)),
static_cast<FloatType> (y - radiusY * std::cos (angle)));
}
/** Returns the dot-product of two points (x1 * x2 + y1 * y2). */
constexpr FloatType getDotProduct (Point other) const noexcept { return x * other.x + y * other.y; }
//==============================================================================
/** Uses a transform to change the point's coordinates.
This will only compile if ValueType = float!
@see AffineTransform::transformPoint
*/
void applyTransform (const AffineTransform& transform) noexcept { transform.transformPoint (x, y); }
/** Returns the position of this point, if it is transformed by a given AffineTransform. */
Point transformedBy (const AffineTransform& transform) const noexcept
{
return Point (static_cast<ValueType> (transform.mat00 * (float) x + transform.mat01 * (float) y + transform.mat02),
static_cast<ValueType> (transform.mat10 * (float) x + transform.mat11 * (float) y + transform.mat12));
}
//==============================================================================
/** Casts this point to a Point<int> object. */
constexpr Point<int> toInt() const noexcept { return Point<int> (static_cast<int> (x), static_cast<int> (y)); }
/** Casts this point to a Point<float> object. */
constexpr Point<float> toFloat() const noexcept { return Point<float> (static_cast<float> (x), static_cast<float> (y)); }
/** Casts this point to a Point<double> object. */
constexpr Point<double> toDouble() const noexcept { return Point<double> (static_cast<double> (x), static_cast<double> (y)); }
/** Casts this point to a Point<int> object using roundToInt() to convert the values. */
constexpr Point<int> roundToInt() const noexcept { return Point<int> (juce::roundToInt (x), juce::roundToInt (y)); }
/** Returns the point as a string in the form "x, y". */
String toString() const { return String (x) + ", " + String (y); }
//==============================================================================
ValueType x{}; /**< The point's X coordinate. */
ValueType y{}; /**< The point's Y coordinate. */
};
/** Multiplies the point's coordinates by a scalar value. */
template <typename ValueType>
Point<ValueType> operator* (ValueType value, Point<ValueType> p) noexcept { return p * value; }
} // namespace juce

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-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
{
struct RectangleUnitTest : public UnitTest
{
RectangleUnitTest() : UnitTest ("Rectangle", UnitTestCategories::graphics) {}
void runTest() override
{
beginTest ("Rectangle/string conversions can be round-tripped");
{
const Rectangle<float> a (0.1f, 0.2f, 0.3f, 0.4f);
expect (Rectangle<float>::fromString (a.toString()) == a);
const Rectangle<double> b (0.1, 0.2, 0.3, 0.4);
expect (Rectangle<double>::fromString (b.toString()) == b);
const Rectangle<int> c (1, 2, 3, 4);
expect (Rectangle<int>::fromString (c.toString()) == c);
}
}
};
static RectangleUnitTest rectangleUnitTest;
} // namespace juce