#ifndef UI_GFX_GEOMETRY_AXIS_TRANSFORM2D_H_
#define UI_GFX_GEOMETRY_AXIS_TRANSFORM2D_H_
#include <optional>
#include "base/check_op.h"
#include "base/component_export.h"
#include "ui/gfx/geometry/clamp_float_geometry.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace mojo {
template <typename DataViewType, typename T>
struct StructTraits;
}
namespace gfx {
namespace mojom {
class AxisTransform2dDataView;
}
struct DecomposedTransform;
class COMPONENT_EXPORT(GEOMETRY) AxisTransform2d {
public:
constexpr AxisTransform2d() = default;
constexpr AxisTransform2d(float scale, const Vector2dF& translation)
: scale_(scale, scale), translation_(translation) {}
static constexpr AxisTransform2d FromScaleAndTranslation(
const Vector2dF& scale,
const Vector2dF& translation) {
return AxisTransform2d(scale, translation);
}
friend constexpr bool operator==(const AxisTransform2d&,
const AxisTransform2d&) = default;
void PreScale(const Vector2dF& scale) { scale_.Scale(scale.x(), scale.y()); }
void PostScale(const Vector2dF& scale) {
scale_.Scale(scale.x(), scale.y());
translation_.Scale(scale.x(), scale.y());
}
void PreTranslate(const Vector2dF& translation) {
translation_ += ScaleVector2d(translation, scale_.x(), scale_.y());
}
void PostTranslate(const Vector2dF& translation) {
translation_ += translation;
}
void PreConcat(const AxisTransform2d& pre) {
PreTranslate(pre.translation_);
PreScale(pre.scale_);
}
void PostConcat(const AxisTransform2d& post) {
PostScale(post.scale_);
PostTranslate(post.translation_);
}
double Determinant() const { return double{scale_.x()} * scale_.y(); }
bool IsInvertible() const {
return std::isnormal(scale_.x() * scale_.y());
}
void Invert() {
DCHECK(IsInvertible());
scale_ = Vector2dF(1.f / scale_.x(), 1.f / scale_.y());
translation_.Scale(-scale_.x(), -scale_.y());
}
void Zoom(float zoom_factor) { translation_.Scale(zoom_factor); }
PointF MapPoint(const PointF& p) const {
return PointF(MapX(p.x()), MapY(p.y()));
}
PointF InverseMapPoint(const PointF& p) const {
return PointF(InverseMapX(p.x()), InverseMapY(p.y()));
}
RectF MapRect(const RectF& r) const {
DCHECK_GE(scale_.x(), 0.f);
DCHECK_GE(scale_.y(), 0.f);
return RectF(MapX(r.x()), MapY(r.y()),
ClampFloatGeometry(r.width() * scale_.x()),
ClampFloatGeometry(r.height() * scale_.y()));
}
RectF InverseMapRect(const RectF& r) const {
DCHECK_GT(scale_.x(), 0.f);
DCHECK_GT(scale_.y(), 0.f);
return RectF(InverseMapX(r.x()), InverseMapY(r.y()),
ClampFloatGeometry(r.width() * (1.f / scale_.x())),
ClampFloatGeometry(r.height() * (1.f / scale_.y())));
}
DecomposedTransform Decompose() const;
constexpr const Vector2dF& scale() const { return scale_; }
constexpr const Vector2dF& translation() const { return translation_; }
std::string ToString() const;
private:
friend struct mojo::StructTraits<mojom::AxisTransform2dDataView,
AxisTransform2d>;
constexpr AxisTransform2d(const Vector2dF& scale,
const Vector2dF& translation)
: scale_(scale), translation_(translation) {}
float MapX(float x) const {
return ClampFloatGeometry(x * scale_.x() + translation_.x());
}
float MapY(float y) const {
return ClampFloatGeometry(y * scale_.y() + translation_.y());
}
float InverseMapX(float x) const {
return ClampFloatGeometry((x - translation_.x()) * (1.f / scale_.x()));
}
float InverseMapY(float y) const {
return ClampFloatGeometry((y - translation_.y()) * (1.f / scale_.y()));
}
Vector2dF scale_{1.f, 1.f};
Vector2dF translation_;
};
inline AxisTransform2d PreScaleAxisTransform2d(const AxisTransform2d& t,
float scale) {
AxisTransform2d result(t);
result.PreScale(Vector2dF(scale, scale));
return result;
}
inline AxisTransform2d PostScaleAxisTransform2d(const AxisTransform2d& t,
float scale) {
AxisTransform2d result(t);
result.PostScale(Vector2dF(scale, scale));
return result;
}
inline AxisTransform2d PreTranslateAxisTransform2d(
const AxisTransform2d& t,
const Vector2dF& translation) {
AxisTransform2d result(t);
result.PreTranslate(translation);
return result;
}
inline AxisTransform2d PostTranslateAxisTransform2d(
const AxisTransform2d& t,
const Vector2dF& translation) {
AxisTransform2d result(t);
result.PostTranslate(translation);
return result;
}
inline AxisTransform2d ConcatAxisTransform2d(const AxisTransform2d& post,
const AxisTransform2d& pre) {
AxisTransform2d result(post);
result.PreConcat(pre);
return result;
}
inline AxisTransform2d InvertAxisTransform2d(const AxisTransform2d& t) {
AxisTransform2d result = t;
result.Invert();
return result;
}
void PrintTo(const AxisTransform2d&, ::std::ostream* os);
}
#endif