1// Aseprite
2// Copyright (C) 2020-2022 Igara Studio S.A.
3// Copyright (C) 2001-2016 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifndef APP_TRANSFORMATION_H_INCLUDED
9#define APP_TRANSFORMATION_H_INCLUDED
10#pragma once
11
12#include "gfx/point.h"
13#include "gfx/rect.h"
14#include <vector>
15
16#define CORNER_THICK_FOR_TILEMAP_MODE 0.001
17#define CORNER_THICK_FOR_PIXELS_MODE 1.0
18
19namespace app {
20
21// Represents a transformation that can be done by the user in the
22// document when he/she moves the mask using the selection handles.
23class Transformation {
24public:
25 class Corners {
26 public:
27 enum {
28 LEFT_TOP = 0,
29 RIGHT_TOP = 1,
30 RIGHT_BOTTOM = 2,
31 LEFT_BOTTOM = 3,
32 NUM_OF_CORNERS = 4
33 };
34
35 Corners() : m_corners(NUM_OF_CORNERS) { }
36 Corners(const gfx::RectF bounds) : m_corners(NUM_OF_CORNERS) {
37 m_corners[LEFT_TOP].x = bounds.x;
38 m_corners[LEFT_TOP].y = bounds.y;
39 m_corners[RIGHT_TOP].x = bounds.x2();
40 m_corners[RIGHT_TOP].y = bounds.y;
41 m_corners[RIGHT_BOTTOM].x = bounds.x2();
42 m_corners[RIGHT_BOTTOM].y = bounds.y2();
43 m_corners[LEFT_BOTTOM].x = bounds.x;
44 m_corners[LEFT_BOTTOM].y = bounds.y2();
45 }
46
47 std::size_t size() const { return m_corners.size(); }
48
49 gfx::PointF& operator[](int index) { return m_corners[index]; }
50 const gfx::PointF& operator[](int index) const { return m_corners[index]; }
51
52 const gfx::PointF& leftTop() const { return m_corners[LEFT_TOP]; }
53 const gfx::PointF& rightTop() const { return m_corners[RIGHT_TOP]; }
54 const gfx::PointF& rightBottom() const { return m_corners[RIGHT_BOTTOM]; }
55 const gfx::PointF& leftBottom() const { return m_corners[LEFT_BOTTOM]; }
56
57 void leftTop(const gfx::PointF& pt) { m_corners[LEFT_TOP] = pt; }
58 void rightTop(const gfx::PointF& pt) { m_corners[RIGHT_TOP] = pt; }
59 void rightBottom(const gfx::PointF& pt) { m_corners[RIGHT_BOTTOM] = pt; }
60 void leftBottom(const gfx::PointF& pt) { m_corners[LEFT_BOTTOM] = pt; }
61
62 gfx::RectF bounds(double cornerThick) const {
63 gfx::RectF bounds;
64 for (int i=0; i<Corners::NUM_OF_CORNERS; ++i)
65 bounds |= gfx::RectF(m_corners[i].x, m_corners[i].y, cornerThick, cornerThick);
66 return bounds.floor();
67 }
68
69 private:
70 std::vector<gfx::PointF> m_corners;
71 };
72
73 Transformation();
74 Transformation(const gfx::RectF& bounds, double cornerThick);
75
76 // Simple getters and setters. The angle is in radians.
77
78 const gfx::RectF& bounds() const { return m_bounds; }
79 const gfx::PointF& pivot() const { return m_pivot; }
80 double cornerThick() const { return m_cornerThick; }
81 double angle() const { return m_angle; }
82 double skew() const { return m_skew; }
83
84 void bounds(const gfx::RectF& bounds) { m_bounds = bounds; }
85 void pivot(const gfx::PointF& pivot) { m_pivot = pivot; }
86 void angle(double angle) { m_angle = angle; }
87 void skew(double angle) { m_skew = angle; }
88
89 // Applies the transformation (rotation with angle/pivot) to the
90 // current bounds (m_bounds).
91 Corners transformedCorners() const;
92
93 // Changes the pivot to another location, adjusting the bounds to
94 // keep the current rotated-corners in the same location.
95 void displacePivotTo(const gfx::PointF& newPivot);
96
97 gfx::RectF transformedBounds() const;
98
99 // Static helper method to rotate points.
100 static gfx::PointF rotatePoint(const gfx::PointF& point,
101 const gfx::PointF& pivot,
102 const double angle,
103 const double skew);
104
105private:
106 gfx::RectF m_bounds = gfx::RectF(0.0, 0.0, 0.0, 0.0);
107 gfx::PointF m_pivot = gfx::PointF(0.0, 0.0);
108 double m_angle = 0.0;
109 double m_skew = 0.0;
110 double m_cornerThick = CORNER_THICK_FOR_PIXELS_MODE;
111};
112
113} // namespace app
114
115#endif
116