更完
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace renderive {
|
||||
|
||||
enum class Event_Type : std::uint8_t {
|
||||
Resize,
|
||||
Show,
|
||||
Hide,
|
||||
Leave,
|
||||
Pointer_Move,
|
||||
Pointer_Press,
|
||||
Pointer_Release,
|
||||
Wheel,
|
||||
Key_Press,
|
||||
Key_Release
|
||||
};
|
||||
|
||||
struct Event {
|
||||
explicit Event(Event_Type value) : type(value) {}
|
||||
virtual ~Event() = default;
|
||||
|
||||
void accept() const noexcept { accepted_ = true; }
|
||||
[[nodiscard]] bool is_accepted() const noexcept { return accepted_; }
|
||||
|
||||
Event_Type type;
|
||||
|
||||
private:
|
||||
mutable bool accepted_{};
|
||||
};
|
||||
|
||||
template <class T>
|
||||
concept Event_Object = std::derived_from<std::remove_cvref_t<T>, Event>;
|
||||
|
||||
template <class T>
|
||||
concept Event_Point = std::default_initializable<T> && requires(T value) {
|
||||
value.x;
|
||||
value.y;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
concept Event_Size = std::default_initializable<T> && requires(T value) {
|
||||
value.width;
|
||||
value.height;
|
||||
};
|
||||
|
||||
enum class Mouse_Button : std::uint8_t { None, Left, Right, Middle };
|
||||
using Mouse_Button_Mask = std::uint8_t;
|
||||
|
||||
enum class Keyboard_Modifier : std::uint8_t {
|
||||
None = 0,
|
||||
Ctrl = 1 << 0,
|
||||
Shift = 1 << 1,
|
||||
Alt = 1 << 2,
|
||||
Meta = 1 << 3
|
||||
};
|
||||
|
||||
constexpr Keyboard_Modifier operator|(Keyboard_Modifier left,
|
||||
Keyboard_Modifier right) noexcept {
|
||||
return static_cast<Keyboard_Modifier>(static_cast<std::uint8_t>(left) |
|
||||
static_cast<std::uint8_t>(right));
|
||||
}
|
||||
|
||||
template <Event_Point Point>
|
||||
struct Basic_Pointer_Event : Event {
|
||||
explicit Basic_Pointer_Event(Event_Type value = Event_Type::Pointer_Move)
|
||||
: Event(value) {}
|
||||
|
||||
Point position;
|
||||
Point global_position;
|
||||
Mouse_Button button = Mouse_Button::None;
|
||||
Mouse_Button_Mask buttons{};
|
||||
Keyboard_Modifier modifiers = Keyboard_Modifier::None;
|
||||
};
|
||||
|
||||
template <Event_Point Point>
|
||||
struct Basic_Wheel_Event : Basic_Pointer_Event<Point> {
|
||||
Basic_Wheel_Event() : Basic_Pointer_Event<Point>(Event_Type::Wheel) {}
|
||||
|
||||
double angle_delta_x{};
|
||||
double angle_delta_y{};
|
||||
double pixel_delta_x{};
|
||||
double pixel_delta_y{};
|
||||
};
|
||||
|
||||
template <Event_Size Size>
|
||||
struct Basic_Resize_Event : Event {
|
||||
Basic_Resize_Event() : Event(Event_Type::Resize) {}
|
||||
|
||||
Size old_size;
|
||||
Size new_size;
|
||||
};
|
||||
|
||||
enum class Key : std::uint16_t {
|
||||
Unknown,
|
||||
Escape,
|
||||
Enter,
|
||||
Space,
|
||||
Delete,
|
||||
Backspace,
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down
|
||||
};
|
||||
|
||||
struct Key_Event : Event {
|
||||
explicit Key_Event(Event_Type value) : Event(value) {}
|
||||
|
||||
Key key = Key::Unknown;
|
||||
std::uint32_t native_key{};
|
||||
Keyboard_Modifier modifiers = Keyboard_Modifier::None;
|
||||
bool auto_repeat{};
|
||||
};
|
||||
|
||||
} // namespace renderive
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <renderive/event/Event.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace {
|
||||
|
||||
struct Point3 {
|
||||
double x{};
|
||||
double y{};
|
||||
double z{};
|
||||
};
|
||||
|
||||
struct Viewport_Extent {
|
||||
int width{};
|
||||
int height{};
|
||||
};
|
||||
|
||||
static_assert(renderive::Event_Point<Point3>);
|
||||
static_assert(renderive::Event_Size<Viewport_Extent>);
|
||||
static_assert(renderive::Event_Object<renderive::Basic_Pointer_Event<Point3>>);
|
||||
|
||||
TEST(RenderiveEvent, SupportsDomainSpecificCoordinateAndSizeTypes) {
|
||||
renderive::Basic_Pointer_Event<Point3> pointer(renderive::Event_Type::Pointer_Press);
|
||||
pointer.position = {1.0, 2.0, 3.0};
|
||||
EXPECT_EQ(pointer.type, renderive::Event_Type::Pointer_Press);
|
||||
EXPECT_DOUBLE_EQ(pointer.position.z, 3.0);
|
||||
|
||||
renderive::Basic_Resize_Event<Viewport_Extent> resize;
|
||||
resize.old_size = {640, 480};
|
||||
resize.new_size = {1280, 720};
|
||||
EXPECT_EQ(resize.new_size.width, 1280);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
+4
-83
@@ -1,92 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../base/Types.h"
|
||||
#include <renderive/event/Event.hpp>
|
||||
|
||||
namespace renderive {
|
||||
|
||||
enum class Event_Type : std::uint8_t {
|
||||
Resize,
|
||||
Show,
|
||||
Hide,
|
||||
Leave,
|
||||
Pointer_Move,
|
||||
Pointer_Press,
|
||||
Pointer_Release,
|
||||
Wheel,
|
||||
Key_Press,
|
||||
Key_Release
|
||||
};
|
||||
|
||||
struct Event {
|
||||
explicit Event(Event_Type value) : type(value) {}
|
||||
virtual ~Event() = default;
|
||||
void accept() const noexcept { accepted = true; }
|
||||
[[nodiscard]] bool is_accepted() const noexcept { return accepted; }
|
||||
|
||||
Event_Type type;
|
||||
private:
|
||||
mutable bool accepted{};
|
||||
};
|
||||
|
||||
enum class Mouse_Button : std::uint8_t { None, Left, Right, Middle };
|
||||
using Mouse_Button_Mask = std::uint8_t;
|
||||
|
||||
enum class Keyboard_Modifier : std::uint8_t {
|
||||
None = 0,
|
||||
Ctrl = 1 << 0,
|
||||
Shift = 1 << 1,
|
||||
Alt = 1 << 2,
|
||||
Meta = 1 << 3
|
||||
};
|
||||
|
||||
constexpr Keyboard_Modifier operator|(Keyboard_Modifier left, Keyboard_Modifier right) noexcept {
|
||||
return static_cast<Keyboard_Modifier>(static_cast<std::uint8_t>(left) |
|
||||
static_cast<std::uint8_t>(right));
|
||||
}
|
||||
|
||||
struct Pointer_Event : Event {
|
||||
explicit Pointer_Event(Event_Type value = Event_Type::Pointer_Move) : Event(value) {}
|
||||
PointF position;
|
||||
PointF global_position;
|
||||
Mouse_Button button = Mouse_Button::None;
|
||||
Mouse_Button_Mask buttons{};
|
||||
Keyboard_Modifier modifiers = Keyboard_Modifier::None;
|
||||
};
|
||||
|
||||
struct Wheel_Event : Pointer_Event {
|
||||
Wheel_Event() : Pointer_Event(Event_Type::Wheel) {}
|
||||
double angle_delta_x{};
|
||||
double angle_delta_y{};
|
||||
double pixel_delta_x{};
|
||||
double pixel_delta_y{};
|
||||
};
|
||||
|
||||
struct Resize_Event : Event {
|
||||
Resize_Event() : Event(Event_Type::Resize) {}
|
||||
Size old_size;
|
||||
Size new_size;
|
||||
};
|
||||
|
||||
enum class Key : std::uint16_t {
|
||||
Unknown,
|
||||
Escape,
|
||||
Enter,
|
||||
Space,
|
||||
Delete,
|
||||
Backspace,
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down
|
||||
};
|
||||
|
||||
struct Key_Event : Event {
|
||||
explicit Key_Event(Event_Type value) : Event(value) {}
|
||||
Key key = Key::Unknown;
|
||||
std::uint32_t native_key{};
|
||||
Keyboard_Modifier modifiers = Keyboard_Modifier::None;
|
||||
bool auto_repeat{};
|
||||
};
|
||||
using Pointer_Event = Basic_Pointer_Event<PointF>;
|
||||
using Wheel_Event = Basic_Wheel_Event<PointF>;
|
||||
using Resize_Event = Basic_Resize_Event<Size>;
|
||||
|
||||
} // namespace renderive
|
||||
|
||||
|
||||
@@ -71,11 +71,9 @@ protected:
|
||||
Properties properties() const {
|
||||
return Base::read([](const Properties& value) { return value; });
|
||||
}
|
||||
void replace_properties(Properties value) {
|
||||
Base::update([&value](Properties& current) {
|
||||
current = std::move(value);
|
||||
});
|
||||
this->changed();
|
||||
template <auto Member>
|
||||
auto property_value() const {
|
||||
return Base::read([](const Properties& value) { return value.*Member; });
|
||||
}
|
||||
const Properties& render_properties(const Render_State_View& state) const noexcept {
|
||||
return state.get(static_cast<const Base&>(*this));
|
||||
|
||||
@@ -97,10 +97,6 @@ std::string_view frame_mode_name(Gallery_Frame_Mode mode) {
|
||||
return "low_latency";
|
||||
}
|
||||
} // namespace
|
||||
struct Gallery_Pixel_Copy {
|
||||
Pixel_Frame_Copy image;
|
||||
Color background;
|
||||
};
|
||||
class Gallery_Scene final {
|
||||
public:
|
||||
Gallery_Scene(std::uint64_t session_id, std::string case_id,
|
||||
@@ -220,19 +216,19 @@ public:
|
||||
rendered_since_last_pixel_ = true;
|
||||
return rendered;
|
||||
}
|
||||
[[nodiscard]] std::optional<Gallery_Pixel_Copy> copy_latest_pixels() {
|
||||
[[nodiscard]] std::optional<std::string> encode_latest_pixels() {
|
||||
if (!plot_.view_active())
|
||||
return std::nullopt;
|
||||
if (!rendered_since_last_pixel_ && !can_render_automatically() &&
|
||||
!render_latest_frame())
|
||||
return std::nullopt;
|
||||
rendered_since_last_pixel_ = false;
|
||||
Gallery_Pixel_Copy copy;
|
||||
copy.background = plot_.background_color();
|
||||
plot_.with_frame([©](Image_View image) {
|
||||
copy.image = copy_pixel_frame(image);
|
||||
std::string pixels;
|
||||
const Color background = plot_.background_color();
|
||||
plot_.with_frame([&pixels, background](Image_View image) {
|
||||
pixels = encode_pixel_frame(image, background);
|
||||
});
|
||||
return copy.image.empty() ? std::nullopt : std::optional<Gallery_Pixel_Copy>(std::move(copy));
|
||||
return pixels.empty() ? std::nullopt : std::optional<std::string>(std::move(pixels));
|
||||
}
|
||||
void record_pixel_response(std::chrono::steady_clock::time_point request_started,
|
||||
std::chrono::steady_clock::time_point encode_started,
|
||||
@@ -1591,31 +1587,21 @@ struct Gallery_Plot_Session::Impl {
|
||||
}
|
||||
std::optional<Web_Response> handle_frame_request() {
|
||||
const auto request_started = std::chrono::steady_clock::now();
|
||||
std::optional<Gallery_Pixel_Copy> copy;
|
||||
std::uint64_t generation{};
|
||||
std::optional<std::string> pixels;
|
||||
{
|
||||
auto lock = acquire_foreground_lock();
|
||||
if (!scene)
|
||||
return std::nullopt;
|
||||
generation = scene_generation;
|
||||
copy = scene->copy_latest_pixels();
|
||||
}
|
||||
if (!copy)
|
||||
return std::nullopt;
|
||||
const auto encode_started = std::chrono::steady_clock::now();
|
||||
std::string pixels = encode_pixel_frame(copy->image, copy->background);
|
||||
const auto encode_finished = std::chrono::steady_clock::now();
|
||||
{
|
||||
auto lock = acquire_foreground_lock();
|
||||
if (scene && scene_generation == generation)
|
||||
const auto encode_started = std::chrono::steady_clock::now();
|
||||
pixels = scene->encode_latest_pixels();
|
||||
const auto encode_finished = std::chrono::steady_clock::now();
|
||||
if (pixels)
|
||||
scene->record_pixel_response(request_started, encode_started, encode_finished,
|
||||
pixels.size());
|
||||
pixels->size());
|
||||
}
|
||||
return pixels.empty()
|
||||
? std::nullopt
|
||||
: std::optional<Web_Response>(Web_Response{
|
||||
Web_Response_Type::Pixels, std::move(pixels)
|
||||
});
|
||||
if (!pixels)
|
||||
return std::nullopt;
|
||||
return Web_Response{Web_Response_Type::Pixels, std::move(*pixels)};
|
||||
}
|
||||
std::optional<Web_Response> handle(const Web_Event& event) {
|
||||
if (std::holds_alternative<Frame_Request>(event))
|
||||
@@ -1641,7 +1627,7 @@ struct Gallery_Plot_Session::Impl {
|
||||
if (scene)
|
||||
scene->dispatch(value);
|
||||
}
|
||||
else if constexpr (std::is_base_of_v<Event, T>) {
|
||||
else if constexpr (Event_Object<T>) {
|
||||
if (scene)
|
||||
scene->dispatch(value);
|
||||
}
|
||||
|
||||
@@ -10,16 +10,33 @@
|
||||
#include "render_2D/plottable/Spectrum.h"
|
||||
#include "render_2D/plottable/Sweep_Spectrum.h"
|
||||
#include "render_2D/plottable/Waterfall.h"
|
||||
#include <structive/property/accessor.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace adminive {
|
||||
template <class T>
|
||||
struct Object_Adapter;
|
||||
}
|
||||
|
||||
namespace renderive::web {
|
||||
|
||||
template <class Object, auto Member>
|
||||
struct Gallery_Property_Accessor {
|
||||
using object_type = Object;
|
||||
using Properties = typename Object::Properties;
|
||||
using value_type = std::remove_cvref_t<decltype(std::declval<Properties>().*Member)>;
|
||||
using storage_identity = void;
|
||||
using dependency_spec = structive::No_Property_Dependencies;
|
||||
static constexpr bool readable = true;
|
||||
static constexpr bool writable = true;
|
||||
static constexpr bool synchronized_view_read = false;
|
||||
static constexpr bool trusted_object_access = true;
|
||||
|
||||
value_type read(const Object& object) const {
|
||||
return object.template adminive_read<Member>();
|
||||
}
|
||||
void write(Object& object, value_type value) const {
|
||||
object.template adminive_write<Member>(std::move(value));
|
||||
}
|
||||
};
|
||||
|
||||
template <class Base>
|
||||
class Gallery_Plottable : public Base {
|
||||
public:
|
||||
@@ -27,16 +44,16 @@ public:
|
||||
using Base::Base;
|
||||
|
||||
private:
|
||||
[[nodiscard]] Properties adminive_snapshot() const {
|
||||
return this->properties();
|
||||
template <auto Member>
|
||||
auto adminive_read() const {
|
||||
return this->template property_value<Member>();
|
||||
}
|
||||
|
||||
void adminive_commit(Properties properties) {
|
||||
this->replace_properties(std::move(properties));
|
||||
template <auto Member, class Value>
|
||||
void adminive_write(Value&& value) {
|
||||
this->template set<Member>(std::forward<Value>(value));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend struct ::adminive::Object_Adapter;
|
||||
template <class Object, auto Member>
|
||||
friend struct Gallery_Property_Accessor;
|
||||
};
|
||||
|
||||
class Gallery_Spectrum final : public Gallery_Plottable<Spectrum> {
|
||||
@@ -90,20 +107,18 @@ public:
|
||||
using Base::Base;
|
||||
|
||||
private:
|
||||
[[nodiscard]] Properties adminive_snapshot() const {
|
||||
template <auto Member>
|
||||
auto adminive_read() const {
|
||||
return this->read([](const auto& state) {
|
||||
return Properties(static_cast<const Properties&>(state));
|
||||
return state.*Member;
|
||||
});
|
||||
}
|
||||
|
||||
void adminive_commit(Properties properties) {
|
||||
this->update([&properties](auto& state) {
|
||||
static_cast<Properties&>(state) = std::move(properties);
|
||||
});
|
||||
template <auto Member, class Value>
|
||||
void adminive_write(Value&& value) {
|
||||
this->template set<Member>(std::forward<Value>(value));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend struct ::adminive::Object_Adapter;
|
||||
template <class Object, auto Member>
|
||||
friend struct Gallery_Property_Accessor;
|
||||
};
|
||||
|
||||
class Gallery_Axis final : public Gallery_Axis_Base<Axis, Axis_Properties> {
|
||||
|
||||
@@ -169,6 +169,57 @@ auto color(std::string name, std::string label) {
|
||||
return editable<Member>(std::move(name), std::move(label), adminive::Field_Control::color);
|
||||
}
|
||||
|
||||
template <class Object, auto Member>
|
||||
auto property(std::string name, std::string label, adminive::Field_Control control) {
|
||||
return adminive::field(std::move(name), std::move(label),
|
||||
Gallery_Property_Accessor<Object, Member>{})
|
||||
.editable()
|
||||
.unsynchronized()
|
||||
.control(control);
|
||||
}
|
||||
|
||||
template <class Object, auto Member>
|
||||
auto readonly_property(std::string name, std::string label) {
|
||||
return adminive::field(std::move(name), std::move(label),
|
||||
Gallery_Property_Accessor<Object, Member>{});
|
||||
}
|
||||
|
||||
template <class Object, auto Member>
|
||||
auto property_number(std::string name, std::string label) {
|
||||
return property<Object, Member>(std::move(name), std::move(label),
|
||||
adminive::Field_Control::number);
|
||||
}
|
||||
|
||||
template <class Object, auto Member>
|
||||
auto property_boolean(std::string name, std::string label) {
|
||||
return property<Object, Member>(std::move(name), std::move(label),
|
||||
adminive::Field_Control::boolean);
|
||||
}
|
||||
|
||||
template <class Object, auto Member>
|
||||
auto property_select(std::string name, std::string label) {
|
||||
return property<Object, Member>(std::move(name), std::move(label),
|
||||
adminive::Field_Control::select);
|
||||
}
|
||||
|
||||
template <class Object, auto Member>
|
||||
auto property_color(std::string name, std::string label) {
|
||||
return property<Object, Member>(std::move(name), std::move(label),
|
||||
adminive::Field_Control::color);
|
||||
}
|
||||
|
||||
template <class Object, auto Member>
|
||||
auto property_object(std::string name, std::string label) {
|
||||
return property<Object, Member>(std::move(name), std::move(label),
|
||||
adminive::Field_Control::automatic);
|
||||
}
|
||||
|
||||
template <class Object, auto Member>
|
||||
auto property_text(std::string name, std::string label) {
|
||||
return property<Object, Member>(std::move(name), std::move(label),
|
||||
adminive::Field_Control::text);
|
||||
}
|
||||
|
||||
} // namespace renderive::web::gallery_adminive
|
||||
|
||||
namespace adminive {
|
||||
@@ -295,7 +346,7 @@ struct Type_Descriptor<renderive::Range> {
|
||||
static auto get() {
|
||||
using T = renderive::Range;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return object<T>("range", "Range",
|
||||
return adminive::object<T>("range", "Range",
|
||||
number<&T::origin>("origin", "Origin"),
|
||||
number<&T::target>("target", "Target"));
|
||||
}
|
||||
@@ -306,7 +357,7 @@ struct Type_Descriptor<renderive::Pen> {
|
||||
static auto get() {
|
||||
using T = renderive::Pen;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return object<T>("pen", "Pen",
|
||||
return adminive::object<T>("pen", "Pen",
|
||||
color<&T::color>("color", "Color"),
|
||||
number<&T::width>("width", "Width"),
|
||||
select<&T::style>("style", "Style"),
|
||||
@@ -320,7 +371,7 @@ struct Type_Descriptor<renderive::Brush> {
|
||||
static auto get() {
|
||||
using T = renderive::Brush;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return object<T>("brush", "Brush",
|
||||
return adminive::object<T>("brush", "Brush",
|
||||
color<&T::color>("color", "Color"),
|
||||
select<&T::style>("style", "Style"));
|
||||
}
|
||||
@@ -331,158 +382,158 @@ struct Type_Descriptor<renderive::Font> {
|
||||
static auto get() {
|
||||
using T = renderive::Font;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return object<T>("font", "Font",
|
||||
return adminive::object<T>("font", "Font",
|
||||
number<&T::size>("size", "Size"),
|
||||
number<&T::weight>("weight", "Weight"),
|
||||
boolean<&T::italic>("italic", "Italic"));
|
||||
}
|
||||
};
|
||||
|
||||
#define RENDERIVE_AXIS_DESCRIPTOR(GalleryType, Name, Label) \
|
||||
template <> struct Type_Descriptor<renderive::web::GalleryType> { \
|
||||
static auto get() { \
|
||||
using T = renderive::web::GalleryType; \
|
||||
using B = renderive::Axis_Base_Properties; \
|
||||
using P = renderive::Axis_Properties; \
|
||||
using namespace renderive::web::gallery_adminive; \
|
||||
return adminive::object<T>(Name, Label, \
|
||||
readonly_property<T, &B::x>("x", "X"), \
|
||||
readonly_property<T, &B::y>("y", "Y"), \
|
||||
property_select<T, &B::orientation>("orientation", "Orientation"), \
|
||||
readonly_property<T, &B::pixel_length>("pixel_length", "Pixel length"), \
|
||||
property_number<T, &B::tick_length>("tick_length", "Tick length"), \
|
||||
property_number<T, &B::sub_tick_length>("sub_tick_length", "Sub tick length"), \
|
||||
property_color<T, &B::color>("color", "Color"), \
|
||||
property_select<T, &B::locale>("locale", "Decimal separator"), \
|
||||
property_text<T, &B::unit_text>("unit_text", "Unit text"), \
|
||||
property_object<T, &B::unit_text_font>("unit_text_font", "Unit font"), \
|
||||
property_object<T, &B::unit_text_pen>("unit_text_pen", "Unit pen"), \
|
||||
property_object<T, &B::unit_text_background_brush>("unit_text_background_brush", "Unit background"), \
|
||||
property_number<T, &B::label_rotation_degrees>("label_rotation_degrees", "Label rotation"), \
|
||||
property_object<T, &P::coordinates>("coordinates", "Coordinates"), \
|
||||
property_number<T, &P::precision>("precision", "Precision"), \
|
||||
property_boolean<T, &P::wheel>("wheel", "Wheel zoom"), \
|
||||
property_boolean<T, &P::drag>("drag", "Drag pan")); \
|
||||
} \
|
||||
}
|
||||
|
||||
RENDERIVE_AXIS_DESCRIPTOR(Gallery_Axis, "gallery_axis", "Axis");
|
||||
RENDERIVE_AXIS_DESCRIPTOR(Gallery_Frequency_Axis, "gallery_frequency_axis", "Frequency axis");
|
||||
#undef RENDERIVE_AXIS_DESCRIPTOR
|
||||
|
||||
template <>
|
||||
struct Type_Descriptor<renderive::Axis_Properties> {
|
||||
struct Type_Descriptor<renderive::web::Gallery_Time_Axis> {
|
||||
static auto get() {
|
||||
using T = renderive::Axis_Properties;
|
||||
using T = renderive::web::Gallery_Time_Axis;
|
||||
using B = renderive::Axis_Base_Properties;
|
||||
using P = renderive::Time_Axis_Properties;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return object<T>("axis_properties", "Axis",
|
||||
select<&B::orientation>("orientation", "Orientation"),
|
||||
number<&B::tick_length>("tick_length", "Tick length"),
|
||||
number<&B::sub_tick_length>("sub_tick_length", "Sub tick length"),
|
||||
color<&B::color>("color", "Color"),
|
||||
select<&B::locale>("locale", "Decimal separator"),
|
||||
editable<&B::unit_text>("unit_text", "Unit text", Field_Control::text),
|
||||
editable<&B::unit_text_font>("unit_text_font", "Unit font", Field_Control::automatic),
|
||||
editable<&B::unit_text_pen>("unit_text_pen", "Unit pen", Field_Control::automatic),
|
||||
editable<&B::unit_text_background_brush>("unit_text_background_brush", "Unit background", Field_Control::automatic),
|
||||
number<&B::label_rotation_degrees>("label_rotation_degrees", "Label rotation"),
|
||||
editable<&T::coordinates>("coordinates", "Coordinates", Field_Control::automatic),
|
||||
number<&T::precision>("precision", "Precision"),
|
||||
boolean<&T::wheel>("wheel", "Wheel zoom"),
|
||||
boolean<&T::drag>("drag", "Drag pan"));
|
||||
return adminive::object<T>("gallery_time_axis", "Time axis",
|
||||
readonly_property<T, &B::x>("x", "X"),
|
||||
readonly_property<T, &B::y>("y", "Y"),
|
||||
property_select<T, &B::orientation>("orientation", "Orientation"),
|
||||
readonly_property<T, &B::pixel_length>("pixel_length", "Pixel length"),
|
||||
property_number<T, &B::tick_length>("tick_length", "Tick length"),
|
||||
property_number<T, &B::sub_tick_length>("sub_tick_length", "Sub tick length"),
|
||||
property_color<T, &B::color>("color", "Color"),
|
||||
property_select<T, &B::locale>("locale", "Decimal separator"),
|
||||
property_text<T, &B::unit_text>("unit_text", "Unit text"),
|
||||
property_object<T, &B::unit_text_font>("unit_text_font", "Unit font"),
|
||||
property_object<T, &B::unit_text_pen>("unit_text_pen", "Unit pen"),
|
||||
property_object<T, &B::unit_text_background_brush>("unit_text_background_brush", "Unit background"),
|
||||
property_number<T, &B::label_rotation_degrees>("label_rotation_degrees", "Label rotation"),
|
||||
property_number<T, &P::visible_count>("visible_count", "Visible points"),
|
||||
property_number<T, &P::tick_label_spacing_px>("tick_label_spacing_px", "Label spacing"),
|
||||
property_text<T, &P::format>("format", "Time format"),
|
||||
property_boolean<T, &P::newest_at_start>("newest_at_start", "Newest at start"));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Type_Descriptor<renderive::Time_Axis_Properties> {
|
||||
static auto get() {
|
||||
using T = renderive::Time_Axis_Properties;
|
||||
using B = renderive::Axis_Base_Properties;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return object<T>("time_axis_properties", "Time axis",
|
||||
select<&B::orientation>("orientation", "Orientation"),
|
||||
number<&B::tick_length>("tick_length", "Tick length"),
|
||||
number<&B::sub_tick_length>("sub_tick_length", "Sub tick length"),
|
||||
color<&B::color>("color", "Color"),
|
||||
editable<&B::unit_text>("unit_text", "Unit text", Field_Control::text),
|
||||
editable<&B::unit_text_font>("unit_text_font", "Unit font", Field_Control::automatic),
|
||||
number<&T::visible_count>("visible_count", "Visible points"),
|
||||
number<&T::tick_label_spacing_px>("tick_label_spacing_px", "Label spacing"),
|
||||
editable<&T::format>("format", "Time format", Field_Control::text),
|
||||
boolean<&T::newest_at_start>("newest_at_start", "Newest at start"));
|
||||
}
|
||||
};
|
||||
|
||||
#define RENDERIVE_PROPERTY_DESCRIPTOR(Type, Label, ...) \
|
||||
template <> struct Type_Descriptor<renderive::Type> { \
|
||||
static auto get() { using T = renderive::Type; using namespace renderive::web::gallery_adminive; \
|
||||
return object<T>(#Type, Label, __VA_ARGS__); } }
|
||||
|
||||
RENDERIVE_PROPERTY_DESCRIPTOR(Spectrum_Properties, "Spectrum",
|
||||
number<&T::frequency_point_size>("frequency_point_size", "Frequency points"),
|
||||
editable<&T::frequency_range>("frequency_range", "Frequency range", Field_Control::automatic),
|
||||
number<&T::center_frequency>("center_frequency", "Center frequency"),
|
||||
editable<&T::sweep_frequency_range>("sweep_frequency_range", "Sweep range", Field_Control::automatic),
|
||||
boolean<&T::max_hold_visible>("max_hold_visible", "Max hold"),
|
||||
boolean<&T::min_hold_visible>("min_hold_visible", "Min hold"),
|
||||
boolean<&T::max_marker_visible>("max_marker_visible", "Max marker"),
|
||||
boolean<&T::use_min_marker>("use_min_marker", "Use min marker"),
|
||||
boolean<&T::sweep_region_visible>("sweep_region_visible", "Sweep region"),
|
||||
boolean<&T::visible_range_only>("visible_range_only", "Visible range only"),
|
||||
select<&T::interpolation_mode>("interpolation_mode", "Interpolation"),
|
||||
editable<&T::max_brush>("max_brush", "Max brush", Field_Control::automatic),
|
||||
editable<&T::current_brush>("current_brush", "Current brush", Field_Control::automatic),
|
||||
editable<&T::min_brush>("min_brush", "Min brush", Field_Control::automatic),
|
||||
editable<&T::max_pen>("max_pen", "Max pen", Field_Control::automatic),
|
||||
editable<&T::current_pen>("current_pen", "Current pen", Field_Control::automatic),
|
||||
editable<&T::min_pen>("min_pen", "Min pen", Field_Control::automatic),
|
||||
editable<&T::selected_marker_pen>("selected_marker_pen", "Selected marker pen", Field_Control::automatic),
|
||||
editable<&T::marker_pen>("marker_pen", "Marker pen", Field_Control::automatic),
|
||||
editable<&T::middle_frequency_pen>("middle_frequency_pen", "Middle frequency pen", Field_Control::automatic),
|
||||
editable<&T::sweep_region_brush>("sweep_region_brush", "Sweep region brush", Field_Control::automatic),
|
||||
boolean<&T::tooltip_enabled>("tooltip_enabled", "Tooltip"),
|
||||
editable<&T::tooltip_font>("tooltip_font", "Tooltip font", Field_Control::automatic),
|
||||
editable<&T::tooltip_text_pen>("tooltip_text_pen", "Tooltip text pen", Field_Control::automatic),
|
||||
editable<&T::tooltip_background_brush>("tooltip_background_brush", "Tooltip background", Field_Control::automatic));
|
||||
|
||||
RENDERIVE_PROPERTY_DESCRIPTOR(Waterfall_Properties, "Waterfall",
|
||||
editable<&T::frequency_range>("frequency_range", "Frequency range", Field_Control::automatic),
|
||||
editable<&T::power_range>("power_range", "Power range", Field_Control::automatic),
|
||||
number<&T::frequency_bin_count>("frequency_bin_count", "Frequency bins"),
|
||||
boolean<&T::visible_range_only>("visible_range_only", "Visible range only"),
|
||||
select<&T::interpolation_mode>("interpolation_mode", "Interpolation"),
|
||||
select<&T::color_map>("color_map", "Color map"),
|
||||
boolean<&T::tooltip_enabled>("tooltip_enabled", "Tooltip"),
|
||||
editable<&T::tooltip_font>("tooltip_font", "Tooltip font", Field_Control::automatic),
|
||||
editable<&T::tooltip_text_pen>("tooltip_text_pen", "Tooltip text pen", Field_Control::automatic),
|
||||
editable<&T::tooltip_background_brush>("tooltip_background_brush", "Tooltip background", Field_Control::automatic));
|
||||
|
||||
RENDERIVE_PROPERTY_DESCRIPTOR(Afterglow_Properties, "Afterglow",
|
||||
editable<&T::frequency_range>("frequency_range", "Frequency range", Field_Control::automatic),
|
||||
editable<&T::power_range>("power_range", "Power range", Field_Control::automatic),
|
||||
number<&T::frequency_point_size>("frequency_point_size", "Frequency points"),
|
||||
number<&T::power_point_size>("power_point_size", "Power points"),
|
||||
boolean<&T::interpolate>("interpolate", "Interpolate power"),
|
||||
number<&T::attenuation_rate>("attenuation_rate", "Attenuation"),
|
||||
select<&T::color_map>("color_map", "Color map"));
|
||||
|
||||
RENDERIVE_PROPERTY_DESCRIPTOR(Sweep_Spectrum_Properties, "Sweep spectrum",
|
||||
editable<&T::frequency_range>("frequency_range", "Frequency range", Field_Control::automatic),
|
||||
number<&T::bins_per_block>("bins_per_block", "Bins per block"),
|
||||
number<&T::block_count>("block_count", "Block count"),
|
||||
editable<&T::pen>("pen", "Sweep pen", Field_Control::automatic),
|
||||
editable<&T::current_frequency_pen>("current_frequency_pen", "Current frequency pen", Field_Control::automatic),
|
||||
boolean<&T::visible_range_only>("visible_range_only", "Visible range only"),
|
||||
select<&T::interpolation_mode>("interpolation_mode", "Interpolation"));
|
||||
|
||||
RENDERIVE_PROPERTY_DESCRIPTOR(Frequency_Trace_Properties, "Frequency trace",
|
||||
editable<&T::pen>("pen", "Trace pen", Field_Control::automatic));
|
||||
|
||||
RENDERIVE_PROPERTY_DESCRIPTOR(Selection_Rectangle_Overlay_Properties, "Selection overlay",
|
||||
editable<&T::label_font>("label_font", "Label font", Field_Control::automatic),
|
||||
editable<&T::label_pen>("label_pen", "Label pen", Field_Control::automatic),
|
||||
editable<&T::selection_brush>("selection_brush", "Selection brush", Field_Control::automatic),
|
||||
editable<&T::selection_border_pen>("selection_border_pen", "Selection border", Field_Control::automatic));
|
||||
|
||||
RENDERIVE_PROPERTY_DESCRIPTOR(Constellation_Diagram_Properties, "Constellation diagram",
|
||||
editable<&T::i_range>("i_range", "I range", Field_Control::automatic),
|
||||
editable<&T::q_range>("q_range", "Q range", Field_Control::automatic),
|
||||
color<&T::point_color>("point_color", "Point color"),
|
||||
color<&T::anchor_color>("anchor_color", "Anchor color"),
|
||||
number<&T::point_lifetime_ms>("point_lifetime_ms", "Point lifetime"),
|
||||
select<&T::type>("type", "Constellation"),
|
||||
number<&T::phase_offset_radians>("phase_offset_radians", "Phase offset"));
|
||||
|
||||
#undef RENDERIVE_PROPERTY_DESCRIPTOR
|
||||
|
||||
#define RENDERIVE_OBJECT_ADAPTER(GalleryType, PropertiesType) \
|
||||
template <> struct Object_Adapter<renderive::web::GalleryType> { \
|
||||
using object_type = renderive::web::GalleryType; \
|
||||
using model_type = renderive::PropertiesType; \
|
||||
static model_type snapshot(const object_type& value) { return value.adminive_snapshot(); } \
|
||||
static void commit(object_type& target, model_type value) { target.adminive_commit(std::move(value)); } \
|
||||
#define RENDERIVE_GALLERY_DESCRIPTOR(GalleryType, PropertiesType, Label, ...) \
|
||||
template <> struct Type_Descriptor<renderive::web::GalleryType> { \
|
||||
static auto get() { \
|
||||
using T = renderive::web::GalleryType; \
|
||||
using P = renderive::PropertiesType; \
|
||||
using namespace renderive::web::gallery_adminive; \
|
||||
return adminive::object<T>(#GalleryType, Label, __VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
RENDERIVE_OBJECT_ADAPTER(Gallery_Spectrum, Spectrum_Properties);
|
||||
RENDERIVE_OBJECT_ADAPTER(Gallery_Waterfall, Waterfall_Properties);
|
||||
RENDERIVE_OBJECT_ADAPTER(Gallery_Afterglow, Afterglow_Properties);
|
||||
RENDERIVE_OBJECT_ADAPTER(Gallery_Sweep_Spectrum, Sweep_Spectrum_Properties);
|
||||
RENDERIVE_OBJECT_ADAPTER(Gallery_Frequency_Trace, Frequency_Trace_Properties);
|
||||
RENDERIVE_OBJECT_ADAPTER(Gallery_Selection_Rectangle_Overlay, Selection_Rectangle_Overlay_Properties);
|
||||
RENDERIVE_OBJECT_ADAPTER(Gallery_Constellation_Diagram, Constellation_Diagram_Properties);
|
||||
RENDERIVE_OBJECT_ADAPTER(Gallery_Axis, Axis_Properties);
|
||||
RENDERIVE_OBJECT_ADAPTER(Gallery_Frequency_Axis, Axis_Properties);
|
||||
RENDERIVE_OBJECT_ADAPTER(Gallery_Time_Axis, Time_Axis_Properties);
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Spectrum, Spectrum_Properties, "Spectrum",
|
||||
property_number<T, &P::frequency_point_size>("frequency_point_size", "Frequency points"),
|
||||
property_object<T, &P::frequency_range>("frequency_range", "Frequency range"),
|
||||
property_number<T, &P::center_frequency>("center_frequency", "Center frequency"),
|
||||
property_object<T, &P::sweep_frequency_range>("sweep_frequency_range", "Sweep range"),
|
||||
property_boolean<T, &P::max_hold_visible>("max_hold_visible", "Max hold"),
|
||||
property_boolean<T, &P::min_hold_visible>("min_hold_visible", "Min hold"),
|
||||
property_boolean<T, &P::max_marker_visible>("max_marker_visible", "Max marker"),
|
||||
property_boolean<T, &P::use_min_marker>("use_min_marker", "Use min marker"),
|
||||
property_boolean<T, &P::sweep_region_visible>("sweep_region_visible", "Sweep region"),
|
||||
property_boolean<T, &P::visible_range_only>("visible_range_only", "Visible range only"),
|
||||
property_select<T, &P::interpolation_mode>("interpolation_mode", "Interpolation"),
|
||||
property_object<T, &P::max_brush>("max_brush", "Max brush"),
|
||||
property_object<T, &P::current_brush>("current_brush", "Current brush"),
|
||||
property_object<T, &P::min_brush>("min_brush", "Min brush"),
|
||||
property_object<T, &P::max_pen>("max_pen", "Max pen"),
|
||||
property_object<T, &P::current_pen>("current_pen", "Current pen"),
|
||||
property_object<T, &P::min_pen>("min_pen", "Min pen"),
|
||||
property_object<T, &P::selected_marker_pen>("selected_marker_pen", "Selected marker pen"),
|
||||
property_object<T, &P::marker_pen>("marker_pen", "Marker pen"),
|
||||
property_object<T, &P::middle_frequency_pen>("middle_frequency_pen", "Middle frequency pen"),
|
||||
property_object<T, &P::sweep_region_brush>("sweep_region_brush", "Sweep region brush"),
|
||||
property_boolean<T, &P::tooltip_enabled>("tooltip_enabled", "Tooltip"),
|
||||
property_object<T, &P::tooltip_font>("tooltip_font", "Tooltip font"),
|
||||
property_object<T, &P::tooltip_text_pen>("tooltip_text_pen", "Tooltip text pen"),
|
||||
property_object<T, &P::tooltip_background_brush>("tooltip_background_brush", "Tooltip background"));
|
||||
|
||||
#undef RENDERIVE_OBJECT_ADAPTER
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Waterfall, Waterfall_Properties, "Waterfall",
|
||||
property_object<T, &P::frequency_range>("frequency_range", "Frequency range"),
|
||||
property_object<T, &P::power_range>("power_range", "Power range"),
|
||||
property_number<T, &P::frequency_bin_count>("frequency_bin_count", "Frequency bins"),
|
||||
property_boolean<T, &P::visible_range_only>("visible_range_only", "Visible range only"),
|
||||
property_select<T, &P::interpolation_mode>("interpolation_mode", "Interpolation"),
|
||||
property_select<T, &P::color_map>("color_map", "Color map"),
|
||||
property_boolean<T, &P::tooltip_enabled>("tooltip_enabled", "Tooltip"),
|
||||
property_object<T, &P::tooltip_font>("tooltip_font", "Tooltip font"),
|
||||
property_object<T, &P::tooltip_text_pen>("tooltip_text_pen", "Tooltip text pen"),
|
||||
property_object<T, &P::tooltip_background_brush>("tooltip_background_brush", "Tooltip background"));
|
||||
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Afterglow, Afterglow_Properties, "Afterglow",
|
||||
property_object<T, &P::frequency_range>("frequency_range", "Frequency range"),
|
||||
property_object<T, &P::power_range>("power_range", "Power range"),
|
||||
property_number<T, &P::frequency_point_size>("frequency_point_size", "Frequency points"),
|
||||
property_number<T, &P::power_point_size>("power_point_size", "Power points"),
|
||||
property_boolean<T, &P::interpolate>("interpolate", "Interpolate power"),
|
||||
property_number<T, &P::attenuation_rate>("attenuation_rate", "Attenuation"),
|
||||
property_select<T, &P::color_map>("color_map", "Color map"));
|
||||
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Sweep_Spectrum, Sweep_Spectrum_Properties, "Sweep spectrum",
|
||||
property_object<T, &P::frequency_range>("frequency_range", "Frequency range"),
|
||||
property_number<T, &P::bins_per_block>("bins_per_block", "Bins per block"),
|
||||
property_number<T, &P::block_count>("block_count", "Block count"),
|
||||
property_object<T, &P::pen>("pen", "Sweep pen"),
|
||||
property_object<T, &P::current_frequency_pen>("current_frequency_pen", "Current frequency pen"),
|
||||
property_boolean<T, &P::visible_range_only>("visible_range_only", "Visible range only"),
|
||||
property_select<T, &P::interpolation_mode>("interpolation_mode", "Interpolation"));
|
||||
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Frequency_Trace, Frequency_Trace_Properties, "Frequency trace",
|
||||
property_object<T, &P::pen>("pen", "Trace pen"));
|
||||
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Selection_Rectangle_Overlay, Selection_Rectangle_Overlay_Properties, "Selection overlay",
|
||||
property_object<T, &P::label_font>("label_font", "Label font"),
|
||||
property_object<T, &P::label_pen>("label_pen", "Label pen"),
|
||||
property_object<T, &P::selection_brush>("selection_brush", "Selection brush"),
|
||||
property_object<T, &P::selection_border_pen>("selection_border_pen", "Selection border"));
|
||||
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Constellation_Diagram, Constellation_Diagram_Properties, "Constellation diagram",
|
||||
property_object<T, &P::i_range>("i_range", "I range"),
|
||||
property_object<T, &P::q_range>("q_range", "Q range"),
|
||||
property_color<T, &P::point_color>("point_color", "Point color"),
|
||||
property_color<T, &P::anchor_color>("anchor_color", "Anchor color"),
|
||||
property_number<T, &P::point_lifetime_ms>("point_lifetime_ms", "Point lifetime"),
|
||||
property_select<T, &P::type>("type", "Constellation"),
|
||||
property_number<T, &P::phase_offset_radians>("phase_offset_radians", "Phase offset"));
|
||||
|
||||
#undef RENDERIVE_GALLERY_DESCRIPTOR
|
||||
|
||||
} // namespace adminive
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
#include "Gallery_Session_Control.h"
|
||||
|
||||
namespace renderive::web {
|
||||
|
||||
Gallery_Session_Control_Model Gallery_Session_Control::adminive_snapshot() const {
|
||||
Gallery_Session_Control_Model result;
|
||||
result.background_color = plot_.background_color();
|
||||
if (const auto overlay = plot_.performance_overlay())
|
||||
result.performance_overlay = overlay->enabled();
|
||||
result.renderable_visible = renderable_.is_visible();
|
||||
result.cache_mode = renderable_.get_cache_mode();
|
||||
result.object_name = renderable_.object_name();
|
||||
result.max_render_fps = plot_.max_render_fps();
|
||||
result.frequency_limit_enabled = result.max_render_fps > 0.0;
|
||||
if (!result.frequency_limit_enabled)
|
||||
result.max_render_fps = 30.0;
|
||||
result.feedback = feedback_;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Gallery_Session_Control::adminive_commit(Gallery_Session_Control_Model model) {
|
||||
const auto current = adminive_snapshot();
|
||||
if (model.frequency_limit_enabled != current.frequency_limit_enabled) {
|
||||
if (model.frequency_limit_enabled)
|
||||
plot_.set_max_render_fps(model.max_render_fps);
|
||||
else
|
||||
plot_.clear_max_render_fps();
|
||||
} else if (model.frequency_limit_enabled &&
|
||||
model.max_render_fps != current.max_render_fps) {
|
||||
plot_.set_max_render_fps(model.max_render_fps);
|
||||
}
|
||||
if (model.background_color != current.background_color)
|
||||
plot_.set_background_color(model.background_color);
|
||||
if (model.performance_overlay != current.performance_overlay)
|
||||
set_performance_overlay_enabled(plot_, model.performance_overlay);
|
||||
if (model.renderable_visible != current.renderable_visible)
|
||||
renderable_.set_visible(model.renderable_visible);
|
||||
if (model.cache_mode != current.cache_mode)
|
||||
renderable_.set_cache_mode(model.cache_mode);
|
||||
if (model.object_name != current.object_name)
|
||||
renderable_.set_object_name(std::move(model.object_name));
|
||||
if (model.feedback != current.feedback)
|
||||
feedback_ = model.feedback;
|
||||
plot_.notify_model_dirty();
|
||||
}
|
||||
|
||||
} // namespace renderive::web
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
namespace adminive {
|
||||
template <class T>
|
||||
struct Object_Adapter;
|
||||
struct Type_Descriptor;
|
||||
}
|
||||
|
||||
namespace renderive::web {
|
||||
@@ -25,17 +25,6 @@ struct Gallery_Feedback_Policy {
|
||||
const Gallery_Feedback_Policy&) = default;
|
||||
};
|
||||
|
||||
struct Gallery_Session_Control_Model {
|
||||
Color background_color{7, 17, 31, 255};
|
||||
bool performance_overlay{};
|
||||
bool renderable_visible{true};
|
||||
Renderable_Cache_Mode cache_mode{Renderable_Cache_Mode::Local_Pixel};
|
||||
std::string object_name{"Gallery_Renderable"};
|
||||
bool frequency_limit_enabled{true};
|
||||
double max_render_fps{30.0};
|
||||
Gallery_Feedback_Policy feedback;
|
||||
};
|
||||
|
||||
class Gallery_Session_Control final {
|
||||
public:
|
||||
Gallery_Session_Control(Plot_Core& plot, Renderable& renderable,
|
||||
@@ -43,15 +32,12 @@ public:
|
||||
: plot_(plot), renderable_(renderable), feedback_(feedback) {}
|
||||
|
||||
private:
|
||||
[[nodiscard]] Gallery_Session_Control_Model adminive_snapshot() const;
|
||||
void adminive_commit(Gallery_Session_Control_Model model);
|
||||
|
||||
Plot_Core& plot_;
|
||||
Renderable& renderable_;
|
||||
Gallery_Feedback_Policy& feedback_;
|
||||
|
||||
template <class T>
|
||||
friend struct ::adminive::Object_Adapter;
|
||||
friend struct ::adminive::Type_Descriptor;
|
||||
};
|
||||
|
||||
} // namespace renderive::web
|
||||
|
||||
@@ -3,6 +3,25 @@
|
||||
#include "Gallery_Session_Control.h"
|
||||
#include "Gallery_Renderables_Adminive.h"
|
||||
|
||||
#include <concepts>
|
||||
#include <utility>
|
||||
|
||||
namespace renderive::web::gallery_adminive {
|
||||
|
||||
template <class Object, class Value, class Reader, class Writer>
|
||||
auto callback_property(std::string name, std::string label,
|
||||
adminive::Field_Control control, Reader reader, Writer writer) {
|
||||
auto accessor = structive::trusted_callable_accessor<Object>(
|
||||
std::move(reader), std::move(writer));
|
||||
static_assert(std::same_as<typename decltype(accessor)::value_type, Value>);
|
||||
return adminive::field(std::move(name), std::move(label), std::move(accessor))
|
||||
.editable()
|
||||
.unsynchronized()
|
||||
.control(control);
|
||||
}
|
||||
|
||||
} // namespace renderive::web::gallery_adminive
|
||||
|
||||
namespace adminive {
|
||||
|
||||
template <>
|
||||
@@ -10,7 +29,7 @@ struct Type_Descriptor<renderive::web::Gallery_Feedback_Policy> {
|
||||
static auto get() {
|
||||
using T = renderive::web::Gallery_Feedback_Policy;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return object<T>("feedback_policy", "Consumer feedback",
|
||||
return adminive::object<T>("feedback_policy", "Consumer feedback",
|
||||
boolean<&T::enabled>("enabled", "Enabled"),
|
||||
boolean<&T::pixel>("pixel", "Pixel feedback"),
|
||||
boolean<&T::presentation>("presentation", "Presentation feedback"),
|
||||
@@ -20,30 +39,84 @@ struct Type_Descriptor<renderive::web::Gallery_Feedback_Policy> {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Type_Descriptor<renderive::web::Gallery_Session_Control_Model> {
|
||||
struct Type_Descriptor<renderive::web::Gallery_Session_Control> {
|
||||
static auto get() {
|
||||
using T = renderive::web::Gallery_Session_Control_Model;
|
||||
using T = renderive::web::Gallery_Session_Control;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return object<T>("gallery_session", "Scene",
|
||||
color<&T::background_color>("background_color", "Background"),
|
||||
boolean<&T::performance_overlay>("performance_overlay", "Performance overlay"),
|
||||
boolean<&T::renderable_visible>("renderable_visible", "Renderable visible"),
|
||||
select<&T::cache_mode>("cache_mode", "Cache mode"),
|
||||
editable<&T::object_name>("object_name", "Object name", Field_Control::text),
|
||||
boolean<&T::frequency_limit_enabled>("frequency_limit_enabled", "Frequency limit"),
|
||||
number<&T::max_render_fps>("max_render_fps", "Maximum render FPS"),
|
||||
editable<&T::feedback>("feedback", "Consumer feedback", Field_Control::automatic));
|
||||
const auto dirty = [](T& value) { value.plot_.notify_model_dirty(); };
|
||||
return adminive::object<T>("gallery_session", "Scene",
|
||||
callback_property<T, renderive::Color>(
|
||||
"background_color", "Background", Field_Control::color,
|
||||
[](const T& value) { return value.plot_.background_color(); },
|
||||
[dirty](T& value, renderive::Color color) {
|
||||
value.plot_.set_background_color(color);
|
||||
dirty(value);
|
||||
}),
|
||||
callback_property<T, bool>(
|
||||
"performance_overlay", "Performance overlay", Field_Control::boolean,
|
||||
[](const T& value) {
|
||||
const auto overlay = value.plot_.performance_overlay();
|
||||
return overlay && overlay->enabled();
|
||||
},
|
||||
[dirty](T& value, bool enabled) {
|
||||
renderive::set_performance_overlay_enabled(value.plot_, enabled);
|
||||
dirty(value);
|
||||
}),
|
||||
callback_property<T, bool>(
|
||||
"renderable_visible", "Renderable visible", Field_Control::boolean,
|
||||
[](const T& value) { return value.renderable_.is_visible(); },
|
||||
[dirty](T& value, bool visible) {
|
||||
value.renderable_.set_visible(visible);
|
||||
dirty(value);
|
||||
}),
|
||||
callback_property<T, renderive::Renderable_Cache_Mode>(
|
||||
"cache_mode", "Cache mode", Field_Control::select,
|
||||
[](const T& value) { return value.renderable_.get_cache_mode(); },
|
||||
[dirty](T& value, renderive::Renderable_Cache_Mode mode) {
|
||||
value.renderable_.set_cache_mode(mode);
|
||||
dirty(value);
|
||||
}),
|
||||
callback_property<T, std::string>(
|
||||
"object_name", "Object name", Field_Control::text,
|
||||
[](const T& value) { return value.renderable_.object_name(); },
|
||||
[dirty](T& value, std::string name) {
|
||||
value.renderable_.set_object_name(std::move(name));
|
||||
dirty(value);
|
||||
}),
|
||||
callback_property<T, bool>(
|
||||
"frequency_limit_enabled", "Frequency limit", Field_Control::boolean,
|
||||
[](const T& value) { return value.plot_.max_render_fps() > 0.0; },
|
||||
[dirty](T& value, bool enabled) {
|
||||
if (enabled) {
|
||||
if (value.plot_.max_render_fps() <= 0.0)
|
||||
value.plot_.set_max_render_fps(30.0);
|
||||
} else {
|
||||
value.plot_.clear_max_render_fps();
|
||||
}
|
||||
dirty(value);
|
||||
}),
|
||||
callback_property<T, double>(
|
||||
"max_render_fps", "Maximum render FPS", Field_Control::number,
|
||||
[](const T& value) {
|
||||
const double fps = value.plot_.max_render_fps();
|
||||
return fps > 0.0 ? fps : 30.0;
|
||||
},
|
||||
[dirty](T& value, double fps) {
|
||||
if (value.plot_.max_render_fps() > 0.0)
|
||||
value.plot_.set_max_render_fps(fps);
|
||||
dirty(value);
|
||||
}),
|
||||
callback_property<T, renderive::web::Gallery_Feedback_Policy>(
|
||||
"feedback", "Consumer feedback", Field_Control::automatic,
|
||||
[](const T& value) { return value.feedback_; },
|
||||
[dirty](T& value, renderive::web::Gallery_Feedback_Policy feedback) {
|
||||
value.feedback_ = std::move(feedback);
|
||||
dirty(value);
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Object_Adapter<renderive::web::Gallery_Session_Control> {
|
||||
using object_type = renderive::web::Gallery_Session_Control;
|
||||
using model_type = renderive::web::Gallery_Session_Control_Model;
|
||||
static model_type snapshot(const object_type& value) { return value.adminive_snapshot(); }
|
||||
static void commit(object_type& target, model_type value) {
|
||||
target.adminive_commit(std::move(value));
|
||||
}
|
||||
};
|
||||
static_assert(Described_Type<renderive::web::Gallery_Session_Control>);
|
||||
static_assert(Direct_Described_Object<renderive::web::Gallery_Session_Control>);
|
||||
|
||||
} // namespace adminive
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "Pixel_Frame.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#if defined(_M_X64) || defined(__x86_64__)
|
||||
#include <tmmintrin.h>
|
||||
@@ -26,24 +25,6 @@ std::uint8_t flatten(std::uint8_t premultiplied, std::uint8_t alpha,
|
||||
|
||||
} // namespace
|
||||
|
||||
Pixel_Frame_Copy copy_pixel_frame(Image_View image) {
|
||||
Pixel_Frame_Copy copy;
|
||||
if (image.empty() || image.format != Pixel_Format::Premultiplied_32 ||
|
||||
image.stride < image.width * static_cast<int>(sizeof(Pixel))) {
|
||||
return copy;
|
||||
}
|
||||
copy.width = image.width;
|
||||
copy.height = image.height;
|
||||
copy.pixels.resize(static_cast<std::size_t>(image.width) *
|
||||
static_cast<std::size_t>(image.height));
|
||||
for (int y = 0; y < image.height; ++y) {
|
||||
const auto* source = image.data + static_cast<std::ptrdiff_t>(y) * image.stride;
|
||||
std::memcpy(copy.pixels.data() + static_cast<std::size_t>(y) * image.width,
|
||||
source, static_cast<std::size_t>(image.width) * sizeof(Pixel));
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
std::string encode_pixel_frame(Image_View image, Color background) {
|
||||
if (image.empty() || image.format != Pixel_Format::Premultiplied_32 ||
|
||||
image.stride < image.width * static_cast<int>(sizeof(Pixel))) {
|
||||
@@ -129,13 +110,4 @@ std::string encode_pixel_frame(Image_View image, Color background) {
|
||||
return frame;
|
||||
}
|
||||
|
||||
std::string encode_pixel_frame(const Pixel_Frame_Copy& image, Color background) {
|
||||
if (image.empty())
|
||||
return {};
|
||||
return encode_pixel_frame(
|
||||
{reinterpret_cast<const std::byte*>(image.pixels.data()), image.width, image.height,
|
||||
image.width * static_cast<int>(sizeof(Pixel)), Pixel_Format::Premultiplied_32},
|
||||
background);
|
||||
}
|
||||
|
||||
} // namespace renderive::web
|
||||
|
||||
@@ -2,20 +2,8 @@
|
||||
#include "render_2D/base/Types.h"
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
namespace renderive::web {
|
||||
inline constexpr std::size_t pixel_frame_header_size = 16;
|
||||
struct Pixel_Frame_Copy {
|
||||
int width{};
|
||||
int height{};
|
||||
std::vector<Pixel> pixels;
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return width <= 0 || height <= 0 || pixels.empty();
|
||||
}
|
||||
};
|
||||
[[nodiscard]] Pixel_Frame_Copy copy_pixel_frame(Image_View image);
|
||||
[[nodiscard]] std::string encode_pixel_frame(Image_View image,
|
||||
Color background = Color::black());
|
||||
[[nodiscard]] std::string encode_pixel_frame(const Pixel_Frame_Copy& image,
|
||||
Color background = Color::black());
|
||||
} // namespace renderive::web
|
||||
|
||||
+15
-14
@@ -32,18 +32,19 @@ struct Gallery_Request {
|
||||
Gallery_Request_Kind kind = Gallery_Request_Kind::Catalog;
|
||||
std::string message;
|
||||
};
|
||||
using Web_Event = std::variant<Frame_Request,
|
||||
Viewport_Resize,
|
||||
Event,
|
||||
Pointer_Event,
|
||||
Wheel_Event,
|
||||
Key_Event,
|
||||
Set_Demo_Mode,
|
||||
Set_Center_Frequency,
|
||||
Set_Bandwidth,
|
||||
Set_Gain,
|
||||
Set_Max_Hold,
|
||||
Set_Smoothing,
|
||||
Clear_Selection,
|
||||
Gallery_Request>;
|
||||
|
||||
template <class... Scene_Events>
|
||||
using Basic_Web_Event = std::variant<Frame_Request,
|
||||
Viewport_Resize,
|
||||
Scene_Events...,
|
||||
Set_Demo_Mode,
|
||||
Set_Center_Frequency,
|
||||
Set_Bandwidth,
|
||||
Set_Gain,
|
||||
Set_Max_Hold,
|
||||
Set_Smoothing,
|
||||
Clear_Selection,
|
||||
Gallery_Request>;
|
||||
|
||||
using Web_Event = Basic_Web_Event<Event, Pointer_Event, Wheel_Event, Key_Event>;
|
||||
} // namespace renderive::web
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "Web_Event_Adapter.h"
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
@@ -10,19 +8,6 @@
|
||||
namespace renderive::web {
|
||||
namespace {
|
||||
|
||||
std::optional<Json::Value> parse_json(std::string_view message) {
|
||||
Json::CharReaderBuilder builder;
|
||||
builder["collectComments"] = false;
|
||||
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
|
||||
Json::Value root;
|
||||
std::string errors;
|
||||
if (!reader->parse(message.data(), message.data() + message.size(), &root, &errors) ||
|
||||
!root.isObject()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
std::optional<double> finite_number(const Json::Value& root, const char* name) {
|
||||
const auto& value = root[name];
|
||||
if (!value.isNumeric())
|
||||
@@ -75,7 +60,8 @@ Key key(const Json::Value& root) {
|
||||
return Key::Unknown;
|
||||
}
|
||||
|
||||
std::optional<Web_Event> pointer_event(const Json::Value& root, Event_Type type) {
|
||||
std::optional<Render_2D_Web_Events> pointer_event(const Json::Value& root,
|
||||
Event_Type type) {
|
||||
const auto x = finite_number(root, "x");
|
||||
const auto y = finite_number(root, "y");
|
||||
if (!x || !y)
|
||||
@@ -90,7 +76,7 @@ std::optional<Web_Event> pointer_event(const Json::Value& root, Event_Type type)
|
||||
return event;
|
||||
}
|
||||
|
||||
std::optional<Web_Event> wheel_event(const Json::Value& root) {
|
||||
std::optional<Render_2D_Web_Events> wheel_event(const Json::Value& root) {
|
||||
const auto x = finite_number(root, "x");
|
||||
const auto y = finite_number(root, "y");
|
||||
const auto pixel_x = finite_number(root, "pixelDeltaX");
|
||||
@@ -112,7 +98,7 @@ std::optional<Web_Event> wheel_event(const Json::Value& root) {
|
||||
return event;
|
||||
}
|
||||
|
||||
std::optional<Web_Event> key_event(const Json::Value& root, Event_Type type) {
|
||||
std::optional<Render_2D_Web_Events> key_event(const Json::Value& root, Event_Type type) {
|
||||
Key_Event event(type);
|
||||
event.key = key(root);
|
||||
event.native_key = root["nativeKey"].isUInt() ? root["nativeKey"].asUInt() : 0;
|
||||
@@ -121,61 +107,24 @@ std::optional<Web_Event> key_event(const Json::Value& root, Event_Type type) {
|
||||
return event;
|
||||
}
|
||||
|
||||
std::optional<Web_Event> control_event(const Json::Value& root) {
|
||||
if (!root["control"].isString())
|
||||
return std::nullopt;
|
||||
const std::string control = root["control"].asString();
|
||||
if (control == "mode" && root["value"].isString()) {
|
||||
const std::string value = root["value"].asString();
|
||||
if (value == "AM")
|
||||
return Set_Demo_Mode{Demo_Mode::Am};
|
||||
if (value == "FM")
|
||||
return Set_Demo_Mode{Demo_Mode::Fm};
|
||||
if (value == "USB")
|
||||
return Set_Demo_Mode{Demo_Mode::Usb};
|
||||
if (value == "LSB")
|
||||
return Set_Demo_Mode{Demo_Mode::Lsb};
|
||||
return std::nullopt;
|
||||
}
|
||||
if (control == "center_frequency_mhz") {
|
||||
const auto value = finite_number(root, "value");
|
||||
return value && *value >= 1.0 && *value <= 6000.0
|
||||
? std::optional<Web_Event>(Set_Center_Frequency{*value})
|
||||
: std::nullopt;
|
||||
}
|
||||
if (control == "bandwidth_khz") {
|
||||
const auto value = finite_number(root, "value");
|
||||
return value && *value >= 10.0 && *value <= 50000.0
|
||||
? std::optional<Web_Event>(Set_Bandwidth{*value})
|
||||
: std::nullopt;
|
||||
}
|
||||
if (control == "gain_db") {
|
||||
const auto value = finite_number(root, "value");
|
||||
return value && *value >= -30.0 && *value <= 80.0
|
||||
? std::optional<Web_Event>(Set_Gain{*value})
|
||||
: std::nullopt;
|
||||
}
|
||||
if (control == "max_hold") {
|
||||
const auto value = boolean(root, "value");
|
||||
return value ? std::optional<Web_Event>(Set_Max_Hold{*value}) : std::nullopt;
|
||||
}
|
||||
if (control == "smoothing") {
|
||||
const auto value = boolean(root, "value");
|
||||
return value ? std::optional<Web_Event>(Set_Smoothing{*value}) : std::nullopt;
|
||||
}
|
||||
if (control == "clear_selection")
|
||||
return Clear_Selection{};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::optional<Web_Event> Web_Event_Adapter::decode(std::string_view message) {
|
||||
const auto parsed = parse_json(message);
|
||||
if (!parsed || (*parsed)["category"].asString() != "event" || !(*parsed)["type"].isString())
|
||||
std::optional<Json::Value> parse_web_event(std::string_view message) {
|
||||
Json::CharReaderBuilder builder;
|
||||
builder["collectComments"] = false;
|
||||
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
|
||||
Json::Value root;
|
||||
std::string errors;
|
||||
if (!reader->parse(message.data(), message.data() + message.size(), &root, &errors) ||
|
||||
!root.isObject() || root["category"].asString() != "event" ||
|
||||
!root["type"].isString()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
const Json::Value& root = *parsed;
|
||||
std::optional<Web_Transport_Events> Web_Transport_Event_Decoder::decode(
|
||||
const Json::Value& root, std::string_view source) {
|
||||
const std::string type = root["type"].asString();
|
||||
if (type == "frame")
|
||||
return Frame_Request{};
|
||||
@@ -187,6 +136,25 @@ std::optional<Web_Event> Web_Event_Adapter::decode(std::string_view message) {
|
||||
return Viewport_Resize{{static_cast<int>(std::clamp(*width, 240.0, 1920.0)),
|
||||
static_cast<int>(std::clamp(*height, 180.0, 1200.0))}};
|
||||
}
|
||||
Gallery_Request_Kind kind;
|
||||
if (type == "gallery_catalog")
|
||||
kind = Gallery_Request_Kind::Catalog;
|
||||
else if (type == "gallery_open")
|
||||
kind = Gallery_Request_Kind::Open;
|
||||
else if (type == "gallery_patch")
|
||||
kind = Gallery_Request_Kind::Patch;
|
||||
else if (type == "gallery_action")
|
||||
kind = Gallery_Request_Kind::Action;
|
||||
else if (type == "gallery_observe")
|
||||
kind = Gallery_Request_Kind::Observe;
|
||||
else
|
||||
return std::nullopt;
|
||||
return Gallery_Request{kind, std::string(source)};
|
||||
}
|
||||
|
||||
std::optional<Render_2D_Web_Events> Render_2D_Web_Event_Decoder::decode(
|
||||
const Json::Value& root, std::string_view) {
|
||||
const std::string type = root["type"].asString();
|
||||
if (type == "show")
|
||||
return Event(Event_Type::Show);
|
||||
if (type == "hide")
|
||||
@@ -205,18 +173,56 @@ std::optional<Web_Event> Web_Event_Adapter::decode(std::string_view message) {
|
||||
return key_event(root, Event_Type::Key_Press);
|
||||
if (type == "key_release")
|
||||
return key_event(root, Event_Type::Key_Release);
|
||||
if (type == "control")
|
||||
return control_event(root);
|
||||
if (type == "gallery_catalog")
|
||||
return Gallery_Request{Gallery_Request_Kind::Catalog, std::string(message)};
|
||||
if (type == "gallery_open")
|
||||
return Gallery_Request{Gallery_Request_Kind::Open, std::string(message)};
|
||||
if (type == "gallery_patch")
|
||||
return Gallery_Request{Gallery_Request_Kind::Patch, std::string(message)};
|
||||
if (type == "gallery_action")
|
||||
return Gallery_Request{Gallery_Request_Kind::Action, std::string(message)};
|
||||
if (type == "gallery_observe")
|
||||
return Gallery_Request{Gallery_Request_Kind::Observe, std::string(message)};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Web_Demo_Control_Events> Web_Demo_Control_Decoder::decode(
|
||||
const Json::Value& root, std::string_view) {
|
||||
if (root["type"].asString() != "control" || !root["control"].isString())
|
||||
return std::nullopt;
|
||||
const std::string control = root["control"].asString();
|
||||
if (control == "mode" && root["value"].isString()) {
|
||||
const std::string value = root["value"].asString();
|
||||
if (value == "AM")
|
||||
return Set_Demo_Mode{Demo_Mode::Am};
|
||||
if (value == "FM")
|
||||
return Set_Demo_Mode{Demo_Mode::Fm};
|
||||
if (value == "USB")
|
||||
return Set_Demo_Mode{Demo_Mode::Usb};
|
||||
if (value == "LSB")
|
||||
return Set_Demo_Mode{Demo_Mode::Lsb};
|
||||
return std::nullopt;
|
||||
}
|
||||
if (control == "center_frequency_mhz") {
|
||||
const auto value = finite_number(root, "value");
|
||||
return value && *value >= 1.0 && *value <= 6000.0
|
||||
? std::optional<Web_Demo_Control_Events>(Set_Center_Frequency{*value})
|
||||
: std::nullopt;
|
||||
}
|
||||
if (control == "bandwidth_khz") {
|
||||
const auto value = finite_number(root, "value");
|
||||
return value && *value >= 10.0 && *value <= 50000.0
|
||||
? std::optional<Web_Demo_Control_Events>(Set_Bandwidth{*value})
|
||||
: std::nullopt;
|
||||
}
|
||||
if (control == "gain_db") {
|
||||
const auto value = finite_number(root, "value");
|
||||
return value && *value >= -30.0 && *value <= 80.0
|
||||
? std::optional<Web_Demo_Control_Events>(Set_Gain{*value})
|
||||
: std::nullopt;
|
||||
}
|
||||
if (control == "max_hold") {
|
||||
const auto value = boolean(root, "value");
|
||||
return value ? std::optional<Web_Demo_Control_Events>(Set_Max_Hold{*value})
|
||||
: std::nullopt;
|
||||
}
|
||||
if (control == "smoothing") {
|
||||
const auto value = boolean(root, "value");
|
||||
return value ? std::optional<Web_Demo_Control_Events>(Set_Smoothing{*value})
|
||||
: std::nullopt;
|
||||
}
|
||||
if (control == "clear_selection")
|
||||
return Clear_Selection{};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,95 @@
|
||||
|
||||
#include "Web_Event.h"
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <concepts>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace renderive::web {
|
||||
|
||||
class Web_Event_Adapter final {
|
||||
public:
|
||||
[[nodiscard]] static std::optional<Web_Event> decode(std::string_view message);
|
||||
using Web_Transport_Events = std::variant<Frame_Request, Viewport_Resize, Gallery_Request>;
|
||||
using Render_2D_Web_Events = std::variant<Event, Pointer_Event, Wheel_Event, Key_Event>;
|
||||
using Web_Demo_Control_Events = std::variant<Set_Demo_Mode,
|
||||
Set_Center_Frequency,
|
||||
Set_Bandwidth,
|
||||
Set_Gain,
|
||||
Set_Max_Hold,
|
||||
Set_Smoothing,
|
||||
Clear_Selection>;
|
||||
|
||||
struct Web_Transport_Event_Decoder {
|
||||
using event_type = Web_Transport_Events;
|
||||
[[nodiscard]] static std::optional<event_type> decode(const Json::Value& root,
|
||||
std::string_view source);
|
||||
};
|
||||
|
||||
struct Render_2D_Web_Event_Decoder {
|
||||
using event_type = Render_2D_Web_Events;
|
||||
[[nodiscard]] static std::optional<event_type> decode(const Json::Value& root,
|
||||
std::string_view source);
|
||||
};
|
||||
|
||||
struct Web_Demo_Control_Decoder {
|
||||
using event_type = Web_Demo_Control_Events;
|
||||
[[nodiscard]] static std::optional<event_type> decode(const Json::Value& root,
|
||||
std::string_view source);
|
||||
};
|
||||
|
||||
template <class Decoder>
|
||||
concept Web_Event_Decoder = requires(const Json::Value& root, std::string_view source) {
|
||||
typename Decoder::event_type;
|
||||
{ Decoder::decode(root, source) } ->
|
||||
std::same_as<std::optional<typename Decoder::event_type>>;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::optional<Json::Value> parse_web_event(std::string_view message);
|
||||
|
||||
template <class T>
|
||||
struct Is_Variant : std::false_type {};
|
||||
|
||||
template <class... Values>
|
||||
struct Is_Variant<std::variant<Values...>> : std::true_type {};
|
||||
|
||||
template <class Event_Variant, Web_Event_Decoder... Decoders>
|
||||
class Basic_Web_Event_Adapter final {
|
||||
public:
|
||||
[[nodiscard]] static std::optional<Event_Variant> decode(std::string_view message) {
|
||||
const auto root = parse_web_event(message);
|
||||
if (!root)
|
||||
return std::nullopt;
|
||||
|
||||
std::optional<Event_Variant> result;
|
||||
([&] {
|
||||
if (result)
|
||||
return;
|
||||
auto decoded = Decoders::decode(*root, message);
|
||||
if (decoded)
|
||||
result = widen(std::move(*decoded));
|
||||
}(), ...);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
template <class Decoded>
|
||||
static Event_Variant widen(Decoded decoded) {
|
||||
if constexpr (Is_Variant<std::remove_cvref_t<Decoded>>::value) {
|
||||
return std::visit([](auto&& value) -> Event_Variant {
|
||||
return Event_Variant(std::forward<decltype(value)>(value));
|
||||
}, std::move(decoded));
|
||||
} else {
|
||||
return Event_Variant(std::move(decoded));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using Web_Event_Adapter = Basic_Web_Event_Adapter<Web_Event,
|
||||
Web_Transport_Event_Decoder,
|
||||
Render_2D_Web_Event_Decoder,
|
||||
Web_Demo_Control_Decoder>;
|
||||
|
||||
} // namespace renderive::web
|
||||
|
||||
@@ -286,7 +286,7 @@ struct Web_Plot_Session::Impl {
|
||||
plot.deactivate_view();
|
||||
plot.dispatch_event(value);
|
||||
}
|
||||
else if constexpr (std::is_base_of_v<Event, T>) {
|
||||
else if constexpr (Event_Object<T>) {
|
||||
plot.dispatch_event(value);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, Set_Demo_Mode>) {
|
||||
|
||||
@@ -23,6 +23,26 @@
|
||||
namespace renderive::web {
|
||||
namespace {
|
||||
|
||||
struct Spatial_Test_Event {
|
||||
int layer{};
|
||||
};
|
||||
|
||||
struct Spatial_Test_Decoder {
|
||||
using event_type = Spatial_Test_Event;
|
||||
|
||||
static std::optional<event_type> decode(const Json::Value& root, std::string_view) {
|
||||
if (root["type"].asString() != "spatial_test" || !root["layer"].isInt())
|
||||
return std::nullopt;
|
||||
return Spatial_Test_Event{root["layer"].asInt()};
|
||||
}
|
||||
};
|
||||
|
||||
using Extended_Web_Event = Basic_Web_Event<Event, Pointer_Event, Wheel_Event,
|
||||
Key_Event, Spatial_Test_Event>;
|
||||
using Extended_Web_Event_Adapter = Basic_Web_Event_Adapter<
|
||||
Extended_Web_Event, Web_Transport_Event_Decoder, Render_2D_Web_Event_Decoder,
|
||||
Web_Demo_Control_Decoder, Spatial_Test_Decoder>;
|
||||
|
||||
std::uint32_t read_u32_le(const std::string& value, std::size_t offset) {
|
||||
return static_cast<std::uint8_t>(value[offset]) |
|
||||
(static_cast<std::uint32_t>(static_cast<std::uint8_t>(value[offset + 1])) << 8U) |
|
||||
@@ -247,6 +267,14 @@ TEST(RenderiveWebGallery, CatalogOwnsDashboardFieldsAndDynamicBehavior) {
|
||||
EXPECT_TRUE(action->at("request_frame"));
|
||||
}
|
||||
|
||||
TEST(RenderiveWebBridge, ComposesAdditionalEventDecoders) {
|
||||
const auto event = Extended_Web_Event_Adapter::decode(
|
||||
R"({"category":"event","type":"spatial_test","layer":7})");
|
||||
ASSERT_TRUE(event.has_value());
|
||||
ASSERT_TRUE(std::holds_alternative<Spatial_Test_Event>(*event));
|
||||
EXPECT_EQ(std::get<Spatial_Test_Event>(*event).layer, 7);
|
||||
}
|
||||
|
||||
TEST(RenderiveWebGallery, AdminiveResourcesPatchTheRealRenderableState) {
|
||||
Gallery_Plot_Session session;
|
||||
auto response = response_json(session.handle(gallery_request(
|
||||
@@ -265,7 +293,7 @@ TEST(RenderiveWebGallery, AdminiveResourcesPatchTheRealRenderableState) {
|
||||
};
|
||||
|
||||
const auto& spectrum = find_resource(response, "spectrum");
|
||||
EXPECT_EQ(spectrum.at("descriptor").at("name"), "Spectrum_Properties");
|
||||
EXPECT_EQ(spectrum.at("descriptor").at("name"), "Gallery_Spectrum");
|
||||
EXPECT_TRUE(spectrum.at("data").at("frequency_range").is_object());
|
||||
EXPECT_EQ(spectrum.at("data").at("frequency_point_size"), 512);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user