From cffbdc7c58f5b5ead273562c95bd9471d65bc2d6 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 8 Dec 2020 23:09:21 +0300 Subject: [PATCH] Added initial implementation of Linear Blobs. --- CMakeLists.txt | 4 + ui/paint/blob.h | 3 - ui/paint/blob_linear.cpp | 153 ++++++++++++++++++++++++++++++++++++++ ui/paint/blob_linear.h | 67 +++++++++++++++++ ui/paint/blobs_linear.cpp | 113 ++++++++++++++++++++++++++++ ui/paint/blobs_linear.h | 69 +++++++++++++++++ 6 files changed, 406 insertions(+), 3 deletions(-) create mode 100644 ui/paint/blob_linear.cpp create mode 100644 ui/paint/blob_linear.h create mode 100644 ui/paint/blobs_linear.cpp create mode 100644 ui/paint/blobs_linear.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0884919..4628547 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,8 +71,12 @@ 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 + ui/paint/blobs_linear.h ui/platform/linux/ui_window_linux.cpp ui/platform/linux/ui_window_linux.h ui/platform/linux/ui_utility_linux.cpp diff --git a/ui/paint/blob.h b/ui/paint/blob.h index bd1d990..700cebf 100644 --- a/ui/paint/blob.h +++ b/ui/paint/blob.h @@ -30,9 +30,6 @@ public: void update(float level, float speedScale); void generateBlob(); - void setMinRadius(float value); - void setMaxRadius(float value); - void setRadiuses(Radiuses values); Radiuses radiuses() const; diff --git a/ui/paint/blob_linear.cpp b/ui/paint/blob_linear.cpp new file mode 100644 index 0000000..92a5015 --- /dev/null +++ b/ui/paint/blob_linear.cpp @@ -0,0 +1,153 @@ +// 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 +#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, + float minScale, + float minSpeed, + float maxSpeed) +: _segmentsCount(n) +, _minSpeed(minSpeed ? minSpeed : kMinSpeed) +, _maxSpeed(maxSpeed ? maxSpeed : kMaxSpeed) +, _pen(Qt::NoBrush, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin) +, _segments(n + 1) { +} + +void LinearBlobBezier::paint( + Painter &p, + const QBrush &brush, + const QRect &rect, + float pinnedTop, + float progressToPinned) { + auto path = QPainterPath(); + auto m = QMatrix(); + + const auto left = rect.x(); + const auto top = rect.y(); + const auto right = rect.x() + rect.width(); + const auto bottom = rect.y() + rect.height(); + + // path.moveTo(right, bottom); + // path.lineTo(left, bottom); + path.moveTo(right, top); + path.lineTo(left, top); + + 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 = (bottom + r1);// * progressToPinned + // const auto y = (top - r1) * progressToPinned + // + pinnedTop * (1. - progressToPinned); + 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 = (bottom + r1);// * progressToPinned + // const auto y1 = (top - r1) * progressToPinned + // + pinnedTop * (1. - progressToPinned); + const auto y2 = (bottom + r2);// * progressToPinned + // const auto y2 = (top - r2) * progressToPinned + // + pinnedTop * (1. - progressToPinned); + path.cubicTo( + QPointF(cx, y1), + QPointF(cx, y2), + QPointF(x2, y2) + ); + if (i == n) { + path.lineTo(right, top); + // path.lineTo(right, bottom); + } + } + } + + 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) { + _scale = level; + 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 new file mode 100644 index 0000000..688fb09 --- /dev/null +++ b/ui/paint/blob_linear.h @@ -0,0 +1,67 @@ +// 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: + 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, + float minScale, + float minSpeed = 0, + float maxSpeed = 0); + + void paint( + Painter &p, + const QBrush &brush, + const QRect &rect, + float pinnedTop, + float progressToPinned); + 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; + + std::vector _segments; + + float64 _scale = 0; + Radiuses _radiuses; + +}; + +} // namespace Ui::Paint diff --git a/ui/paint/blobs_linear.cpp b/ui/paint/blobs_linear.cpp new file mode 100644 index 0000000..0ebcaae --- /dev/null +++ b/ui/paint/blobs_linear.cpp @@ -0,0 +1,113 @@ +// 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/blobs_linear.h" + +#include "ui/painter.h" + +namespace Ui::Paint { + +LinearBlobs::LinearBlobs( + std::vector blobDatas, + float levelDuration, + float levelDuration2, + float maxLevel) +: _maxLevel(maxLevel) +, _blobDatas(std::move(blobDatas)) +, _levelValue(levelDuration) +, _levelValue2(levelDuration2) { + init(); +} + +void LinearBlobs::init() { + for (const auto &data : _blobDatas) { + auto blob = Paint::LinearBlobBezier( + data.segmentsCount, + data.minScale); + blob.setRadiuses({ 0, data.minRadius }); + blob.generateBlob(); + _blobs.push_back(std::move(blob)); + } +} + +float LinearBlobs::maxRadius() const { + const auto maxOfRadiuses = [](const BlobData data) { + return std::max(data.maxRadius, data.minRadius); + }; + const auto max = *ranges::max_element( + _blobDatas, + std::less<>(), + maxOfRadiuses); + return maxOfRadiuses(max); +} + +int LinearBlobs::size() const { + return _blobs.size(); +} + +void LinearBlobs::setRadiusesAt( + rpl::producer &&radiuses, + int index) { + Expects(index >= 0 && index < size()); + std::move( + radiuses + ) | rpl::start_with_next([=](LinearBlobBezier::Radiuses r) { + _blobs[index].setRadiuses(std::move(r)); + }, _lifetime); +} + +LinearBlobBezier::Radiuses LinearBlobs::radiusesAt(int index) { + Expects(index >= 0 && index < size()); + return _blobs[index].radiuses(); +} + +void LinearBlobs::setLevel(float value) { + const auto to = std::min(_maxLevel, value) / _maxLevel; + _levelValue.start(to); + _levelValue2.start(to); +} + +void LinearBlobs::paint( + Painter &p, + const QBrush &brush, + const QRect &rect, + float pinnedTop, + float progressToPinned) { + const auto opacity = p.opacity(); + for (auto i = 0; i < _blobs.size(); i++) { + auto r = rect; + r.setTop(r.top() - _blobDatas[i].topOffset * _levelValue2.current()); + + _blobs[i].update(_levelValue.current(), _blobDatas[i].speedScale); + const auto alpha = _blobDatas[i].alpha; + if (alpha != 1.) { + p.setOpacity(opacity * alpha); + } + _blobs[i].paint(p, brush, r, pinnedTop, progressToPinned); + if (alpha != 1.) { + p.setOpacity(opacity); + } + } +} + +void LinearBlobs::updateLevel(crl::time dt) { + const auto d = (dt > 20) ? 17 : dt; + _levelValue.update(d); + _levelValue2.update(d); + + for (auto i = 0; i < _blobs.size(); i++) { + const auto &data = _blobDatas[i]; + _blobs[i].setRadiuses({ + 0, + data.minRadius + data.maxRadius * (float)currentLevel() }); + } +} + +float64 LinearBlobs::currentLevel() const { + return _levelValue.current(); +} + +} // namespace Ui::Paint diff --git a/ui/paint/blobs_linear.h b/ui/paint/blobs_linear.h new file mode 100644 index 0000000..a4a30e5 --- /dev/null +++ b/ui/paint/blobs_linear.h @@ -0,0 +1,69 @@ +// 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 + +#include "ui/effects/animation_value.h" +#include "ui/paint/blob_linear.h" + +class Painter; + +namespace Ui::Paint { + +class LinearBlobs final { +public: + struct BlobData { + int segmentsCount = 0; + float minScale = 0; + float minRadius = 0; + float maxRadius = 0; + float speedScale = 0; + float alpha = 0; + int topOffset = 0; + }; + + LinearBlobs( + std::vector blobDatas, + float levelDuration, + float levelDuration2, + float maxLevel); + + void setRadiusesAt( + rpl::producer &&radiuses, + int index); + LinearBlobBezier::Radiuses radiusesAt(int index); + + void setLevel(float value); + void paint( + Painter &p, + const QBrush &brush, + const QRect &rect, + float pinnedTop, + float progressToPinned); + void updateLevel(crl::time dt); + + [[nodiscard]] float maxRadius() const; + [[nodiscard]] int size() const; + [[nodiscard]] float64 currentLevel() const; + + static constexpr auto kHideBlobsDuration = 2000; + +private: + void init(); + + const float _maxLevel; + + std::vector _blobDatas; + std::vector _blobs; + + anim::continuous_value _levelValue; + anim::continuous_value _levelValue2; + + rpl::lifetime _lifetime; + +}; + +} // namespace Ui::Paint