diff --git a/CMakeLists.txt b/CMakeLists.txt index 4628547..9860dbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,8 +71,6 @@ PRIVATE ui/layers/layer_widget.h ui/paint/blob.cpp ui/paint/blob.h - ui/paint/blob_linear.cpp - ui/paint/blob_linear.h ui/paint/blobs.cpp ui/paint/blobs.h ui/paint/blobs_linear.cpp diff --git a/ui/paint/blob.cpp b/ui/paint/blob.cpp index 58d905a..669dd37 100644 --- a/ui/paint/blob.cpp +++ b/ui/paint/blob.cpp @@ -19,24 +19,69 @@ namespace { constexpr auto kMaxSpeed = 8.2; constexpr auto kMinSpeed = 0.8; +constexpr auto kMinSegmentSpeed = 0.017; +constexpr auto kSegmentSpeedDiff = 0.003; + float64 RandomAdditional() { return (openssl::RandomValue() % 100 / 100.); } } // namespace -BlobBezier::BlobBezier(int n, float minScale, float minSpeed, float maxSpeed) +Blob::Blob(int n, float minSpeed, float maxSpeed) : _segmentsCount(n) -, _segmentLength((4.0 / 3.0) * std::tan(M_PI / (2 * n))) -, _minScale(minScale) , _minSpeed(minSpeed ? minSpeed : kMinSpeed) , _maxSpeed(maxSpeed ? maxSpeed : kMaxSpeed) -, _pen(Qt::NoBrush, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin) +, _pen(Qt::NoBrush, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin) { +} + +void Blob::generateBlob() { + for (auto i = 0; i < _segmentsCount; i++) { + generateSingleValues(i); + // Fill nexts. + generateTwoValues(i); + // Fill currents. + generateTwoValues(i); + } +} + +void Blob::generateSingleValues(int i) { + auto &segment = segmentAt(i); + segment.progress = 0.; + segment.speed = kMinSegmentSpeed + + kSegmentSpeedDiff * std::abs(RandomAdditional()); +} + +void Blob::update(float level, float speedScale) { + for (auto i = 0; i < _segmentsCount; i++) { + auto &segment = segmentAt(i); + segment.progress += (segment.speed * _minSpeed) + + level * segment.speed * _maxSpeed * speedScale; + if (segment.progress >= 1) { + generateSingleValues(i); + generateTwoValues(i); + } + } +} + +void Blob::setRadiuses(Radiuses values) { + _radiuses = values; +} + +Blob::Radiuses Blob::radiuses() const { + return _radiuses; +} + +RadialBlob::RadialBlob(int n, float minScale, float minSpeed, float maxSpeed) +: Blob(n, minSpeed, maxSpeed) +, _segmentLength((4.0 / 3.0) * std::tan(M_PI / (2 * n))) +, _minScale(minScale) , _segmentAngle(360. / n) +, _angleDiff(_segmentAngle * 0.05) , _segments(n) { } -void BlobBezier::paint(Painter &p, const QBrush &brush) { +void RadialBlob::paint(Painter &p, const QBrush &brush) { auto path = QPainterPath(); auto m = QMatrix(); @@ -55,14 +100,14 @@ void BlobBezier::paint(Painter &p, const QBrush &brush) { const auto progress = segment.progress; const auto progressNext = nextSegment.progress; - const auto r1 = segment.radius * (1. - progress) - + segment.radiusNext * progress; - const auto r2 = nextSegment.radius * (1. - progressNext) - + nextSegment.radiusNext * progressNext; - const auto angle1 = segment.angle * (1. - progress) - + segment.angleNext * progress; - const auto angle2 = nextSegment.angle * (1. - progressNext) - + nextSegment.angleNext * progressNext; + const auto r1 = segment.radius.current * (1. - progress) + + segment.radius.next * progress; + const auto r2 = nextSegment.radius.current * (1. - progressNext) + + nextSegment.radius.next * progressNext; + const auto angle1 = segment.angle.current * (1. - progress) + + segment.angle.next * progress; + const auto angle2 = nextSegment.angle.current * (1. - progressNext) + + nextSegment.angle.next * progressNext; const auto l = _segmentLength * (std::min(r1, r2) + (std::max(r1, r2) - std::min(r1, r2)) / 2.); @@ -94,45 +139,98 @@ void BlobBezier::paint(Painter &p, const QBrush &brush) { p.restore(); } -void BlobBezier::generateBlob() { - for (auto i = 0; i < _segmentsCount; i++) { - auto &segment = _segments[i]; - generateBlob(segment.radius, segment.angle, i); - generateBlob(segment.radiusNext, segment.angleNext, i); - segment.progress = 0.; - } -} +void RadialBlob::generateTwoValues(int i) { + auto &radius = _segments[i].radius; + auto &angle = _segments[i].angle; -void BlobBezier::generateBlob(float &radius, float &angle, int i) { - const auto angleDiff = _segmentAngle * 0.05; const auto radDiff = _radiuses.max - _radiuses.min; - radius = _radiuses.min + std::abs(RandomAdditional()) * radDiff; - angle = _segmentAngle * i + RandomAdditional() * angleDiff; - _segments[i].speed = 0.017 + 0.003 * std::abs(RandomAdditional()); + angle.setNext(_segmentAngle * i + RandomAdditional() * _angleDiff); + radius.setNext(_radiuses.min + std::abs(RandomAdditional()) * radDiff); } -void BlobBezier::update(float level, float speedScale) { +void RadialBlob::update(float level, float speedScale) { _scale = level; + Blob::update(level, speedScale); +} + +Blob::Segment &RadialBlob::segmentAt(int i) { + return _segments[i]; +}; + +LinearBlob::LinearBlob( + int n, + Direction direction, + float minSpeed, + float maxSpeed) +: Blob(n + 1) +, _topDown(direction == Direction::TopDown ? 1 : -1) +, _segments(_segmentsCount) { +} + +void LinearBlob::paint(Painter &p, const QBrush &brush, int width) { + auto path = QPainterPath(); + + const auto left = 0; + const auto right = width; + + path.moveTo(right, 0); + path.lineTo(left, 0); + + const auto n = float(_segmentsCount - 1); + + p.save(); + for (auto i = 0; i < _segmentsCount; i++) { - auto &segment = _segments[i]; - segment.progress += (segment.speed * _minSpeed) - + level * segment.speed * _maxSpeed * speedScale; - if (segment.progress >= 1) { - segment.progress = 0.; - segment.radius = segment.radiusNext; - segment.angle = segment.angleNext; - generateBlob(segment.radiusNext, segment.angleNext, i); + const auto &segment = _segments[i]; + + if (!i) { + const auto &progress = segment.progress; + const auto r1 = segment.radius.current * (1. - progress) + + segment.radius.next * progress; + const auto y = r1 * _topDown; + path.lineTo(left, y); + } else { + const auto &prevSegment = _segments[i - 1]; + const auto &progress = prevSegment.progress; + const auto r1 = prevSegment.radius.current * (1. - progress) + + prevSegment.radius.next * progress; + + const auto &progressNext = segment.progress; + const auto r2 = segment.radius.current * (1. - progressNext) + + segment.radius.next * progressNext; + + const auto x1 = (right - left) / n * (i - 1); + const auto x2 = (right - left) / n * i; + const auto cx = x1 + (x2 - x1) / 2; + + const auto y1 = r1 * _topDown; + const auto y2 = r2 * _topDown; + path.cubicTo( + QPointF(cx, y1), + QPointF(cx, y2), + QPointF(x2, y2) + ); } } + path.lineTo(right, 0); + + p.setBrush(Qt::NoBrush); + p.setPen(_pen); + p.fillPath(path, brush); + p.drawPath(path); + + p.restore(); } -void BlobBezier::setRadiuses(Radiuses values) { - _radiuses = values; +void LinearBlob::generateTwoValues(int i) { + auto &radius = _segments[i].radius; + const auto radDiff = _radiuses.max - _radiuses.min; + radius.setNext(_radiuses.min + std::abs(RandomAdditional()) * radDiff); } -BlobBezier::Radiuses BlobBezier::radiuses() const { - return _radiuses; -} +Blob::Segment &LinearBlob::segmentAt(int i) { + return _segments[i]; +}; } // namespace Ui::Paint diff --git a/ui/paint/blob.h b/ui/paint/blob.h index 700cebf..85ae491 100644 --- a/ui/paint/blob.h +++ b/ui/paint/blob.h @@ -10,54 +10,103 @@ class Painter; namespace Ui::Paint { -class BlobBezier final { +class Blob { public: struct Radiuses { float min = 0.; float max = 0.; - - inline bool operator==(const Radiuses &other) const { - return (min == other.min) && (max == other.max); - } - inline bool operator!=(const Radiuses &other) const { - return !(min == other.min) && (max == other.max); - } }; - BlobBezier(int n, float minScale, float minSpeed = 0, float maxSpeed = 0); + Blob(int n, float minSpeed = 0, float maxSpeed = 0); - void paint(Painter &p, const QBrush &brush); void update(float level, float speedScale); void generateBlob(); void setRadiuses(Radiuses values); - Radiuses radiuses() const; + [[nodiscard]] Radiuses radiuses() const; + +protected: + struct TwoValues { + float current = 0.; + float next = 0.; + void setNext(float v) { + current = next; + next = v; + } + }; -private: struct Segment { - float radius = 0.; - float angle = 0.; - float radiusNext = 0.; - float angleNext = 0.; float progress = 0.; float speed = 0.; }; - void generateBlob(float &radius, float &angle, int i); + void generateSingleValues(int i); + virtual void generateTwoValues(int i) = 0; + virtual Segment &segmentAt(int i) = 0; const int _segmentsCount; - const float64 _segmentLength; - const float _minScale; const float _minSpeed; const float _maxSpeed; const QPen _pen; - const float _segmentAngle; - std::vector _segments; - - float64 _scale = 0; Radiuses _radiuses; }; +class RadialBlob final : public Blob { +public: + RadialBlob(int n, float minScale, float minSpeed = 0, float maxSpeed = 0); + + void paint(Painter &p, const QBrush &brush); + void update(float level, float speedScale); + +private: + struct Segment : Blob::Segment { + Blob::TwoValues radius; + Blob::TwoValues angle; + }; + + void generateTwoValues(int i) override; + Blob::Segment &segmentAt(int i) override; + + const float64 _segmentLength; + const float _minScale; + const float _segmentAngle; + const float _angleDiff; + + std::vector _segments; + + float64 _scale = 0; + +}; + +class LinearBlob final : public Blob { +public: + enum class Direction { + TopDown, + BottomUp, + }; + + LinearBlob( + int n, + Direction direction = Direction::TopDown, + float minSpeed = 0, + float maxSpeed = 0); + + void paint(Painter &p, const QBrush &brush, int width); + +private: + struct Segment : Blob::Segment { + Blob::TwoValues radius; + }; + + void generateTwoValues(int i) override; + Blob::Segment &segmentAt(int i) override; + + const int _topDown; + + std::vector _segments; + +}; + } // namespace Ui::Paint diff --git a/ui/paint/blob_linear.cpp b/ui/paint/blob_linear.cpp deleted file mode 100644 index c609efe..0000000 --- a/ui/paint/blob_linear.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// This file is part of Desktop App Toolkit, -// a set of libraries for developing nice desktop applications. -// -// For license and copyright information please follow this link: -// https://github.com/desktop-app/legal/blob/master/LEGAL -// -#include "ui/paint/blob_linear.h" - -#include "base/openssl_help.h" -#include "ui/painter.h" - -#include - -namespace Ui::Paint { - -namespace { - -constexpr auto kMaxSpeed = 8.2; -constexpr auto kMinSpeed = 0.8; - -float64 RandomAdditional() { - return (openssl::RandomValue() % 100 / 100.); -} - -} // namespace - -LinearBlobBezier::LinearBlobBezier( - int n, - Direction direction, - float minSpeed, - float maxSpeed) -: _segmentsCount(n) -, _minSpeed(minSpeed ? minSpeed : kMinSpeed) -, _maxSpeed(maxSpeed ? maxSpeed : kMaxSpeed) -, _pen(Qt::NoBrush, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin) -, _topDown(direction == Direction::TopDown ? 1 : -1) -, _segments(n + 1) { -} - -void LinearBlobBezier::paint(Painter &p, const QBrush &brush, int width) { - auto path = QPainterPath(); - - const auto left = 0; - const auto right = width; - - path.moveTo(right, 0); - path.lineTo(left, 0); - - const auto &n = _segmentsCount; - - p.save(); - - for (auto i = 0; i <= n; i++) { - const auto &segment = _segments[i]; - - if (!i) { - const auto progress = segment.progress; - const auto r1 = segment.radius * (1. - progress) - + segment.radiusNext * progress; - const auto y = r1 * _topDown; - path.lineTo(left, y); - } else { - const auto &prevSegment = _segments[i - 1]; - const auto &progress = prevSegment.progress; - const auto r1 = prevSegment.radius * (1. - progress) - + prevSegment.radiusNext * progress; - - const auto &progressNext = segment.progress; - const auto r2 = segment.radius * (1. - progressNext) - + segment.radiusNext * progressNext; - - const auto x1 = (right - left) / n * (i - 1); - const auto x2 = (right - left) / n * i; - const auto cx = x1 + (x2 - x1) / 2; - - const auto y1 = r1 * _topDown; - const auto y2 = r2 * _topDown; - path.cubicTo( - QPointF(cx, y1), - QPointF(cx, y2), - QPointF(x2, y2) - ); - } - } - path.lineTo(right, 0); - - p.setBrush(Qt::NoBrush); - p.setPen(_pen); - p.fillPath(path, brush); - p.drawPath(path); - - p.restore(); -} - -void LinearBlobBezier::generateBlob() { - for (auto i = 0; i < _segmentsCount; i++) { - auto &segment = _segments[i]; - generateBlob(segment.radius, i); - generateBlob(segment.radiusNext, i); - segment.progress = 0.; - } -} - -void LinearBlobBezier::generateBlob(float &radius, int i) { - const auto radDiff = _radiuses.max - _radiuses.min; - - radius = _radiuses.min + std::abs(RandomAdditional()) * radDiff; - _segments[i].speed = 0.017 + 0.003 * std::abs(RandomAdditional()); -} - -void LinearBlobBezier::update(float level, float speedScale) { - for (auto i = 0; i < _segmentsCount; i++) { - auto &segment = _segments[i]; - segment.progress += (segment.speed * _minSpeed) - + level * segment.speed * _maxSpeed * speedScale; - if (segment.progress >= 1) { - segment.progress = 0.; - segment.radius = segment.radiusNext; - generateBlob(segment.radiusNext, i); - } - } -} - -void LinearBlobBezier::setRadiuses(Radiuses values) { - _radiuses = values; -} - -LinearBlobBezier::Radiuses LinearBlobBezier::radiuses() const { - return _radiuses; -} - -} // namespace Ui::Paint diff --git a/ui/paint/blob_linear.h b/ui/paint/blob_linear.h deleted file mode 100644 index 31db7d7..0000000 --- a/ui/paint/blob_linear.h +++ /dev/null @@ -1,67 +0,0 @@ -// This file is part of Desktop App Toolkit, -// a set of libraries for developing nice desktop applications. -// -// For license and copyright information please follow this link: -// https://github.com/desktop-app/legal/blob/master/LEGAL -// -#pragma once - -class Painter; - -namespace Ui::Paint { - -class LinearBlobBezier final { -public: - enum class Direction { - TopDown, - BottomUp, - }; - - struct Radiuses { - float min = 0.; - float max = 0.; - - inline bool operator==(const Radiuses &other) const { - return (min == other.min) && (max == other.max); - } - inline bool operator!=(const Radiuses &other) const { - return !(min == other.min) && (max == other.max); - } - }; - - LinearBlobBezier( - int n, - Direction direction = Direction::TopDown, - float minSpeed = 0, - float maxSpeed = 0); - - void paint(Painter &p, const QBrush &brush, int width); - void update(float level, float speedScale); - void generateBlob(); - - void setRadiuses(Radiuses values); - Radiuses radiuses() const; - -private: - struct Segment { - float radius = 0.; - float radiusNext = 0.; - float progress = 0.; - float speed = 0.; - }; - - void generateBlob(float &radius, int i); - - const int _segmentsCount; - const float _minSpeed; - const float _maxSpeed; - const QPen _pen; - const int _topDown; - - std::vector _segments; - - Radiuses _radiuses; - -}; - -} // namespace Ui::Paint diff --git a/ui/paint/blobs.cpp b/ui/paint/blobs.cpp index 0273971..700260e 100644 --- a/ui/paint/blobs.cpp +++ b/ui/paint/blobs.cpp @@ -22,7 +22,7 @@ Blobs::Blobs( void Blobs::init() { for (const auto &data : _blobDatas) { - auto blob = Paint::BlobBezier(data.segmentsCount, data.minScale); + auto blob = Paint::RadialBlob(data.segmentsCount, data.minScale); blob.setRadiuses({ data.minRadius, data.maxRadius }); blob.generateBlob(); _blobs.push_back(std::move(blob)); @@ -45,17 +45,17 @@ int Blobs::size() const { } void Blobs::setRadiusesAt( - rpl::producer &&radiuses, + rpl::producer &&radiuses, int index) { Expects(index >= 0 && index < size()); std::move( radiuses - ) | rpl::start_with_next([=](BlobBezier::Radiuses r) { + ) | rpl::start_with_next([=](Blob::Radiuses r) { _blobs[index].setRadiuses(std::move(r)); }, _lifetime); } -BlobBezier::Radiuses Blobs::radiusesAt(int index) { +Blob::Radiuses Blobs::radiusesAt(int index) { Expects(index >= 0 && index < size()); return _blobs[index].radiuses(); } diff --git a/ui/paint/blobs.h b/ui/paint/blobs.h index 6a61d70..a7d5d3d 100644 --- a/ui/paint/blobs.h +++ b/ui/paint/blobs.h @@ -30,9 +30,9 @@ public: float maxLevel); void setRadiusesAt( - rpl::producer &&radiuses, + rpl::producer &&radiuses, int index); - BlobBezier::Radiuses radiusesAt(int index); + Blob::Radiuses radiusesAt(int index); void setLevel(float value); void paint(Painter &p, const QBrush &brush); @@ -50,7 +50,7 @@ private: const float _maxLevel; std::vector _blobDatas; - std::vector _blobs; + std::vector _blobs; anim::continuous_value _levelValue; diff --git a/ui/paint/blobs_linear.cpp b/ui/paint/blobs_linear.cpp index db03988..8aa478b 100644 --- a/ui/paint/blobs_linear.cpp +++ b/ui/paint/blobs_linear.cpp @@ -14,7 +14,7 @@ LinearBlobs::LinearBlobs( std::vector blobDatas, float levelDuration, float maxLevel, - LinearBlobBezier::Direction direction) + LinearBlob::Direction direction) : _maxLevel(maxLevel) , _direction(direction) , _blobDatas(std::move(blobDatas)) @@ -24,7 +24,7 @@ LinearBlobs::LinearBlobs( void LinearBlobs::init() { for (const auto &data : _blobDatas) { - auto blob = Paint::LinearBlobBezier( + auto blob = Paint::LinearBlob( data.segmentsCount, _direction); blob.setRadiuses({ data.minRadius, data.idleRadius }); @@ -49,17 +49,17 @@ int LinearBlobs::size() const { } void LinearBlobs::setRadiusesAt( - rpl::producer &&radiuses, + rpl::producer &&radiuses, int index) { Expects(index >= 0 && index < size()); std::move( radiuses - ) | rpl::start_with_next([=](LinearBlobBezier::Radiuses r) { + ) | rpl::start_with_next([=](Blob::Radiuses r) { _blobs[index].setRadiuses(std::move(r)); }, _lifetime); } -LinearBlobBezier::Radiuses LinearBlobs::radiusesAt(int index) { +Blob::Radiuses LinearBlobs::radiusesAt(int index) { Expects(index >= 0 && index < size()); return _blobs[index].radiuses(); } diff --git a/ui/paint/blobs_linear.h b/ui/paint/blobs_linear.h index 3187e8d..5d88f8e 100644 --- a/ui/paint/blobs_linear.h +++ b/ui/paint/blobs_linear.h @@ -7,7 +7,7 @@ #pragma once #include "ui/effects/animation_value.h" -#include "ui/paint/blob_linear.h" +#include "ui/paint/blob.h" class Painter; @@ -22,19 +22,18 @@ public: float idleRadius = 0; float speedScale = 0; float alpha = 0; - int topOffset = 0; }; LinearBlobs( std::vector blobDatas, float levelDuration, float maxLevel, - LinearBlobBezier::Direction direction); + LinearBlob::Direction direction); void setRadiusesAt( - rpl::producer &&radiuses, + rpl::producer &&radiuses, int index); - LinearBlobBezier::Radiuses radiusesAt(int index); + Blob::Radiuses radiusesAt(int index); void setLevel(float value); void paint(Painter &p, const QBrush &brush, int width); @@ -50,10 +49,10 @@ private: void init(); const float _maxLevel; - const LinearBlobBezier::Direction _direction; + const LinearBlob::Direction _direction; std::vector _blobDatas; - std::vector _blobs; + std::vector _blobs; anim::continuous_value _levelValue; diff --git a/ui/widgets/call_mute_button.cpp b/ui/widgets/call_mute_button.cpp index 0eca708..c85c34e 100644 --- a/ui/widgets/call_mute_button.cpp +++ b/ui/widgets/call_mute_button.cpp @@ -20,7 +20,7 @@ namespace Ui { namespace { -using Radiuses = Paint::BlobBezier::Radiuses; +using Radiuses = Paint::Blob::Radiuses; constexpr auto kMaxLevel = 1.;