diff --git a/YSGraphic_Core/Axis/Abs_Axis.cpp b/YSGraphic_Core/Axis/Abs_Axis.cpp index 9feb2f0..7433acd 100644 --- a/YSGraphic_Core/Axis/Abs_Axis.cpp +++ b/YSGraphic_Core/Axis/Abs_Axis.cpp @@ -1,4 +1,5 @@ #include +#include #include "Abs_Axis_p.h" namespace YSG { using AbsAxis_Binding = Renderable_Binding; @@ -39,7 +40,7 @@ size_t Abs_Axis::pixel_size(SRC src) { return d()->state_value(&Abs_Axis_Render_State::pixel_size, src); } void Abs_Axis::set_pixel_size(size_t value) { - d()->set_state_value(&Abs_Axis_Render_State::pixel_size, value); + d()->set_state_value(&Abs_Axis_Render_State::pixel_size, std::max(1, value)); } int Abs_Axis::tick_length(SRC src) { return d()->state_value(&Abs_Axis_Render_State::tick_length, src); @@ -114,12 +115,12 @@ QVector Abs_Axis_Private::create_tick_vector(double tickStep, const Rang // do not use qFloor here, or we'll lose 64 bit precision // qDebug() << "tickStep:" << tickStep; // - auto firstStep = qint64(floor(qAbs((range.lower - mTickOrigin) / tickStep))); - auto lastStep = qint64(ceil((qAbs(range.upper - mTickOrigin) / tickStep))); + auto firstStep = qint64(floor(qAbs((range.origin - mTickOrigin) / tickStep))); + auto lastStep = qint64(ceil((qAbs(range.target - mTickOrigin) / tickStep))); int tickcount = int(qAbs(lastStep - firstStep) + 1); if (tickcount < 0) tickcount = 0; - if (range.lower > range.upper) + if (range.origin > range.target) tickStep = -tickStep; result.resize(tickcount); for (int i = 0; i < tickcount; ++i) { @@ -244,6 +245,8 @@ double Abs_Axis_Private::clean_mantissa(double input) { return pickClosest(mantissa, QVector() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0) * magnitude; } double Abs_Axis_Private::pickClosest(double target, const QVector& candidates) { + if (candidates.empty()) + return target; if (candidates.size() == 1) return candidates.first(); QVector::const_iterator it = std::lower_bound(candidates.constBegin(), candidates.constEnd(), target); @@ -272,7 +275,10 @@ Range Abs_Axis::coord_range(SRC src) { } int Abs_Axis::get_pixel_point_size(Range range, SRC src) { auto render_state = d()->state_data(src); - return static_cast(qAbs(range.length() / coord_range(src).length()) * render_state->pixel_size); + double length = coord_range(src).length(); + if (length == 0.0) + return 0; + return static_cast(qAbs(range.length() / length) * render_state->pixel_size); } int Abs_Axis::get_pixel_point_size(SRC src) { auto render_state = d()->state_data(src); diff --git a/YSGraphic_Core/Axis/Abs_Axis.h b/YSGraphic_Core/Axis/Abs_Axis.h index c93c881..956700d 100644 --- a/YSGraphic_Core/Axis/Abs_Axis.h +++ b/YSGraphic_Core/Axis/Abs_Axis.h @@ -90,9 +90,8 @@ struct Abs_Axis::BuilderT : protected AbsAxis_Prop { PROP_BT(QPen, unit_text_pen) PROP_BT(QBrush, unit_text_background_brush) PROP_BT(int, rotate) // 不旋转 - SETTER_T(QString, layerName, "axis") protected: - Plot* plot{}; + std::shared_ptr parent{}; size_t pixel_size{}; void set(Abs_Axis* ret); }; diff --git a/YSGraphic_Core/Axis/Abs_Axis_p.h b/YSGraphic_Core/Axis/Abs_Axis_p.h index 760b8ff..8c87dcb 100644 --- a/YSGraphic_Core/Axis/Abs_Axis_p.h +++ b/YSGraphic_Core/Axis/Abs_Axis_p.h @@ -1,4 +1,5 @@ #pragma once +#include #include "Abs_Axis.h" namespace YSG { struct Abs_Axis_Render_State : Render_State, AbsAxis_Prop { @@ -19,10 +20,10 @@ struct Abs_Axis_Private : Typed_Render_Data& candidates); - void prepare_data() override { + void prepare_data(const Plot_Render_Snapshot&) override { sync_state_pipeline(); } - void draw(QPainter* painter) override { + void draw(QPainter* painter, const Plot_Render_Snapshot&) override { Abs_Axis_Render_State* s = render_state(); pixelStart = s->orientation == Qt::Horizontal ? s->x : s->y; Range range = {s->coord_start, s->coord_start + s->coord_length}; @@ -125,13 +126,13 @@ protected: }; template void Abs_Axis::BuilderT::set(Abs_Axis* ret) { - ret->init(plot, layerName); + ret->init(parent); Abs_Axis_Private* pd = ret->d(); Abs_Axis_Render_State* sc = reinterpret_cast(pd->state(State_Edit)); sc->x = this->x; sc->y = this->y; sc->orientation = this->orientation; - sc->pixel_size = this->pixel_size; + sc->pixel_size = std::max(1, this->pixel_size); sc->tick_length = this->tick_length; sc->sub_tick_length = this->sub_tick_length; sc->color = this->color; diff --git a/YSGraphic_Core/Axis/Axis.cpp b/YSGraphic_Core/Axis/Axis.cpp index 6cf934d..14dc84c 100644 --- a/YSGraphic_Core/Axis/Axis.cpp +++ b/YSGraphic_Core/Axis/Axis.cpp @@ -32,20 +32,25 @@ double Axis::coord_length(SRC src) { return d()->state_value(&Axis_Render_State::coord_length, src); } void Axis::set_coord_length(double value) { + if (value == 0.0) + return; d()->set_state_value(&Axis_Render_State::coord_length, value); } void Axis::set_coord_range(Range range) { + if (range.length() == 0.0) + return; Render_Edit_Lease edit(d()); - edit->coord_start = range.lower; - edit->coord_length = range.upper - range.lower; + edit->coord_start = range.origin; + edit->coord_length = range.target - range.origin; } -Axis::Builder::Builder(Plot* plot, Qt::Orientation orientation) { - this->plot = plot; +Axis::Builder::Builder(std::shared_ptr parent, Qt::Orientation orientation) { + ASSERT(parent && parent->mPlot, "Axis build error! parent invalid"); + this->parent = parent; this->orientation = orientation; } -Axis* Axis::Builder::build() { - Axis* ret = new Axis(); - Axis::BuilderT::set(ret); +std::shared_ptr Axis::Builder::build() { + std::shared_ptr ret(new Axis()); + Axis::BuilderT::set(ret.get()); return ret; } } // namespace YSG diff --git a/YSGraphic_Core/Axis/Axis.h b/YSGraphic_Core/Axis/Axis.h index 76fb72e..a543bee 100644 --- a/YSGraphic_Core/Axis/Axis.h +++ b/YSGraphic_Core/Axis/Axis.h @@ -1,5 +1,6 @@ #pragma once #include "Abs_Axis.h" +#include namespace YSG { struct Axis_Render_State; struct Axis_Input_Data; @@ -31,7 +32,9 @@ struct Axis::BuilderT : Abs_Axis::BuilderT { // PROP_BT(double, coord_length) That& set_coord_range(Range r) { That& t = *static_cast(this); - t.coord_start = std::move(r.lower); + if (r.length() == 0.0) + return t; + t.coord_start = std::move(r.origin); t.coord_length = std::move(r.length()); return t; } @@ -53,8 +56,8 @@ protected: }; struct LIB_DECL Axis::Builder : BuilderT { /// 创建数值轴构造器。 - Builder(Plot* plot, Qt::Orientation orientation); + Builder(std::shared_ptr parent, Qt::Orientation orientation); /// 构建并返回数值轴对象。 - Axis* build(); + std::shared_ptr build(); }; } // namespace YSG diff --git a/YSGraphic_Core/Axis/Axis_p.h b/YSGraphic_Core/Axis/Axis_p.h index 2aa1a75..f0e4c4c 100644 --- a/YSGraphic_Core/Axis/Axis_p.h +++ b/YSGraphic_Core/Axis/Axis_p.h @@ -74,6 +74,8 @@ void Axis::BuilderT::set(Axis* ret) { PROP_RT(int, number_precision) PROP_RT(double, coord_start) PROP_RT(double, coord_length) + if (sc->coord_length == 0.0) + sc->coord_length = 1.0; pd->useWheel = useWheel; pd->useDrag = useDrag; } diff --git a/YSGraphic_Core/Axis/Frequent_Axis.cpp b/YSGraphic_Core/Axis/Frequent_Axis.cpp index 925d998..bd088ff 100644 --- a/YSGraphic_Core/Axis/Frequent_Axis.cpp +++ b/YSGraphic_Core/Axis/Frequent_Axis.cpp @@ -17,13 +17,14 @@ Render_State* Frequent_Axis::create_render_state() { Input_Data* Frequent_Axis::create_input_data() { return FrequentAxis_Binding::create_input_data(); } -Frequent_Axis::Builder::Builder(Plot* plot, Qt::Orientation orientation) { - this->plot = plot; +Frequent_Axis::Builder::Builder(std::shared_ptr parent, Qt::Orientation orientation) { + ASSERT(parent && parent->mPlot, "Frequent_Axis build error! parent invalid"); + this->parent = parent; this->orientation = orientation; } -Frequent_Axis* Frequent_Axis::Builder::build() { - auto ret = new Frequent_Axis(); - set(ret); +std::shared_ptr Frequent_Axis::Builder::build() { + std::shared_ptr ret(new Frequent_Axis()); + set(ret.get()); return ret; } } // namespace YSG diff --git a/YSGraphic_Core/Axis/Frequent_Axis.h b/YSGraphic_Core/Axis/Frequent_Axis.h index 74e05c8..6603701 100644 --- a/YSGraphic_Core/Axis/Frequent_Axis.h +++ b/YSGraphic_Core/Axis/Frequent_Axis.h @@ -1,5 +1,6 @@ #pragma once #include "Axis.h" +#include namespace YSG { struct Frequent_Axis_Private; /// 频率轴,负责频率单位显示。 @@ -17,8 +18,8 @@ public: }; struct LIB_DECL Frequent_Axis::Builder : BuilderT { /// 创建频率轴构造器。 - Builder(Plot* plot, Qt::Orientation orientation); + Builder(std::shared_ptr parent, Qt::Orientation orientation); /// 构建并返回频率轴对象。 - Frequent_Axis* build(); + std::shared_ptr build(); }; } // namespace YSG diff --git a/YSGraphic_Core/Axis/Time_Axis.cpp b/YSGraphic_Core/Axis/Time_Axis.cpp index c776107..cac260e 100644 --- a/YSGraphic_Core/Axis/Time_Axis.cpp +++ b/YSGraphic_Core/Axis/Time_Axis.cpp @@ -20,13 +20,13 @@ int Time_Axis::time_point_size(SRC src) { return d()->state_value(&Time_Axis_Render_State::time_point_size, src); } void Time_Axis::set_time_point_size(int value) { - d()->set_state_value(&Time_Axis_Render_State::time_point_size, value); + d()->set_state_value(&Time_Axis_Render_State::time_point_size, qMax(1, value)); } int Time_Axis::tick_pixel_space(SRC src) { return d()->state_value(&Time_Axis_Render_State::tick_pixel_space, src); } void Time_Axis::set_tick_pixel_space(int value) { - d()->set_state_value(&Time_Axis_Render_State::tick_pixel_space, value); + d()->set_state_value(&Time_Axis_Render_State::tick_pixel_space, qMax(1, value)); } QString Time_Axis::time_format(SRC src) { return d()->state_value(&Time_Axis_Render_State::time_format, src); @@ -57,19 +57,22 @@ QTime Time_Axis::tick_to_time(int tick, SRC src) { int offset = tick - (int)render_state->coord_start; return d()->time_by_offset(offset); } -Time_Axis::Builder::Builder(Plot* plot, Qt::Orientation orientation) { - this->plot = plot; +Time_Axis::Builder::Builder(std::shared_ptr parent, Qt::Orientation orientation) { + ASSERT(parent && parent->mPlot, "Time_Axis build error! parent invalid"); + this->parent = parent; this->orientation = orientation; } -Time_Axis* Time_Axis::Builder::build() { - auto ret = new Time_Axis(); - set(ret); +std::shared_ptr Time_Axis::Builder::build() { + std::shared_ptr ret(new Time_Axis()); + set(ret.get()); Time_Axis_Private* pd = ret->d(); Time_Axis_Render_State* sc = reinterpret_cast(pd->state(State_Edit)); PROP_R(int, time_point_size) PROP_R(int, tick_pixel_space) PROP_R(QString, time_format) PROP_R(QFont, font) + sc->time_point_size = qMax(1, sc->time_point_size); + sc->tick_pixel_space = qMax(1, sc->tick_pixel_space); return ret; } } // namespace YSG diff --git a/YSGraphic_Core/Axis/Time_Axis.h b/YSGraphic_Core/Axis/Time_Axis.h index 3221449..e8956e1 100644 --- a/YSGraphic_Core/Axis/Time_Axis.h +++ b/YSGraphic_Core/Axis/Time_Axis.h @@ -1,5 +1,6 @@ #pragma once #include "Abs_Axis.h" +#include namespace YSG { struct Time_Axis_Private; struct Time_Ticker; @@ -37,8 +38,8 @@ struct LIB_DECL Time_Axis::Builder : Abs_Axis::BuilderT, PROP_B(QString, time_format) PROP_B(QFont, font) /// 创建时间轴构造器。 - Builder(Plot* plot, Qt::Orientation orientation); + Builder(std::shared_ptr parent, Qt::Orientation orientation); /// 构建并返回时间轴对象。 - Time_Axis* build(); + std::shared_ptr build(); }; } // namespace YSG diff --git a/YSGraphic_Core/Axis/Time_Axis_p.h b/YSGraphic_Core/Axis/Time_Axis_p.h index 01c0b28..9a09635 100644 --- a/YSGraphic_Core/Axis/Time_Axis_p.h +++ b/YSGraphic_Core/Axis/Time_Axis_p.h @@ -64,14 +64,15 @@ struct TimeTick { int mTick{}; }; struct Time_Axis_Input_Data : Abs_Axis_Input_Data { + static constexpr std::size_t Max_Pending_Time_Count = 32; + static constexpr std::size_t Max_Pending_Tick_Count = 32; Bounded_Input_Buffer mData; Bounded_Input_Buffer mAllTime; - std::vector mTickReadBuffer; void push_time(const QTime& time) { - mAllTime.push(&time, sizeof(time)); + mAllTime.push(&time, sizeof(time), Max_Pending_Time_Count); } void push_tick(const TimeTick& tick) { - mData.push(&tick, sizeof(tick)); + mData.push(&tick, sizeof(tick), Max_Pending_Tick_Count); } template void read_all_time(Handler handler) { @@ -83,17 +84,13 @@ struct Time_Axis_Input_Data : Abs_Axis_Input_Data { }); } template - void read_all_tick_reverse(Handler handler) { - mTickReadBuffer.clear(); - mData.read_all([this](const void* data, std::size_t size) { + void read_all_tick(Handler handler) { + mData.read_all([&handler](const void* data, std::size_t size) { (void)size; TimeTick tick; std::memcpy(&tick, data, sizeof(tick)); - mTickReadBuffer.push_back(tick); + handler(tick); }); - for (int i = static_cast(mTickReadBuffer.size()) - 1; i >= 0; --i) { - handler(mTickReadBuffer[static_cast(i)]); - } } void clear() override { mData.clear(); @@ -105,6 +102,15 @@ struct Time_Axis_Input_Data : Abs_Axis_Input_Data { std::uint64_t dropped_count() const override { return mData.dropped_count() + mAllTime.dropped_count(); } + std::uint64_t capacity_dropped_count() const override { + return mData.capacity_dropped_count() + mAllTime.capacity_dropped_count(); + } + std::uint64_t rebuild_dropped_count() const override { + return mData.rebuild_dropped_count() + mAllTime.rebuild_dropped_count(); + } + std::uint64_t consumer_dropped_count() const override { + return mData.consumer_dropped_count() + mAllTime.consumer_dropped_count(); + } std::uint64_t pushed_count() const override { return mData.pushed_count() + mAllTime.pushed_count(); } @@ -121,7 +127,7 @@ struct Time_Axis_Render_State : Abs_Axis_Render_State, Time_Axis_Prop { struct Time_Axis_Private : Typed_Render_Data_Base { friend class Time_Axis; Time_Ticker mTimeTicker{}; - int mTickPixelSpace; + int mTickPixelSpace{}; QFont mFont; QString mTimeFormat; Psc::StreamRingBuffer_ST buffer; @@ -152,7 +158,7 @@ struct Time_Axis_Private : Typed_Render_Data_Baseread_all_tick_reverse([this](const TimeTick& tick) { + d->read_all_tick([this](const TimeTick& tick) { mData.push_front(tick); }); clear_render_input(); @@ -238,8 +244,8 @@ struct Time_Axis_Private : Typed_Render_Data_Base labels; QVector ticks; - void draw(QPainter* painter) override { - Abs_Axis_Private::draw(painter); + void draw(QPainter* painter, const Plot_Render_Snapshot& snapshot) override { + Abs_Axis_Private::draw(painter, snapshot); } QString get_tick_label(double tick, SRC src) override { auto render_state = reinterpret_cast(state(src)); diff --git a/YSGraphic_Core/Demo_Gallery/Afterglow_Plot.h b/YSGraphic_Core/Demo_Gallery/Afterglow_Plot.h index 65dbd1b..bcd4e57 100644 --- a/YSGraphic_Core/Demo_Gallery/Afterglow_Plot.h +++ b/YSGraphic_Core/Demo_Gallery/Afterglow_Plot.h @@ -3,21 +3,24 @@ static QWidget* createAfterglow() { class AfterglowPlot : public YSG::Plot { public: - YSG::Frequent_Axis* xAxis{}; - YSG::Axis* yAxis{}; - YSG::Afterglow* ag{}; - YSG::Muti_Select_Rect* msr{}; + std::shared_ptr xAxis{}; + std::shared_ptr yAxis{}; + std::shared_ptr ag{}; + std::shared_ptr msr{}; QMenu* mMenu{}; void init() override { Plot::init(); object_name = "AfterglowPlot"; - xAxis = YSG::Frequent_Axis::Builder(this, Qt::Horizontal).build(); - yAxis = YSG::Axis::Builder(this, Qt::Vertical).build(); - ag = YSG::Afterglow::Builder(xAxis, yAxis) + auto data = create_renderable_node(root_renderable(), "DataRenderable"); + auto axis = create_renderable_node(root_renderable(), "AxisRenderable"); + auto overlay = create_renderable_node(root_renderable(), "OverlayRenderable"); + xAxis = YSG::Frequent_Axis::Builder(axis, Qt::Horizontal).build(); + yAxis = YSG::Axis::Builder(axis, Qt::Vertical).build(); + ag = YSG::Afterglow::Builder(data, xAxis, yAxis) .set_frequent_range({0, 100}) .set_power_range({0, 100}) .build(); - msr = YSG::Muti_Select_Rect::Builder(xAxis, yAxis).build(); + msr = YSG::Muti_Select_Rect::Builder(overlay, xAxis, yAxis).build(); } protected: void resizeEvent(QResizeEvent* event) override { @@ -33,10 +36,10 @@ static QWidget* createAfterglow() { void contextMenuEvent(QContextMenuEvent* event) override { if (!mMenu) { auto ws = - bw(cw(ag, -1), "余辉图") + - bw(cw(xAxis), "频率轴") + - bw(cw(yAxis), "功率概率轴") + - bw(cw(msr), "多选框"); + bw(cw(ag.get(), -1), "余辉图") + + bw(cw(xAxis.get()), "频率轴") + + bw(cw(yAxis.get()), "功率概率轴") + + bw(cw(msr.get()), "多选框"); mMenu = createMenu(this, ws); } mMenu->exec(event->globalPos()); diff --git a/YSGraphic_Core/Demo_Gallery/Audio_Plot.h b/YSGraphic_Core/Demo_Gallery/Audio_Plot.h index b88964e..2596542 100644 --- a/YSGraphic_Core/Demo_Gallery/Audio_Plot.h +++ b/YSGraphic_Core/Demo_Gallery/Audio_Plot.h @@ -3,21 +3,24 @@ static QWidget* createAudioPlot() { class Audio_Plot : public YSG::Plot { public: - YSG::Time_Axis* xAxis{}; - YSG::Axis* yAxis{}; - YSG::Audio_Frequent* audio{}; - YSG::Muti_Select_Rect* msr{}; + std::shared_ptr xAxis{}; + std::shared_ptr yAxis{}; + std::shared_ptr audio{}; + std::shared_ptr msr{}; QMenu* mMenu{}; void init() override { Plot::init(); object_name = "AudioPlot"; - xAxis = YSG::Time_Axis::Builder(this, Qt::Horizontal) + auto data = create_renderable_node(root_renderable(), "DataRenderable"); + auto axis = create_renderable_node(root_renderable(), "AxisRenderable"); + auto overlay = create_renderable_node(root_renderable(), "OverlayRenderable"); + xAxis = YSG::Time_Axis::Builder(axis, Qt::Horizontal) .set_tick_length(-10) .set_sub_tick_length(-5) .build(); - yAxis = YSG::Axis::Builder(this, Qt::Vertical).build(); - audio = YSG::Audio_Frequent::Builder(xAxis, yAxis).build(); - msr = YSG::Muti_Select_Rect::Builder(xAxis, yAxis).build(); + yAxis = YSG::Axis::Builder(axis, Qt::Vertical).build(); + audio = YSG::Audio_Frequent::Builder(data, xAxis, yAxis).build(); + msr = YSG::Muti_Select_Rect::Builder(overlay, xAxis, yAxis).build(); } protected: void resizeEvent(QResizeEvent* event) override { @@ -32,10 +35,10 @@ static QWidget* createAudioPlot() { void contextMenuEvent(QContextMenuEvent* event) override { if (!mMenu) { auto ws = - bw(cw(audio, -1), "音频图") + - bw(cw(yAxis), "功率y轴") + - bw(cw(xAxis), "时间轴") + - bw(cw(msr), "多选框"); + bw(cw(audio.get(), -1), "音频图") + + bw(cw(yAxis.get()), "功率y轴") + + bw(cw(xAxis.get()), "时间轴") + + bw(cw(msr.get()), "多选框"); mMenu = createMenu(this, ws); } mMenu->exec(event->globalPos()); diff --git a/YSGraphic_Core/Demo_Gallery/Planisphere_Plot.h b/YSGraphic_Core/Demo_Gallery/Planisphere_Plot.h index a7c13b8..8a28a8b 100644 --- a/YSGraphic_Core/Demo_Gallery/Planisphere_Plot.h +++ b/YSGraphic_Core/Demo_Gallery/Planisphere_Plot.h @@ -4,24 +4,26 @@ static QWidget* create_planisphere_plot() { class TimeAxis; class Planisphere_Plot : public YSG::Plot { public: - YSG::Axis* xAxis{}; - YSG::Axis* yAxis{}; - YSG::Planisphere* mPlanisphere{}; - YSG::Muti_Select_Rect* msr{}; + std::shared_ptr xAxis{}; + std::shared_ptr yAxis{}; + std::shared_ptr mPlanisphere{}; + std::shared_ptr msr{}; QMenu* mMenu{}; void init() override { Plot::init(); object_name = "PlanispherePlot"; - xAxis = YSG::Axis::Builder(this, Qt::Horizontal) + auto data = create_renderable_node(root_renderable(), "DataRenderable"); + auto axis = create_renderable_node(root_renderable(), "AxisRenderable"); + auto overlay = create_renderable_node(root_renderable(), "OverlayRenderable"); + xAxis = YSG::Axis::Builder(axis, Qt::Horizontal) .set_tick_length(-10) .set_sub_tick_length(-5) .build(); - yAxis = YSG::Axis::Builder(this, Qt::Vertical).build(); - mPlanisphere = YSG::Planisphere::Builder(xAxis, yAxis) - .set_layerName("plottable") + yAxis = YSG::Axis::Builder(axis, Qt::Vertical).build(); + mPlanisphere = YSG::Planisphere::Builder(data, xAxis, yAxis) .set_continue_millisecond(2000) .build(); - msr = YSG::Muti_Select_Rect::Builder(xAxis, yAxis).build(); + msr = YSG::Muti_Select_Rect::Builder(overlay, xAxis, yAxis).build(); } protected: void resizeEvent(QResizeEvent* event) override { @@ -36,9 +38,9 @@ static QWidget* create_planisphere_plot() { void contextMenuEvent(QContextMenuEvent* event) override { if (!mMenu) { auto ws = - bw(cw(mPlanisphere, -1), "星座图") + - bw(cw(xAxis), "I轴") + - bw(cw(yAxis), "Q轴"); + bw(cw(mPlanisphere.get(), -1), "星座图") + + bw(cw(xAxis.get()), "I轴") + + bw(cw(yAxis.get()), "Q轴"); mMenu = createMenu(this, ws); } mMenu->exec(event->globalPos()); diff --git a/YSGraphic_Core/Demo_Gallery/Spectrum_Plot.h b/YSGraphic_Core/Demo_Gallery/Spectrum_Plot.h index 03a9b8f..5f628bf 100644 --- a/YSGraphic_Core/Demo_Gallery/Spectrum_Plot.h +++ b/YSGraphic_Core/Demo_Gallery/Spectrum_Plot.h @@ -3,30 +3,33 @@ static QWidget* create_spectrun_plot() { class Spectrum_Plot : public YSG::Plot { public: - YSG::Frequent_Axis* xAxis{}; - YSG::Axis* yAxis{}; - YSG::Muti_Select_Rect* msr{}; - YSG::Spectrum* sp{}; + std::shared_ptr xAxis{}; + std::shared_ptr yAxis{}; + std::shared_ptr msr{}; + std::shared_ptr sp{}; QMenu* mMenu{}; void init() override { Plot::init(); object_name = "SpectrumPlot"; - xAxis = YSG::Frequent_Axis::Builder(this, Qt::Horizontal) + auto data = create_renderable_node(root_renderable(), "DataRenderable"); + auto axis = create_renderable_node(root_renderable(), "AxisRenderable"); + auto overlay = create_renderable_node(root_renderable(), "OverlayRenderable"); + xAxis = YSG::Frequent_Axis::Builder(axis, Qt::Horizontal) .set_coord_range({0, 100}) .set_tick_length(-10) .set_sub_tick_length(-5) .set_use_wheel(true) .set_use_drag(true) .build(); - yAxis = YSG::Axis::Builder(this, Qt::Vertical) + yAxis = YSG::Axis::Builder(axis, Qt::Vertical) .set_coord_range({100, 0}) .set_unit_text("功率") .set_use_drag(true) .build(); - sp = YSG::Spectrum::Builder(xAxis, yAxis) + sp = YSG::Spectrum::Builder(data, xAxis, yAxis) .set_frequent_range({0, 100}) .build(); - msr = YSG::Muti_Select_Rect::Builder(xAxis, yAxis).build(); + msr = YSG::Muti_Select_Rect::Builder(overlay, xAxis, yAxis).build(); } protected: void resizeEvent(QResizeEvent* event) override { @@ -44,10 +47,10 @@ static QWidget* create_spectrun_plot() { void contextMenuEvent(QContextMenuEvent* event) override { if (!mMenu) { auto ws = - bw(cw(sp, -1), "频谱图") + - bw(cw(xAxis), "频率x轴") + - bw(cw(yAxis), "功率y轴") + - bw(cw(msr), "多选框"); + bw(cw(sp.get(), -1), "频谱图") + + bw(cw(xAxis.get()), "频率x轴") + + bw(cw(yAxis.get()), "功率y轴") + + bw(cw(msr.get()), "多选框"); mMenu = createMenu(this, ws); } mMenu->exec(event->globalPos()); diff --git a/YSGraphic_Core/Demo_Gallery/Sweep_Frequent_Plot.h b/YSGraphic_Core/Demo_Gallery/Sweep_Frequent_Plot.h index 18312ea..ca51b49 100644 --- a/YSGraphic_Core/Demo_Gallery/Sweep_Frequent_Plot.h +++ b/YSGraphic_Core/Demo_Gallery/Sweep_Frequent_Plot.h @@ -3,27 +3,30 @@ static QWidget* create_sweep_frequent_plot() { class Sweep_Frequent_Plot : public YSG::Plot { public: - YSG::Axis* xAxis{}; - YSG::Axis* yAxis{}; - YSG::Muti_Select_Rect* msr{}; - YSG::Sweep_Frequent* sf{}; + std::shared_ptr xAxis{}; + std::shared_ptr yAxis{}; + std::shared_ptr msr{}; + std::shared_ptr sf{}; QMenu* mMenu{}; void init() override { Plot::init(); object_name = "AxisTestPlot"; - xAxis = YSG::Axis::Builder(this, Qt::Horizontal) + auto data = create_renderable_node(root_renderable(), "DataRenderable"); + auto axis = create_renderable_node(root_renderable(), "AxisRenderable"); + auto overlay = create_renderable_node(root_renderable(), "OverlayRenderable"); + xAxis = YSG::Axis::Builder(axis, Qt::Horizontal) .set_coord_range({0, 100}) .set_tick_length(-10) .build(); - yAxis = YSG::Axis::Builder(this, Qt::Vertical) + yAxis = YSG::Axis::Builder(axis, Qt::Vertical) .set_coord_range({100, 0}) .build(); - sf = YSG::Sweep_Frequent::Builder(xAxis, yAxis) + sf = YSG::Sweep_Frequent::Builder(data, xAxis, yAxis) .set_block_num(200) .set_block_frequent_point_size(20) .set_frequent_range({0, 100}) .build(); - msr = YSG::Muti_Select_Rect::Builder(xAxis, yAxis).build(); + msr = YSG::Muti_Select_Rect::Builder(overlay, xAxis, yAxis).build(); } protected: void resizeEvent(QResizeEvent* event) override { @@ -37,10 +40,10 @@ static QWidget* create_sweep_frequent_plot() { void contextMenuEvent(QContextMenuEvent* event) override { if (!mMenu) { auto ws = - bw(cw(sf, -1), "扫频图") + - bw(cw(xAxis), "x轴") + - bw(cw(yAxis), "y轴") + - bw(cw(msr), "多选框"); + bw(cw(sf.get(), -1), "扫频图") + + bw(cw(xAxis.get()), "x轴") + + bw(cw(yAxis.get()), "y轴") + + bw(cw(msr.get()), "多选框"); mMenu = createMenu(this, ws); } mMenu->exec(event->globalPos()); diff --git a/YSGraphic_Core/Demo_Gallery/Tool.h b/YSGraphic_Core/Demo_Gallery/Tool.h index bf15260..92063f2 100644 --- a/YSGraphic_Core/Demo_Gallery/Tool.h +++ b/YSGraphic_Core/Demo_Gallery/Tool.h @@ -99,7 +99,7 @@ static QWidget* createRange(Axis* axis, const QString& text = "更改轴范围") return createEditWidget_List(text, [axis](const QVector& values) { axis->set_coord_range(Range{values[0], values[1]}); }, - {axis->coord_range().lower, axis->coord_range().upper}); + {axis->coord_range().origin, axis->coord_range().target}); } static QMenu* findParentMenu(QWidget* widget) { // 从当前窗口开始,逐步检查父窗口 @@ -216,7 +216,7 @@ static QVector bw(const QVector& widgets, const QString& nam } return {ret}; } -static QWidget* createPlotRenderScheduleCombo(YSG::Plot* plot) { +static QWidget* createPlotRenderScheduleCombo(YSG::Abs_Plot* plot) { auto container = new QWidget; auto layout = new QHBoxLayout(container); layout->setAlignment(Qt::AlignLeft); @@ -280,7 +280,7 @@ static QWidget* create_image_interpolation_combo(YSG::Waterfall* waterfall) { layout->addWidget(combo); return container; } -static QWidget* createControl(YSG::Plot* plot, const QVector& widgets = {}) { +static QWidget* createControl(YSG::Abs_Plot* plot, const QVector& widgets = {}) { auto control = new QWidget; auto l = new QVBoxLayout(control); l->setAlignment(Qt::AlignTop); @@ -475,8 +475,8 @@ static QVector cw(YSG::Planisphere* it, int numPreSecond = -1) { {it->continue_millisecond()}), YSG::createSelectColor("更改IQ点颜色", [it](const QColor& color) { it->set_point_color(color); }, it->point_color()), YSG::createSelectColor("更改锚点颜色", [it](const QColor& color) { it->set_anchor_color(color); }, it->anchor_color()), - YSG::createEditWidget_List("I范围", [it](QVector data) { it->set_i_range({data[0], data[1]}); }, {it->i_range().lower, it->i_range().upper}), - YSG::createEditWidget_List("Q范围", [it](QVector data) { it->set_q_range({data[0], data[1]}); }, {it->q_range().lower, it->q_range().upper}), + YSG::createEditWidget_List("I范围", [it](QVector data) { it->set_i_range({data[0], data[1]}); }, {it->i_range().origin, it->i_range().target}), + YSG::createEditWidget_List("Q范围", [it](QVector data) { it->set_q_range({data[0], data[1]}); }, {it->q_range().origin, it->q_range().target}), YSG::createButton("设置到轴中心", [it]() { it->set_to_axis_center(); }), }; return t; @@ -491,13 +491,13 @@ static QVector cw(YSG::Afterglow* it, int numPreSecond = -1) { for (int i = 1; i < it->frequent_point_size(); ++i) { double rate; if (i % 20 == 0) { - double that = qAbs(ret[i - 1] - r.lower) / r.size() - 0.5; + double that = qAbs(ret[i - 1] - r.origin) / r.size() - 0.5; rate = (1.0 - that) * YSG::get_data({0.8, 1.2}); } else { rate = YSG::get_data({0.8, 1.2}); } - ret[i] = YSG::clamp(ret[i - 1] * rate, r.lower, r.upper); + ret[i] = YSG::clamp(ret[i - 1] * rate, r.origin, r.target); } return ret; }; @@ -509,7 +509,7 @@ static QVector cw(YSG::Afterglow* it, int numPreSecond = -1) { QVector curData(n); for (int i = 0; i < n; ++i) { double rate = YSG::get_data({0.8, 1.2}); - curData[i] = YSG::clamp(baseData[i] * rate, r.lower, r.upper); + curData[i] = YSG::clamp(baseData[i] * rate, r.origin, r.target); } it->give_data(curData); }); @@ -528,11 +528,11 @@ static QVector cw(YSG::Afterglow* it, int numPreSecond = -1) { YSG::createEditWidget_List("频率范围", [it](QVector data) { it->set_frequent_range({data[0], data[1]}); }, - {it->frequent_range().lower, it->frequent_range().upper}), + {it->frequent_range().origin, it->frequent_range().target}), YSG::createEditWidget_List("功率概率范围", [it](QVector data) { it->set_power_range({data[0], data[1]}); }, - {it->power_range().lower, it->power_range().upper}), + {it->power_range().origin, it->power_range().target}), YSG::createButton("更改基础线", []() { baseData = createBaseData(); }), @@ -558,13 +558,13 @@ static QVector cw(YSG::Spectrum* it, int numPreSecond = -1) { else { timer->start((int)(1000.0 / data[0])); } }, {timer->interval() > 0 ? 1000.0 / timer->interval() : -1}), - YSG::createEditWidget_List("频率范围", [it](QVector data) { it->set_frequent_range({data[0], data[1]}); }, {it->frequent_range().lower, it->frequent_range().upper}), + YSG::createEditWidget_List("频率范围", [it](QVector data) { it->set_frequent_range({data[0], data[1]}); }, {it->frequent_range().origin, it->frequent_range().target}), YSG::createEditWidget_List("设置频率点数", [it](QVector data) { it->set_frequent_point_size(data[0]); }, {it->frequent_point_size()}), YSG::createLabel("sweepRect"), YSG::createToggleButton("停止sweepRect", "启动sweepRect", [it]() { it->set_use_sweep_frequent_rect(!it->use_sweep_frequent_rect()); }, it->use_sweep_frequent_rect()), YSG::createEditWidget_List("sweepRect 范围", [it](QVector data) { if (!it->use_sweep_frequent_rect()) it->set_use_sweep_frequent_rect(true); - it->set_sweep_frequent_range({data[0], data[1]}); }, {it->sweep_frequent_range().lower, it->sweep_frequent_range().upper}), + it->set_sweep_frequent_range({data[0], data[1]}); }, {it->sweep_frequent_range().origin, it->sweep_frequent_range().target}), YSG::createEditWidget_List("中频", [it](QVector data) { if (!it->use_sweep_frequent_rect()) it->set_use_sweep_frequent_rect(true); it->set_middle_sweep_frequent(data[0]); }, {it->middle_sweep_frequent()}), @@ -627,10 +627,10 @@ static QVector cw(YSG::Sweep_Frequent* it, int numPreSecond = -1) { } }, {timer->interval() > 0 ? 1000.0 / timer->interval() : -1}), YSG::createSelectColor("线颜色", [it](const QColor& color) { it->set_pen(QPen(color)); }, it->pen().color()), YSG::createSelectColor("扫频线颜色", [it](const QColor& color) { QPen p = it->cur_frequent_pen(); p.setColor(color); - it->set_cur_frequent_pen(p); }, it->cur_frequent_pen().color()), YSG::createEditWidget_List("频率范围", [it](const QVector& data) { it->set_frequent_range({data[0], data[1]}); }, {it->frequent_range().lower, it->frequent_range().upper})}; + it->set_cur_frequent_pen(p); }, it->cur_frequent_pen().color()), YSG::createEditWidget_List("频率范围", [it](const QVector& data) { it->set_frequent_range({data[0], data[1]}); }, {it->frequent_range().origin, it->frequent_range().target})}; return t; } -static QMenu* createMenu(YSG::Plot* plot, const QVector& widgets) { +static QMenu* createMenu(YSG::Abs_Plot* plot, const QVector& widgets) { auto menu = new QMenu(plot); menu->setObjectName("memu"); // menu->setStyle(nullptr); diff --git a/YSGraphic_Core/Demo_Gallery/Waterfall_Plot.h b/YSGraphic_Core/Demo_Gallery/Waterfall_Plot.h index 1754f25..5d4265f 100644 --- a/YSGraphic_Core/Demo_Gallery/Waterfall_Plot.h +++ b/YSGraphic_Core/Demo_Gallery/Waterfall_Plot.h @@ -3,26 +3,28 @@ static QWidget* create_Water_Fall_Plot() { class Waterfall_Plot : public YSG::Plot { public: - YSG::Frequent_Axis* xAxis{}; - YSG::Time_Axis* yAxis{}; - YSG::Muti_Select_Rect* msr{}; - YSG::Waterfall* wf{}; + std::shared_ptr xAxis{}; + std::shared_ptr yAxis{}; + std::shared_ptr msr{}; + std::shared_ptr wf{}; QMenu* mMenu{}; void init() override { Plot::init(); object_name = "WaterFallPlot"; - xAxis = YSG::Frequent_Axis::Builder(this, Qt::Horizontal) + auto data = create_renderable_node(root_renderable(), "DataRenderable"); + auto axis = create_renderable_node(root_renderable(), "AxisRenderable"); + auto overlay = create_renderable_node(root_renderable(), "OverlayRenderable"); + xAxis = YSG::Frequent_Axis::Builder(axis, Qt::Horizontal) .set_tick_length(-10) .set_sub_tick_length(-5) .build(); - yAxis = YSG::Time_Axis::Builder(this, Qt::Vertical) + yAxis = YSG::Time_Axis::Builder(axis, Qt::Vertical) .set_time_point_size(800) .build(); - wf = YSG::Waterfall::Builder(xAxis, yAxis) - .set_layerName("plottable") + wf = YSG::Waterfall::Builder(data, xAxis, yAxis) .set_frequent_range({0, 20}) .build(); - msr = YSG::Muti_Select_Rect::Builder(xAxis, yAxis).build(); + msr = YSG::Muti_Select_Rect::Builder(overlay, xAxis, yAxis).build(); } protected: void resizeEvent(QResizeEvent* event) override { @@ -39,10 +41,10 @@ static QWidget* create_Water_Fall_Plot() { void contextMenuEvent(QContextMenuEvent* event) override { if (!mMenu) { auto ws = - bw(cw(wf, -1), "瀑布图") + - bw(cw(xAxis), "频率轴") + - bw(cw(yAxis), "时间轴") + - bw(cw(msr), "多选框"); + bw(cw(wf.get(), -1), "瀑布图") + + bw(cw(xAxis.get()), "频率轴") + + bw(cw(yAxis.get()), "时间轴") + + bw(cw(msr.get()), "多选框"); mMenu = createMenu(this, ws); } mMenu->exec(event->globalPos()); diff --git a/YSGraphic_Core/Demo_Gallery/base/Axis_Test.h b/YSGraphic_Core/Demo_Gallery/base/Axis_Test.h index 74fdee3..6b95e60 100644 --- a/YSGraphic_Core/Demo_Gallery/base/Axis_Test.h +++ b/YSGraphic_Core/Demo_Gallery/base/Axis_Test.h @@ -3,12 +3,13 @@ static QWidget* createAxisTestPlot() { class AxisTestPlot : public YSG::Plot { public: - YSG::Axis* axis{}; + std::shared_ptr axis{}; QMenu* mMenu{}; void init() override { Plot::init(); object_name = "AxisTestPlot"; - axis = YSG::Axis::Builder(this, Qt::Horizontal) + auto axisNode = create_renderable_node(root_renderable(), "AxisRenderable"); + axis = YSG::Axis::Builder(axisNode, Qt::Horizontal) .set_tick_length(-10) .set_sub_tick_length(-5) .set_coord_range({-12000, 12000}) @@ -25,7 +26,7 @@ static QWidget* createAxisTestPlot() { void contextMenuEvent(QContextMenuEvent* event) override { if (!mMenu) { auto ws = - bw(cw(axis), "轴"); + bw(cw(axis.get()), "轴"); mMenu = createMenu(this, ws); } mMenu->exec(event->globalPos()); diff --git a/YSGraphic_Core/Demo_Gallery/base/Frequent_Axis.h b/YSGraphic_Core/Demo_Gallery/base/Frequent_Axis.h index f771dfa..b6c47f6 100644 --- a/YSGraphic_Core/Demo_Gallery/base/Frequent_Axis.h +++ b/YSGraphic_Core/Demo_Gallery/base/Frequent_Axis.h @@ -3,27 +3,27 @@ static QWidget* create_frequent_axis_test_plot() { class Axis_Test_Plot : public YSG::Plot { public: - YSG::Frequent_Axis* axis{}; + std::shared_ptr axis{}; QMenu* mMenu{}; void init() override { Plot::init(); object_name = "AxisTestPlot"; - axis = new YSG::Frequent_Axis; - axis->init(this, "axis"); - axis->setOrientation(Qt::Horizontal); - axis->setTickLength(-10); - axis->setSubTickLength(-5); + auto axisNode = create_renderable_node(root_renderable(), "AxisRenderable"); + axis = YSG::Frequent_Axis::Builder(axisNode, Qt::Horizontal) + .set_tick_length(-10) + .set_sub_tick_length(-5) + .build(); } protected: void resizeEvent(QResizeEvent* event) override { int space = 50; - axis->setX(space); - axis->setY(height() / 2); - axis->setPixelLength(width() - space * 2); + axis->set_x(space); + axis->set_y(height() / 2); + axis->set_pixel_size(width() - space * 2); } void contextMenuEvent(QContextMenuEvent* event) override { if (!mMenu) { - auto ws = bw(cw(axis), "轴"); + auto ws = bw(cw(axis.get()), "轴"); mMenu = createMenu(this, ws); } mMenu->exec(event->globalPos()); diff --git a/YSGraphic_Core/Demo_Gallery/base/Frequent_Axis_Test.h b/YSGraphic_Core/Demo_Gallery/base/Frequent_Axis_Test.h index 7a2d07b..b44e9bb 100644 --- a/YSGraphic_Core/Demo_Gallery/base/Frequent_Axis_Test.h +++ b/YSGraphic_Core/Demo_Gallery/base/Frequent_Axis_Test.h @@ -3,12 +3,13 @@ static QWidget* create_frequent_axis_test_plot() { class AxisTestPlot : public YSG::Plot { public: - YSG::Frequent_Axis* axis{}; + std::shared_ptr axis{}; QMenu* mMenu{}; void init() override { Plot::init(); object_name = "AxisTestPlot"; - axis = YSG::Frequent_Axis::Builder(this, Qt::Horizontal) + auto axisNode = create_renderable_node(root_renderable(), "AxisRenderable"); + axis = YSG::Frequent_Axis::Builder(axisNode, Qt::Horizontal) .set_tick_length(-10) .set_sub_tick_length(-5) .set_coord_range({-12000, 12000}) @@ -23,7 +24,7 @@ static QWidget* create_frequent_axis_test_plot() { } void contextMenuEvent(QContextMenuEvent* event) override { if (!mMenu) { - auto ws = bw(cw(axis), "轴"); + auto ws = bw(cw(axis.get()), "轴"); mMenu = createMenu(this, ws); } mMenu->exec(event->globalPos()); diff --git a/YSGraphic_Core/Demo_Gallery/base/Muti_Select_Test.h b/YSGraphic_Core/Demo_Gallery/base/Muti_Select_Test.h index 04ed230..d0d8991 100644 --- a/YSGraphic_Core/Demo_Gallery/base/Muti_Select_Test.h +++ b/YSGraphic_Core/Demo_Gallery/base/Muti_Select_Test.h @@ -3,19 +3,21 @@ static QWidget* create_Muti_Select_Plot() { class Muti_Select_Plot : public YSG::Plot { public: - YSG::Axis* xAxis{}; - YSG::Axis* yAxis{}; - YSG::Muti_Select_Rect* mMutiSelectRect{}; + std::shared_ptr xAxis{}; + std::shared_ptr yAxis{}; + std::shared_ptr mMutiSelectRect{}; QMenu* mMenu{}; void init() override { Plot::init(); object_name = "PlanispherePlot"; - xAxis = YSG::Axis::Builder(this, Qt::Horizontal) + auto axis = create_renderable_node(root_renderable(), "AxisRenderable"); + auto overlay = create_renderable_node(root_renderable(), "OverlayRenderable"); + xAxis = YSG::Axis::Builder(axis, Qt::Horizontal) .set_tick_length(-10) .set_sub_tick_length(-5) .build(); - yAxis = YSG::Axis::Builder(this, Qt::Vertical).build(); - mMutiSelectRect = YSG::Muti_Select_Rect::Builder(xAxis, yAxis).build(); + yAxis = YSG::Axis::Builder(axis, Qt::Vertical).build(); + mMutiSelectRect = YSG::Muti_Select_Rect::Builder(overlay, xAxis, yAxis).build(); } protected: void resizeEvent(QResizeEvent* event) override { @@ -29,7 +31,7 @@ static QWidget* create_Muti_Select_Plot() { } void contextMenuEvent(QContextMenuEvent* event) override { if (!mMenu) { - auto ws = bw(cw(mMutiSelectRect), "多选框"); + auto ws = bw(cw(mMutiSelectRect.get()), "多选框"); mMenu = createMenu(this, ws); } mMenu->exec(event->globalPos()); diff --git a/YSGraphic_Core/Demo_Gallery/base/Time_Axis_Test.h b/YSGraphic_Core/Demo_Gallery/base/Time_Axis_Test.h index 8d8c01b..c9c7238 100644 --- a/YSGraphic_Core/Demo_Gallery/base/Time_Axis_Test.h +++ b/YSGraphic_Core/Demo_Gallery/base/Time_Axis_Test.h @@ -3,12 +3,13 @@ static QWidget* create_time_axis_test_plot() { class Axis_Test_Plot : public YSG::Plot { public: - YSG::Time_Axis* axis{}; + std::shared_ptr axis{}; QMenu* mMenu{}; void init() override { Plot::init(); object_name = "TimeAxisTestPlot"; - axis = YSG::Time_Axis::Builder(this, Qt::Horizontal) + auto axisNode = create_renderable_node(root_renderable(), "AxisRenderable"); + axis = YSG::Time_Axis::Builder(axisNode, Qt::Horizontal) .set_tick_length(-10) .set_sub_tick_length(-5) .build(); @@ -22,7 +23,7 @@ static QWidget* create_time_axis_test_plot() { } void contextMenuEvent(QContextMenuEvent* event) override { if (!mMenu) { - auto ws = bw(cw(axis, 30), "时间轴"); + auto ws = bw(cw(axis.get(), 30), "时间轴"); mMenu = createMenu(this, ws); } mMenu->exec(event->globalPos()); diff --git a/YSGraphic_Core/Generate_Mock_Data.cpp b/YSGraphic_Core/Generate_Mock_Data.cpp index 5e5ca48..ba5c5cf 100644 --- a/YSGraphic_Core/Generate_Mock_Data.cpp +++ b/YSGraphic_Core/Generate_Mock_Data.cpp @@ -4,10 +4,10 @@ namespace YSG { std::random_device rd; std::default_random_engine eng(rd()); QVector get_data(Range valueRange, int size) { - if (valueRange.lower > valueRange.upper) { - std::swap(valueRange.lower, valueRange.upper); + if (valueRange.origin > valueRange.target) { + std::swap(valueRange.origin, valueRange.target); } - std::uniform_real_distribution dist(valueRange.lower, valueRange.upper); + std::uniform_real_distribution dist(valueRange.origin, valueRange.target); QVector ret(size); for (auto& value : ret) { value = dist(eng); @@ -15,10 +15,10 @@ QVector get_data(Range valueRange, int size) { return ret; } double get_data(Range valueRange) { - if (valueRange.lower > valueRange.upper) { - std::swap(valueRange.lower, valueRange.upper); + if (valueRange.origin > valueRange.target) { + std::swap(valueRange.origin, valueRange.target); } - std::uniform_real_distribution dist(valueRange.lower, valueRange.upper); + std::uniform_real_distribution dist(valueRange.origin, valueRange.target); return dist(eng); } QVector get_data(int value, int size) { diff --git a/YSGraphic_Core/architecture/Bounded_Input_Buffer.h b/YSGraphic_Core/architecture/Bounded_Input_Buffer.h index 1171eb9..8b836db 100644 --- a/YSGraphic_Core/architecture/Bounded_Input_Buffer.h +++ b/YSGraphic_Core/architecture/Bounded_Input_Buffer.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include #include @@ -8,18 +9,17 @@ namespace YSG { class Bounded_Input_Buffer { public: - static constexpr std::size_t Default_Max_Packet_Count = 64; - void push(const void* data, std::size_t size, std::size_t maxPacketCount = Default_Max_Packet_Count) { + void push(const void* data, std::size_t size, std::size_t maxPacketCount) { configure(size, maxPacketCount); while (!mBuffer.write(data, size)) { if (!mBuffer.skip_one()) return; - ++mDroppedCount; - if (mPendingCount) - --mPendingCount; + mDroppedCount.fetch_add(1, std::memory_order_relaxed); + mCapacityDroppedCount.fetch_add(1, std::memory_order_relaxed); + decrement_pending(); } - ++mPendingCount; - ++mPushedCount; + mPendingCount.fetch_add(1, std::memory_order_relaxed); + mPushedCount.fetch_add(1, std::memory_order_relaxed); } template void read_all(Handler handler) { @@ -29,24 +29,36 @@ public: std::size_t readSize = packetSize; if (!mBuffer.read(mReadBuffer.data(), readSize)) return; - if (mPendingCount) - --mPendingCount; + decrement_pending(); handler(mReadBuffer.data(), readSize); } } void clear() { while (mBuffer.skip_one()) { } - mPendingCount = 0; + mPendingCount.store(0, std::memory_order_relaxed); } [[nodiscard]] std::uint64_t pending_count() const { - return mPendingCount; + return mPendingCount.load(std::memory_order_relaxed); } [[nodiscard]] std::uint64_t dropped_count() const { - return mDroppedCount; + return mDroppedCount.load(std::memory_order_relaxed); + } + [[nodiscard]] std::uint64_t capacity_dropped_count() const { + return mCapacityDroppedCount.load(std::memory_order_relaxed); + } + [[nodiscard]] std::uint64_t rebuild_dropped_count() const { + return mRebuildDroppedCount.load(std::memory_order_relaxed); + } + [[nodiscard]] std::uint64_t consumer_dropped_count() const { + return mConsumerDroppedCount.load(std::memory_order_relaxed); } [[nodiscard]] std::uint64_t pushed_count() const { - return mPushedCount; + return mPushedCount.load(std::memory_order_relaxed); + } + void record_consumer_dropped() { + mDroppedCount.fetch_add(1, std::memory_order_relaxed); + mConsumerDroppedCount.fetch_add(1, std::memory_order_relaxed); } private: void configure(std::size_t maxPacketSize, std::size_t maxPacketCount) { @@ -54,24 +66,35 @@ private: maxPacketSize = std::max(1, maxPacketSize); if (mInited && maxPacketSize <= mMaxPacketSize && maxPacketCount == mMaxPacketCount) return; + std::uint64_t pending = mPendingCount.load(std::memory_order_relaxed); + mDroppedCount.fetch_add(pending, std::memory_order_relaxed); + mRebuildDroppedCount.fetch_add(pending, std::memory_order_relaxed); mMaxPacketSize = maxPacketSize; mMaxPacketCount = maxPacketCount; - mPendingCount = 0; + mPendingCount.store(0, std::memory_order_relaxed); mBuffer.init((Psc::RingBuffer_ST::HEADER_SIZE + mMaxPacketSize) * mMaxPacketCount); mInited = true; } + void decrement_pending() { + std::uint64_t pending = mPendingCount.load(std::memory_order_relaxed); + while (pending && !mPendingCount.compare_exchange_weak(pending, pending - 1, std::memory_order_relaxed)) { + } + } bool mInited = false; std::size_t mMaxPacketSize{}; std::size_t mMaxPacketCount{}; - std::uint64_t mPendingCount{}; - std::uint64_t mDroppedCount{}; - std::uint64_t mPushedCount{}; + std::atomic mPendingCount{}; + std::atomic mDroppedCount{}; + std::atomic mCapacityDroppedCount{}; + std::atomic mRebuildDroppedCount{}; + std::atomic mConsumerDroppedCount{}; + std::atomic mPushedCount{}; Psc::RingBuffer_ST mBuffer; std::vector mReadBuffer; }; class Bounded_Vector_Double_Input_Buffer { public: - void push(const QVector& data, std::size_t maxPacketCount = Bounded_Input_Buffer::Default_Max_Packet_Count) { + void push(const QVector& data, std::size_t maxPacketCount) { mBuffer.push(data.constData(), static_cast(data.size()) * sizeof(double), maxPacketCount); } template @@ -82,16 +105,6 @@ public: handler(mVectorBuffer); }); } - template - void read_all_reverse(Handler handler) { - mReverseVectors.clear(); - read_all([this](const QVector& data) { - mReverseVectors.append(data); - }); - for (int i = mReverseVectors.size() - 1; i >= 0; --i) { - handler(mReverseVectors[i]); - } - } void clear() { mBuffer.clear(); } @@ -101,31 +114,37 @@ public: [[nodiscard]] std::uint64_t dropped_count() const { return mBuffer.dropped_count(); } + [[nodiscard]] std::uint64_t capacity_dropped_count() const { + return mBuffer.capacity_dropped_count(); + } + [[nodiscard]] std::uint64_t rebuild_dropped_count() const { + return mBuffer.rebuild_dropped_count(); + } + [[nodiscard]] std::uint64_t consumer_dropped_count() const { + return mBuffer.consumer_dropped_count(); + } [[nodiscard]] std::uint64_t pushed_count() const { return mBuffer.pushed_count(); } + void record_consumer_dropped() { + mBuffer.record_consumer_dropped(); + } private: Bounded_Input_Buffer mBuffer; QVector mVectorBuffer; - QVector> mReverseVectors; }; class Bounded_Double_Input_Buffer { public: - void push(double data, std::size_t maxPacketCount = Bounded_Input_Buffer::Default_Max_Packet_Count) { + void push(double data, std::size_t maxPacketCount) { mBuffer.push(&data, sizeof(data), maxPacketCount); } template - void read_all_reverse(Handler handler) { - mReverseValues.clear(); - mBuffer.read_all([this](const void* data, std::size_t size) { + void read_all(Handler handler) { + mBuffer.read_all([this, &handler](const void* data, std::size_t size) { (void)size; - double value; - std::memcpy(&value, data, sizeof(value)); - mReverseValues.append(value); + std::memcpy(&mValueBuffer, data, sizeof(mValueBuffer)); + handler(mValueBuffer); }); - for (int i = mReverseValues.size() - 1; i >= 0; --i) { - handler(mReverseValues[i]); - } } void clear() { mBuffer.clear(); @@ -136,11 +155,23 @@ public: [[nodiscard]] std::uint64_t dropped_count() const { return mBuffer.dropped_count(); } + [[nodiscard]] std::uint64_t capacity_dropped_count() const { + return mBuffer.capacity_dropped_count(); + } + [[nodiscard]] std::uint64_t rebuild_dropped_count() const { + return mBuffer.rebuild_dropped_count(); + } + [[nodiscard]] std::uint64_t consumer_dropped_count() const { + return mBuffer.consumer_dropped_count(); + } [[nodiscard]] std::uint64_t pushed_count() const { return mBuffer.pushed_count(); } + void record_consumer_dropped() { + mBuffer.record_consumer_dropped(); + } private: Bounded_Input_Buffer mBuffer; - QVector mReverseValues; + double mValueBuffer{}; }; -} // namespace YSG +} diff --git a/YSGraphic_Core/architecture/Plot.cpp b/YSGraphic_Core/architecture/Plot.cpp index f011087..31ab20c 100644 --- a/YSGraphic_Core/architecture/Plot.cpp +++ b/YSGraphic_Core/architecture/Plot.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "../base/Global.h" +#include "../base/global.h" #include "../plottable/Performance_Shower_p.h" #include "Plot_p.h" namespace YSG { @@ -27,6 +27,15 @@ static QString render_able_name(Renderable* able) { return able->mObjectName; return QString::number(reinterpret_cast(able), 16); } +static std::shared_ptr performance_shower_owner(Plot_Private* data) { + if (!data || !data->q) + return {}; + auto context = data->q->render_context(); + if (!context) + return {}; + std::lock_guard lock(context->mutex); + return context->performanceShower; +} static QString acquire_mode_text(Plot_Buffer_Acquire_Mode mode) { return mode == Plot_Buffer_Acquire_Mode::Wait ? "Wait" : "Try"; } @@ -58,6 +67,15 @@ static QString render_able_schedule_mode_text(RenderAble_Render_Schedule_Mode mo } return {}; } +static QString renderable_cache_mode_text(Renderable_Cache_Mode mode) { + switch (mode) { + case Renderable_Cache_Mode::Direct: + return "Direct"; + case Renderable_Cache_Mode::Local_Pixel: + return "Local_Pixel"; + } + return {}; +} static void publish_triple_buffer_stats(Performance_Shower* shower, const QString& name, const Triple_Buffer_Stats& stats) { if (!shower || !shower->enabled()) return; @@ -78,25 +96,38 @@ static void publish_render_able_buffer_stats(Performance_Shower* shower, Rendera QString name = render_able_name(able); shower->set_state("RenderAble", name + ".buffer_acquire_mode", acquire_mode_text(able->buffer_acquire_mode())); shower->set_state("RenderAble", name + ".render_schedule_mode", render_able_schedule_mode_text(able->render_schedule_mode())); + shower->set_state("RenderAble", name + ".cache_mode", renderable_cache_mode_text(able->cache_mode())); shower->set_gauge("RenderAble", name + ".render_schedule_max_fps", able->render_schedule_max_fps()); std::uint64_t inputPending = 0; std::uint64_t inputDropped = 0; + std::uint64_t inputCapacityDropped = 0; + std::uint64_t inputRebuildDropped = 0; + std::uint64_t inputConsumerDropped = 0; std::uint64_t inputPushed = 0; for (const Input_Buffer_Slot& slot : able->dPtr->mInputBuffers) { if (!slot.data) continue; inputPending += slot.data->pending_count(); inputDropped += slot.data->dropped_count(); + inputCapacityDropped += slot.data->capacity_dropped_count(); + inputRebuildDropped += slot.data->rebuild_dropped_count(); + inputConsumerDropped += slot.data->consumer_dropped_count(); inputPushed += slot.data->pushed_count(); } shower->set_gauge("RenderAble", name + ".input_pending", static_cast(inputPending)); shower->set_gauge("RenderAble", name + ".input_dropped", static_cast(inputDropped)); + shower->set_gauge("RenderAble", name + ".input_capacity_dropped", static_cast(inputCapacityDropped)); + shower->set_gauge("RenderAble", name + ".input_rebuild_dropped", static_cast(inputRebuildDropped)); + shower->set_gauge("RenderAble", name + ".input_consumer_dropped", static_cast(inputConsumerDropped)); shower->set_gauge("RenderAble", name + ".input_pushed", static_cast(inputPushed)); + shower->set_gauge("RenderAble", name + ".pixel_cache_area", static_cast(able->cache_image_area())); + shower->set_gauge("RenderAble", name + ".pixel_cache_bytes", static_cast(able->cache_image_bytes())); publish_triple_buffer_stats(shower, name + ".State", able->dPtr->mStateBufferStats); publish_triple_buffer_stats(shower, name + ".Input", able->dPtr->mInputBufferStats); } -static void publish_pipeline_stats(PlotPrivate* data) { - Performance_Shower* shower = data->mShower; +static void publish_pipeline_stats(Plot_Private* data) { + auto showerOwner = performance_shower_owner(data); + Performance_Shower* shower = showerOwner.get(); if (!shower || !shower->enabled()) return; Renderable::Render_Pipeline& pipeline = data->mPipeline; @@ -114,51 +145,73 @@ static void publish_pipeline_stats(PlotPrivate* data) { shower->set_gauge("Pipeline", "renderStateVersion", static_cast(pipeline.renderStateVersion)); publish_triple_buffer_stats(shower, "Color", pipeline.colorBufferStats); } -static void notify_render_task_done(PlotPrivate* data) { +static void notify_render_task_done(Plot_Private* data) { if (data->mActiveRenderTasks.fetch_sub(1, std::memory_order_acq_rel) == 1) data->mRenderTaskDone.notify_all(); } void start_render_scheduler() { Global::instance()->start_render_scheduler(); } -Plot::Plot() { - d = new PlotPrivate(this); +Abs_Plot::Abs_Plot() { + d = new Plot_Private(this); + mRenderContext = std::make_shared(); + { + std::lock_guard lock(mRenderContext->mutex); + mRenderContext->backgroundColor = d->mBackground; + mRenderContext->refreshTimesPerSecond = d->mRefreshTimesPreSecond; + mRenderContext->plot = this; + } setAttribute(Qt::WA_OpaquePaintEvent, true); setMouseTracking(true); setFocusPolicy(Qt::NoFocus); Global::instance()->renderScheduler().register_plot(this); } -void Plot::init() { - init_layer(); +void Abs_Plot::init() { + init_renderable_tree(); } -void Plot::init_layer() { - auto addLayer = [this](QString&& name) { - auto layer = new Layer; - layer->layer_name = name; - layer_list.append(layer); - mLayerMap[name] = layer; - }; - addLayer("background"); - addLayer("plottable"); - addLayer("axis"); - addLayer("legend"); - default_layer_name = "plottable"; +void Abs_Plot::init_renderable_tree() { + mRootRenderable = std::make_shared(); + mRootRenderable->mObjectName = "RootRenderable"; + mRootRenderable->init_root(this); } -bool Plot::use_performance_shower() { - return d->mShower && d->mShower->enabled(); +bool Abs_Plot::use_performance_shower() { + auto shower = performance_shower_owner(d); + return shower && shower->enabled(); } -void Plot::add_renderable(Renderable* able, const QString& layer_name) { - mLayerMap[layer_name]->renderable_list.append(able); -} -void Plot::set_use_performance_shower(bool use) { - if (!d->mShower) { - d->mShower = new Performance_Shower; - d->mShower->init(this, "legend"); +void Abs_Plot::remove_renderable(const std::shared_ptr& able) { + if (!able || able->mPlot != this) + return; + if (able == mRootRenderable) + return; + std::shared_ptr parent = able->mParentRenderable.lock(); + if (parent) + parent->remove_child(able); + { + std::lock_guard lock(mRenderContext->mutex); + if (mRenderContext->performanceShower.get() == able.get()) + mRenderContext->performanceShower.reset(); } - d->mShower->setEnabled(use); + mark_render_state_dirty(); } -Plot::~Plot() { - d->mDestroying.store(true, std::memory_order_release); +void Abs_Plot::set_use_performance_shower(bool use) { + auto shower = performance_shower_owner(d); + if (!shower && use) { + auto createdShower = std::make_shared(); + createdShower->init(mRootRenderable); + std::lock_guard lock(mRenderContext->mutex); + mRenderContext->performanceShower = createdShower; + } + shower = performance_shower_owner(d); + if (shower) + shower->setEnabled(use); +} +Abs_Plot::~Abs_Plot() { + { + std::lock_guard lock(mRenderContext->mutex); + d->mDestroying.store(true, std::memory_order_release); + mRenderContext->destroying.store(true, std::memory_order_release); + mRenderContext->plot = nullptr; + } d->mRenderEnabled.store(false, std::memory_order_release); Global::instance()->renderScheduler().remove_plot(this); { @@ -167,39 +220,77 @@ Plot::~Plot() { return d->mActiveRenderTasks.load(std::memory_order_acquire) == 0; }); } - QSet renderables; - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - renderables.insert(able); - } + { + std::lock_guard lock(mRenderableMutex); + if (mRootRenderable) + mRootRenderable->detach_from_plot_tree(); + mRootRenderable.reset(); } - for (Renderable* able : renderables) { - delete able; + { + std::lock_guard lock(mRenderContext->mutex); + mRenderContext->performanceShower.reset(); } - for (Layer* layer : layer_list) { - delete layer; - } - layer_list.clear(); - mLayerMap.clear(); - d->mShower = nullptr; + mRenderContext.reset(); delete d; } -bool Plot::is_rendering() { +std::shared_ptr Abs_Plot::render_context() const { + return mRenderContext; +} +std::shared_ptr Abs_Plot::root_renderable() const { + return mRootRenderable; +} +std::shared_ptr Abs_Plot::create_renderable_node(const std::shared_ptr& parent, const QString& objectName) { + if (!parent || parent->mPlot != this) { + ASSERT(parent && parent->mPlot == this, "Abs_Plot create_renderable_node error! parent invalid"); + return {}; + } + auto able = std::make_shared(); + able->mObjectName = objectName; + able->init(parent); + return able; +} +Abs_Plot::RenderableSnapshot Abs_Plot::renderable_snapshot() const { + std::lock_guard lock(mRenderableMutex); + RenderableSnapshot snapshot; + append_renderable_snapshot(snapshot, mRootRenderable); + return snapshot; +} +void Abs_Plot::append_renderable_snapshot(RenderableSnapshot& snapshot, const std::shared_ptr& able) const { + if (!able) + return; + snapshot.append(able); + QVector> children = able->children_snapshot(); + for (const auto& child : children) + append_renderable_snapshot(snapshot, child); +} +std::shared_ptr Abs_Plot::render_snapshot() const { + auto snapshot = std::make_shared(); + snapshot->context = mRenderContext; + if (!mRenderContext) + return snapshot; + std::lock_guard lock(mRenderContext->mutex); + snapshot->size = mRenderContext->renderSize; + snapshot->backgroundColor = mRenderContext->backgroundColor; + snapshot->refreshTimesPerSecond = mRenderContext->refreshTimesPerSecond; + snapshot->performanceShower = mRenderContext->performanceShower; + return snapshot; +} +bool Abs_Plot::is_rendering() { return d->mRenderEnabled.load(std::memory_order_acquire); } -void Plot::start_render(int refreshTimesPreSecond) { +void Abs_Plot::start_render(int refreshTimesPreSecond) { set_render_check_fps(refreshTimesPreSecond); start_render(); } -void Plot::start_render() const { +void Abs_Plot::start_render() const { if (d->mDestroying.load(std::memory_order_acquire)) return; d->mRenderEnabled.store(true, std::memory_order_release); - Plot* self = const_cast(this); - PlotPrivate* privateData = d; + Abs_Plot* self = const_cast(this); + Plot_Private* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); Global::instance()->renderScheduler().post([self, privateData]() { - PlotPrivate* d = self->d; + Plot_Private* d = self->d; if (!d->mDestroying.load(std::memory_order_acquire)) { if (d->mRenderTimer) { d->mRenderTimer->cancel(); @@ -210,15 +301,15 @@ void Plot::start_render() const { notify_render_task_done(privateData); }); } -void Plot::pause_render() const { +void Abs_Plot::pause_render() const { d->mRenderEnabled.store(false, std::memory_order_release); if (d->mDestroying.load(std::memory_order_acquire)) return; - Plot* self = const_cast(this); - PlotPrivate* privateData = d; + Abs_Plot* self = const_cast(this); + Plot_Private* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); Global::instance()->renderScheduler().post([self, privateData]() { - PlotPrivate* d = self->d; + Plot_Private* d = self->d; if (!d->mDestroying.load(std::memory_order_acquire)) { if (d->mRenderTimer) { d->mRenderTimer->cancel(); @@ -232,18 +323,20 @@ void Plot::pause_render() const { notify_render_task_done(privateData); }); } -void Plot::paintEvent(QPaintEvent* event) { +void Abs_Plot::paintEvent(QPaintEvent* event) { Q_UNUSED(event) - Cacl paintData("Pipeline", "paintEvent", d->mShower); + auto showerOwner = performance_shower_owner(d); + Performance_Shower* shower = showerOwner.get(); + Cacl paintData("Pipeline", "paintEvent", shower); std::uint64_t paintNs = steady_now_ns(); std::uint64_t updateRequestNs = d->mLastUpdateRequestNs.exchange(0, std::memory_order_acq_rel); - if (updateRequestNs && d->mShower) - d->mShower->record_duration("Pipeline", "updateToPaint", elapsed_ms(updateRequestNs, paintNs)); + if (updateRequestNs && shower) + shower->record_duration("Pipeline", "updateToPaint", elapsed_ms(updateRequestNs, paintNs)); QPainter painter(this); auto mode = to_triple_buffer_mode(d->mPaintBufferAcquireMode.load(std::memory_order_acquire)); bool colorLeaseAcquired = false; { - Cacl consumeData("Buffer", "Color.consumeReady", d->mShower); + Cacl consumeData("Buffer", "Color.consumeReady", shower); auto colorLease = d->mPipeline.consume_ready_color(mode); colorLeaseAcquired = static_cast(colorLease); Renderable::Color_Buffer* color = colorLease.buffer; @@ -252,72 +345,64 @@ void Plot::paintEvent(QPaintEvent* event) { else painter.fillRect(rect(), d->mBackground); } - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - if (!able->mVisable || !able->dPtr->independent_pixel_cache()) - continue; - auto pixelLease = able->dPtr->consume_ready_pixel(Triple_Buffer_Mode::Try); - if (pixelLease && !pixelLease.buffer->image.isNull()) - painter.drawImage(QPoint(0, 0), pixelLease.buffer->image); - if (pixelLease) - able->dPtr->unmark_pixel(pixelLease); - } - } painter.end(); - if (d->mShower) { + if (shower) { if (colorLeaseAcquired) - d->mShower->increment_counter("Buffer", "Color.frontLeaseSuccess"); + shower->increment_counter("Buffer", "Color.frontLeaseSuccess"); else - d->mShower->increment_counter("Buffer", "Color.frontLeaseMiss"); - d->mShower->increment_counter("Pipeline", "paint"); + shower->increment_counter("Buffer", "Color.frontLeaseMiss"); + shower->increment_counter("Pipeline", "paint"); publish_pipeline_stats(d); } } -void Plot::render() { +void Abs_Plot::render() { request_render(Render_Request_Source::Manual); } -Plot_Buffer_Acquire_Mode Plot::paint_buffer_acquire_mode() const { +Plot_Buffer_Acquire_Mode Abs_Plot::paint_buffer_acquire_mode() const { return d->mPaintBufferAcquireMode.load(std::memory_order_acquire); } -void Plot::set_paint_buffer_acquire_mode(Plot_Buffer_Acquire_Mode mode) { +void Abs_Plot::set_paint_buffer_acquire_mode(Plot_Buffer_Acquire_Mode mode) { d->mPaintBufferAcquireMode.store(mode, std::memory_order_release); } -Plot_Buffer_Acquire_Mode Plot::render_able_buffer_acquire_mode() const { +Plot_Buffer_Acquire_Mode Abs_Plot::render_able_buffer_acquire_mode() const { return d->mRenderAbleBufferAcquireMode.load(std::memory_order_acquire); } -void Plot::set_render_able_buffer_acquire_mode(Plot_Buffer_Acquire_Mode mode) { +void Abs_Plot::set_render_able_buffer_acquire_mode(Plot_Buffer_Acquire_Mode mode) { d->mRenderAbleBufferAcquireMode.store(mode, std::memory_order_release); } -Plot_Render_Schedule_Mode Plot::render_schedule_mode() const { +Plot_Render_Schedule_Mode Abs_Plot::render_schedule_mode() const { return d->mRenderScheduleMode.load(std::memory_order_acquire); } -void Plot::set_render_schedule_mode(Plot_Render_Schedule_Mode mode) { +void Abs_Plot::set_render_schedule_mode(Plot_Render_Schedule_Mode mode) { d->mRenderScheduleMode.store(mode, std::memory_order_release); if (mode == Plot_Render_Schedule_Mode::Max_Fps && d->mMaxRenderFps.load(std::memory_order_acquire) <= 0.0) d->mMaxRenderFps.store(render_check_fps(), std::memory_order_release); request_render(Render_Request_Source::Dirty); } -double Plot::render_check_fps() const { +double Abs_Plot::render_check_fps() const { return static_cast(d->mRefreshTimesPreSecond); } -void Plot::set_render_check_fps(double fps) { +void Abs_Plot::set_render_check_fps(double fps) { d->mRefreshTimesPreSecond = qMax(1, qRound(fps)); + std::lock_guard lock(mRenderContext->mutex); + mRenderContext->refreshTimesPerSecond = d->mRefreshTimesPreSecond; } -double Plot::max_render_fps() const { +double Abs_Plot::max_render_fps() const { return d->mMaxRenderFps.load(std::memory_order_acquire); } -void Plot::set_max_render_fps(double fps) { +void Abs_Plot::set_max_render_fps(double fps) { d->mMaxRenderFps.store(fps, std::memory_order_release); } -bool Plot::submit_render_once() { +bool Abs_Plot::submit_render_once() { Render_Scheduler& scheduler = Global::instance()->renderScheduler(); if (!scheduler.is_scheduler_thread()) { - PlotPrivate* privateData = d; + Plot_Private* privateData = d; std::uint64_t postNs = steady_now_ns(); privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); scheduler.post([this, privateData, postNs]() { - if (privateData->mShower) - privateData->mShower->record_duration("Pipeline", "schedulerQueueWait", elapsed_ms(postNs, steady_now_ns())); + auto shower = performance_shower_owner(privateData); + if (shower) + shower->record_duration("Pipeline", "schedulerQueueWait", elapsed_ms(postNs, steady_now_ns())); if (!privateData->mDestroying.load(std::memory_order_acquire)) submit_render_once(); notify_render_task_done(privateData); @@ -329,107 +414,103 @@ bool Plot::submit_render_once() { std::uint64_t submitNs = steady_now_ns(); Render_Request_Source source = d->mSubmitSource; std::uint64_t requestNs = d->mLastRequestNs.exchange(0, std::memory_order_acq_rel); - if (requestNs && d->mShower) - d->mShower->record_duration("Pipeline", "requestToSubmit", elapsed_ms(requestNs, submitNs)); + { + auto shower = performance_shower_owner(d); + if (requestNs && shower) + shower->record_duration("Pipeline", "requestToSubmit", elapsed_ms(requestNs, submitNs)); + } if (first_resize) return false; if (!d->mRenderEnabled.load(std::memory_order_acquire)) return false; - QSize renderSize = d->mRenderSize; + auto frameSnapshot = render_snapshot(); + if (frameSnapshot->destroying()) + return false; + QSize renderSize = frameSnapshot->size; if (renderSize.width() <= 0 || renderSize.height() <= 0) return false; Renderable::Render_Pipeline& pipeline = d->mPipeline; if (pipeline.jobState != Renderable::JobState::Idle) return false; - if (!has_submit_candidate(source, submitNs)) + auto snapshot = renderable_snapshot(); + auto root = root_renderable(); + if (!root) + return false; + if (!first_prepare_data && !pipeline.has_new_state()) return false; pipeline.jobState = Renderable::JobState::Queued; pipeline.renderStateVersion = pipeline.editStateVersion.load(std::memory_order_acquire); - if (d->mShower) { - d->mShower->increment_counter("Pipeline", "renderFrame"); - d->mShower->set_info("interval", QString("渲染:%1, global:%2").arg(qRound(1000.0 / (double)d->mRefreshTimesPreSecond)).arg(Global::instance()->mInterval)); + Performance_Shower* shower = frameSnapshot->performanceShower.get(); + if (shower) { + shower->increment_counter("Pipeline", "renderFrame"); + shower->set_info("interval", QString("渲染:%1, global:%2").arg(qRound(1000.0 / (double)frameSnapshot->refreshTimesPerSecond)).arg(Global::instance()->mInterval)); publish_pipeline_stats(d); } { - Cacl prepare_data("Pipeline", "prepare_data", d->mShower); - for (Layer* layer : layer_list) { - for (Renderable* cur : layer->renderable_list) { - for (auto rely : cur->mBeRelyList) { - if (rely->dPtr->independent_pixel_cache()) - continue; - if (!render_able_ready_for_submit(rely, source, submitNs, false)) - continue; - if (!rely->mPrepared) { - { - Cacl renderAblePrepare("RenderAble", render_able_name(rely) + ".prepare_data", d->mShower); - rely->prepare_data(); - } - mark_render_able_submitted(rely, submitNs); - publish_render_able_buffer_stats(d->mShower, rely); - rely->mPrepared = true; - } - } - if (cur->dPtr->independent_pixel_cache()) - continue; - if (!render_able_ready_for_submit(cur, source, submitNs, false)) - continue; - if (!cur->mPrepared) { - { - Cacl renderAblePrepare("RenderAble", render_able_name(cur) + ".prepare_data", d->mShower); - cur->prepare_data(); - } - mark_render_able_submitted(cur, submitNs); - publish_render_able_buffer_stats(d->mShower, cur); - cur->mPrepared = true; + Cacl prepare_data("Pipeline", "prepare_data", shower); + for (const auto& cur : snapshot) { + if (!cur || cur->retiring() || !cur->mVisable || !cur->dPtr) + continue; + if (!render_able_ready_for_submit(cur, source, submitNs)) + continue; + if (!cur->mPrepared) { + { + Cacl renderAblePrepare("RenderAble", render_able_name(cur.get()) + ".prepare_data", shower); + cur->prepare_data(*frameSnapshot); } + mark_render_able_submitted(cur, submitNs); + publish_render_able_buffer_stats(shower, cur.get()); + cur->mPrepared = true; } } - if (first_prepare_data) + if (first_prepare_data) { first_prepare_data = false; - } - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - able->mPrepared = false; + mRenderContext->firstPrepareData.store(false, std::memory_order_release); } } + for (const auto& able : snapshot) { + if (able) + able->mPrepared = false; + } Renderable::Color_Buffer_Lease colorLease; { - Cacl wait_render_color("Buffer", "Color.waitRenderLease", d->mShower); + Cacl wait_render_color("Buffer", "Color.waitRenderLease", shower); colorLease = pipeline.wait_render_color(); } Renderable::Color_Buffer* color = colorLease.buffer; auto colorLeaseOwner = std::make_shared(std::move(colorLease)); - QColor background = d->mBackground; std::uint64_t version = pipeline.renderStateVersion; pipeline.jobState = Renderable::JobState::Running; - if (d->mShower) + if (shower) publish_pipeline_stats(d); - PlotPrivate* privateData = d; + Plot_Private* privateData = d; std::uint64_t cpuPostNs = steady_now_ns(); + auto renderableSnapshot = std::make_shared(std::move(snapshot)); + Abs_Plot* plot = this; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); - scheduler.post_cpu([this, privateData, colorLeaseOwner, color, renderSize, background, version, cpuPostNs]() { - if (privateData->mShower) - privateData->mShower->record_duration("Pipeline", "cpuQueueWait", elapsed_ms(cpuPostNs, steady_now_ns())); + scheduler.post_cpu([privateData, colorLeaseOwner, color, version, cpuPostNs, root, renderableSnapshot, frameSnapshot, plot]() { + if (frameSnapshot->performanceShower) + frameSnapshot->performanceShower->record_duration("Pipeline", "cpuQueueWait", elapsed_ms(cpuPostNs, steady_now_ns())); if (privateData->mDestroying.load(std::memory_order_acquire)) { colorLeaseOwner->reset(); notify_render_task_done(privateData); return; } - render_color(color, renderSize, background, version); + Abs_Plot::render_color(color, version, root, *renderableSnapshot, *frameSnapshot); colorLeaseOwner->reset(); if (privateData->mDestroying.load(std::memory_order_acquire)) { notify_render_task_done(privateData); return; } - Global::instance()->renderScheduler().post([this, privateData]() { + Global::instance()->renderScheduler().post([plot, privateData]() { if (!privateData->mDestroying.load(std::memory_order_acquire)) - finish_render(); + plot->finish_render(); notify_render_task_done(privateData); }); }); return true; } -void Plot::on_render_request(Render_Request_Source source) { +void Abs_Plot::on_render_request(Render_Request_Source source) { if (d->mDestroying.load(std::memory_order_acquire)) return; Plot_Render_Schedule_Mode mode = d->mRenderScheduleMode.load(std::memory_order_acquire); @@ -450,16 +531,10 @@ void Plot::on_render_request(Render_Request_Source source) { submit_render_once(); break; } - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - if (able->mVisable && able->dPtr->independent_pixel_cache() && able->dPtr->has_new_pixel_state()) - submit_independent_render(able, source); - } - } - if (d->mShower) + if (performance_shower_owner(d)) publish_pipeline_stats(d); } -bool Plot::submit_render_with_fps_limit() { +bool Abs_Plot::submit_render_with_fps_limit() { double fps = d->mMaxRenderFps.load(std::memory_order_acquire); if (fps <= 0.0) return submit_render_once(); @@ -480,7 +555,7 @@ bool Plot::submit_render_with_fps_limit() { } return submitted; } -void Plot::schedule_max_fps_submit(std::uint64_t targetNs) { +void Abs_Plot::schedule_max_fps_submit(std::uint64_t targetNs) { if (!d->mSubmitTimer || !d->mRenderEnabled.load(std::memory_order_acquire)) return; if (d->mSubmitTimerTargetNs && d->mSubmitTimerTargetNs <= targetNs) @@ -489,7 +564,7 @@ void Plot::schedule_max_fps_submit(std::uint64_t targetNs) { std::uint64_t now = steady_now_ns(); std::uint64_t delayNs = targetNs > now ? targetNs - now : 0; d->mSubmitTimer->expires_after(std::chrono::nanoseconds(delayNs)); - PlotPrivate* privateData = d; + Plot_Private* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); d->mSubmitTimer->async_wait([this, privateData, targetNs](const asio::error_code& error) { if (!error && !privateData->mDestroying.load(std::memory_order_acquire) && privateData->mSubmitTimerTargetNs == targetNs) { @@ -500,21 +575,9 @@ void Plot::schedule_max_fps_submit(std::uint64_t targetNs) { notify_render_task_done(privateData); }); } -bool Plot::has_submit_candidate(Render_Request_Source source, std::uint64_t nowNs) { - if (first_prepare_data) - return true; - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - if (!able->mVisable || able->dPtr->independent_pixel_cache()) - continue; - if (able->dPtr->has_pending_render_data() && render_able_ready_for_submit(able, source, nowNs, false)) - return true; - } - } - return false; -} -bool Plot::render_able_ready_for_submit(Renderable* able, Render_Request_Source source, std::uint64_t nowNs, bool independent) { - Q_UNUSED(independent) +bool Abs_Plot::render_able_ready_for_submit(const std::shared_ptr& able, Render_Request_Source source, std::uint64_t nowNs) { + if (!able || able->retiring()) + return false; RenderAble_Render_Schedule_Mode mode = able->render_schedule_mode(); if (mode == RenderAble_Render_Schedule_Mode::Inherit) { switch (d->mRenderScheduleMode.load(std::memory_order_acquire)) { @@ -551,7 +614,7 @@ bool Plot::render_able_ready_for_submit(Renderable* able, Render_Request_Source } return true; } -void Plot::mark_render_able_submitted(Renderable* able, std::uint64_t nowNs) { +void Abs_Plot::mark_render_able_submitted(const std::shared_ptr& able, std::uint64_t nowNs) { if (able->render_schedule_mode() != RenderAble_Render_Schedule_Mode::Max_Fps) return; double fps = able->render_schedule_max_fps(); @@ -559,172 +622,98 @@ void Plot::mark_render_able_submitted(Renderable* able, std::uint64_t nowNs) { return; able->dPtr->mNextRenderSubmitNs.store(nowNs + static_cast(1000000000.0 / fps), std::memory_order_release); } -void Plot::finish_render() { +void Abs_Plot::finish_render() { if (d->mDestroying.load(std::memory_order_acquire)) return; Renderable::Render_Pipeline& pipeline = d->mPipeline; QRegion updateRegion; + auto showerOwner = performance_shower_owner(d); + Performance_Shower* shower = showerOwner.get(); { - Cacl publishData("Pipeline", "publish_render_color", d->mShower); + Cacl publishData("Pipeline", "publish_render_color", shower); updateRegion = pipeline.publish_render_color(); } pipeline.jobState = Renderable::JobState::Idle; - if (d->mShower) + if (shower) publish_pipeline_stats(d); if (!updateRegion.isEmpty()) request_update(updateRegion); on_render_request(Render_Request_Source::Finish); } -void Plot::render_color(Renderable::Color_Buffer* color, QSize size, QColor background, std::uint64_t version) { - Cacl renderData("Pipeline", "render_color", d->mShower); +void Abs_Plot::render_color(Renderable::Color_Buffer* color, std::uint64_t version, const std::shared_ptr& root, const RenderableSnapshot& snapshot, const Plot_Render_Snapshot& renderSnapshot) { + Cacl renderData("Pipeline", "render_color", renderSnapshot.performanceShower.get()); + QSize size = renderSnapshot.size; if (color->image.size() != size || color->image.format() != QImage::Format_ARGB32) { color->image = QImage(size, QImage::Format_ARGB32); } color->image.detach(); - color->image.fill(background); - QVector leasedAbles; - QVector stateLeases; + color->image.fill(renderSnapshot.backgroundColor); QPainter painter(&color->image); + int renderableCount = 0; + int visibleRenderableCount = 0; + int directDrawCount = 0; + int localCacheCount = 0; { - Cacl drawData("Pipeline", "draw", d->mShower); - for (Layer* layer : layer_list) { - for (Renderable* cur : layer->renderable_list) { - if (!cur->mVisable || cur->dPtr->independent_pixel_cache()) - continue; - auto lease = cur->dPtr->mark_state_role(State_Render); - if (!lease) { - if (d->mShower) - d->mShower->increment_counter("RenderAble", render_able_name(cur) + ".stateLeaseMiss"); - continue; - } - leasedAbles.append(cur); - stateLeases.append(lease); - } + Cacl drawData("Pipeline", "draw", renderSnapshot.performanceShower.get()); + for (const auto& cur : snapshot) { + ++renderableCount; + if (!cur || cur->retiring() || !cur->mVisable) + continue; + ++visibleRenderableCount; + if (cur->cache_mode() == Renderable_Cache_Mode::Local_Pixel) + ++localCacheCount; + else + ++directDrawCount; } - for (Renderable* able : leasedAbles) { - Cacl renderAbleDraw("RenderAble", render_able_name(able) + ".draw", d->mShower); - able->draw(&painter); + if (root) { + Cacl renderAbleDraw("RenderAble", render_able_name(root.get()) + ".render", renderSnapshot.performanceShower.get()); + root->render(&painter, renderSnapshot); } } - for (int i = 0; i < leasedAbles.size(); ++i) { - leasedAbles[i]->dPtr->unmark_state(stateLeases[i]); - } painter.end(); color->version.store(version, std::memory_order_release); color->dirtyRegion = QRegion(QRect(QPoint(0, 0), size)); + if (renderSnapshot.performanceShower) { + double area = static_cast(size.width()) * static_cast(size.height()); + renderSnapshot.performanceShower->set_gauge("Pipeline", "main_render_area", area); + renderSnapshot.performanceShower->set_gauge("Pipeline", "main_render_full_ratio", area > 0.0 ? 1.0 : 0.0); + renderSnapshot.performanceShower->set_gauge("Pipeline", "main_root_child_count", root ? static_cast(root->children_snapshot().size()) : 0.0); + renderSnapshot.performanceShower->set_gauge("Pipeline", "main_renderable_count", static_cast(renderableCount)); + renderSnapshot.performanceShower->set_gauge("Pipeline", "main_visible_renderable_count", static_cast(visibleRenderableCount)); + renderSnapshot.performanceShower->set_gauge("Pipeline", "main_direct_draw_count", static_cast(directDrawCount)); + renderSnapshot.performanceShower->set_gauge("Pipeline", "main_local_cache_count", static_cast(localCacheCount)); + renderSnapshot.performanceShower->set_gauge("Pipeline", "main_state_lease_miss_count", 0.0); + } } -void Plot::mark_independent_render_dirty(Renderable* able, Render_Request_Source source) { - if (d->mDestroying.load(std::memory_order_acquire)) - return; - PlotPrivate* privateData = d; - privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); - Global::instance()->renderScheduler().post([this, privateData, able, source]() { - if (!privateData->mDestroying.load(std::memory_order_acquire)) - submit_independent_render(able, source); - notify_render_task_done(privateData); - }); -} -void Plot::submit_independent_render(Renderable* able, Render_Request_Source source) { - Render_Scheduler& scheduler = Global::instance()->renderScheduler(); - if (!scheduler.is_scheduler_thread()) { - mark_independent_render_dirty(able, source); - return; - } - if (d->mDestroying.load(std::memory_order_acquire)) - return; - if (!able || !able->mVisable || !able->dPtr->independent_pixel_cache()) - return; - if (able->dPtr->mIndependentRenderQueued.exchange(true, std::memory_order_acq_rel)) - return; - std::uint64_t now = steady_now_ns(); - if (!render_able_ready_for_submit(able, source, now, true)) { - able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release); - return; - } - QSize renderSize = d->mRenderSize; - if (renderSize.width() <= 0 || renderSize.height() <= 0) { - able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release); - return; - } - if (!able->dPtr->has_new_pixel_state()) { - able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release); - return; - } - able->prepare_data(); - mark_render_able_submitted(able, now); - auto pixelLease = able->dPtr->wait_render_pixel(); - Pixel_Buffer_Slot* pixel = pixelLease.buffer; - std::uint64_t version = able->dPtr->mPixelEditVersion.load(std::memory_order_acquire); - PlotPrivate* privateData = d; - privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); - scheduler.post_cpu([this, privateData, able, pixelLease, pixel, renderSize, version]() { - if (privateData->mDestroying.load(std::memory_order_acquire)) { - able->dPtr->unmark_pixel(pixelLease); - notify_render_task_done(privateData); - return; - } - render_independent_color(able, pixel, renderSize, version); - able->dPtr->unmark_pixel(pixelLease); - Global::instance()->renderScheduler().post([this, privateData, able]() { - if (!privateData->mDestroying.load(std::memory_order_acquire)) - finish_independent_render(able); - notify_render_task_done(privateData); - }); - }); -} -void Plot::render_independent_color(Renderable* able, Pixel_Buffer_Slot* pixel, QSize size, std::uint64_t version) { - if (pixel->image.size() != size || pixel->image.format() != QImage::Format_ARGB32) { - pixel->image = QImage(size, QImage::Format_ARGB32); - } - pixel->image.detach(); - pixel->image.fill(Qt::transparent); - QPainter painter(&pixel->image); - auto lease = able->dPtr->mark_state_role(State_Render); - if (lease) { - able->draw(&painter); - able->dPtr->unmark_state(lease); - } - painter.end(); - pixel->version.store(version, std::memory_order_release); - pixel->dirtyRegion = QRegion(QRect(QPoint(0, 0), size)); - able->dPtr->mPixelRenderVersion = version; -} -void Plot::finish_independent_render(Renderable* able) { - QRegion updateRegion = able->dPtr->publish_render_pixel(); - able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release); - if (!updateRegion.isEmpty()) - request_update(updateRegion); - if (able->dPtr->has_new_pixel_state()) - submit_independent_render(able, Render_Request_Source::Finish); -} -void Plot::schedule_render_timer() { +void Abs_Plot::schedule_render_timer() { if (d->mDestroying.load(std::memory_order_acquire) || !d->mRenderTimer || !d->mRenderEnabled.load(std::memory_order_acquire)) return; int interval = std::max(1, qRound(1000.0 / (double)d->mRefreshTimesPreSecond)); d->mRenderTimer->expires_after(std::chrono::milliseconds(interval)); - PlotPrivate* privateData = d; + Plot_Private* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); d->mRenderTimer->async_wait([this, privateData](const asio::error_code& error) { if (!error && !d->mDestroying.load(std::memory_order_acquire)) { - if (d->mShower) - d->mShower->increment_counter("Pipeline", "timerTick"); + auto shower = performance_shower_owner(privateData); + if (shower) + shower->increment_counter("Pipeline", "timerTick"); on_render_request(Render_Request_Source::Timer); schedule_render_timer(); } notify_render_task_done(privateData); }); } -void Plot::mark_render_state_dirty() { +void Abs_Plot::mark_render_state_dirty() { if (d->mDestroying.load(std::memory_order_acquire)) return; d->mPipeline.mark_edit_state(); request_render(); } -void Plot::request_render(Render_Request_Source source) { +void Abs_Plot::request_render(Render_Request_Source source) { if (d->mDestroying.load(std::memory_order_acquire)) return; d->mLastRequestNs.store(steady_now_ns(), std::memory_order_release); - PlotPrivate* privateData = d; + Plot_Private* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); Global::instance()->renderScheduler().post([this, privateData, source]() { if (!d->mDestroying.load(std::memory_order_acquire)) @@ -732,7 +721,7 @@ void Plot::request_render(Render_Request_Source source) { notify_render_task_done(privateData); }); } -void Plot::request_update(const QRegion& region) { +void Abs_Plot::request_update(const QRegion& region) { d->mLastUpdateRequestNs.store(steady_now_ns(), std::memory_order_release); QTimer::singleShot(0, this, [this, region]() { if (d->mDestroying.load(std::memory_order_acquire)) @@ -743,13 +732,17 @@ void Plot::request_update(const QRegion& region) { update(region); }); } -QColor Plot::background_color() { - return d->mBackground; +QColor Abs_Plot::background_color() { + std::lock_guard lock(mRenderContext->mutex); + return mRenderContext->backgroundColor; } -void Plot::set_background_color(const QColor& color) const { +void Abs_Plot::set_background_color(const QColor& color) const { d->mBackground = color; + std::lock_guard lock(mRenderContext->mutex); + mRenderContext->backgroundColor = color; + const_cast(this)->mark_render_state_dirty(); } -bool Plot::event(QEvent* event) { +bool Abs_Plot::event(QEvent* event) { bool ret = QWidget::event(event); switch (event->type()) { case QEvent::Resize: { @@ -757,27 +750,33 @@ bool Plot::event(QEvent* event) { QSize renderSize = e->size(); d->mPendingRenderWidth.store(renderSize.width(), std::memory_order_release); d->mPendingRenderHeight.store(renderSize.height(), std::memory_order_release); - PlotPrivate* privateData = d; + Plot_Private* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); - Global::instance()->renderScheduler().post([privateData, renderSize]() { - if (!privateData->mDestroying.load(std::memory_order_acquire)) + Global::instance()->renderScheduler().post([this, privateData, renderSize]() { + if (!privateData->mDestroying.load(std::memory_order_acquire)) { privateData->mRenderSize = renderSize; + { + std::lock_guard lock(mRenderContext->mutex); + mRenderContext->renderSize = renderSize; + } + privateData->mPipeline.mark_edit_state(); + on_render_request(Render_Request_Source::Dirty); + } notify_render_task_done(privateData); }); if (first_resize) { - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - able->first_resize_event(e); - } - } + auto snapshot = renderable_snapshot(); + for (const auto& able : snapshot) + able->first_resize_event(e); } - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { + { + auto snapshot = renderable_snapshot(); + for (const auto& able : snapshot) able->resizeEvent(e); - } } if (first_resize) first_resize = false; + mRenderContext->firstResize.store(false, std::memory_order_release); mark_render_state_dirty(); break; } @@ -793,83 +792,72 @@ bool Plot::event(QEvent* event) { break; } case QEvent::Leave: { - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - able->set_hover_state(QPoint(), false); - } - } + auto snapshot = renderable_snapshot(); + for (const auto& able : snapshot) + able->set_hover_state(QPoint(), false); break; } case QEvent::Wheel: { auto e = reinterpret_cast(event); - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - able->wheelEvent(e); - } - } + auto snapshot = renderable_snapshot(); + for (const auto& able : snapshot) + able->wheelEvent(e); break; } case QEvent::MouseMove: { auto e = reinterpret_cast(event); - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - bool selected = able->select_test(e->pos()); - able->set_hover_state(e->pos(), selected); - if (selected) { - able->mouseMoveEvent(e); - } - } + auto snapshot = renderable_snapshot(); + auto root = root_renderable(); + auto hits = root ? root->hit_renderables(e->pos()) : RenderableSnapshot{}; + for (const auto& able : snapshot) { + if (able) + able->set_hover_state(e->pos(), false); + } + for (const auto& able : hits) { + if (!able) + continue; + able->set_hover_state(e->pos(), true); + able->mouseMoveEvent(e); } break; } case QEvent::MouseButtonPress: { auto e = reinterpret_cast(event); - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - if (able->select_test(e->pos())) { - able->mousePressEvent(e); - } - } - } + auto root = root_renderable(); + auto hits = root ? root->hit_renderables(e->pos()) : RenderableSnapshot{}; + for (const auto& able : hits) + able->mousePressEvent(e); break; } case QEvent::MouseButtonRelease: { auto e = reinterpret_cast(event); - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - if (able->select_test(e->pos())) { - able->mouseReleaseEvent(e); - } - } - } + auto root = root_renderable(); + auto hits = root ? root->hit_renderables(e->pos()) : RenderableSnapshot{}; + for (const auto& able : hits) + able->mouseReleaseEvent(e); break; } case QEvent::KeyPress: { auto e = reinterpret_cast(event); - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - able->keyPressEvent(e); - } - } + auto snapshot = renderable_snapshot(); + for (const auto& able : snapshot) + able->keyPressEvent(e); break; } case QEvent::KeyRelease: { auto e = reinterpret_cast(event); - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - able->keyReleaseEvent(e); - } - } + auto snapshot = renderable_snapshot(); + for (const auto& able : snapshot) + able->keyReleaseEvent(e); break; } default: break; } - for (Layer* layer : layer_list) { - for (Renderable* able : layer->renderable_list) { - able->plot_event(event); - } - } + auto snapshot = renderable_snapshot(); + for (const auto& able : snapshot) + able->plot_event(event); return ret; } +Plot::Plot() = default; } // namespace YSG diff --git a/YSGraphic_Core/architecture/Plot.h b/YSGraphic_Core/architecture/Plot.h index 6aa1c40..9c19f96 100644 --- a/YSGraphic_Core/architecture/Plot.h +++ b/YSGraphic_Core/architecture/Plot.h @@ -1,26 +1,32 @@ #pragma once #include #include +#include +#include +#include "Plot_Render_Context.h" #include "Renderable.h" namespace YSG { void LIB_DECL start_render_scheduler(); class Render_Scheduler; -struct PlotPrivate; +struct Plot_Private; class Performance_Shower; /// Qt 图表窗口和全部 Renderable 的渲染调度入口。 -class LIB_DECL Plot : public QWidget { +class LIB_DECL Abs_Plot : public QWidget { Q_OBJECT public: /// 返回性能面板是否启用。 bool use_performance_shower(); /// 启用或关闭性能面板。 void set_use_performance_shower(bool use); - /// 初始化 Plot 默认层结构。 + /// 初始化 Plot Renderable 根节点。 virtual void init(); /// 同步请求一次渲染。 void render(); /// 返回背景颜色。 QColor background_color(); + std::shared_ptr render_context() const; + std::shared_ptr root_renderable() const; + std::shared_ptr create_renderable_node(const std::shared_ptr& parent, const QString& objectName = {}); bool first_resize = true, first_prepare_data = true; /// 设置背景颜色。 void set_background_color(const QColor& color) const; @@ -43,19 +49,11 @@ public: void mark_render_state_dirty(); /// 按请求来源交给调度策略尝试提交。 void request_render(Render_Request_Source source = Render_Request_Source::Dirty); - /// 把 Renderable 放入指定绘制层。 - void add_renderable(Renderable* able, const QString& layer_name); - /// 请求指定 Renderable 的独立像素缓存后台渲染。 - void mark_independent_render_dirty(Renderable* able, Render_Request_Source source = Render_Request_Source::Dirty); - struct Layer { - QString layer_name; - QVector renderable_list; - }; + /// 从 Renderable 子树移除对象。 + void remove_renderable(const std::shared_ptr& able); QString object_name; - QList layer_list; - QString default_layer_name; - /// 创建默认绘制层。 - virtual void init_layer(); + /// 创建 Renderable 根节点。 + virtual void init_renderable_tree(); /// 判断是否有 Plot 级 CPU 渲染任务运行。 bool is_rendering(); /// 启动渲染检查定时器。 @@ -64,27 +62,35 @@ public: void start_render() const; /// 暂停自动渲染检查。 void pause_render() const; - Plot(); - ~Plot() override; - PlotPrivate* d{}; + Abs_Plot(); + ~Abs_Plot() override; + Plot_Private* d{}; private: - QHash mLayerMap; + std::shared_ptr mRenderContext; + std::shared_ptr mRootRenderable; + mutable std::mutex mRenderableMutex; + using RenderableSnapshot = QVector>; bool event(QEvent* event) final; void paintEvent(QPaintEvent* event) final; + RenderableSnapshot renderable_snapshot() const; + void append_renderable_snapshot(RenderableSnapshot& snapshot, const std::shared_ptr& able) const; + std::shared_ptr render_snapshot() const; void schedule_render_timer(); bool submit_render_once(); void on_render_request(Render_Request_Source source); bool submit_render_with_fps_limit(); void schedule_max_fps_submit(std::uint64_t targetNs); - bool has_submit_candidate(Render_Request_Source source, std::uint64_t nowNs); - bool render_able_ready_for_submit(Renderable* able, Render_Request_Source source, std::uint64_t nowNs, bool independent); - void mark_render_able_submitted(Renderable* able, std::uint64_t nowNs); + bool render_able_ready_for_submit(const std::shared_ptr& able, Render_Request_Source source, std::uint64_t nowNs); + void mark_render_able_submitted(const std::shared_ptr& able, std::uint64_t nowNs); void finish_render(); void request_update(const QRegion& region); - void render_color(Renderable::Color_Buffer* color, QSize size, QColor background, std::uint64_t version); - void submit_independent_render(Renderable* able, Render_Request_Source source); - void render_independent_color(Renderable* able, Pixel_Buffer_Slot* pixel, QSize size, std::uint64_t version); - void finish_independent_render(Renderable* able); + static void render_color(Renderable::Color_Buffer* color, std::uint64_t version, const std::shared_ptr& root, const RenderableSnapshot& snapshot, const Plot_Render_Snapshot& renderSnapshot); friend class Render_Scheduler; + friend struct Renderable; +}; +class LIB_DECL Plot : public Abs_Plot { +public: + Plot(); + ~Plot() override = default; }; } // namespace YSG diff --git a/YSGraphic_Core/architecture/Plot_Render_Context.h b/YSGraphic_Core/architecture/Plot_Render_Context.h new file mode 100644 index 0000000..a999251 --- /dev/null +++ b/YSGraphic_Core/architecture/Plot_Render_Context.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include +#include +#include +namespace YSG { +class Abs_Plot; +class Performance_Shower; +struct Plot_Render_Context { + std::atomic_bool destroying{false}; + std::atomic_bool firstResize{true}; + std::atomic_bool firstPrepareData{true}; + mutable std::mutex mutex; + QSize renderSize; + QColor backgroundColor = Qt::black; + int refreshTimesPerSecond = 30; + std::shared_ptr performanceShower; + Abs_Plot* plot{}; +}; +struct Plot_Render_Snapshot { + std::shared_ptr context; + QSize size; + QColor backgroundColor = Qt::black; + int refreshTimesPerSecond = 30; + std::shared_ptr performanceShower; + bool destroying() const { + return !context || context->destroying.load(std::memory_order_acquire); + } +}; +} // namespace YSG diff --git a/YSGraphic_Core/architecture/Plot_p.h b/YSGraphic_Core/architecture/Plot_p.h index febeb97..ad0a644 100644 --- a/YSGraphic_Core/architecture/Plot_p.h +++ b/YSGraphic_Core/architecture/Plot_p.h @@ -7,9 +7,9 @@ #include "Plot.h" #include "asio.hpp" namespace YSG { -struct PlotPrivate { - explicit PlotPrivate(Plot* plot) : q(plot) {}; - Plot* q{}; +struct Plot_Private { + explicit Plot_Private(Abs_Plot* plot) : q(plot) {}; + Abs_Plot* q{}; int mRefreshTimesPreSecond = 30; std::unique_ptr mRenderTimer; std::unique_ptr mSubmitTimer; @@ -33,6 +33,5 @@ struct PlotPrivate { std::atomic mPaintBufferAcquireMode{Plot_Buffer_Acquire_Mode::Try}; std::atomic mRenderAbleBufferAcquireMode{Plot_Buffer_Acquire_Mode::Wait}; QColor mBackground = Qt::black; - Performance_Shower* mShower{}; }; } // namespace YSG diff --git a/YSGraphic_Core/architecture/Render_Config.h b/YSGraphic_Core/architecture/Render_Config.h index 324c3c8..c23de07 100644 --- a/YSGraphic_Core/architecture/Render_Config.h +++ b/YSGraphic_Core/architecture/Render_Config.h @@ -33,6 +33,14 @@ enum class RenderAble_Render_Schedule_Mode : std::uint8_t { /// 本对象只接受手动请求。 Manual }; +enum class Renderable_Cache_Mode : std::uint8_t { + Direct, + Local_Pixel +}; +enum class Renderable_Child_Render_Order : std::uint8_t { + Self_Before_Children, + Children_Before_Self +}; /// 渲染请求来源。 enum class Render_Request_Source : std::uint8_t { /// 调度定时器触发。 diff --git a/YSGraphic_Core/architecture/Render_Data.cpp b/YSGraphic_Core/architecture/Render_Data.cpp index cad2cda..882df2f 100644 --- a/YSGraphic_Core/architecture/Render_Data.cpp +++ b/YSGraphic_Core/architecture/Render_Data.cpp @@ -2,7 +2,7 @@ namespace YSG { QDebug operator<<(QDebug debug, const Range& range) { QDebugStateSaver saver(debug); - debug.nospace() << "Range(" << range.lower << ", " << range.upper << ")"; + debug.nospace() << "Range(" << range.origin << ", " << range.target << ")"; return debug; } Render_Edit_Guard::Render_Edit_Guard(Render_Data* data, Triple_Buffer_Lease lease) : mData(data), mLease(lease) { diff --git a/YSGraphic_Core/architecture/Render_Data.h b/YSGraphic_Core/architecture/Render_Data.h index d6f8da8..bbd6950 100644 --- a/YSGraphic_Core/architecture/Render_Data.h +++ b/YSGraphic_Core/architecture/Render_Data.h @@ -1,10 +1,12 @@ #pragma once #include +#include #include #include #include #include #include "Render_Config.h" +#include "Plot_Render_Context.h" #include "global.h" class QEvent; class QKeyEvent; @@ -26,6 +28,15 @@ struct Input_Data { virtual std::uint64_t dropped_count() const { return 0; } + virtual std::uint64_t capacity_dropped_count() const { + return 0; + } + virtual std::uint64_t rebuild_dropped_count() const { + return 0; + } + virtual std::uint64_t consumer_dropped_count() const { + return 0; + } virtual std::uint64_t pushed_count() const { return 0; } @@ -41,23 +52,10 @@ struct Input_Buffer_Slot { std::uint64_t version = 0; bool pending = false; }; -struct Pixel_Buffer_Slot { - std::atomic version{0}; - QImage image; - QRegion dirtyRegion; -}; -struct Pixel_Buffer_Lease { - Pixel_Buffer_Slot* buffer{}; - Triple_Buffer_Lease lease{}; - explicit operator bool() const { - return buffer && lease; - } -}; struct Render_Data { Render_Data() { mStateControl.set_stats(&mStateBufferStats); mInputControl.set_stats(&mInputBufferStats); - mPixelControl.set_stats(&mPixelBufferStats); } Renderable* qPtr{}; bool mRenderStart = false; @@ -72,13 +70,6 @@ struct Render_Data { std::atomic mRenderScheduleMode{RenderAble_Render_Schedule_Mode::Inherit}; std::atomic mRenderScheduleMaxFps{0.0}; std::atomic mNextRenderSubmitNs{0}; - Triple_Role_Buffer_Control mPixelControl; - Triple_Buffer_Stats mPixelBufferStats; - std::array mPixelBuffers{}; - std::atomic_bool mIndependentPixelCache{false}; - std::atomic_bool mIndependentRenderQueued{false}; - std::atomic mPixelEditVersion{1}; - std::uint64_t mPixelRenderVersion = 0; Render_State* state(std::uint8_t role) const { return state_by_index(mStateControl.read_view()[role]); } @@ -177,61 +168,6 @@ struct Render_Data { mInputVersion = 0; mInputControl.reset(); } - bool independent_pixel_cache() const { - return mIndependentPixelCache.load(std::memory_order_acquire); - } - void set_independent_pixel_cache(bool enabled) { - mIndependentPixelCache.store(enabled, std::memory_order_release); - } - Pixel_Buffer_Slot* pixel_by_index(std::uint8_t index) { - return &mPixelBuffers[index]; - } - Pixel_Buffer_Lease wait_render_pixel() { - auto lease = mPixelControl.wait_mark_use_role(Color_Render); - return {pixel_by_index(lease.index), lease}; - } - Pixel_Buffer_Lease try_front_pixel() { - auto record = mPixelControl.try_mark_use_role(Color_Front); - if (record.result != Triple_Buffer_Result::Success) - return {}; - return {pixel_by_index(record.lease.index), record.lease}; - } - void unmark_pixel(Pixel_Buffer_Lease lease) { - mPixelControl.unmark_use(lease.lease); - } - bool has_new_pixel_state() const { - return buffer_version_newer(mPixelEditVersion.load(std::memory_order_acquire), mPixelRenderVersion); - } - void mark_pixel_dirty() { - mPixelEditVersion.fetch_add(1, std::memory_order_release); - } - QRegion publish_render_pixel() { - mPixelControl.wait_swap_role(Color_Ready, Color_Render, [this](const Triple_Buffer_View& view) { - auto render = pixel_by_index(view[Color_Render]); - auto ready = pixel_by_index(view[Color_Ready]); - return buffer_version_newer(render->version.load(std::memory_order_acquire), ready->version.load(std::memory_order_acquire)); - }); - auto view = mPixelControl.read_view(); - auto ready = pixel_by_index(view[Color_Ready]); - auto front = pixel_by_index(view[Color_Front]); - if (buffer_version_newer(ready->version.load(std::memory_order_acquire), front->version.load(std::memory_order_acquire))) - return ready->dirtyRegion; - return {}; - } - Pixel_Buffer_Lease consume_ready_pixel(Triple_Buffer_Mode mode) { - auto canSwap = [this](const Triple_Buffer_View& view) { - auto ready = pixel_by_index(view[Color_Ready]); - auto front = pixel_by_index(view[Color_Front]); - return buffer_version_newer(ready->version.load(std::memory_order_acquire), front->version.load(std::memory_order_acquire)); - }; - if (mode == Triple_Buffer_Mode::Wait) { - mPixelControl.wait_swap_role(Color_Front, Color_Ready, canSwap); - } - else { - mPixelControl.try_swap_role(Color_Front, Color_Ready, canSwap); - } - return try_front_pixel(); - } public: /// 同步 State/Input 的 ready 角色到 render 角色。 virtual void sync_state_pipeline() = 0; @@ -250,12 +186,15 @@ public: clear_input_buffers(); } /// 绘制当前已准备好的数据。 - virtual void draw(QPainter* painter) { + virtual void draw(QPainter* painter, const Plot_Render_Snapshot& snapshot) { } /// 准备当前帧数据,默认只同步三缓冲管线。 - virtual void prepare_data() { + virtual void prepare_data(const Plot_Render_Snapshot& snapshot) { sync_state_pipeline(); } + virtual QRect pixel_bounds(const QSize& plotSize) { + return QRect(QPoint(0, 0), plotSize); + } /// 判断指定坐标是否命中对象。 virtual bool select_test(const QPointF& pos) { return false; @@ -300,7 +239,7 @@ struct Base_Render_Data : Render_Data { void sync_state_pipeline() override { } }; -class Plot; +class Abs_Plot; struct LIB_DECL Render_Edit_Guard { Render_Edit_Guard(Render_Data* data, Triple_Buffer_Lease lease); ~Render_Edit_Guard(); diff --git a/YSGraphic_Core/architecture/Renderable.cpp b/YSGraphic_Core/architecture/Renderable.cpp index 8334a01..8647cbe 100644 --- a/YSGraphic_Core/architecture/Renderable.cpp +++ b/YSGraphic_Core/architecture/Renderable.cpp @@ -1,4 +1,6 @@ +#include #include +#include "../base/global.h" #include "Plot.h" #include "Renderable_Core.h" namespace YSG { @@ -44,18 +46,58 @@ double Renderable::render_schedule_max_fps() const { void Renderable::set_render_schedule_max_fps(double fps) { dPtr->set_render_schedule_max_fps(fps); } -bool Renderable::independent_pixel_cache() const { - return dPtr->independent_pixel_cache(); +Renderable_Cache_Mode Renderable::cache_mode() const { + return mCacheMode.load(std::memory_order_acquire); } -void Renderable::set_independent_pixel_cache(bool enabled) { - dPtr->set_independent_pixel_cache(enabled); +void Renderable::set_cache_mode(Renderable_Cache_Mode mode) { + mCacheMode.store(mode, std::memory_order_release); + mCacheDirty.store(true, std::memory_order_release); + mark_render_dirty(); } -void Renderable::init(Plot* plot, QString layerName) { - if (layerName.isEmpty()) { - layerName = plot->default_layer_name; +Renderable_Child_Render_Order Renderable::child_render_order() const { + return mChildRenderOrder.load(std::memory_order_acquire); +} +void Renderable::set_child_render_order(Renderable_Child_Render_Order order) { + mChildRenderOrder.store(order, std::memory_order_release); + mark_render_dirty(); +} +std::uint64_t Renderable::cache_image_bytes() const { + std::lock_guard lock(mCacheMutex); + return mCacheImage.isNull() ? 0 : static_cast(mCacheImage.sizeInBytes()); +} +std::uint64_t Renderable::cache_image_area() const { + std::lock_guard lock(mCacheMutex); + return static_cast(mCacheBounds.width()) * static_cast(mCacheBounds.height()); +} +void Renderable::init_root(Abs_Plot* plot) { + if (!plot) { + ASSERT(plot, "Renderable root init error! plot == nullptr"); + return; } - plot->add_renderable(this, layerName); + bool initialized = mInitialized.exchange(true, std::memory_order_acq_rel); + if (initialized) { + ASSERT(!initialized, "Renderable root init error! renderable already initialized"); + return; + } + init_common(plot); +} +void Renderable::init(const std::shared_ptr& parent) { + if (!parent || !parent->mPlot) { + ASSERT(parent && parent->mPlot, "Renderable init error! parent invalid"); + return; + } + bool initialized = mInitialized.exchange(true, std::memory_order_acq_rel); + if (initialized) { + ASSERT(!initialized, "Renderable init error! renderable already initialized"); + return; + } + init_common(parent->mPlot); + parent->add_child(shared_from_this()); +} +void Renderable::init_common(Abs_Plot* plot) { mPlot = plot; + mPlotContext = plot->render_context(); + mRetiring.store(false, std::memory_order_release); if (!dPtr) dPtr = create_render_data(); dPtr->set_buffer_acquire_mode(plot->render_able_buffer_acquire_mode()); @@ -72,52 +114,232 @@ void Renderable::init(Plot* plot, QString layerName) { } dPtr->qPtr = this; dPtr->mStateBuffers[State_Edit]->version = 1; - if (mPlot) - mPlot->mark_render_state_dirty(); + plot->mark_render_state_dirty(); } -void Renderable::draw(QPainter* painter) { - dPtr->draw(painter); +void Renderable::add_child(const std::shared_ptr& child) { + if (!child || child.get() == this || child->mPlot != mPlot || !child->mParentRenderable.expired()) { + ASSERT(child && child.get() != this && child->mPlot == mPlot && child->mParentRenderable.expired(), "Renderable add_child error! invalid child"); + return; + } + std::lock_guard lock(mChildrenMutex); + if (mChildren.contains(child)) + return; + child->mParentRenderable = shared_from_this(); + mChildren.append(child); + mark_render_dirty(); } -void Renderable::prepare_data() { - dPtr->prepare_data(); +void Renderable::remove_child(const std::shared_ptr& child) { + if (!child || child->mParentRenderable.lock().get() != this) + return; + { + std::lock_guard lock(mChildrenMutex); + mChildren.erase(std::remove(mChildren.begin(), mChildren.end(), child), mChildren.end()); + } + child->detach_from_plot_tree(); + mark_render_dirty(); +} +QVector> Renderable::children_snapshot() const { + std::lock_guard lock(mChildrenMutex); + return mChildren; +} +QVector> Renderable::hit_renderables(const QPointF& pos) { + QVector> hits; + append_hit_renderables(hits, pos); + return hits; +} +void Renderable::append_hit_renderables(QVector>& hits, const QPointF& pos) { + if (retiring() || !mVisable) + return; + if (child_render_order() == Renderable_Child_Render_Order::Children_Before_Self && select_test(pos)) + hits.append(shared_from_this()); + QVector> children = children_snapshot(); + for (int i = children.size() - 1; i >= 0; --i) { + const auto& child = children.at(i); + if (child) + child->append_hit_renderables(hits, pos); + } + if (child_render_order() == Renderable_Child_Render_Order::Self_Before_Children && select_test(pos)) + hits.append(shared_from_this()); +} +void Renderable::render(QPainter* painter, const Plot_Render_Snapshot& snapshot) { + if (retiring() || !mVisable) + return; + if (cache_mode() == Renderable_Cache_Mode::Local_Pixel) + render_cached(painter, snapshot); + else + render_direct(painter, snapshot); +} +void Renderable::render_direct(QPainter* painter, const Plot_Render_Snapshot& snapshot) { + if (child_render_order() == Renderable_Child_Render_Order::Children_Before_Self) { + render_children(painter, snapshot); + render_self(painter, snapshot); + } + else { + render_self(painter, snapshot); + render_children(painter, snapshot); + } +} +void Renderable::render_children(QPainter* painter, const Plot_Render_Snapshot& snapshot) { + QVector> children = children_snapshot(); + for (const auto& child : children) { + if (child) + child->render(painter, snapshot); + } +} +void Renderable::render_cached(QPainter* painter, const Plot_Render_Snapshot& snapshot) { + QSize size = snapshot.size; + QRect bounds = dPtr ? dPtr->pixel_bounds(size).intersected(QRect(QPoint(0, 0), size)) : QRect(QPoint(0, 0), size); + if (bounds.isEmpty()) + return; + std::uint64_t editVersion = mCacheEditVersion.load(std::memory_order_acquire); + { + std::lock_guard lock(mCacheMutex); + bool ready = !mCacheDirty.load(std::memory_order_acquire) && mCacheReadyVersion.load(std::memory_order_acquire) == editVersion && mCacheImage.size() == bounds.size() && mCacheImage.format() == QImage::Format_ARGB32 && mCacheBounds == bounds; + if (ready) { + painter->drawImage(bounds.topLeft(), mCacheImage); + return; + } + if (!mCacheImage.isNull() && mCacheBounds == bounds) { + schedule_cache_render(bounds, snapshot, editVersion); + painter->drawImage(bounds.topLeft(), mCacheImage); + return; + } + } + render_cache_image(bounds, snapshot, editVersion); + std::lock_guard lock(mCacheMutex); + if (!mCacheImage.isNull()) + painter->drawImage(bounds.topLeft(), mCacheImage); +} +void Renderable::render_cache_image(const QRect& bounds, const Plot_Render_Snapshot& snapshot, std::uint64_t version) { + if (retiring()) + return; + QImage cacheImage(bounds.size(), QImage::Format_ARGB32); + cacheImage.fill(Qt::transparent); + { + QPainter cachePainter(&cacheImage); + cachePainter.translate(-bounds.topLeft()); + render_direct(&cachePainter, snapshot); + cachePainter.end(); + } + { + std::lock_guard lock(mCacheMutex); + mCacheBounds = bounds; + mCacheImage = cacheImage; + mCacheReadyVersion.store(version, std::memory_order_release); + bool dirty = mCacheEditVersion.load(std::memory_order_acquire) != version; + mCacheDirty.store(dirty, std::memory_order_release); + } +} +void Renderable::schedule_cache_render(const QRect& bounds, const Plot_Render_Snapshot& snapshot, std::uint64_t version) { + if (mCacheRenderQueued.exchange(true, std::memory_order_acq_rel)) + return; + auto self = shared_from_this(); + auto snapshotOwner = std::make_shared(snapshot); + Global::instance()->renderScheduler().post_cpu([self, snapshotOwner, bounds, version]() { + if (self->retiring() || !snapshotOwner || snapshotOwner->destroying()) { + self->mCacheRenderQueued.store(false, std::memory_order_release); + return; + } + self->render_cache_image(bounds, *snapshotOwner, version); + self->mCacheRenderQueued.store(false, std::memory_order_release); + if (!self->retiring()) + self->notify_parent_render_dirty(); + }); +} +void Renderable::notify_parent_render_dirty() { + if (retiring()) + return; + auto context = mPlotContext.lock(); + if (!context || context->destroying.load(std::memory_order_acquire)) + return; + if (auto parent = mParentRenderable.lock()) { + parent->mark_render_dirty(); + return; + } + std::lock_guard lock(context->mutex); + if (!context->destroying.load(std::memory_order_acquire) && context->plot) + context->plot->mark_render_state_dirty(); +} +void Renderable::render_self(QPainter* painter, const Plot_Render_Snapshot& snapshot) { + if (!dPtr) + return; + auto lease = dPtr->mark_state_role(State_Render); + if (!lease) + return; + draw(painter, snapshot); + dPtr->unmark_state(lease); +} +void Renderable::draw(QPainter* painter, const Plot_Render_Snapshot& snapshot) { + if (retiring()) + return; + dPtr->draw(painter, snapshot); +} +void Renderable::prepare_data(const Plot_Render_Snapshot& snapshot) { + if (retiring()) + return; + dPtr->prepare_data(snapshot); } bool Renderable::select_test(const QPointF& pos) { + if (retiring()) + return false; return dPtr->select_test(pos); } void Renderable::plot_event(QEvent* event) { + if (retiring()) + return; dPtr->plot_event(event); } void Renderable::mousePressEvent(QMouseEvent* event) { + if (retiring()) + return; dPtr->mousePressEvent(event); } void Renderable::mouseMoveEvent(QMouseEvent* event) { + if (retiring()) + return; dPtr->mouseMoveEvent(event); } void Renderable::mouseReleaseEvent(QMouseEvent* event) { + if (retiring()) + return; dPtr->mouseReleaseEvent(event); } void Renderable::set_hover_state(const QPoint& pos, bool active) { + if (retiring()) + return; dPtr->set_hover_state(pos, active); } void Renderable::keyPressEvent(QKeyEvent* event) { + if (retiring()) + return; dPtr->keyPressEvent(event); } void Renderable::keyReleaseEvent(QKeyEvent* event) { + if (retiring()) + return; dPtr->keyReleaseEvent(event); } void Renderable::first_resize_event(QResizeEvent* event) { + if (retiring()) + return; dPtr->first_resize_event(event); } void Renderable::resizeEvent(QResizeEvent* event) { + if (retiring()) + return; dPtr->resizeEvent(event); } void Renderable::wheelEvent(QWheelEvent* event) { + if (retiring()) + return; dPtr->wheelEvent(event); } bool Renderable::version_newer(std::uint64_t a, std::uint64_t b) { return buffer_version_newer(a, b); } void Renderable::mark_state_dirty() { + if (retiring()) + return; if (dPtr) { auto lease = dPtr->mStateControl.wait_mark_use_role(State_Edit); ++dPtr->state_by_index(lease.index)->version; @@ -126,63 +348,50 @@ void Renderable::mark_state_dirty() { mark_render_dirty(); } void Renderable::mark_render_dirty() { - if (!mPlot) + if (retiring()) return; - if (dPtr && dPtr->independent_pixel_cache()) { - dPtr->mark_pixel_dirty(); - mPlot->mark_independent_render_dirty(this); + auto context = mPlotContext.lock(); + if (!context || context->destroying.load(std::memory_order_acquire)) + return; + mCacheDirty.store(true, std::memory_order_release); + mCacheEditVersion.fetch_add(1, std::memory_order_acq_rel); + if (auto parent = mParentRenderable.lock()) { + parent->mark_render_dirty(); return; } - mPlot->mark_render_state_dirty(); + std::lock_guard lock(context->mutex); + if (!context->destroying.load(std::memory_order_acquire) && context->plot) + context->plot->mark_render_state_dirty(); } bool Renderable::ok() const { - return dPtr->mRenderStart && mPlot && !mPlot->first_resize && !mPlot->first_prepare_data; + auto context = mPlotContext.lock(); + return dPtr && dPtr->mRenderStart && !retiring() && context && !context->destroying.load(std::memory_order_acquire) && !context->firstResize.load(std::memory_order_acquire) && !context->firstPrepareData.load(std::memory_order_acquire); +} +bool Renderable::retiring() const { + return mRetiring.load(std::memory_order_acquire); +} +void Renderable::detach_from_plot_tree() { + mRetiring.store(true, std::memory_order_release); + QVector> children = children_snapshot(); + { + std::lock_guard lock(mChildrenMutex); + mChildren.clear(); + } + for (const auto& child : children) { + if (child) + child->detach_from_plot_tree(); + } + mPlot = nullptr; + mCacheRenderQueued.store(false, std::memory_order_release); + { + std::lock_guard lock(mCacheMutex); + mCacheImage = QImage(); + mCacheBounds = {}; + } } Renderable::~Renderable() { + detach_from_plot_tree(); delete dPtr; dPtr = nullptr; } -bool Renderable::dect_cycle_rely() { - QQueue queue; - queue.enqueue(this); - while (!queue.isEmpty()) { - Renderable* current = queue.dequeue(); - QSet visited; - QSet recursionStack; - QVector path; - bool has = has_cycle_rely(current, visited, recursionStack, path); - if (has) { - return true; - } - } - qDebug() << this << " 检测成功!"; - return false; -} -bool Renderable::has_cycle_rely(Renderable* node, QSet& visited, QSet& recursionStack, - QVector& currentPath) { - if (!node) - return false; - if (recursionStack.contains(node)) { - qDebug() << "Cycle detected in path:"; - for (Renderable* n : currentPath) { - qDebug() << n->mObjectName << "(" << n << ")->"; - } - qDebug() << node; - return true; - } - if (visited.contains(node)) { - return false; - } - visited.insert(node); - recursionStack.insert(node); - currentPath.append(node); - for (Renderable* child : node->mBeRelyList) { - if (has_cycle_rely(child, visited, recursionStack, currentPath)) { - return true; - } - } - recursionStack.remove(node); - currentPath.removeLast(); - return false; -} } // namespace YSG diff --git a/YSGraphic_Core/architecture/Renderable_Core.h b/YSGraphic_Core/architecture/Renderable_Core.h index 447efbe..cfafbba 100644 --- a/YSGraphic_Core/architecture/Renderable_Core.h +++ b/YSGraphic_Core/architecture/Renderable_Core.h @@ -1,15 +1,15 @@ #pragma once #include -#include -#include #include #include #include #include +#include +#include #include "Render_Data.h" namespace YSG { /// 可渲染对象的基础接口和每对象渲染管线。 -struct Renderable { +struct Renderable : std::enable_shared_from_this { /// 后台渲染任务状态。 enum class JobState { /// 没有任务运行或排队。 @@ -78,12 +78,19 @@ struct Renderable { virtual Input_Data* create_input_data() { return new Input_Data; } - /// 把对象挂接到 Plot 的指定层。 - virtual void init(Plot* plot, QString layerName = ""); + /// 把对象挂接到 parent renderable。 + virtual void init(const std::shared_ptr& parent); + /// 初始化 Plot 根节点。 + virtual void init_root(Abs_Plot* plot); + void add_child(const std::shared_ptr& child); + void remove_child(const std::shared_ptr& child); + QVector> children_snapshot() const; + QVector> hit_renderables(const QPointF& pos); + void render(QPainter* painter, const Plot_Render_Snapshot& snapshot); /// 绘制已准备好的 Render_Data。 - virtual void draw(QPainter* painter); + virtual void draw(QPainter* painter, const Plot_Render_Snapshot& snapshot); /// 在 CPU render 阶段准备渲染数据。 - virtual void prepare_data(); + virtual void prepare_data(const Plot_Render_Snapshot& snapshot); /// 判断指定 Plot 坐标是否命中当前对象。 virtual bool select_test(const QPointF& pos); /// 转发 Qt 事件给私有渲染数据。 @@ -108,28 +115,54 @@ struct Renderable { /// 当前对象自身最大提交帧率,0 表示继承或不限速。 double render_schedule_max_fps() const; void set_render_schedule_max_fps(double fps); - /// 判断是否启用对象独立像素缓存。 - bool independent_pixel_cache() const; - void set_independent_pixel_cache(bool enabled); + /// 当前 Renderable 输出缓存策略。 + Renderable_Cache_Mode cache_mode() const; + void set_cache_mode(Renderable_Cache_Mode mode); + Renderable_Child_Render_Order child_render_order() const; + void set_child_render_order(Renderable_Child_Render_Order order); + std::uint64_t cache_image_bytes() const; + std::uint64_t cache_image_area() const; /// 标记 State 缓冲有新版本。 void mark_state_dirty(); /// 请求父 Plot 调度一次渲染。 void mark_render_dirty(); QString mObjectName; Render_Data* dPtr{}; - Plot* mPlot{}; + Abs_Plot* mPlot{}; + std::weak_ptr mParentRenderable; [[nodiscard]] bool ok() const; + [[nodiscard]] bool retiring() const; bool mPrepared = false; - // 这里面可以预先准备数据用来同步 virtual ~Renderable(); virtual bool select(QPointF pos) { return false; } bool mVisable = true; - Renderable* mParent{}; - QVector mBeRelyList; - bool dect_cycle_rely(); -private: - bool has_cycle_rely(Renderable* node, QSet& visited, QSet& recursionStack, QVector& currentPath); + QVector> mChildren; + mutable std::mutex mChildrenMutex; + std::weak_ptr mPlotContext; + std::atomic mCacheMode{Renderable_Cache_Mode::Direct}; + std::atomic mChildRenderOrder{Renderable_Child_Render_Order::Self_Before_Children}; + std::atomic_bool mCacheDirty{true}; + std::atomic_bool mCacheRenderQueued{false}; + std::atomic mCacheEditVersion{1}; + std::atomic mCacheReadyVersion{0}; + mutable std::mutex mCacheMutex; + QImage mCacheImage; + QRect mCacheBounds; + std::atomic_bool mRetiring{true}; + std::atomic_bool mInitialized{false}; +protected: + friend class Abs_Plot; + void init_common(Abs_Plot* plot); + void render_direct(QPainter* painter, const Plot_Render_Snapshot& snapshot); + void render_cached(QPainter* painter, const Plot_Render_Snapshot& snapshot); + void render_self(QPainter* painter, const Plot_Render_Snapshot& snapshot); + void render_children(QPainter* painter, const Plot_Render_Snapshot& snapshot); + void render_cache_image(const QRect& bounds, const Plot_Render_Snapshot& snapshot, std::uint64_t version); + void schedule_cache_render(const QRect& bounds, const Plot_Render_Snapshot& snapshot, std::uint64_t version); + void notify_parent_render_dirty(); + void append_hit_renderables(QVector>& hits, const QPointF& pos); + void detach_from_plot_tree(); }; } // namespace YSG diff --git a/YSGraphic_Core/architecture/global.h b/YSGraphic_Core/architecture/global.h index fe282fe..abce02b 100644 --- a/YSGraphic_Core/architecture/global.h +++ b/YSGraphic_Core/architecture/global.h @@ -11,6 +11,7 @@ #include #include #include +#include // Q_DECL_IMPORT #ifdef NDEBUG #define ASSERT(condition, message) ((void)0) // 在发布模式下什么都不做 @@ -42,34 +43,36 @@ class Abs_Axis; class Axis; struct Render_Data; struct Renderable; +class Abs_Plot; class Plot; +// 注意这仅仅只是一个范围 这是一个向量,大小相反就反向画 struct LIB_DECL Range { - double lower; - double upper; + double origin; + double target; double center() { - return (lower + upper) / 2.0; + return (origin + target) / 2.0; } [[nodiscard]] double size() const { - return qAbs(upper - lower); + return qAbs(target - origin); } [[nodiscard]] double length() const { - return upper - lower; + return target - origin; } [[nodiscard]] double middle() const { - return lower + (upper - lower) / 2; + return origin + (target - origin) / 2; } bool operator==(const Range& other) const { - return (lower == other.lower && upper == other.upper); + return (origin == other.origin && target == other.target); } bool operator!=(const Range& other) const { return !(*this == other); } [[nodiscard]] bool contain(double value) const { - if (lower < upper) { - return value > lower - 0.0001 && value < upper + 0.0001; + if (origin < target) { + return value > origin - 0.0001 && value < target + 0.0001; } else { - return value > upper - 0.0001 && value < lower + 0.0001; + return value > target - 0.0001 && value < origin + 0.0001; } } }; @@ -138,8 +141,11 @@ public: \ namespace YSG { template constexpr const T& clamp(const T& v, const T& lo, const T& hi) { - return (v < lo) ? lo : (v > hi) ? hi - : v; + return (v < lo) + ? lo + : (v > hi) + ? hi + : v; } } // namespace YSG #undef min diff --git a/YSGraphic_Core/base/Graphic.cpp b/YSGraphic_Core/base/Graphic.cpp index d35a70b..b5e86cc 100644 --- a/YSGraphic_Core/base/Graphic.cpp +++ b/YSGraphic_Core/base/Graphic.cpp @@ -1,5 +1,7 @@ #include "Graphic.h" namespace YSG { +void Opengl_Renderable::draw() { +} Graphic::Graphic() { setSurfaceType(OpenGLSurface); } @@ -8,10 +10,9 @@ bool Graphic::event(QEvent* event) { switch (event->type()) { case QEvent::Resize: { auto e = reinterpret_cast(event); - for (Painter_Layer* b : mPainterBuffers) { - QSize size = e->size(); - b->mBuffer1 = new QPixmap(size); - b->mBuffer2 = new QPixmap(size); + QSize size = e->size(); + for (Painter_Renderable* b : mPainterBuffers) { + b->resize_buffers(size); } if (mFirstResize) mFirstResize = false; diff --git a/YSGraphic_Core/base/Graphic.h b/YSGraphic_Core/base/Graphic.h index 5fef0ab..8912839 100644 --- a/YSGraphic_Core/base/Graphic.h +++ b/YSGraphic_Core/base/Graphic.h @@ -1,53 +1,59 @@ #pragma once #include +#include #include "../architecture/global.h" namespace YSG { -class LIB_DECL Graphic_Layer { +class LIB_DECL Graphic_Renderable { public: + virtual ~Graphic_Renderable() = default; }; -class Painter_Layer : public Graphic_Layer { +class Painter_Renderable : public Graphic_Renderable { public: - QPixmap* mBuffer1{}; - QPixmap* mBuffer2{}; + std::unique_ptr mBuffer1{}; + std::unique_ptr mBuffer2{}; bool it = true; + void resize_buffers(const QSize& size) { + mBuffer1 = std::make_unique(size); + mBuffer2 = std::make_unique(size); + } void draw() { + if (!mBuffer1 || !mBuffer2) + return; it = !it; - QPixmap* cur = it ? mBuffer1 : mBuffer2; + QPixmap* cur = it ? mBuffer1.get() : mBuffer2.get(); QPainter painter(cur); painter.fillRect(cur->rect(), Qt::red); } void update_texture() { - QPixmap* cur = it ? mBuffer1 : mBuffer2; + QPixmap* cur = it ? mBuffer1.get() : mBuffer2.get(); + Q_UNUSED(cur) } }; -class LIB_DECL Opengl_Layer : public Graphic_Layer { +class LIB_DECL Opengl_Renderable : public Graphic_Renderable { public: void draw(); }; class Graphic : public QWindow { public: Graphic(); - QVector mLayers; - QVector mPainterBuffers; - QVector mOpenglBuffers; - bool mFirstResize; + QVector mRenderables; + QVector mPainterBuffers; + QVector mOpenglBuffers; + bool mFirstResize = true; protected: bool event(QEvent*) override; public: - virtual void create_layers() {}; + virtual void create_renderables() {}; void render() { if (mFirstResize) return; - for (Opengl_Layer* b : mOpenglBuffers) { + for (Opengl_Renderable* b : mOpenglBuffers) { b->draw(); } - // 插入opengl同步变量 - for (Painter_Layer* b : mPainterBuffers) { + for (Painter_Renderable* b : mPainterBuffers) { b->draw(); b->update_texture(); } - // 等待opengl同步变量 - // opengl汇总绘制 } }; } // namespace YSG diff --git a/YSGraphic_Core/base/global.cpp b/YSGraphic_Core/base/global.cpp index 1ce5773..796c1ce 100644 --- a/YSGraphic_Core/base/global.cpp +++ b/YSGraphic_Core/base/global.cpp @@ -1,4 +1,4 @@ -#include "Global.h" +#include "global.h" #include #include #include "../architecture/Plot_p.h" @@ -10,7 +10,7 @@ struct Render_Scheduler_Private { std::unique_ptr mCpuPool; std::thread mSchedulerThread; std::thread::id mSchedulerThreadId; - QList mPlots; + QList mPlots; std::atomic_bool mRunning{false}; std::atomic_bool mCpuJoined{true}; }; @@ -32,7 +32,7 @@ void Render_Scheduler::start() { d->mCpuJoined.store(false, std::memory_order_release); d->mSchedulerThread = std::thread([this]() { d->mSchedulerThreadId = std::this_thread::get_id(); - for (Plot* plot : d->mPlots) { + for (Abs_Plot* plot : d->mPlots) { initialize_plot_on_scheduler(plot); } d->mIoContext.run(); @@ -76,7 +76,7 @@ void Render_Scheduler::post_cpu(std::function task) { post(std::move(task)); } } -void Render_Scheduler::register_plot(Plot* plot) { +void Render_Scheduler::register_plot(Abs_Plot* plot) { if (d->mRunning.load(std::memory_order_acquire) && !is_scheduler_thread()) { post_and_wait([this, plot]() { register_plot_on_scheduler(plot); @@ -85,7 +85,7 @@ void Render_Scheduler::register_plot(Plot* plot) { } register_plot_on_scheduler(plot); } -void Render_Scheduler::remove_plot(Plot* plot) { +void Render_Scheduler::remove_plot(Abs_Plot* plot) { if (d->mRunning.load(std::memory_order_acquire) && !is_scheduler_thread()) { post_and_wait([this, plot]() { remove_plot_on_scheduler(plot); @@ -128,15 +128,15 @@ void Render_Scheduler::post_and_wait(std::function task) { return done; }); } -void Render_Scheduler::register_plot_on_scheduler(Plot* plot) { +void Render_Scheduler::register_plot_on_scheduler(Abs_Plot* plot) { if (!d->mPlots.contains(plot)) d->mPlots.append(plot); if (d->mRunning.load(std::memory_order_acquire)) initialize_plot_on_scheduler(plot); } -void Render_Scheduler::remove_plot_on_scheduler(Plot* plot) { +void Render_Scheduler::remove_plot_on_scheduler(Abs_Plot* plot) { d->mPlots.removeAll(plot); - PlotPrivate* data = plot->d; + Plot_Private* data = plot->d; data->mRenderEnabled.store(false, std::memory_order_release); if (data->mRenderTimer) { data->mRenderTimer->cancel(); @@ -148,8 +148,8 @@ void Render_Scheduler::remove_plot_on_scheduler(Plot* plot) { } } void Render_Scheduler::shutdown_on_scheduler() { - for (Plot* plot : d->mPlots) { - PlotPrivate* data = plot->d; + for (Abs_Plot* plot : d->mPlots) { + Plot_Private* data = plot->d; data->mDestroying.store(true, std::memory_order_release); data->mRenderEnabled.store(false, std::memory_order_release); if (data->mRenderTimer) { @@ -163,8 +163,8 @@ void Render_Scheduler::shutdown_on_scheduler() { } d->mPlots.clear(); } -void Render_Scheduler::initialize_plot_on_scheduler(Plot* plot) { - PlotPrivate* data = plot->d; +void Render_Scheduler::initialize_plot_on_scheduler(Abs_Plot* plot) { + Plot_Private* data = plot->d; if (!data->mRenderTimer) data->mRenderTimer = std::make_unique(d->mIoContext); if (!data->mSubmitTimer) diff --git a/YSGraphic_Core/base/global.h b/YSGraphic_Core/base/global.h index b15d7d7..9d0904c 100644 --- a/YSGraphic_Core/base/global.h +++ b/YSGraphic_Core/base/global.h @@ -2,11 +2,12 @@ #include "YSGraphic_Core/architecture/global.h" #include "psc_global_include/Singleton.hpp" extern int qInitResources_resource(); +extern int qInitResources_dict(); namespace asio { class io_context; } namespace YSG { -class Plot; +class Abs_Plot; struct Render_Scheduler_Private; class LIB_DECL Render_Scheduler { public: @@ -16,23 +17,24 @@ public: void shutdown(); void post(std::function task); void post_cpu(std::function task); - void register_plot(Plot* plot); - void remove_plot(Plot* plot); + void register_plot(Abs_Plot* plot); + void remove_plot(Abs_Plot* plot); [[nodiscard]] bool is_scheduler_thread() const; [[nodiscard]] bool is_running() const; asio::io_context& io_context(); private: void join_cpu_Pool(); void post_and_wait(std::function task); - void register_plot_on_scheduler(Plot* plot); - void remove_plot_on_scheduler(Plot* plot); + void register_plot_on_scheduler(Abs_Plot* plot); + void remove_plot_on_scheduler(Abs_Plot* plot); void shutdown_on_scheduler(); - void initialize_plot_on_scheduler(Plot* plot); + void initialize_plot_on_scheduler(Abs_Plot* plot); std::unique_ptr d; }; class LIB_DECL Global : public QObject, public Psc::Singleton { public: int r = qInitResources_resource(); + int dictResource = qInitResources_dict(); int mInterval = 10; Render_Scheduler& renderScheduler(); void start_render_scheduler(); diff --git a/YSGraphic_Core/component/SVG.cpp b/YSGraphic_Core/component/SVG.cpp index 9e3b299..b5db4e7 100644 --- a/YSGraphic_Core/component/SVG.cpp +++ b/YSGraphic_Core/component/SVG.cpp @@ -4,7 +4,7 @@ #include #include #include -#include "../base/Global.h" +#include "../base/global.h" void SVG_Image_Render::render(QPainter* painter, const QRectF& rect) { render(painter, rect, _color); } diff --git a/YSGraphic_Core/component/Table/Base_Table_Model.cpp b/YSGraphic_Core/component/Table/Base_Table_Model.cpp index 650d66a..e3a4f5a 100644 --- a/YSGraphic_Core/component/Table/Base_Table_Model.cpp +++ b/YSGraphic_Core/component/Table/Base_Table_Model.cpp @@ -1,6 +1,6 @@ #include "Base_Table_Model.h" #include "../../Generate_Mock_Data.h" -#include "../../base/Global.h" +#include "../../base/global.h" #include "Base_Info.h" #include "Base_Table_Data.h" namespace Psc { diff --git a/YSGraphic_Core/export.h b/YSGraphic_Core/export.h index 5738157..c312860 100644 --- a/YSGraphic_Core/export.h +++ b/YSGraphic_Core/export.h @@ -6,6 +6,7 @@ #include "base/export.h" #include "component/export.h" #include "plottable/export.h" +#include "primitive/export.h" struct ScreenInfo { QSize fullSize; QSize availableSize; diff --git a/YSGraphic_Core/plottable/Afterglow.cpp b/YSGraphic_Core/plottable/Afterglow.cpp index d99eced..2cbc341 100644 --- a/YSGraphic_Core/plottable/Afterglow.cpp +++ b/YSGraphic_Core/plottable/Afterglow.cpp @@ -1,4 +1,4 @@ -#include +#include #include "Afterglow_p.h" namespace YSG { using Afterglow_Binding = Renderable_Binding; @@ -17,29 +17,44 @@ Render_State* Afterglow::create_render_state() { Input_Data* Afterglow::create_input_data() { return Afterglow_Binding::create_input_data(); } -Frequent_Axis* Afterglow::frequent_axis(SRC src) { - return d()->state_value(&Afterglow_Render_State::frequent_axis, src); +std::shared_ptr Afterglow::frequent_axis(SRC src) { + return d()->state_value(&Afterglow_Render_State::frequent_axis, src).lock(); } -void Afterglow::set_frequent_axis(Frequent_Axis* value) { - d()->set_state_value(&Afterglow_Render_State::frequent_axis, value); +void Afterglow::set_frequent_axis(std::shared_ptr value) { + d()->set_state_value(&Afterglow_Render_State::frequent_axis, std::weak_ptr(value)); + if (auto image = mImageItem.lock()) + image->set_x_axis(value); } -Axis* Afterglow::power_axis(SRC src) { - return d()->state_value(&Afterglow_Render_State::power_axis, src); +std::shared_ptr Afterglow::power_axis(SRC src) { + return d()->state_value(&Afterglow_Render_State::power_axis, src).lock(); } -void Afterglow::set_power_axis(Axis* value) { - d()->set_state_value(&Afterglow_Render_State::power_axis, value); +void Afterglow::set_power_axis(std::shared_ptr value) { + d()->set_state_value(&Afterglow_Render_State::power_axis, std::weak_ptr(value)); + if (auto image = mImageItem.lock()) + image->set_y_axis(value); } Range Afterglow::frequent_range(SRC src) { return d()->state_value(&Afterglow_Render_State::frequent_range, src); } void Afterglow::set_frequent_range(Range value) { + if (value.length() == 0.0) + return; d()->set_state_value(&Afterglow_Render_State::frequent_range, std::move(value)); + if (auto image = mImageItem.lock()) + image->set_x_range(frequent_range()); } Range Afterglow::power_range(SRC src) { return d()->state_value(&Afterglow_Render_State::power_range, src); } void Afterglow::set_power_range(Range value) { + if (value.size() == 0.0) + return; d()->set_state_value(&Afterglow_Render_State::power_range, std::move(value)); + if (auto image = mImageItem.lock()) + image->set_y_range(power_range()); + auto bar = mColorBar.lock(); + if (bar) + bar->set_range(power_range()); } int Afterglow::frequent_point_size(SRC src) { return d()->state_value(&Afterglow_Render_State::frequent_point_size, src); @@ -51,7 +66,7 @@ int Afterglow::buffer_size(SRC src) { return d()->state_value(&Afterglow_Render_State::buffer_size, src); } void Afterglow::set_buffer_size(int value) { - d()->set_state_value(&Afterglow_Render_State::buffer_size, value); + d()->set_state_value(&Afterglow_Render_State::buffer_size, qMax(1, value)); } bool Afterglow::interpolate(SRC src) { return d()->state_value(&Afterglow_Render_State::interpolate, src); @@ -63,32 +78,46 @@ double Afterglow::attenuation_rate(SRC src) { return d()->state_value(&Afterglow_Render_State::attenuation_rate, src); } void Afterglow::set_attenuation_rate(double value) { - d()->set_state_value(&Afterglow_Render_State::attenuation_rate, value); + d()->set_state_value(&Afterglow_Render_State::attenuation_rate, std::clamp(value, 0.0, 1.0)); } int Afterglow::init_strong_value(SRC src) { return d()->state_value(&Afterglow_Render_State::init_strong_value, src); } void Afterglow::set_init_strong_value(int value) { - d()->set_state_value(&Afterglow_Render_State::init_strong_value, value); + d()->set_state_value(&Afterglow_Render_State::init_strong_value, qMax(1, value)); +} +Color_Map Afterglow::color_map(SRC src) { + return d()->state_value(&Afterglow_Render_State::color_map, src); +} +void Afterglow::set_color_map(Color_Map value) { + auto bar = mColorBar.lock(); + if (bar) + bar->set_color_map(value); + d()->set_state_value(&Afterglow_Render_State::color_map, std::move(value)); +} +std::shared_ptr Afterglow::color_bar() const { + return mColorBar.lock(); } void Afterglow::set_frequent_point_size(int t) { Afterglow_Private* pd = d(); Render_Edit_Lease edit(pd); - edit->frequent_point_size = std::move(t); + edit->frequent_point_size = qMax(1, t); + edit->power_point_size = qMax(1, edit->power_point_size); edit->buffer_size = edit->frequent_point_size * edit->power_point_size; } void Afterglow::set_power_point_size(int t) { Afterglow_Private* pd = d(); Render_Edit_Lease edit(pd); - edit->power_point_size = std::move(t); + edit->frequent_point_size = qMax(1, edit->frequent_point_size); + edit->power_point_size = qMax(1, t); edit->buffer_size = edit->frequent_point_size * edit->power_point_size; } -Afterglow::Builder::Builder(Frequent_Axis* frequent_axis, Axis* power_axis) { - init(frequent_axis, power_axis); +Afterglow::Builder::Builder(std::shared_ptr parent, std::shared_ptr frequent_axis, std::shared_ptr power_axis) { + init(parent, frequent_axis, power_axis); } -Afterglow* Afterglow::Builder::build() { - auto ret = new Afterglow(); - set(ret); +std::shared_ptr Afterglow::Builder::build() { + std::shared_ptr ret(new Afterglow()); + set(ret.get()); return ret; } void Afterglow::give_data(const QVector& powerRangeData) { @@ -97,31 +126,22 @@ void Afterglow::give_data(const QVector& powerRangeData) { Afterglow_Private* pd = d(); Render_State_Lease state(pd); int frequent_point_size = state->frequent_point_size; - if (powerRangeData.size() != frequent_point_size) { - qDebug() << QString("push_data(const QVector &powerRangeData) error 数组大小不匹配 需要%1, 实际为%2") - .arg(frequent_point_size) - .arg(powerRangeData.size()); + if (powerRangeData.size() != frequent_point_size) return; - } Render_Input_Lease input(pd); - input->mDataList.push(powerRangeData); + input->mDataList.push(powerRangeData, Afterglow_Input_Data::Max_Pending_Frame_Count); } void Afterglow_Private::push_data(const QVector& powerRangeData, const Afterglow_Render_State* s) { - if (curIndex == s->init_strong_value) { - curIndex = 0; - double oldRemain = 1.0 - s->attenuation_rate; - double cacheRemain = s->attenuation_rate / (double)s->init_strong_value; - for (int i = 0; i < cacheBufferSize; ++i) { - mergedPowerData[i] = oldRemain * oldCachePowerData[i] + cacheRemain * cachedPowerData[i]; - cachedPowerData[i] = 0; - } - std::memmove(oldCachePowerData.data(), mergedPowerData.data(), cacheBufferSize * sizeof(double)); - cacheImageDirty = true; - } + if (powerRangeData.size() != s->frequent_point_size || + s->init_strong_value <= 0 || + s->power_range.size() == 0.0 || + s->frequent_point_size <= 0 || + s->power_point_size <= 0) + return; QVector indexList(s->frequent_point_size); double rate = (double)s->power_point_size / s->power_range.size(); for (int i = 0; i < s->frequent_point_size; ++i) { - int lineIndex = (int)((powerRangeData[i] - s->power_range.lower) * rate); + int lineIndex = (int)((powerRangeData[i] - s->power_range.origin) * rate); if (lineIndex < 0) lineIndex = 0; if (lineIndex > s->power_point_size - 1) @@ -160,5 +180,16 @@ void Afterglow_Private::push_data(const QVector& powerRangeData, const A } } curIndex++; + if (curIndex < s->init_strong_value) + return; + curIndex = 0; + double oldRemain = s->attenuation_rate == 1.0 ? 1.0 : 1.0 - s->attenuation_rate; + double cacheRemain = s->attenuation_rate / (double)s->init_strong_value; + for (int i = 0; i < cacheBufferSize; ++i) { + mergedPowerData[i] = oldRemain * oldCachePowerData[i] + cacheRemain * cachedPowerData[i]; + cachedPowerData[i] = 0; + } + oldCachePowerData = mergedPowerData; + cacheImageDirty = true; } } // namespace YSG diff --git a/YSGraphic_Core/plottable/Afterglow.h b/YSGraphic_Core/plottable/Afterglow.h index c2f4aab..1c93b4d 100644 --- a/YSGraphic_Core/plottable/Afterglow.h +++ b/YSGraphic_Core/plottable/Afterglow.h @@ -1,5 +1,8 @@ #pragma once #include "global.h" +#include "../primitive/Color_Bar.h" +#include "../primitive/Color_Map.h" +#include "../primitive/Raster_Image.h" namespace YSG { class Frequent_Axis; class Afterglow_Plot; @@ -15,8 +18,8 @@ public: protected: Afterglow(); public: - PROP(Frequent_Axis*, frequent_axis) - PROP(Axis*, power_axis) + PROP(std::shared_ptr, frequent_axis) + PROP(std::shared_ptr, power_axis) PROP(Range, frequent_range) PROP(Range, power_range) PROP(int, frequent_point_size) @@ -25,15 +28,19 @@ public: PROP(bool, interpolate) PROP(double, attenuation_rate) PROP(int, init_strong_value) + PROP(Color_Map, color_map) + std::shared_ptr color_bar() const; /// 写入一帧功率数据。 void give_data(const QVector& powerRangeData); template struct BuilderT; struct Builder; + std::weak_ptr mColorBar; + std::weak_ptr mImageItem; }; struct Afterglow_Prop { - Frequent_Axis* frequent_axis{}; - Axis* power_axis{}; + std::weak_ptr frequent_axis{}; + std::weak_ptr power_axis{}; Range frequent_range{0, 10}; Range power_range{0, 10}; int frequent_point_size{}; @@ -42,11 +49,13 @@ struct Afterglow_Prop { bool interpolate = true; // 线性插值 double attenuation_rate = 0.2; // 衰减率 衰减率为 1.0代表 不使用衰减 int init_strong_value = 100; // 初始强度 + Color_Map color_map{}; + bool color_bar_enabled = false; }; template struct Afterglow::BuilderT { - PROP_BT(Frequent_Axis*, frequent_axis) - PROP_BT(Axis*, power_axis) + PROP_BT(std::shared_ptr, frequent_axis) + PROP_BT(std::shared_ptr, power_axis) PROP_BT(Range, frequent_range) PROP_BT(Range, power_range) PROP_BT(int, frequent_point_size) @@ -55,18 +64,19 @@ struct Afterglow::BuilderT { PROP_BT(bool, interpolate) PROP_BT(double, attenuation_rate) PROP_BT(int, init_strong_value) - SETTER_T(QString, layerName, "plottable") + PROP_BT(Color_Map, color_map) + PROP_BT(bool, color_bar_enabled) /// 初始化残影构造器。 - void init(Frequent_Axis* frequent_axis, Axis* power_axis); + void init(std::shared_ptr parent, std::shared_ptr frequent_axis, std::shared_ptr power_axis); /// 把构造参数写入目标对象。 void set(Afterglow* t); protected: - Plot* plot{}; + std::shared_ptr parent{}; }; struct LIB_DECL Afterglow::Builder : Afterglow::BuilderT, Afterglow_Prop { /// 构建并返回残影对象。 - Afterglow* build(); + std::shared_ptr build(); /// 创建残影构造器。 - Builder(Frequent_Axis* frequent_axis, Axis* power_axis); + Builder(std::shared_ptr parent, std::shared_ptr frequent_axis, std::shared_ptr power_axis); }; } // namespace YSG diff --git a/YSGraphic_Core/plottable/Afterglow_p.h b/YSGraphic_Core/plottable/Afterglow_p.h index 038c4ba..996af74 100644 --- a/YSGraphic_Core/plottable/Afterglow_p.h +++ b/YSGraphic_Core/plottable/Afterglow_p.h @@ -5,12 +5,14 @@ #include "../Axis/Frequent_Axis_p.h" #include "../architecture/Bounded_Input_Buffer.h" #include "../architecture/Plot_p.h" -#include "../base/Global.h" +#include "../base/global.h" +#include "../primitive/Raster_Image.h" #include "Afterglow.h" #include "Performance_Shower_p.h" namespace YSG { struct Afterglow_Render_State : Render_State, Afterglow_Prop {}; struct Afterglow_Input_Data : Input_Data { + static constexpr std::size_t Max_Pending_Frame_Count = 4; Bounded_Vector_Double_Input_Buffer mDataList; void clear() override { mDataList.clear(); @@ -21,18 +23,28 @@ struct Afterglow_Input_Data : Input_Data { std::uint64_t dropped_count() const override { return mDataList.dropped_count(); } + std::uint64_t capacity_dropped_count() const override { + return mDataList.capacity_dropped_count(); + } + std::uint64_t rebuild_dropped_count() const override { + return mDataList.rebuild_dropped_count(); + } + std::uint64_t consumer_dropped_count() const override { + return mDataList.consumer_dropped_count(); + } std::uint64_t pushed_count() const override { return mDataList.pushed_count(); } }; struct Afterglow_Private : Typed_Render_Data { friend class Afterglow; - QImage image; + Raster_Image_Buffer image; bool cacheImageDirty = false; int curIndex = 0; int cacheFrequentPointSize{}; int cachePowerPointSize{}; int cacheBufferSize{}; + std::uint64_t cacheColorMapHash{}; QVector cachedPowerData; QVector oldCachePowerData; QVector mergedPowerData; @@ -54,79 +66,70 @@ struct Afterglow_Private : Typed_Render_Data& powerRangeData, const Afterglow_Render_State* s); - void draw(QPainter* painter) override { + void draw(QPainter* painter, const Plot_Render_Snapshot& snapshot) override { + Q_UNUSED(painter) Afterglow_Render_State* s = render_state(); - Performance_Shower* shower = q()->mPlot->d->mShower; + Performance_Shower* shower = snapshot.performanceShower.get(); if (shower) { shower->set_performance_line("colCount, rowCount", Performance_Line(QString("colCount:%1, rowCount:%2").arg(s->frequent_point_size).arg(s->power_point_size))); } - Abs_Axis *hAxis = s->frequent_axis, *vAxis = s->power_axis, *value_axis = s->power_axis; - Range &hRange = s->frequent_range, &vRange = s->power_range; - int colCount = s->frequent_point_size, rowCount = s->power_point_size; - if (image.width() != colCount || image.height() != rowCount) { - image = QImage(colCount, rowCount, QImage::Format_ARGB32_Premultiplied); - image.fill(0); - } - { - if (cacheImageDirty) { - QVector& mColorMap = Global::instance()->mColorMap; - Cacl c("Afterglow setTime:%1 SRTT:%2 RTTVAR:%3", q()->mPlot->d->mShower); - for (int row = 0; row < rowCount; ++row) { - QRgb* scanLine = reinterpret_cast(image.scanLine(row)); - double* list = oldCachePowerData.data() + row * colCount; - for (int col = 0; col < colCount; ++col) { - int colorOffset = (int)(list[col] * 255); - if (colorOffset < 0 || colorOffset > 255) { - qDebug() << "colorOffset:" << colorOffset << " value: " << list[col] - << " start_coord:" << value_axis->start_coord() << " end_coord:" << value_axis->end_coord(); - } - scanLine[col] = mColorMap.at(std::clamp(colorOffset, 0, 255)); - } - // std::cout << std::endl; - } - cacheImageDirty = false; - } - } - double x = hAxis->coord_to_pixel(hRange.lower, SRC::Render); - double y = vAxis->coord_to_pixel(vRange.lower, SRC::Render); - double w = hAxis->coord_to_pixel(hRange.upper, SRC::Render) - x; - double h = vAxis->coord_to_pixel(vRange.upper, SRC::Render) - y; - Range &&hAxisRange = hAxis->coord_range(), &&vAxisRange = vAxis->coord_range(); - double hr = ((hAxisRange.lower < hAxisRange.upper) ^ (hRange.lower < hRange.upper)) ? -1 : 1; - double vr = ((vAxisRange.lower < vAxisRange.upper) ^ (vRange.lower < vRange.upper)) ? -1 : 1; - painter->save(); - painter->scale(hr, vr); - painter->drawImage(QRectF(hr * x, vr * y, hr * w, vr * h), image); - painter->restore(); } - void prepare_data() override { + void prepare_data(const Plot_Render_Snapshot& snapshot) override { sync_state_pipeline(); Afterglow_Render_State* s = render_state(); + if (s->frequent_point_size <= 0 || s->power_point_size <= 0) + return; if (cacheFrequentPointSize != s->frequent_point_size || cachePowerPointSize != s->power_point_size) { update_cache_size(s->frequent_point_size, s->power_point_size); } + bool imageSizeChanged = image.width() != s->frequent_point_size || image.height() != s->power_point_size; + std::uint64_t colorMapHash = s->color_map.hash(); + if (cacheColorMapHash != colorMapHash) { + cacheColorMapHash = colorMapHash; + cacheImageDirty = true; + } + if (imageSizeChanged) { + image.resize(s->frequent_point_size, s->power_point_size); + image.fill(Qt::transparent); + cacheImageDirty = true; + } Afterglow_Input_Data* input = render_input_data(); - input->mDataList.read_all([this, s](const QVector& data) { + input->mDataList.read_all([this, s, input](const QVector& data) { + if (data.size() != s->frequent_point_size) { + input->mDataList.record_consumer_dropped(); + return; + } push_data(data, s); }); + if (cacheImageDirty) { + Cacl c("Afterglow setTime:%1 SRTT:%2 RTTVAR:%3", snapshot.performanceShower.get()); + image.write_normalized_grid(oldCachePowerData.data(), s->frequent_point_size, s->power_point_size, s->color_map); + cacheImageDirty = false; + if (auto imageItem = q()->mImageItem.lock()) + imageItem->give_image(image.image(), false); + } clear_render_input(); } }; template -void Afterglow::BuilderT::init(Frequent_Axis* frequent_axis, Axis* power_axis) { +void Afterglow::BuilderT::init(std::shared_ptr parent, std::shared_ptr frequent_axis, std::shared_ptr power_axis) { + ASSERT(parent && parent->mPlot, "Afterglow build error! parent invalid"); + ASSERT(frequent_axis && power_axis, "Afterglow build error! axis == nullptr"); ASSERT(frequent_axis->mPlot != nullptr, "Afterglow build error! frequent_axis->mPlot == nullptr"); ASSERT(frequent_axis->mPlot == power_axis->mPlot, "Afterglow build error! frequent_axis->mPlot != power_axis->mPlot"); - plot = frequent_axis->mPlot; + ASSERT(parent->mPlot == frequent_axis->mPlot, "Afterglow build error! parent->mPlot != frequent_axis->mPlot"); + this->parent = parent; static_cast(this)->frequent_axis = frequent_axis; static_cast(this)->power_axis = power_axis; } template void Afterglow::BuilderT::set(Afterglow* ret) { - ret->init(plot, layerName); + ret->init(parent); + std::shared_ptr owner = ret->shared_from_this(); Afterglow_Private* pd = ret->d(); Afterglow_Render_State* sc = reinterpret_cast(pd->state(State_Edit)); - PROP_RT(FrequentAxis*, frequent_axis) - PROP_RT(Axis*, power_axis) + PROP_RT(std::weak_ptr, frequent_axis) + PROP_RT(std::weak_ptr, power_axis) PROP_RT(Range, frequent_range) PROP_RT(Range, power_range) PROP_RT(int, frequent_point_size) @@ -135,5 +138,22 @@ void Afterglow::BuilderT::set(Afterglow* ret) { PROP_RT(bool, interpolate) PROP_RT(double, attenuation_rate) PROP_RT(int, init_strong_value) + PROP_RT(Color_Map, color_map) + sc->frequent_point_size = qMax(1, sc->frequent_point_size); + sc->power_point_size = qMax(1, sc->power_point_size); + sc->buffer_size = qMax(1, sc->frequent_point_size * sc->power_point_size); + sc->attenuation_rate = std::clamp(sc->attenuation_rate, 0.0, 1.0); + sc->init_strong_value = qMax(1, sc->init_strong_value); + auto frequentAxis = static_cast(this)->frequent_axis.lock(); + auto powerAxis = static_cast(this)->power_axis.lock(); + if (frequentAxis && powerAxis) { + auto image = Raster_Image::Builder(owner, frequentAxis, powerAxis).set_x_range(sc->frequent_range).set_y_range(sc->power_range).build(); + image->mObjectName = "AfterglowRasterImage"; + ret->mImageItem = image; + } + if (static_cast(this)->color_bar_enabled) { + auto bar = Color_Bar::Builder(owner).set_color_map(sc->color_map).set_range(sc->power_range).set_name("Afterglow").build(); + ret->mColorBar = bar; + } } } // namespace YSG diff --git a/YSGraphic_Core/plottable/Audio_Frequent.cpp b/YSGraphic_Core/plottable/Audio_Frequent.cpp index f031fed..7f82a42 100644 --- a/YSGraphic_Core/plottable/Audio_Frequent.cpp +++ b/YSGraphic_Core/plottable/Audio_Frequent.cpp @@ -21,49 +21,75 @@ int Audio_Frequent::time_point_size(SRC src) { return d()->state_value(&Audio_Frequent_Render_State::time_point_size, src); } void Audio_Frequent::set_time_point_size(int value) { - d()->set_state_value(&Audio_Frequent_Render_State::time_point_size, value); + d()->set_state_value(&Audio_Frequent_Render_State::time_point_size, qMax(1, value)); } -Time_Axis* Audio_Frequent::time_axis(SRC src) { - return d()->state_value(&Audio_Frequent_Render_State::time_axis, src); +std::shared_ptr Audio_Frequent::time_axis(SRC src) { + return d()->state_value(&Audio_Frequent_Render_State::time_axis, src).lock(); } -void Audio_Frequent::set_time_axis(Time_Axis* value) { - d()->set_state_value(&Audio_Frequent_Render_State::time_axis, value); +void Audio_Frequent::set_time_axis(std::shared_ptr value) { + d()->set_state_value(&Audio_Frequent_Render_State::time_axis, std::weak_ptr(value)); + if (auto curve = mCurve.lock()) + curve->set_x_axis(value); } -Axis* Audio_Frequent::value_axis(SRC src) { - return d()->state_value(&Audio_Frequent_Render_State::value_axis, src); +std::shared_ptr Audio_Frequent::value_axis(SRC src) { + return d()->state_value(&Audio_Frequent_Render_State::value_axis, src).lock(); } -void Audio_Frequent::set_value_axis(Axis* value) { - d()->set_state_value(&Audio_Frequent_Render_State::value_axis, value); +void Audio_Frequent::set_value_axis(std::shared_ptr value) { + d()->set_state_value(&Audio_Frequent_Render_State::value_axis, std::weak_ptr(value)); + if (auto curve = mCurve.lock()) + curve->set_y_axis(value); } Range Audio_Frequent::key_range(SRC src) { return d()->state_value(&Audio_Frequent_Render_State::key_range, src); } void Audio_Frequent::set_key_range(Range value) { + if (value.length() == 0.0) + return; d()->set_state_value(&Audio_Frequent_Render_State::key_range, std::move(value)); } QColor Audio_Frequent::color(SRC src) { return d()->state_value(&Audio_Frequent_Render_State::color, src); } void Audio_Frequent::set_color(QColor value) { + if (auto curve = mCurve.lock()) { + QPen pen(value); + pen.setWidth(0); + curve->set_pen(pen); + } d()->set_state_value(&Audio_Frequent_Render_State::color, std::move(value)); } -Audio_Frequent::Builder::Builder(Time_Axis* time_axis, Axis* value_axis) { +Audio_Frequent::Builder::Builder(std::shared_ptr parent, std::shared_ptr time_axis, std::shared_ptr value_axis) { + ASSERT(parent && parent->mPlot, "AudioFrequent build error! parent invalid"); + ASSERT(time_axis && value_axis, "AudioFrequent build error! axis == nullptr"); ASSERT(time_axis->mPlot != nullptr, "AudioFrequent build error! time_axis->mPlot == nullptr"); ASSERT(time_axis->mPlot == value_axis->mPlot, "AudioFrequent build error! time_axis->mPlot != value_axis->mPlot"); - plot = time_axis->mPlot; + ASSERT(parent->mPlot == time_axis->mPlot, "AudioFrequent build error! parent->mPlot != time_axis->mPlot"); + this->parent = parent; this->time_axis = time_axis; this->value_axis = value_axis; } -Audio_Frequent* Audio_Frequent::Builder::build() { - auto ret = new Audio_Frequent(); - ret->init(plot, layerName); +std::shared_ptr Audio_Frequent::Builder::build() { + std::shared_ptr ret(new Audio_Frequent()); + ret->init(parent); Audio_Frequent_Private* pd = ret->d(); Audio_Frequent_Render_State* sc = reinterpret_cast(pd->state(State_Edit)); PROP_R(int, time_point_size) - PROP_R(TimeAxis*, time_axis) - PROP_R(Axis*, value_axis) + PROP_R(std::weak_ptr, time_axis) + PROP_R(std::weak_ptr, value_axis) PROP_R(Range, key_range) PROP_R(QColor, color) + sc->time_point_size = qMax(1, sc->time_point_size); + if (sc->key_range.length() == 0.0) + sc->key_range = {0.0, 1.0}; + auto timeAxis = this->time_axis.lock(); + auto valueAxis = this->value_axis.lock(); + if (timeAxis && valueAxis) { + QPen pen(sc->color); + pen.setWidth(0); + auto curve = Curve::Builder(ret, timeAxis, valueAxis).set_pen(pen).build(); + curve->mObjectName = "AudioFrequentCurve"; + ret->mCurve = curve; + } return ret; } void Audio_Frequent::give_data(int tick, double data) { @@ -71,6 +97,6 @@ void Audio_Frequent::give_data(int tick, double data) { if (!ok()) return; Render_Input_Lease input(d()); - input->mDataList.push(data); + input->mDataList.push(data, Audio_Frequent_Input_Data::Max_Pending_Value_Count); } } // namespace YSG diff --git a/YSGraphic_Core/plottable/Audio_Frequent.h b/YSGraphic_Core/plottable/Audio_Frequent.h index 961b0cd..ab0172e 100644 --- a/YSGraphic_Core/plottable/Audio_Frequent.h +++ b/YSGraphic_Core/plottable/Audio_Frequent.h @@ -1,5 +1,6 @@ #pragma once #include "global.h" +#include "../primitive/Curve.h" namespace YSG { class Time_Axis; struct Audio_Frequent_Private; @@ -17,33 +18,33 @@ public: /// 写入一个 tick 和对应数值。 void give_data(int tick, double data); PROP(int, time_point_size) - PROP(Time_Axis*, time_axis) - PROP(Axis*, value_axis) + PROP(std::shared_ptr, time_axis) + PROP(std::shared_ptr, value_axis) PROP(Range, key_range) PROP(QColor, color) struct Builder; +protected: + friend struct Audio_Frequent_Private; + std::weak_ptr mCurve; }; struct AudioFrequent_Prop { int time_point_size = 100; - Time_Axis* time_axis; - Axis* value_axis{}; + std::weak_ptr time_axis; + std::weak_ptr value_axis{}; Range key_range = {0, 20}; QColor color = Qt::yellow; }; struct LIB_DECL Audio_Frequent::Builder : AudioFrequent_Prop { PROP_B(int, time_point_size) - PROP_B(Time_Axis*, time_axis) - PROP_B(Axis*, value_axis) + PROP_B(std::shared_ptr, time_axis) + PROP_B(std::shared_ptr, value_axis) PROP_B(Range, key_range) PROP_B(QColor, color) - SETTER(QString, layerName, "plottable") /// 创建音频频率构造器。 - Builder(Time_Axis* time_axis, Axis* value_axis); + Builder(std::shared_ptr parent, std::shared_ptr time_axis, std::shared_ptr value_axis); /// 构建并返回音频频率对象。 - Audio_Frequent* build(); + std::shared_ptr build(); protected: - Axis* value_axis; - Time_Axis* time_axis{}; - Plot* plot{}; + std::shared_ptr parent{}; }; } // namespace YSG diff --git a/YSGraphic_Core/plottable/Audio_Frequent_p.h b/YSGraphic_Core/plottable/Audio_Frequent_p.h index 7e38653..b5f0947 100644 --- a/YSGraphic_Core/plottable/Audio_Frequent_p.h +++ b/YSGraphic_Core/plottable/Audio_Frequent_p.h @@ -4,10 +4,12 @@ #include "../architecture/Bounded_Input_Buffer.h" #include "../architecture/Plot_p.h" #include "../architecture/Renderable.h" +#include "../primitive/Curve.h" #include "Audio_Frequent.h" #include "Performance_Shower_p.h" namespace YSG { struct Audio_Frequent_Input_Data : Input_Data { + static constexpr std::size_t Max_Pending_Value_Count = 16; Bounded_Double_Input_Buffer mDataList; void clear() override { mDataList.clear(); @@ -18,6 +20,15 @@ struct Audio_Frequent_Input_Data : Input_Data { std::uint64_t dropped_count() const override { return mDataList.dropped_count(); } + std::uint64_t capacity_dropped_count() const override { + return mDataList.capacity_dropped_count(); + } + std::uint64_t rebuild_dropped_count() const override { + return mDataList.rebuild_dropped_count(); + } + std::uint64_t consumer_dropped_count() const override { + return mDataList.consumer_dropped_count(); + } std::uint64_t pushed_count() const override { return mDataList.pushed_count(); } @@ -48,44 +59,45 @@ struct Audio_Frequent_Private : Typed_Render_Datatime_point_size) { resizePowerBuffer(s->time_point_size); - Performance_Shower* shower = q()->mPlot->d->mShower; + Performance_Shower* shower = snapshot.performanceShower.get(); if (shower) { shower->set_performance_line("TimePointSize", Performance_Line(QString("time_point_size:%1").arg(s->time_point_size))); } } - d->mDataList.read_all_reverse([this](double power) { + d->mDataList.read_all([this](double power) { pushPower(power); }); snapshotPowerBuffer(); + auto timeAxis = s->time_axis.lock(); + auto valueAxis = s->value_axis.lock(); + if (timeAxis && valueAxis) { + int start = (int)timeAxis->start_coord(); + QVector points = Curve_Utils::create_points(dataCount, [this, timeAxis, start](int i) { + double* power = powerSnapshot.data() + dataCount - 1 - i; + int index = timeAxis->d()->mTimeTicker.mStartCoordToEndCoord ? start + i : start + timeAxis->d()->mTimeTicker.mTimePointSize - i; + return QPointF(index, *power); + }); + if (auto curve = q()->mCurve.lock()) + curve->give_points(points, false); + } clear_render_input(); } - void draw(QPainter* painter) override { + void draw(QPainter* painter, const Plot_Render_Snapshot& snapshot) override { + Q_UNUSED(painter) Audio_Frequent_Render_State* s = render_state(); - Performance_Shower* shower = q()->mPlot->d->mShower; + auto timeAxis = s->time_axis.lock(); + if (!timeAxis) + return; + Performance_Shower* shower = snapshot.performanceShower.get(); if (shower) { - shower->set_performance_line("time_point_size", Performance_Line(QString("time_point_size:%1").arg(s->time_axis->time_point_size()))); + shower->set_performance_line("time_point_size", Performance_Line(QString("time_point_size:%1").arg(timeAxis->time_point_size()))); } - painter->save(); - QPolygonF polyline; - int start = (int)s->time_axis->start_coord(); - QPen newPen = QPen(s->color); - newPen.setWidth(0); - painter->setPen(newPen); - for (int i = 0; i < dataCount; ++i) { - double* power = powerSnapshot.data() + dataCount - 1 - i; - int index = s->time_axis->d()->mTimeTicker.mStartCoordToEndCoord ? start + i : start + s->time_axis->d()->mTimeTicker.mTimePointSize - i; - double x = s->time_axis->coord_to_pixel(index, SRC::Render); - double y = s->value_axis->coord_to_pixel(*power, SRC::Render); - polyline.append({x, y}); - } - painter->drawPolyline(polyline); - painter->restore(); } }; } // namespace YSG diff --git a/YSGraphic_Core/plottable/Interpolation_p.h b/YSGraphic_Core/plottable/Interpolation_p.h index fca0b11..effcd08 100644 --- a/YSGraphic_Core/plottable/Interpolation_p.h +++ b/YSGraphic_Core/plottable/Interpolation_p.h @@ -16,34 +16,34 @@ struct Line_Sample_Window { } }; inline bool intersect_range(const Range& data_range, const Range& clip_range, Range& result) { - double data_min = std::min(data_range.lower, data_range.upper); - double data_max = std::max(data_range.lower, data_range.upper); - double clip_min = std::min(clip_range.lower, clip_range.upper); - double clip_max = std::max(clip_range.lower, clip_range.upper); + double data_min = std::min(data_range.origin, data_range.target); + double data_max = std::max(data_range.origin, data_range.target); + double clip_min = std::min(clip_range.origin, clip_range.target); + double clip_max = std::max(clip_range.origin, clip_range.target); double lower = std::max(data_min, clip_min); double upper = std::min(data_max, clip_max); if (lower > upper) return false; - result = data_range.lower <= data_range.upper ? Range{lower, upper} : Range{upper, lower}; + result = data_range.origin <= data_range.target ? Range{lower, upper} : Range{upper, lower}; return true; } inline double source_index_to_coord(const Range& data_range, int total_count, double index) { - return data_range.lower + data_range.length() * index / static_cast(total_count - 1); + return data_range.origin + data_range.length() * index / static_cast(total_count - 1); } inline double coord_to_source_index(const Range& data_range, int total_count, double coord) { - return static_cast(total_count - 1) * (coord - data_range.lower) / data_range.length(); + return static_cast(total_count - 1) * (coord - data_range.origin) / data_range.length(); } inline Line_Sample_Window line_sample_window(Abs_Axis* axis, const Range& data_range, int total_count, int available_count, bool visible_range_only) { if (total_count < 2 || available_count < 1 || data_range.length() == 0.0) return {}; - Range available_range{data_range.lower, source_index_to_coord(data_range, total_count, available_count - 1)}; + Range available_range{data_range.origin, source_index_to_coord(data_range, total_count, available_count - 1)}; Range sample_range = available_range; if (visible_range_only && !intersect_range(available_range, axis->coord_range(SRC::Render), sample_range)) return {}; - double first_index = coord_to_source_index(data_range, total_count, sample_range.lower); - double last_index = coord_to_source_index(data_range, total_count, sample_range.upper); - double first_pixel = axis->coord_to_pixel(sample_range.lower, SRC::Render); - double last_pixel = axis->coord_to_pixel(sample_range.upper, SRC::Render); + double first_index = coord_to_source_index(data_range, total_count, sample_range.origin); + double last_index = coord_to_source_index(data_range, total_count, sample_range.target); + double first_pixel = axis->coord_to_pixel(sample_range.origin, SRC::Render); + double last_pixel = axis->coord_to_pixel(sample_range.target, SRC::Render); int source_samples = static_cast(std::ceil(std::abs(last_index - first_index))) + 1; int pixel_samples = static_cast(std::ceil(std::abs(last_pixel - first_pixel))) + 1; pixel_samples = std::min(pixel_samples, static_cast(axis->pixel_size(SRC::Render)) + 1); diff --git a/YSGraphic_Core/plottable/Muti_Select_Rect.cpp b/YSGraphic_Core/plottable/Muti_Select_Rect.cpp index 8762827..ff1e94b 100644 --- a/YSGraphic_Core/plottable/Muti_Select_Rect.cpp +++ b/YSGraphic_Core/plottable/Muti_Select_Rect.cpp @@ -16,16 +16,19 @@ Render_State* Muti_Select_Rect::create_render_state() { Input_Data* Muti_Select_Rect::create_input_data() { return MutiSelectRect_Binding::create_input_data(); } -Muti_Select_Rect::Builder::Builder(Abs_Axis* keyAxis, Abs_Axis* value_axis) { - ASSERT(keyAxis->mPlot != nullptr, "Afterglow build error! keyAxis->mPlot == nullptr"); - ASSERT(keyAxis->mPlot == value_axis->mPlot, "Afterglow build error! keyAxis->mPlot != value_axis->mPlot"); - plot = keyAxis->mPlot; +Muti_Select_Rect::Builder::Builder(std::shared_ptr parent, std::shared_ptr keyAxis, std::shared_ptr value_axis) { + ASSERT(parent && parent->mPlot, "Muti_Select_Rect build error! parent invalid"); + ASSERT(keyAxis && value_axis, "Muti_Select_Rect build error! axis == nullptr"); + ASSERT(keyAxis->mPlot != nullptr, "Muti_Select_Rect build error! keyAxis->mPlot == nullptr"); + ASSERT(keyAxis->mPlot == value_axis->mPlot, "Muti_Select_Rect build error! keyAxis->mPlot != value_axis->mPlot"); + ASSERT(parent->mPlot == keyAxis->mPlot, "Muti_Select_Rect build error! parent->mPlot != keyAxis->mPlot"); + this->parent = parent; this->keyAxis = keyAxis; this->value_axis = value_axis; } -Muti_Select_Rect* Muti_Select_Rect::Builder::build() { - auto ret = new Muti_Select_Rect(); - ret->init(plot, layerName); +std::shared_ptr Muti_Select_Rect::Builder::build() { + std::shared_ptr ret(new Muti_Select_Rect()); + ret->init(parent); Muti_Select_Rect_Private* pd = ret->d(); MutiSelectRectRenderState* sc = reinterpret_cast(pd->state(State_Edit)); sc->mHorizontalAxis = keyAxis; @@ -36,13 +39,13 @@ Muti_Select_Rect* Muti_Select_Rect::Builder::build() { sc->mBorderPen = border_pen; return ret; } -void Muti_Select_Rect::set_horizontal_axis(Abs_Axis* axis) { +void Muti_Select_Rect::set_horizontal_axis(std::shared_ptr axis) { Render_Edit_Lease edit(d()); - edit->mHorizontalAxis = axis; + edit->mHorizontalAxis = std::weak_ptr(axis); } -void Muti_Select_Rect::set_vertical_axis(Abs_Axis* axis) { +void Muti_Select_Rect::set_vertical_axis(std::shared_ptr axis) { Render_Edit_Lease edit(d()); - edit->mVerticalAxis = axis; + edit->mVerticalAxis = std::weak_ptr(axis); } QFont Muti_Select_Rect::font(SRC src) { return d()->state_data(src)->mFont; diff --git a/YSGraphic_Core/plottable/Muti_Select_Rect.h b/YSGraphic_Core/plottable/Muti_Select_Rect.h index b0ad33b..14c94c6 100644 --- a/YSGraphic_Core/plottable/Muti_Select_Rect.h +++ b/YSGraphic_Core/plottable/Muti_Select_Rect.h @@ -14,9 +14,9 @@ protected: Muti_Select_Rect(); public: /// 设置水平坐标轴。 - void set_horizontal_axis(Abs_Axis* axis); + void set_horizontal_axis(std::shared_ptr axis); /// 设置垂直坐标轴。 - void set_vertical_axis(Abs_Axis* axis); + void set_vertical_axis(std::shared_ptr axis); /// 返回标注字体。 QFont font(SRC = SRC::Edit); /// 返回标注画笔。 @@ -44,14 +44,13 @@ struct LIB_DECL Muti_Select_Rect::Builder { pen.setStyle(Qt::DashLine); return pen; }()) - SETTER(QString, layerName, "legend") /// 创建多区域框选构造器。 - Builder(Abs_Axis* keyAxis, Abs_Axis* value_axis); + Builder(std::shared_ptr parent, std::shared_ptr keyAxis, std::shared_ptr value_axis); /// 构建并返回多区域框选对象。 - Muti_Select_Rect* build(); + std::shared_ptr build(); protected: - Abs_Axis* keyAxis; - Abs_Axis* value_axis{}; - Plot* plot{}; + std::weak_ptr keyAxis; + std::weak_ptr value_axis{}; + std::shared_ptr parent{}; }; } // namespace YSG diff --git a/YSGraphic_Core/plottable/Muti_Select_Rect_p.h b/YSGraphic_Core/plottable/Muti_Select_Rect_p.h index 54745fb..3da4b22 100644 --- a/YSGraphic_Core/plottable/Muti_Select_Rect_p.h +++ b/YSGraphic_Core/plottable/Muti_Select_Rect_p.h @@ -8,7 +8,7 @@ struct MutiSelectRectRenderState : YSG::Render_State { QFont mFont; QPen mBorderPen, mFontPen = QPen(Qt::white); QBrush mRectBrush = QColor(0, 0, 255, 50); - Abs_Axis *mHorizontalAxis{}, *mVerticalAxis{}; + std::weak_ptr mHorizontalAxis{}, mVerticalAxis{}; MutiSelectRectRenderState() { // NoPen, // SolidLine, @@ -35,6 +35,10 @@ protected: if (event->button() != Qt::LeftButton) return; Render_Edit_Lease edit(this); + auto horizontalAxis = edit->mHorizontalAxis.lock(); + auto verticalAxis = edit->mVerticalAxis.lock(); + if (!horizontalAxis || !verticalAxis) + return; QVector& rectList = edit->mRects; if (!(QApplication::keyboardModifiers() & Qt::ControlModifier)) { rectList.clear(); @@ -48,22 +52,28 @@ protected: } } } - double x = edit->mHorizontalAxis->pixel_to_coord(event->pos().x(), SRC::Render); - double y = edit->mVerticalAxis->pixel_to_coord(event->pos().y(), SRC::Render); + double x = horizontalAxis->pixel_to_coord(event->pos().x(), SRC::Render); + double y = verticalAxis->pixel_to_coord(event->pos().y(), SRC::Render); rectList.append(QRectF(QPointF(x, y), QPointF(x, y))); mActive = true; } void mouseMoveEvent(QMouseEvent* event) override { + if (!mActive) + return; if (!(event->buttons() & Qt::LeftButton)) return; Render_Edit_Lease edit(this); + auto horizontalAxis = edit->mHorizontalAxis.lock(); + auto verticalAxis = edit->mVerticalAxis.lock(); + if (!horizontalAxis || !verticalAxis) + return; QVector& rectList = edit->mRects; if (rectList.empty()) return; rectList.last().setBottomRight(event->pos()); QRectF& r = rectList.last(); - r.setRight(edit->mHorizontalAxis->pixel_to_coord(r.right(), SRC::Render)); - r.setBottom(edit->mVerticalAxis->pixel_to_coord(r.bottom(), SRC::Render)); + r.setRight(horizontalAxis->pixel_to_coord(r.right(), SRC::Render)); + r.setBottom(verticalAxis->pixel_to_coord(r.bottom(), SRC::Render)); } void mouseReleaseEvent(QMouseEvent* event) override { } @@ -79,23 +89,27 @@ protected: } void keyReleaseEvent(QKeyEvent* event) override { } - void draw(QPainter* painter) override { + void draw(QPainter* painter, const Plot_Render_Snapshot&) override { if (!mActive) return; MutiSelectRectRenderState* s = render_state(); + auto horizontalAxis = s->mHorizontalAxis.lock(); + auto verticalAxis = s->mVerticalAxis.lock(); + if (!horizontalAxis || !verticalAxis) + return; QList drawRectList; for (QRectF r : s->mRects) { if (qFuzzyCompare(r.width(), 0.0) || qFuzzyCompare(r.height(), 0.0)) continue; - r.setX(s->mHorizontalAxis->coord_to_pixel(r.x(), SRC::Render)); - r.setY(s->mVerticalAxis->coord_to_pixel(r.y(), SRC::Render)); - r.setRight(s->mHorizontalAxis->coord_to_pixel(r.right(), SRC::Render)); - r.setBottom(s->mVerticalAxis->coord_to_pixel(r.bottom(), SRC::Render)); + r.setX(horizontalAxis->coord_to_pixel(r.x(), SRC::Render)); + r.setY(verticalAxis->coord_to_pixel(r.y(), SRC::Render)); + r.setRight(horizontalAxis->coord_to_pixel(r.right(), SRC::Render)); + r.setBottom(verticalAxis->coord_to_pixel(r.bottom(), SRC::Render)); drawRectList.append(r); } painter->save(); - QFontMetrics xfm(s->mHorizontalAxis->unit_text_font()); - QFontMetrics yfm(s->mVerticalAxis->unit_text_font()); + QFontMetrics xfm(horizontalAxis->unit_text_font()); + QFontMetrics yfm(verticalAxis->unit_text_font()); painter->setPen(s->mBorderPen); painter->setBrush(s->mRectBrush); for (auto& rect : drawRectList) { @@ -109,8 +123,8 @@ protected: QRectF& coordRect = s->mRects[i]; double OffsetX = coordRect.right() - coordRect.left(); double OffsetY = coordRect.bottom() - coordRect.top(); - QString text1 = "OffsetX == " + QString::number(OffsetX) + s->mHorizontalAxis->unit_text(); - QString text2 = "OffsetY == " + QString::number(OffsetY) + s->mVerticalAxis->unit_text(); + QString text1 = "OffsetX == " + QString::number(OffsetX) + horizontalAxis->unit_text(); + QString text2 = "OffsetY == " + QString::number(OffsetY) + verticalAxis->unit_text(); double width1 = xfm.horizontalAdvance(text1) + 32; double width2 = yfm.horizontalAdvance(text2) + 32; double rect1X = rect.x() + rect.width() / 2 - width1 / 2; diff --git a/YSGraphic_Core/plottable/Performance_Shower.cpp b/YSGraphic_Core/plottable/Performance_Shower.cpp index c19cf93..4d911b5 100644 --- a/YSGraphic_Core/plottable/Performance_Shower.cpp +++ b/YSGraphic_Core/plottable/Performance_Shower.cpp @@ -3,7 +3,6 @@ namespace YSG { using PerformanceShower_Binding = Renderable_Binding; Performance_Shower::Performance_Shower() { PerformanceShower_Binding::init(this, "PerformanceShower"); - set_independent_pixel_cache(true); } Performance_Shower_Private* Performance_Shower::d() { return PerformanceShower_Binding::d(this); diff --git a/YSGraphic_Core/plottable/Performance_Shower_p.h b/YSGraphic_Core/plottable/Performance_Shower_p.h index 6086573..59b3cac 100644 --- a/YSGraphic_Core/plottable/Performance_Shower_p.h +++ b/YSGraphic_Core/plottable/Performance_Shower_p.h @@ -191,7 +191,7 @@ struct Performance_Shower_Private : Typed_Render_Dataevents) { @@ -223,7 +223,7 @@ struct Performance_Shower_Private : Typed_Render_DataangleDelta().y() > 0 ? -3 : 3; q()->scroll_view(steps); } - void draw(QPainter* painter) override { + void draw(QPainter* painter, const Plot_Render_Snapshot&) override { if (!q()->enabled() || mGroupItems.isEmpty()) return; auto s = render_state(); @@ -276,6 +276,23 @@ struct Performance_Shower_Private : Typed_Render_Datarestore(); } + QRect pixel_bounds(const QSize& plotSize) override { + if (!q()->enabled() || mGroupItems.isEmpty()) + return {}; + auto s = render_state(); + QFontMetrics fm(s->font); + if (mSidebarWidthDirty) + update_sidebar_width(fm, s); + if (mContentWidthDirty) + update_content_width(fm, s); + int sideLineCount = qMax(mGroupItems.size(), mTargetItems.size()); + int lineCount = qMax(mAllInfo.size(), sideLineCount); + int sidebarWidth = mGroupSidebarWidth + mTargetSidebarWidth; + int width = sidebarWidth + s->columnGap + mContentWidth; + int fullHeight = fm.lineSpacing() * lineCount + s->topMargin + s->bottomMargin; + int height = qMin(fullHeight, plotSize.height()); + return QRect(0, 0, width, height); + } void draw_scrollbar(QPainter* painter, Performance_Shower_Render_State* state, const QRect& rect, int visibleLines) { painter->fillRect(rect, state->scrollbarBackground); double ratio = static_cast(visibleLines) / static_cast(mAllInfo.size()); @@ -355,6 +372,9 @@ struct Performance_Shower_Private : Typed_Render_Data 0) return key.left(inputIndex); + int pixelIndex = key.indexOf(".Pixel."); + if (pixelIndex > 0) + return key.left(pixelIndex); int prepareIndex = key.indexOf(".prepare_data"); if (prepareIndex > 0) return key.left(prepareIndex); @@ -423,6 +443,8 @@ struct Performance_Shower_Private : Typed_Render_Data #include "../Axis/Abs_Axis_p.h" +#include "../primitive/Grid.h" #include "Planisphere_p.h" namespace YSG { using Planisphere_Binding = Renderable_Binding; @@ -18,47 +19,71 @@ Render_State* Planisphere::create_render_state() { Input_Data* Planisphere::create_input_data() { return Planisphere_Binding::create_input_data(); } -Axis* Planisphere::i_axis(SRC src) { - return d()->state_value(&Planisphere_Render_State::i_axis, src); +std::shared_ptr Planisphere::i_axis(SRC src) { + return d()->state_value(&Planisphere_Render_State::i_axis, src).lock(); } -void Planisphere::set_i_axis(Axis* value) { - d()->set_state_value(&Planisphere_Render_State::i_axis, value); +void Planisphere::set_i_axis(std::shared_ptr value) { + d()->set_state_value(&Planisphere_Render_State::i_axis, std::weak_ptr(value)); + if (auto points = mPointSet.lock()) + points->set_x_axis(value); + if (auto anchors = mAnchorSet.lock()) + anchors->set_x_axis(value); + if (auto grid = mGrid.lock()) + grid->set_x_axis(value); } -Axis* Planisphere::q_axis(SRC src) { - return d()->state_value(&Planisphere_Render_State::q_axis, src); +std::shared_ptr Planisphere::q_axis(SRC src) { + return d()->state_value(&Planisphere_Render_State::q_axis, src).lock(); } -void Planisphere::set_q_axis(Axis* value) { - d()->set_state_value(&Planisphere_Render_State::q_axis, value); +void Planisphere::set_q_axis(std::shared_ptr value) { + d()->set_state_value(&Planisphere_Render_State::q_axis, std::weak_ptr(value)); + if (auto points = mPointSet.lock()) + points->set_y_axis(value); + if (auto anchors = mAnchorSet.lock()) + anchors->set_y_axis(value); + if (auto grid = mGrid.lock()) + grid->set_y_axis(value); } Range Planisphere::i_range(SRC src) { return d()->state_value(&Planisphere_Render_State::i_range, src); } void Planisphere::set_i_range(Range value) { + if (value.length() == 0.0) + return; + if (auto grid = mGrid.lock()) + grid->set_x_range(value); d()->set_state_value(&Planisphere_Render_State::i_range, std::move(value)); } Range Planisphere::q_range(SRC src) { return d()->state_value(&Planisphere_Render_State::q_range, src); } void Planisphere::set_q_range(Range value) { + if (value.length() == 0.0) + return; + if (auto grid = mGrid.lock()) + grid->set_y_range(value); d()->set_state_value(&Planisphere_Render_State::q_range, std::move(value)); } QColor Planisphere::point_color(SRC src) { return d()->state_value(&Planisphere_Render_State::point_color, src); } void Planisphere::set_point_color(QColor value) { + if (auto points = mPointSet.lock()) + points->set_brush(QBrush(value)); d()->set_state_value(&Planisphere_Render_State::point_color, std::move(value)); } QColor Planisphere::anchor_color(SRC src) { return d()->state_value(&Planisphere_Render_State::anchor_color, src); } void Planisphere::set_anchor_color(QColor value) { + if (auto anchors = mAnchorSet.lock()) + anchors->set_pen(QPen(value, 2)); d()->set_state_value(&Planisphere_Render_State::anchor_color, std::move(value)); } int Planisphere::continue_millisecond(SRC src) { return d()->state_value(&Planisphere_Render_State::continue_millisecond, src); } void Planisphere::set_continue_millisecond(int value) { - d()->set_state_value(&Planisphere_Render_State::continue_millisecond, value); + d()->set_state_value(&Planisphere_Render_State::continue_millisecond, qMax(1, value)); } Planisphere_Type Planisphere::type(SRC src) { return d()->state_value(&Planisphere_Render_State::type, src); @@ -69,40 +94,65 @@ void Planisphere::set_type(Planisphere_Type value) { void Planisphere::give_data(QPointF pos) { if (!ok()) return; - if (!mPlot->isVisible()) - return; Render_Input_Lease input(d()); input->push(pos); } -Planisphere::Builder::Builder(Axis* i_axis, Axis* q_axis) { - ASSERT(i_axis->mPlot != nullptr, "Afterglow build error! i_axis->mPlot == nullptr"); - ASSERT(i_axis->mPlot == q_axis->mPlot, "Afterglow build error! i_axis->mPlot != q_axis->mPlot"); - plot = i_axis->mPlot; +Planisphere::Builder::Builder(std::shared_ptr parent, std::shared_ptr i_axis, std::shared_ptr q_axis) { + ASSERT(parent, "Planisphere build error! parent == nullptr"); + ASSERT(i_axis && q_axis, "Planisphere build error! axis == nullptr"); + ASSERT(i_axis->mPlot != nullptr, "Planisphere build error! i_axis->mPlot == nullptr"); + ASSERT(i_axis->mPlot == q_axis->mPlot, "Planisphere build error! i_axis->mPlot != q_axis->mPlot"); + ASSERT(parent->mPlot == i_axis->mPlot, "Planisphere build error! parent->mPlot != axis->mPlot"); + this->parent = parent; this->i_axis = i_axis; this->q_axis = q_axis; } -Planisphere* Planisphere::Builder::build() { - auto ret = new Planisphere(); - ret->init(plot, layerName); +std::shared_ptr Planisphere::Builder::build() { + std::shared_ptr ret(new Planisphere()); + ret->init(parent); Planisphere_Private* pd = ret->d(); Planisphere_Render_State* sc = reinterpret_cast(pd->state(State_Edit)); - PROP_R(Axis*, i_axis) - PROP_R(Axis*, q_axis) + PROP_R(std::weak_ptr, i_axis) + PROP_R(std::weak_ptr, q_axis) PROP_R(Range, i_range) PROP_R(Range, q_range) PROP_R(QColor, point_color) PROP_R(QColor, anchor_color) PROP_R(int, continue_millisecond) PROP_R(Planisphere_Type, type) + if (sc->i_range.length() == 0.0) + sc->i_range = {0.0, 1.0}; + if (sc->q_range.length() == 0.0) + sc->q_range = {0.0, 1.0}; + sc->continue_millisecond = qMax(1, sc->continue_millisecond); + auto iAxis = this->i_axis.lock(); + auto qAxis = this->q_axis.lock(); + if (iAxis && qAxis) { + auto grid = Grid::Builder(ret, iAxis, qAxis).set_line_mode(Grid_Line_Mode::Range_Division).set_x_range(sc->i_range).set_y_range(sc->q_range).set_x_divisions(4).set_y_divisions(4).set_x_pen(QPen(Qt::white, 1, Qt::DotLine)).set_y_pen(QPen(Qt::white, 1, Qt::DotLine)).build(); + auto points = Point_Set::Builder(ret, iAxis, qAxis).set_size(4.0).set_pen(QPen(Qt::NoPen)).set_brush(QBrush(sc->point_color)).set_shape(Point_Set_Shape::Circle).set_antialias(true).build(); + auto anchors = Point_Set::Builder(ret, iAxis, qAxis).set_size(12.0).set_pen(QPen(sc->anchor_color, 2)).set_brush(QBrush(Qt::NoBrush)).set_shape(Point_Set_Shape::Cross).set_antialias(true).build(); + grid->mObjectName = "PlanisphereGrid"; + points->mObjectName = "PlanispherePoints"; + anchors->mObjectName = "PlanisphereAnchors"; + ret->mGrid = grid; + ret->mPointSet = points; + ret->mAnchorSet = anchors; + } return ret; } void Planisphere::set_to_axis_center() { Planisphere_Render_State* s = reinterpret_cast(d()->state(State_Edit)); - double w = mPlot->width(), h = mPlot->height(); + auto iAxis = s->i_axis.lock(); + auto qAxis = s->q_axis.lock(); + if (!iAxis || !qAxis) + return; + double w = iAxis->pixel_size(), h = qAxis->pixel_size(); + if (w <= 0.0 || h <= 0.0) + return; double l = w < h ? w : h; - double iL = s->i_axis->coord_range().size() / w * l / 2; - double qL = s->q_axis->coord_range().size() / h * l / 2; - double im = s->i_axis->coord_range().middle(), qm = s->q_axis->coord_range().middle(); + double iL = iAxis->coord_range().size() / w * l / 2; + double qL = qAxis->coord_range().size() / h * l / 2; + double im = iAxis->coord_range().middle(), qm = qAxis->coord_range().middle(); set_i_range({im - iL, im + iL}); set_q_range({qm - qL, qm + qL}); } diff --git a/YSGraphic_Core/plottable/Planisphere.h b/YSGraphic_Core/plottable/Planisphere.h index 03ff535..f4da612 100644 --- a/YSGraphic_Core/plottable/Planisphere.h +++ b/YSGraphic_Core/plottable/Planisphere.h @@ -1,5 +1,6 @@ #pragma once #include "global.h" +#include "../primitive/Point_Set.h" namespace YSG { /// 星座图调制类型。 enum class Planisphere_Type { @@ -7,6 +8,7 @@ enum class Planisphere_Type { Psk8 = 8 }; struct Planisphere_Private; +class Grid; /// I/Q 星座图元。 class LIB_DECL Planisphere : public Renderable { public: @@ -20,8 +22,8 @@ protected: public: /// 写入一个 I/Q 点。 void give_data(QPointF pos); - PROP(Axis*, i_axis) - PROP(Axis*, q_axis) + PROP(std::shared_ptr, i_axis) + PROP(std::shared_ptr, q_axis) PROP(Range, i_range) PROP(Range, q_range) PROP(QColor, point_color) @@ -31,10 +33,15 @@ public: /// 设置坐标轴到星座图中心范围。 void set_to_axis_center(); struct Builder; +protected: + friend struct Planisphere_Private; + std::weak_ptr mGrid; + std::weak_ptr mPointSet; + std::weak_ptr mAnchorSet; }; struct Planisphere_Prop { - Axis* i_axis{}; - Axis* q_axis{}; + std::weak_ptr i_axis{}; + std::weak_ptr q_axis{}; Range i_range{0, 100}; Range q_range{0, 100}; QColor point_color = Qt::red; @@ -43,20 +50,20 @@ struct Planisphere_Prop { Planisphere_Type type = Planisphere_Type::Psk8; }; struct LIB_DECL Planisphere::Builder : Planisphere_Prop { - PROP_B(Axis*, i_axis) - PROP_B(Axis*, q_axis) + PROP_B(std::shared_ptr, parent) + PROP_B(std::shared_ptr, i_axis) + PROP_B(std::shared_ptr, q_axis) PROP_B(Range, i_range) PROP_B(Range, q_range) PROP_B(QColor, point_color) PROP_B(QColor, anchor_color) PROP_B(int, continue_millisecond) PROP_B(Planisphere_Type, type) - SETTER(QString, layerName, "plottable") /// 创建星座图构造器。 - Builder(Axis* i_axis, Axis* q_axis); + Builder(std::shared_ptr parent, std::shared_ptr i_axis, std::shared_ptr q_axis); /// 构建并返回星座图对象。 - Planisphere* build(); + std::shared_ptr build(); protected: - Plot* plot{}; + std::shared_ptr parent{}; }; } // namespace YSG diff --git a/YSGraphic_Core/plottable/Planisphere_p.h b/YSGraphic_Core/plottable/Planisphere_p.h index 006585b..021b0cf 100644 --- a/YSGraphic_Core/plottable/Planisphere_p.h +++ b/YSGraphic_Core/plottable/Planisphere_p.h @@ -12,6 +12,7 @@ struct PlanisphereData { int maxTotalTimes{}; }; struct Planisphere_Input_Data : Input_Data { + static constexpr std::size_t Max_Pending_Point_Count = 128; struct Packet { double x{}; double y{}; @@ -19,7 +20,7 @@ struct Planisphere_Input_Data : Input_Data { Bounded_Input_Buffer mDataList; void push(QPointF pos) { Packet packet{pos.x(), pos.y()}; - mDataList.push(&packet, sizeof(packet)); + mDataList.push(&packet, sizeof(packet), Max_Pending_Point_Count); } template void read_all(Handler handler) { @@ -39,6 +40,15 @@ struct Planisphere_Input_Data : Input_Data { std::uint64_t dropped_count() const override { return mDataList.dropped_count(); } + std::uint64_t capacity_dropped_count() const override { + return mDataList.capacity_dropped_count(); + } + std::uint64_t rebuild_dropped_count() const override { + return mDataList.rebuild_dropped_count(); + } + std::uint64_t consumer_dropped_count() const override { + return mDataList.consumer_dropped_count(); + } std::uint64_t pushed_count() const override { return mDataList.pushed_count(); } @@ -47,38 +57,24 @@ struct Planisphere_Render_State : Render_State, Planisphere_Prop {}; struct Planisphere_Private : Typed_Render_Data { friend class Planisphere; std::list dataList; - QVector xs, ys; - QVector fixedPoints; - void update_fixed_points() { - Planisphere_Render_State* s = render_state(); - fixedPoints.clear(); + QVector anchor_points(const Planisphere_Render_State* s) const { + QVector points; if (s->type == Planisphere_Type::Psk8) { - double xS = s->i_axis->coord_to_pixel(s->i_range.lower, SRC::Render); - double yS = s->q_axis->coord_to_pixel(s->q_range.lower, SRC::Render); - double xL = s->i_axis->coord_to_pixel(s->i_range.upper, SRC::Render) - xS; - double yL = s->q_axis->coord_to_pixel(s->q_range.upper, SRC::Render) - yS; - double xSpace = xL / 6.0; - double ySpace = yL / 6.0; - double xLineSpace = xL / 4.0; - double yLineSpace = yL / 4.0; - fixedPoints.push_back({xS + 2 * xSpace, yS + 1 * ySpace}); - fixedPoints.push_back({xS + 1 * xSpace, yS + 2 * ySpace}); - fixedPoints.push_back({xS + 1 * xSpace, yS + 4 * ySpace}); - fixedPoints.push_back({xS + 2 * xSpace, yS + 5 * ySpace}); - fixedPoints.push_back({xS + 4 * xSpace, yS + 5 * ySpace}); - fixedPoints.push_back({xS + 5 * xSpace, yS + 4 * ySpace}); - fixedPoints.push_back({xS + 5 * xSpace, yS + 2 * ySpace}); - fixedPoints.push_back({xS + 4 * xSpace, yS + 1 * ySpace}); - xs.resize(5); - ys.resize(5); - double curX = xS, curY = yS; - for (int i = 0; i < 5; ++i) { - xs[i] = curX; - curX += xLineSpace; - ys[i] = curY; - curY += yLineSpace; - } + double xSpace = s->i_range.length() / 6.0; + double ySpace = s->q_range.length() / 6.0; + double xS = s->i_range.origin; + double yS = s->q_range.origin; + points.reserve(8); + points.push_back({xS + 2 * xSpace, yS + 1 * ySpace}); + points.push_back({xS + 1 * xSpace, yS + 2 * ySpace}); + points.push_back({xS + 1 * xSpace, yS + 4 * ySpace}); + points.push_back({xS + 2 * xSpace, yS + 5 * ySpace}); + points.push_back({xS + 4 * xSpace, yS + 5 * ySpace}); + points.push_back({xS + 5 * xSpace, yS + 4 * ySpace}); + points.push_back({xS + 5 * xSpace, yS + 2 * ySpace}); + points.push_back({xS + 4 * xSpace, yS + 1 * ySpace}); } + return points; } void refresh() { for (auto it = dataList.begin(); it != dataList.end();) { @@ -88,11 +84,11 @@ struct Planisphere_Private : Typed_Render_Datacontinue_millisecond * q()->mPlot->d->mRefreshTimesPreSecond) / 1000); + int times = qMax(5, (s->continue_millisecond * snapshot.refreshTimesPerSecond) / 1000); d->read_all([this, times](const QPointF& pos) { PlanisphereData data; data.mPos = pos; @@ -102,44 +98,23 @@ struct Planisphere_Private : Typed_Render_Data points; + QVector colors; + points.reserve(static_cast(dataList.size())); + colors.reserve(static_cast(dataList.size())); + for (const PlanisphereData& data : dataList) { + QColor color = s->point_color; + color.setAlphaF(color.alphaF() * data.mRemainTimes / data.maxTotalTimes); + points.append(data.mPos); + colors.append(color); + } + if (auto pointSet = q()->mPointSet.lock()) + pointSet->give_colored_points(points, colors, false); + if (auto anchorSet = q()->mAnchorSet.lock()) + anchorSet->give_points(anchor_points(s), false); } - void draw(QPainter* painter) override { - update_fixed_points(); - Planisphere_Render_State* s = render_state(); - { - double xS = xs.first(), xE = xs.last(); - double yS = ys.first(), yE = ys.last(); - QPen pen; - pen.setStyle(Qt::DotLine); - pen.setColor(Qt::white); - painter->setPen(pen); - for (double x : xs) - painter->drawLine(QPointF(x, yS), QPointF(x, yE)); - for (double y : ys) - painter->drawLine(QPointF(xS, y), QPointF(xE, y)); - } - { - painter->setPen(Qt::NoPen); - for (PlanisphereData& data : dataList) { - QColor c = s->point_color; - c.setAlphaF(c.alphaF() * data.mRemainTimes / data.maxTotalTimes); - painter->setBrush(c); - double centerX = s->i_axis->coord_to_pixel(data.mPos.x(), SRC::Render); - double centerY = s->q_axis->coord_to_pixel(data.mPos.y(), SRC::Render); - double radius = 2; - painter->drawEllipse({centerX - radius, centerY - radius}, 2 * radius, 2 * radius); - } - } - { - painter->setPen(QPen(s->anchor_color, 2)); - painter->setBrush(Qt::NoBrush); - QVector& fixedPoints = this->fixedPoints; - for (auto& point : fixedPoints) { - int radius = 6; - painter->drawLine(QPointF{point.x() - radius, point.y()}, {point.x() + radius, point.y()}); - painter->drawLine(QPointF{point.x(), point.y() - radius}, {point.x(), point.y() + radius}); - } - } + void draw(QPainter* painter, const Plot_Render_Snapshot&) override { + Q_UNUSED(painter) } void plot_event(QEvent* event) override { if (event->type() == QEvent::Resize) { diff --git a/YSGraphic_Core/plottable/Spectrum.cpp b/YSGraphic_Core/plottable/Spectrum.cpp index 579b257..f14405a 100644 --- a/YSGraphic_Core/plottable/Spectrum.cpp +++ b/YSGraphic_Core/plottable/Spectrum.cpp @@ -1,5 +1,19 @@ +#include +#include +#include "../primitive/Curve.h" +#include "../primitive/Label.h" +#include "../primitive/Marker.h" #include "Spectrum_p.h" namespace YSG { +static Curve_Sample_Mode spectrum_curve_sample_mode(Spectrum_Sample_Mode mode) { + switch (mode) { + case Spectrum_Sample_Mode::Source: + return Curve_Sample_Mode::Source; + case Spectrum_Sample_Mode::Pixel_Peak_Bucket: + return Curve_Sample_Mode::Pixel_Peak_Bucket; + } + return Curve_Sample_Mode::Source; +} using Spectrum_Binding = Renderable_Binding; Spectrum::Spectrum() { Spectrum_Binding::init(this, "Spectrum"); @@ -16,17 +30,37 @@ Render_State* Spectrum::create_render_state() { Input_Data* Spectrum::create_input_data() { return Spectrum_Binding::create_input_data(); } -Frequent_Axis* Spectrum::frequent_axis(SRC src) { - return d()->state_value(&Spectrum_Render_State::frequent_axis, src); +std::shared_ptr Spectrum::frequent_axis(SRC src) { + return d()->state_value(&Spectrum_Render_State::frequent_axis, src).lock(); } -void Spectrum::set_frequent_axis(Frequent_Axis* value) { - d()->set_state_value(&Spectrum_Render_State::frequent_axis, value); +void Spectrum::set_frequent_axis(std::shared_ptr value) { + d()->set_state_value(&Spectrum_Render_State::frequent_axis, std::weak_ptr(value)); + if (auto curve = mCurCurve.lock()) + curve->set_x_axis(value); + if (auto curve = mMaxCurve.lock()) + curve->set_x_axis(value); + if (auto curve = mMinCurve.lock()) + curve->set_x_axis(value); + if (auto item = mMarkerItem.lock()) + item->set_x_axis(value); + if (auto item = mLabelItem.lock()) + item->set_x_axis(value); } -Axis* Spectrum::power_axis(SRC src) { - return d()->state_value(&Spectrum_Render_State::power_axis, src); +std::shared_ptr Spectrum::power_axis(SRC src) { + return d()->state_value(&Spectrum_Render_State::power_axis, src).lock(); } -void Spectrum::set_power_axis(Axis* value) { - d()->set_state_value(&Spectrum_Render_State::power_axis, value); +void Spectrum::set_power_axis(std::shared_ptr value) { + d()->set_state_value(&Spectrum_Render_State::power_axis, std::weak_ptr(value)); + if (auto curve = mCurCurve.lock()) + curve->set_y_axis(value); + if (auto curve = mMaxCurve.lock()) + curve->set_y_axis(value); + if (auto curve = mMinCurve.lock()) + curve->set_y_axis(value); + if (auto item = mMarkerItem.lock()) + item->set_y_axis(value); + if (auto item = mLabelItem.lock()) + item->set_y_axis(value); } int Spectrum::frequent_point_size(SRC src) { return d()->state_value(&Spectrum_Render_State::frequent_point_size, src); @@ -44,6 +78,8 @@ Range Spectrum::sweep_frequent_range(SRC src) { return d()->state_value(&Spectrum_Render_State::sweep_frequent_range, src); } void Spectrum::set_sweep_frequent_range(Range value) { + if (value.length() == 0.0) + return; d()->set_state_value(&Spectrum_Render_State::sweep_frequent_range, std::move(value)); } bool Spectrum::use_max_line(SRC src) { @@ -51,12 +87,16 @@ bool Spectrum::use_max_line(SRC src) { } void Spectrum::set_use_max_line(bool value) { d()->set_state_value(&Spectrum_Render_State::use_max_line, value); + if (auto curve = mMaxCurve.lock()) + curve->mVisable = value; } bool Spectrum::use_min_line(SRC src) { return d()->state_value(&Spectrum_Render_State::use_min_line, src); } void Spectrum::set_use_min_line(bool value) { d()->set_state_value(&Spectrum_Render_State::use_min_line, value); + if (auto curve = mMinCurve.lock()) + curve->mVisable = value; } bool Spectrum::use_max_marker(SRC src) { return d()->state_value(&Spectrum_Render_State::use_max_marker, src); @@ -81,47 +121,84 @@ bool Spectrum::visible_range_only(SRC src) { } void Spectrum::set_visible_range_only(bool value) { d()->set_state_value(&Spectrum_Render_State::visible_range_only, value); + if (auto curve = mCurCurve.lock()) + curve->set_visible_range_only(value); + if (auto curve = mMaxCurve.lock()) + curve->set_visible_range_only(value); + if (auto curve = mMinCurve.lock()) + curve->set_visible_range_only(value); } Line_Interpolation_Mode Spectrum::interpolation_mode(SRC src) { return d()->state_value(&Spectrum_Render_State::interpolation_mode, src); } void Spectrum::set_interpolation_mode(Line_Interpolation_Mode value) { d()->set_state_value(&Spectrum_Render_State::interpolation_mode, value); + if (auto curve = mCurCurve.lock()) + curve->set_interpolation_mode(value); + if (auto curve = mMaxCurve.lock()) + curve->set_interpolation_mode(value); + if (auto curve = mMinCurve.lock()) + curve->set_interpolation_mode(value); +} +Spectrum_Sample_Mode Spectrum::sample_mode(SRC src) { + return d()->state_value(&Spectrum_Render_State::sample_mode, src); +} +void Spectrum::set_sample_mode(Spectrum_Sample_Mode value) { + d()->set_state_value(&Spectrum_Render_State::sample_mode, value); + Curve_Sample_Mode curveMode = spectrum_curve_sample_mode(value); + if (auto curve = mCurCurve.lock()) + curve->set_sample_mode(curveMode); + if (auto curve = mMaxCurve.lock()) + curve->set_sample_mode(curveMode); + if (auto curve = mMinCurve.lock()) + curve->set_sample_mode(curveMode); } QBrush Spectrum::max_brush(SRC src) { return d()->state_value(&Spectrum_Render_State::max_brush, src); } void Spectrum::set_max_brush(QBrush value) { + if (auto curve = mMaxCurve.lock()) + curve->set_fill_brush(value); d()->set_state_value(&Spectrum_Render_State::max_brush, std::move(value)); } QBrush Spectrum::cur_brush(SRC src) { return d()->state_value(&Spectrum_Render_State::cur_brush, src); } void Spectrum::set_cur_brush(QBrush value) { + if (auto curve = mCurCurve.lock()) + curve->set_fill_brush(value); d()->set_state_value(&Spectrum_Render_State::cur_brush, std::move(value)); } QBrush Spectrum::min_brush(SRC src) { return d()->state_value(&Spectrum_Render_State::min_brush, src); } void Spectrum::set_min_brush(QBrush value) { + if (auto curve = mMinCurve.lock()) + curve->set_fill_brush(value); d()->set_state_value(&Spectrum_Render_State::min_brush, std::move(value)); } QPen Spectrum::max_pen(SRC src) { return d()->state_value(&Spectrum_Render_State::max_pen, src); } void Spectrum::set_max_pen(QPen value) { + if (auto curve = mMaxCurve.lock()) + curve->set_pen(value); d()->set_state_value(&Spectrum_Render_State::max_pen, std::move(value)); } QPen Spectrum::cur_pen(SRC src) { return d()->state_value(&Spectrum_Render_State::cur_pen, src); } void Spectrum::set_cur_pen(QPen value) { + if (auto curve = mCurCurve.lock()) + curve->set_pen(value); d()->set_state_value(&Spectrum_Render_State::cur_pen, std::move(value)); } QPen Spectrum::min_pen(SRC src) { return d()->state_value(&Spectrum_Render_State::min_pen, src); } void Spectrum::set_min_pen(QPen value) { + if (auto curve = mMinCurve.lock()) + curve->set_pen(value); d()->set_state_value(&Spectrum_Render_State::min_pen, std::move(value)); } QPen Spectrum::select_marker_pen(SRC src) { @@ -150,26 +227,37 @@ void Spectrum::set_sweep_rect_brush(QBrush value) { } void Spectrum::set_frequent_point_size(int frequent_point_size) { Render_Edit_Lease edit(d()); - edit->frequent_point_size = frequent_point_size; + edit->frequent_point_size = qMax(1, frequent_point_size); } void Spectrum::set_frequent_range(Range frequent_range) { + if (frequent_range.length() == 0.0) + return; Render_Edit_Lease edit(d()); edit->frequent_range = frequent_range; + if (auto curve = mCurCurve.lock()) + curve->set_data_range(frequent_range); + if (auto curve = mMaxCurve.lock()) + curve->set_data_range(frequent_range); + if (auto curve = mMinCurve.lock()) + curve->set_data_range(frequent_range); } -Spectrum::Builder::Builder(Frequent_Axis* frequent_axis, Axis* power_axis) { - ASSERT(frequent_axis->mPlot != nullptr, "Afterglow build error! frequent_axis->mPlot == nullptr"); - ASSERT(frequent_axis->mPlot == power_axis->mPlot, "Afterglow build error! frequent_axis->mPlot != power_axis->mPlot"); - plot = frequent_axis->mPlot; +Spectrum::Builder::Builder(std::shared_ptr parent, std::shared_ptr frequent_axis, std::shared_ptr power_axis) { + ASSERT(parent, "Spectrum build error! parent == nullptr"); + ASSERT(frequent_axis && power_axis, "Spectrum build error! axis == nullptr"); + ASSERT(frequent_axis->mPlot != nullptr, "Spectrum build error! frequent_axis->mPlot == nullptr"); + ASSERT(frequent_axis->mPlot == power_axis->mPlot, "Spectrum build error! frequent_axis->mPlot != power_axis->mPlot"); + ASSERT(parent->mPlot == frequent_axis->mPlot, "Spectrum build error! parent->mPlot != axis->mPlot"); + this->parent = parent; this->frequent_axis = frequent_axis; this->power_axis = power_axis; } -Spectrum* Spectrum::Builder::build() { - auto ret = new Spectrum; - ret->init(plot, layerName); +std::shared_ptr Spectrum::Builder::build() { + std::shared_ptr ret(new Spectrum); + ret->init(parent); Spectrum_Private* pd = ret->d(); Spectrum_Render_State* sc = reinterpret_cast(pd->state(State_Edit)); - PROP_R(FrequentAxis*, frequent_axis) - PROP_R(Axis*, power_axis); + PROP_R(std::weak_ptr, frequent_axis) + PROP_R(std::weak_ptr, power_axis); PROP_R(int, frequent_point_size); PROP_R(Range, frequent_range); PROP_R(double, middle_sweep_frequent); @@ -181,6 +269,7 @@ Spectrum* Spectrum::Builder::build() { PROP_R(bool, use_sweep_frequent_rect); PROP_R(bool, visible_range_only); PROP_R(Line_Interpolation_Mode, interpolation_mode); + PROP_R(Spectrum_Sample_Mode, sample_mode); PROP_R(QBrush, max_brush); PROP_R(QBrush, cur_brush); PROP_R(QBrush, min_brush); @@ -191,11 +280,47 @@ Spectrum* Spectrum::Builder::build() { PROP_R(QPen, marker_pen); PROP_R(QPen, middle_frequent_pen); PROP_R(QBrush, sweep_rect_brush); + sc->frequent_point_size = qMax(1, sc->frequent_point_size); + if (sc->frequent_range.length() == 0.0) + sc->frequent_range = {0.0, 1.0}; + if (sc->sweep_frequent_range.length() == 0.0) + sc->sweep_frequent_range = {0.0, 1.0}; + ret->set_child_render_order(Renderable_Child_Render_Order::Children_Before_Self); + auto frequentAxis = this->frequent_axis.lock(); + auto powerAxis = this->power_axis.lock(); + if (frequentAxis && powerAxis) { + Curve_Sample_Mode curveSampleMode = spectrum_curve_sample_mode(sc->sample_mode); + auto maxCurve = Curve::Builder(ret, frequentAxis, powerAxis).set_data_range(sc->frequent_range).set_pen(sc->max_pen).set_fill_brush(sc->max_brush).set_visible_range_only(sc->visible_range_only).set_interpolation_mode(sc->interpolation_mode).set_sample_mode(curveSampleMode).build(); + auto curCurve = Curve::Builder(ret, frequentAxis, powerAxis).set_data_range(sc->frequent_range).set_pen(sc->cur_pen).set_fill_brush(sc->cur_brush).set_visible_range_only(sc->visible_range_only).set_interpolation_mode(sc->interpolation_mode).set_sample_mode(curveSampleMode).build(); + auto minCurve = Curve::Builder(ret, frequentAxis, powerAxis).set_data_range(sc->frequent_range).set_pen(sc->min_pen).set_fill_brush(sc->min_brush).set_visible_range_only(sc->visible_range_only).set_interpolation_mode(sc->interpolation_mode).set_sample_mode(curveSampleMode).build(); + auto markerItem = Marker::Builder(ret, frequentAxis, powerAxis).build(); + auto labelItem = Label::Builder(ret, frequentAxis, powerAxis).build(); + maxCurve->mObjectName = "SpectrumMaxCurve"; + curCurve->mObjectName = "SpectrumCurCurve"; + minCurve->mObjectName = "SpectrumMinCurve"; + markerItem->mObjectName = "SpectrumMarker"; + labelItem->mObjectName = "SpectrumLabel"; + maxCurve->mVisable = sc->use_max_line; + minCurve->mVisable = sc->use_min_line; + ret->mMaxCurve = maxCurve; + ret->mCurCurve = curCurve; + ret->mMinCurve = minCurve; + ret->mMarkerItem = markerItem; + ret->mLabelItem = labelItem; + } return ret; } void Spectrum_Private::update_frequent_point_size(int frequent_point_size, Range frequent_range) { - if (frequent_point_size == 0) + if (frequent_point_size <= 0) { + cacheFrequentPointSize = frequent_point_size; + cacheFrequentRange = frequent_range; + hasPowerData = false; + maxPowers.clear(); + curPowers.clear(); + minPowers.clear(); + frequents.clear(); return; + } cacheFrequentPointSize = frequent_point_size; cacheFrequentRange = frequent_range; hasPowerData = false; @@ -204,22 +329,24 @@ void Spectrum_Private::update_frequent_point_size(int frequent_point_size, Range minPowers.resize(frequent_point_size); frequents.resize(frequent_point_size); if (frequent_point_size == 1) { - frequents[0] = frequent_range.lower; + frequents[0] = frequent_range.origin; return; } - double cur = frequent_range.lower; + double cur = frequent_range.origin; double step = frequent_range.length() / (frequent_point_size - 1); - frequents[0] = frequent_range.lower; + frequents[0] = frequent_range.origin; for (int i = 1; i < frequent_point_size; ++i) { cur += step; frequents[i] = cur; } } -void Spectrum_Private::set_frequent_data(const QVector& lineData, const Spectrum_Render_State* s) { - if (lineData.size() != s->frequent_point_size) { - qDebug() << "setFrenquentData 数据大小不对 " << lineData.size() << " mFrequentPointSize == " << s->frequent_point_size; +void Spectrum_Private::set_frequent_data(const QVector& lineData, const QVector* maxData, const QVector* minData, const Spectrum_Render_State* s) { + if (s->frequent_point_size <= 0) + return; + if (lineData.size() != s->frequent_point_size) + return; + if ((maxData && maxData->size() != s->frequent_point_size) || (minData && minData->size() != s->frequent_point_size)) return; - } curPowers = lineData; curPowers.detach(); if (!hasPowerData) { @@ -234,11 +361,15 @@ void Spectrum_Private::set_frequent_data(const QVector& lineData, const int minIndex = 0, maxIndex = 0; for (int i = 0; i < s->frequent_point_size; ++i) { double& curData = curPowers[i]; - if (s->use_max_line && curData > maxPowers.at(i)) { - maxPowers[i] = curData; + if (s->use_max_line) { + double holdMax = maxData ? maxData->at(i) : curData; + if (holdMax > maxPowers.at(i)) + maxPowers[i] = holdMax; } - if (s->use_min_line && curData < minPowers.at(i)) { - minPowers[i] = curData; + if (s->use_min_line) { + double holdMin = minData ? minData->at(i) : curData; + if (holdMin < minPowers.at(i)) + minPowers[i] = holdMin; } if (s->use_max_marker && curData > max) { max = curData; @@ -265,59 +396,73 @@ double Spectrum_Private::get_y(double x, bool& ok, Line_Interpolation_Mode mode) return curPowers.at(i); }); } -void Spectrum_Private::draw_line(QPainter* painter, const Spectrum_Render_State* s, const QVector& ys, const QPen& pen, const QBrush& brush) { - if (frequents.size() < 2 || ys.size() != frequents.size()) +void Spectrum_Private::update_marker_items(const Spectrum_Render_State* s) { + auto frequentAxis = s->frequent_axis.lock(); + auto powerAxis = s->power_axis.lock(); + QVector markerItems; + QVector labelItems; + if (!frequentAxis || !powerAxis) { + if (auto markerItem = q()->mMarkerItem.lock()) + markerItem->give_items(markerItems, false); + if (auto labelItem = q()->mLabelItem.lock()) + labelItem->give_items(labelItems, false); return; - QVector line_points = create_line_points(s->frequent_axis, s->power_axis, s->frequent_range, frequents.size(), frequents.size(), s->visible_range_only, s->interpolation_mode, [&ys](int i) { - return ys.at(i); - }); - if (line_points.empty()) - return; - QVector fill_points(line_points.size() + 2); - double upperBoundPixel = s->power_axis->coord_to_pixel(s->power_axis->coord_range().upper, SRC::Render); - fill_points.first() = QPointF(line_points.first().x(), upperBoundPixel); - fill_points.last() = QPointF(line_points.last().x(), upperBoundPixel); - for (int i = 0; i < line_points.size(); ++i) { - fill_points[i + 1] = line_points.at(i); } - painter->setPen(pen); - if (pen.style() != Qt::NoPen) - painter->drawPolyline(line_points.data(), line_points.size()); - painter->setBrush(brush); - if (brush.style() != Qt::NoBrush) - painter->drawPolygon(fill_points.data(), fill_points.size()); + auto appendMarker = [&](double frequent, double power, bool drawLine, bool selected) { + QPen pen = selected ? s->select_marker_pen : s->marker_pen; + Marker_Item marker; + marker.position = QPointF(frequent, power); + marker.radius = 2.0; + marker.pen = pen; + marker.mode = drawLine ? Marker_Mode::Vertical_Line : Marker_Mode::Point; + markerItems.append(marker); + QString frequentText = QString::number(frequent, 'f', 2) + frequentAxis->unit_text(); + QString powerText = QString::number(power, 'f', 2) + powerAxis->unit_text(); + Label_Item frequentLabel; + frequentLabel.position = marker.position; + frequentLabel.offset = QPointF(0.0, -28.0); + frequentLabel.text = frequentText; + frequentLabel.alignment = Qt::AlignHCenter | Qt::AlignBottom; + frequentLabel.text_pen = pen; + labelItems.append(frequentLabel); + Label_Item powerLabel = frequentLabel; + powerLabel.offset = QPointF(0.0, -14.0); + powerLabel.text = powerText; + labelItems.append(powerLabel); + }; + if (s->use_max_marker) + appendMarker(maxMarkerKey, maxMarkerValue, false, false); + if (s->use_min_marker) + appendMarker(minMarkerKey, minMarkerValue, false, false); + for (int i = 0; i < s->markerList.size(); ++i) { + const Spectrum_Marker& customMarker = s->markerList.at(i); + bool ok; + double value = get_y(customMarker.frequent, ok, s->interpolation_mode); + if (ok) + appendMarker(customMarker.frequent, value, customMarker.drawLine, i == s->curSelectIndex); + } + if (auto markerItem = q()->mMarkerItem.lock()) + markerItem->give_items(markerItems, false); + if (auto labelItem = q()->mLabelItem.lock()) + labelItem->give_items(labelItems, false); } void Spectrum_Private::draw_spectrum(QPainter* painter, Spectrum_Render_State* s) { - if (hasPowerData) { - if (s->use_max_line) - draw_line(painter, s, maxPowers, s->max_pen, s->max_brush); - draw_line(painter, s, curPowers, s->cur_pen, s->cur_brush); - if (s->use_min_line) - draw_line(painter, s, minPowers, s->min_pen, s->min_brush); - if (s->use_max_marker) - s->draw_marker(painter, maxMarkerKey, maxMarkerValue, false, false); - if (s->use_min_marker) - s->draw_marker(painter, minMarkerKey, minMarkerValue, false, false); - for (Marker& customMarker : s->markerList) { - bool ok; - double value = get_y(customMarker.frequent, ok, s->interpolation_mode); - if (ok) - s->draw_marker(painter, customMarker.frequent, value, customMarker.drawLine, s->markerList.indexOf(customMarker) == s->curSelectIndex); - } - } if (s->use_sweep_frequent_rect) { s->draw_axis_rect(painter, s->sweep_frequent_range, s->middle_sweep_frequent); } } -void Spectrum_Private::draw(QPainter* painter) { +void Spectrum_Private::draw(QPainter* painter, const Plot_Render_Snapshot& snapshot) { auto s = render_state(); - Performance_Shower* shower = q()->mPlot->d->mShower; + Performance_Shower* shower = snapshot.performanceShower.get(); if (shower) { shower->set_performance_line("mFrequentPointSize", Performance_Line(QString("mFrequentPointSize:%1").arg(s->frequent_point_size))); } draw_spectrum(painter, s); if (q()->hover_ok(q())) { - q()->draw_hover(painter, s->frequent_axis, s->power_axis); + auto frequentAxis = s->frequent_axis.lock(); + auto powerAxis = s->power_axis.lock(); + if (frequentAxis && powerAxis) + q()->draw_hover(painter, frequentAxis.get(), powerAxis.get()); } } void Spectrum::give_data(const QVector& lineData) { @@ -326,24 +471,22 @@ void Spectrum::give_data(const QVector& lineData) { Spectrum_Private* pd = d(); Render_State_Lease state(pd); int frequent_point_size = state->frequent_point_size; - if (lineData.size() != frequent_point_size) { - qDebug() << "setFrenquentData 数据大小不对 " << lineData.size() << " mFrequentPointSize == " << frequent_point_size; + if (lineData.size() != frequent_point_size) return; - } Render_Input_Lease input(pd); - input->mDataList.push(lineData); + input->push(lineData, state->use_max_line, state->use_min_line); } void Spectrum::add_custom_marker(double frequent) { Render_Edit_Lease edit(d()); - edit->markerList.append(Marker{frequent, false}); + edit->markerList.append(Spectrum_Marker{frequent, false}); } void Spectrum::add_custom_line_marker(double frequent) { Render_Edit_Lease edit(d()); - edit->markerList.append(Marker{frequent, true}); + edit->markerList.append(Spectrum_Marker{frequent, true}); } void Spectrum::remove_custom_marker(double frequent) { Render_Edit_Lease edit(d()); - for (Marker& marker : edit->markerList) { + for (Spectrum_Marker& marker : edit->markerList) { if (qFuzzyCompare(marker.frequent, frequent)) { edit->markerList.removeOne(marker); break; @@ -353,7 +496,10 @@ void Spectrum::remove_custom_marker(double frequent) { void Spectrum::remove_current_marker(SRC src) { Q_UNUSED(src) Render_Edit_Lease edit(d()); + if (edit->curSelectIndex < 0 || edit->curSelectIndex >= edit->markerList.size()) + return; edit->markerList.removeAt(edit->curSelectIndex); + edit->curSelectIndex = -1; } void Spectrum::clear_all_custom_marker(SRC src) { Q_UNUSED(src) @@ -368,7 +514,7 @@ double Spectrum::get_mark_frequent(int markIndex, SRC src) { } void Spectrum::set_marker_frequent(int markerIndex, double frequent) { Render_Edit_Lease edit(d()); - QVector& markers = edit->markerList; + QVector& markers = edit->markerList; if (markerIndex < 0 || markerIndex >= markers.size()) return; markers[markerIndex].frequent = frequent; diff --git a/YSGraphic_Core/plottable/Spectrum.h b/YSGraphic_Core/plottable/Spectrum.h index 4bc4e57..d4ce1ab 100644 --- a/YSGraphic_Core/plottable/Spectrum.h +++ b/YSGraphic_Core/plottable/Spectrum.h @@ -1,8 +1,15 @@ #pragma once #include "Hover_Info.h" #include "Interpolation.h" +#include "../primitive/Curve.h" namespace YSG { struct Spectrum_Private; +class Label; +class Marker; +enum class Spectrum_Sample_Mode { + Source, + Pixel_Peak_Bucket +}; /// 单条频谱线图元,支持可见范围裁剪和多种线插值策略。 class LIB_DECL Spectrum : public Renderable, public Hover_Info_Render_Able { public: @@ -14,8 +21,8 @@ public: protected: Spectrum(); public: - PROP(Frequent_Axis*, frequent_axis) - PROP(Axis*, power_axis); + PROP(std::shared_ptr, frequent_axis) + PROP(std::shared_ptr, power_axis); PROP(int, frequent_point_size); PROP(Range, frequent_range); PROP(double, middle_sweep_frequent); @@ -27,6 +34,7 @@ public: PROP(bool, use_sweep_frequent_rect); PROP(bool, visible_range_only); PROP(Line_Interpolation_Mode, interpolation_mode); + PROP(Spectrum_Sample_Mode, sample_mode); PROP(QBrush, max_brush); PROP(QBrush, cur_brush); PROP(QBrush, min_brush); @@ -70,10 +78,17 @@ public: /// 设置当前选中标记频率。 void set_current_marker_frequent(double frequent); struct Builder; +protected: + friend struct Spectrum_Private; + std::weak_ptr mCurCurve; + std::weak_ptr mMaxCurve; + std::weak_ptr mMinCurve; + std::weak_ptr mMarkerItem; + std::weak_ptr