migrating to the latest JUCE version
This commit is contained in:
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
@ -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
|
||||
|
863
deps/juce/modules/juce_graphics/geometry/juce_Line.h
vendored
863
deps/juce/modules/juce_graphics/geometry/juce_Line.h
vendored
@ -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
|
||||
|
@ -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
|
||||
|
3078
deps/juce/modules/juce_graphics/geometry/juce_Path.cpp
vendored
3078
deps/juce/modules/juce_graphics/geometry/juce_Path.cpp
vendored
File diff suppressed because it is too large
Load Diff
1678
deps/juce/modules/juce_graphics/geometry/juce_Path.h
vendored
1678
deps/juce/modules/juce_graphics/geometry/juce_Path.h
vendored
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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
@ -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
|
||||
|
@ -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
51
deps/juce/modules/juce_graphics/geometry/juce_Rectangle_test.cpp
vendored
Normal file
51
deps/juce/modules/juce_graphics/geometry/juce_Rectangle_test.cpp
vendored
Normal 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
|
Reference in New Issue
Block a user