diff --git a/src/kiwano/2d/ActionGroup.h b/src/kiwano/2d/ActionGroup.h index d3286225..7615f177 100644 --- a/src/kiwano/2d/ActionGroup.h +++ b/src/kiwano/2d/ActionGroup.h @@ -72,6 +72,9 @@ namespace kiwano }; +#pragma warning(push) +#pragma warning(disable : 4996) + // 顺序动作 class KGE_API ActionSequence : public ActionGroup @@ -100,4 +103,7 @@ namespace kiwano virtual ~ActionSpawn() {} }; + +#pragma warning(pop) + } diff --git a/src/kiwano/2d/ActionTween.cpp b/src/kiwano/2d/ActionTween.cpp index 21f6ddef..437ea893 100644 --- a/src/kiwano/2d/ActionTween.cpp +++ b/src/kiwano/2d/ActionTween.cpp @@ -424,36 +424,84 @@ namespace kiwano // ActionPath //------------------------------------------------------- - ActionPath::ActionPath(Duration duration, GeometryPtr geo, bool rotating, float start, float end, EaseFunc func) + ActionPath::ActionPath(Duration duration, bool rotating, float start, float end, EaseFunc func) : ActionTween(duration, func) , start_(start) , end_(end) - , geo_(geo) , rotating_(rotating) + , path_beginning_(false) + , geo_(nullptr) + , geo_sink_(nullptr) { } ActionPtr ActionPath::Clone() const { - return new ActionPath(dur_, geo_, rotating_, start_, end_, ease_func_); + ActionPathPtr clone = new ActionPath(dur_, rotating_, start_, end_, ease_func_); + if (clone) + { + clone->SetGeometry(geo_); + } + return clone; } ActionPtr ActionPath::Reverse() const { - return new ActionPath(dur_, geo_, rotating_, end_, start_, ease_func_); + ActionPathPtr reverse = new ActionPath(dur_, rotating_, end_, start_, ease_func_); + if (reverse) + { + reverse->SetGeometry(geo_); + } + return reverse; + } + + float ActionPath::GetPathLength() const + { + float length = 0.f; + if (geo_) + { + // no matter it failed or not + geo_->ComputeLength(D2D1::Matrix3x2F::Identity(), &length); + } + return length; + } + + bool ActionPath::ComputePointAtLength(float length, Point* point, Vec2* tangent) const + { + if (geo_) + { + HRESULT hr = geo_->ComputePointAtLength( + length, + D2D1::Matrix3x2F::Identity(), + DX::ConvertToPoint2F(point), + DX::ConvertToPoint2F(tangent) + ); + return SUCCEEDED(hr); + } + return false; } void ActionPath::Init(NodePtr target) { start_pos_ = target->GetPosition(); + + if (path_beginning_) + { + EndPath(); + } + + if (!geo_) + { + Complete(target); + } } void ActionPath::UpdateTween(NodePtr target, float percent) { - float length = geo_->GetLength() * std::min(std::max((end_ - start_) * percent + start_, 0.f), 1.f); + float length = GetPathLength() * std::min(std::max((end_ - start_) * percent + start_, 0.f), 1.f); Point point, tangent; - if (geo_->ComputePointAt(length, &point, &tangent)) + if (ComputePointAtLength(length, &point, &tangent)) { target->SetPosition(start_pos_ + point); @@ -466,6 +514,123 @@ namespace kiwano } } + void ActionPath::BeginPath() + { + if (path_beginning_) return; + path_beginning_ = true; + + geo_ = nullptr; + geo_sink_ = nullptr; + + auto factory = Renderer::Instance()->GetD2DDeviceResources()->GetFactory(); + + ThrowIfFailed( + factory->CreatePathGeometry(&geo_) + ); + + ThrowIfFailed( + geo_->Open(&geo_sink_) + ); + + geo_sink_->BeginFigure(DX::ConvertToPoint2F(Point{ 0, 0 }), D2D1_FIGURE_BEGIN_FILLED); + } + + void ActionPath::EndPath(bool closed) + { + if (!path_beginning_) return; + path_beginning_ = false; + + if (geo_sink_) + { + geo_sink_->EndFigure(closed ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN); + ThrowIfFailed( + geo_sink_->Close() + ); + + // Clear geometry sink + geo_sink_ = nullptr; + } + } + + void ActionPath::AddLine(Point const& point) + { + if (!path_beginning_) + { + BeginPath(); + } + + if (geo_sink_) + { + geo_sink_->AddLine(DX::ConvertToPoint2F(point)); + } + } + + void ActionPath::AddLines(Array const& points) + { + if (!path_beginning_) + { + BeginPath(); + } + + if (geo_sink_ && !points.empty()) + { + geo_sink_->AddLines( + reinterpret_cast(&points[0]), + static_cast(points.size()) + ); + } + } + + void ActionPath::AddBezier(Point const& point1, Point const& point2, Point const& point3) + { + if (!path_beginning_) + { + BeginPath(); + } + + if (geo_sink_) + { + geo_sink_->AddBezier( + D2D1::BezierSegment( + DX::ConvertToPoint2F(point1), + DX::ConvertToPoint2F(point2), + DX::ConvertToPoint2F(point3) + ) + ); + } + } + + void ActionPath::AddArc(Point const& point, Size const& radius, float rotation, bool clockwise, bool is_small) + { + if (!path_beginning_) + { + BeginPath(); + } + + if (geo_sink_) + { + geo_sink_->AddArc( + D2D1::ArcSegment( + DX::ConvertToPoint2F(point), + DX::ConvertToSizeF(radius), + rotation, + clockwise ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE, + is_small ? D2D1_ARC_SIZE_SMALL : D2D1_ARC_SIZE_LARGE + ) + ); + } + } + + void ActionPath::ClearPath() + { + if (path_beginning_) + { + EndPath(); + } + + geo_sink_ = nullptr; + geo_ = nullptr; + } //------------------------------------------------------- // ActionCustom diff --git a/src/kiwano/2d/ActionTween.h b/src/kiwano/2d/ActionTween.h index 272de038..598db22b 100644 --- a/src/kiwano/2d/ActionTween.h +++ b/src/kiwano/2d/ActionTween.h @@ -20,8 +20,8 @@ #pragma once #include "Action.h" -#include "Geometry.h" // ActionPath #include "../base/logs.h" +#include "../renderer/render.h" // ID2D1PathGeometry, ID2D1GeometrySink namespace kiwano { @@ -419,7 +419,6 @@ namespace kiwano public: ActionPath( Duration duration, /* 持续时长 */ - GeometryPtr geo, /* 几何图形 */ bool rotating = false, /* 沿路径切线方向旋转 */ float start = 0.f, /* 起点 */ float end = 1.f, /* 终点 */ @@ -432,17 +431,69 @@ namespace kiwano // 获取该动作的倒转 ActionPtr Reverse() const override; + // 开始添加路径 + void BeginPath(); + + // 结束路径 + void EndPath( + bool closed = false /* 路径是否闭合 */ + ); + + // 添加一条线段 + void AddLine( + Point const& point /* 端点 */ + ); + + // 添加多条线段 + void AddLines( + Array const& points + ); + + // 添加一条三次方贝塞尔曲线 + void AddBezier( + Point const& point1, /* 贝塞尔曲线的第一个控制点 */ + Point const& point2, /* 贝塞尔曲线的第二个控制点 */ + Point const& point3 /* 贝塞尔曲线的终点 */ + ); + + // 添加弧线 + void AddArc( + Point const& point, /* 终点 */ + Size const& radius, /* 椭圆半径 */ + float rotation, /* 椭圆旋转角度 */ + bool clockwise = true, /* 顺时针 or 逆时针 */ + bool is_small = true /* 是否取小于 180° 的弧 */ + ); + + // 清除路径 + void ClearPath(); + + // 获取路径长度 + float GetPathLength() const; + + // 计算当前路径上指定点坐标和切线 + bool ComputePointAtLength(float length, Point* point, Vec2* tangent) const; + + // 获取几何路径 + inline ComPtr GetGeometry() const { return geo_; } + + // 设置几何路径 + inline void SetGeometry(ComPtr geo) { geo_ = geo; } + protected: void Init(NodePtr target) override; void UpdateTween(NodePtr target, float percent) override; protected: - bool rotating_; - float start_; - float end_; - Point start_pos_; - GeometryPtr geo_; + bool path_beginning_; + bool rotating_; + float start_; + float end_; + Point start_pos_; + + ComPtr geo_; + ComPtr geo_sink_; }; diff --git a/src/kiwano/renderer/helper.hpp b/src/kiwano/renderer/helper.hpp index 47098a76..ec536fdf 100644 --- a/src/kiwano/renderer/helper.hpp +++ b/src/kiwano/renderer/helper.hpp @@ -38,36 +38,110 @@ namespace kiwano } } - inline D2D1_POINT_2F const& ConvertToPoint2F(Vec2 const& point) + // + // Point2F + // + + inline D2D1_POINT_2F const& ConvertToPoint2F(Vec2 const& vec2) { - return reinterpret_cast(point); + return reinterpret_cast(vec2); } - inline D2D1_SIZE_F const& ConvertToSizeF(Vec2 const& size) + inline D2D1_POINT_2F& ConvertToPoint2F(Vec2& vec2) { - return reinterpret_cast(size); + return reinterpret_cast(vec2); } + inline const D2D1_POINT_2F* ConvertToPoint2F(const Vec2* vec2) + { + return reinterpret_cast(vec2); + } + + inline D2D1_POINT_2F* ConvertToPoint2F(Vec2* vec2) + { + return reinterpret_cast(vec2); + } + + // + // SizeF + // + + inline D2D1_SIZE_F const& ConvertToSizeF(Vec2 const& vec2) + { + return reinterpret_cast(vec2); + } + + inline D2D1_SIZE_F& ConvertToSizeF(Vec2& vec2) + { + return reinterpret_cast(vec2); + } + + inline const D2D1_SIZE_F* ConvertToSizeF(const Vec2* vec2) + { + return reinterpret_cast(vec2); + } + + inline D2D1_SIZE_F* ConvertToSizeF(Vec2* vec2) + { + return reinterpret_cast(vec2); + } + + // + // SizeF + // + inline D2D1_RECT_F ConvertToRectF(Rect const& rect) { return D2D1_RECT_F{ rect.origin.x, rect.origin.y, rect.origin.x + rect.size.x, rect.origin.y + rect.size.y }; } + // + // SizeF + // inline D2D1_COLOR_F const& ConvertToColorF(Color const& color) { return reinterpret_cast(color); } + inline D2D1_COLOR_F& ConvertToColorF(Color& color) + { + return reinterpret_cast(color); + } + + inline const D2D1_COLOR_F* ConvertToColorF(const Color* color) + { + return reinterpret_cast(color); + } + + inline D2D1_COLOR_F* ConvertToColorF(Color* color) + { + return reinterpret_cast(color); + } + + // + // SizeF + // + inline D2D1_MATRIX_3X2_F const& ConvertToMatrix3x2F(Matrix const& matrix) { return reinterpret_cast(matrix); } + inline D2D1_MATRIX_3X2_F& ConvertToMatrix3x2F(Matrix& matrix) + { + return reinterpret_cast(matrix); + } + inline const D2D1_MATRIX_3X2_F* ConvertToMatrix3x2F(const Matrix* matrix) { return reinterpret_cast(matrix); } + inline D2D1_MATRIX_3X2_F* ConvertToMatrix3x2F(Matrix* matrix) + { + return reinterpret_cast(matrix); + } + // Converts a length in device-independent pixels (DIPs) to a length in physical pixels. inline float ConvertDipsToPixels(float dips, float dpi) {