Files
Renderive/render_3D/render_3D/Point_Visual.cpp
T
2026-08-14 17:53:13 +08:00

129 lines
4.1 KiB
C++

#include "Point_Visual.h"
#include "detail/Point_Core.h"
#include <renderive/real_time_data/Double_Buffer_Strategy.hpp>
#include <renderive/state/Double_State_Storage.hpp>
#include <cmath>
#include <mutex>
#include <stdexcept>
#include <utility>
namespace renderive::render_3d {
namespace {
void validate(const Point_State& state) {
if (!std::isfinite(state.style.stroke_width_px) ||
state.style.stroke_width_px < 0.0F)
throw std::invalid_argument("point stroke width must be finite and nonnegative");
for (float value : state.transform.values) {
if (!std::isfinite(value))
throw std::invalid_argument("point transform must contain finite values");
}
}
void validate(const std::vector<Point>& points) {
for (const auto& point : points) {
if (!std::isfinite(point.position.x) || !std::isfinite(point.position.y) ||
!std::isfinite(point.position.z) || !std::isfinite(point.diameter_px) ||
point.diameter_px <= 0.0F)
throw std::invalid_argument("point payload contains invalid coordinates or diameter");
}
}
} // namespace
namespace detail {
struct Point_Payload_Tag {};
using Point_Payload = std::shared_ptr<const std::vector<Point>>;
using Point_Buffer_Layout = ::Double_Buffer_Layout<
::Buffered_Data<Point_Payload_Tag, Point_Payload>>;
using Point_Buffer_Strategy =
::Multi_Double_Buffer_Strategy<Point_Buffer_Layout, std::mutex>;
using Point_State_Strategy =
::Double_State_Storage<Point_State, std::mutex>;
} // namespace detail
struct Point_Visual::Impl final : detail::Point_Buffer_Strategy,
detail::Point_State_Strategy {
Impl(std::vector<Point> points, const Point_State& initial)
: detail::Point_State_Strategy(initial) {
validate(initial);
update_points(std::move(points));
}
void update_points(std::vector<Point> points) {
validate(points);
write<detail::Point_Payload_Tag>(
std::make_shared<const std::vector<Point>>(std::move(points)));
}
void edit_points(const std::function<void(std::vector<Point>&)>& edit) {
if (!edit)
throw std::invalid_argument("Point_Visual point edit is empty");
const auto& published = points();
auto next = published ? *published : std::vector<Point>{};
edit(next);
update_points(std::move(next));
}
[[nodiscard]] const detail::Point_Payload& points() const noexcept {
return render_buffer_value<detail::Point_Payload_Tag>();
}
[[nodiscard]] std::size_t point_count() const noexcept {
return points() ? points()->size() : 0U;
}
void configure(Point_State state) {
validate(state);
update([state = std::move(state)](Point_State& target) mutable {
target = std::move(state);
});
}
[[nodiscard]] detail::Published_Point publish_frame() {
detail::Point_State_Strategy::publish();
detail::Point_Buffer_Strategy::publish();
return {
detail::Point_State_Strategy::render_state_value(),
detail::Point_Buffer_Strategy::render_buffer_value<
detail::Point_Payload_Tag>(),
detail::Point_State_Strategy::state_revision(),
detail::Point_Buffer_Strategy::revision()};
}
};
Point_Visual::Point_Visual(std::vector<Point> points, Point_State initial)
: impl_(std::make_unique<Impl>(std::move(points), initial)) {}
Point_Visual::~Point_Visual() = default;
void Point_Visual::update_points(std::vector<Point> points) {
impl_->update_points(std::move(points));
}
void Point_Visual::edit_points(
const std::function<void(std::vector<Point>&)>& edit) {
impl_->edit_points(edit);
}
std::size_t Point_Visual::point_count() const {
return impl_->point_count();
}
std::uint64_t Point_Visual::data_revision() const {
return impl_->revision();
}
void Point_Visual::configure(Point_State state) {
impl_->configure(std::move(state));
}
detail::Published_Point detail::Point_State_Access::publish(Point_Visual& visual) {
return visual.impl_->publish_frame();
}
} // namespace renderive::render_3d