1// Aseprite
2// Copyright (C) 2019 Igara Studio S.A.
3// Copyright (C) 2017-2018 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/script/luacpp.h"
13#include "fmt/format.h"
14#include "gfx/point.h"
15
16#include <cmath>
17
18namespace app {
19namespace script {
20
21namespace {
22
23gfx::Point Point_new(lua_State* L, int index)
24{
25 gfx::Point pt(0, 0);
26 // Copy other rectangle
27 if (auto pt2 = may_get_obj<gfx::Point>(L, index)) {
28 pt = *pt2;
29 }
30 // Convert {x=int,y=int} or {int,int} into a Point
31 else if (lua_istable(L, index)) {
32 const int type = lua_getfield(L, index, "x");
33 if (VALID_LUATYPE(type)) {
34 lua_getfield(L, index, "y");
35 pt.x = lua_tointeger(L, -2);
36 pt.y = lua_tointeger(L, -1);
37 lua_pop(L, 2);
38 }
39 else {
40 lua_pop(L, 1);
41
42 // TODO Investigate this further: why we cannot use two
43 // lua_geti() calls and then lua_pop(L, 2) when we are iterating
44 // points in a table defined like {{0,0},{32,32}}
45
46 lua_geti(L, index, 1);
47 pt.x = lua_tointeger(L, -1);
48 lua_pop(L, 1);
49
50 lua_geti(L, index, 2);
51 pt.y = lua_tointeger(L, -1);
52 lua_pop(L, 1);
53 }
54 }
55 else {
56 pt.x = lua_tointeger(L, index);
57 pt.y = lua_tointeger(L, index+1);
58 }
59 return pt;
60}
61
62int Point_new(lua_State* L)
63{
64 push_obj(L, Point_new(L, 1));
65 return 1;
66}
67
68int Point_gc(lua_State* L)
69{
70 get_obj<gfx::Point>(L, 1)->~PointT();
71 return 0;
72}
73
74int Point_eq(lua_State* L)
75{
76 const auto a = get_obj<gfx::Point>(L, 1);
77 const auto b = get_obj<gfx::Point>(L, 2);
78 lua_pushboolean(L, *a == *b);
79 return 1;
80}
81
82int Point_tostring(lua_State* L)
83{
84 const auto pt = get_obj<gfx::Point>(L, 1);
85 lua_pushstring(L, fmt::format("Point{{ x={}, y={} }}",
86 pt->x, pt->y).c_str());
87 return 1;
88}
89
90int Point_unm(lua_State* L)
91{
92 const auto pt = get_obj<gfx::Point>(L, 1);
93 push_obj(L, -(*pt));
94 return 1;
95}
96
97int Point_add(lua_State* L)
98{
99 gfx::Point result(0, 0);
100 if (lua_isuserdata(L, 1))
101 result += *get_obj<gfx::Point>(L, 1);
102 else
103 result += lua_tointeger(L, 1);
104 if (lua_isuserdata(L, 2))
105 result += *get_obj<gfx::Point>(L, 2);
106 else
107 result += lua_tointeger(L, 2);
108 push_obj(L, result);
109 return 1;
110}
111
112int Point_sub(lua_State* L)
113{
114 gfx::Point result = *get_obj<gfx::Point>(L, 1);
115 if (lua_isuserdata(L, 2))
116 result -= *get_obj<gfx::Point>(L, 2);
117 else
118 result -= lua_tointeger(L, 2);
119 push_obj(L, result);
120 return 1;
121}
122
123int Point_mul(lua_State* L)
124{
125 gfx::Point result = *get_obj<gfx::Point>(L, 1);
126 result *= lua_tointeger(L, 2);
127 push_obj(L, result);
128 return 1;
129}
130
131int Point_div(lua_State* L)
132{
133 gfx::Point result = *get_obj<gfx::Point>(L, 1);
134 const int value = lua_tointeger(L, 2);
135 if (value == 0)
136 return luaL_error(L, "attempt to divide by zero");
137 result /= value;
138 push_obj(L, result);
139 return 1;
140}
141
142int Point_mod(lua_State* L)
143{
144 gfx::Point result = *get_obj<gfx::Point>(L, 1);
145 const int value = lua_tointeger(L, 2);
146 if (value == 0)
147 return luaL_error(L, "attempt to divide by zero");
148 result.x %= value;
149 result.y %= value;
150 push_obj(L, result);
151 return 1;
152}
153
154int Point_pow(lua_State* L)
155{
156 gfx::Point result = *get_obj<gfx::Point>(L, 1);
157 const int value = lua_tointeger(L, 2);
158 result.x = std::pow(result.x, value);
159 result.y = std::pow(result.y, value);
160 push_obj(L, result);
161 return 1;
162}
163
164int Point_get_x(lua_State* L)
165{
166 const auto pt = get_obj<gfx::Point>(L, 1);
167 lua_pushinteger(L, pt->x);
168 return 1;
169}
170
171int Point_get_y(lua_State* L)
172{
173 const auto pt = get_obj<gfx::Point>(L, 1);
174 lua_pushinteger(L, pt->y);
175 return 1;
176}
177
178int Point_set_x(lua_State* L)
179{
180 auto pt = get_obj<gfx::Point>(L, 1);
181 pt->x = lua_tointeger(L, 2);
182 return 0;
183}
184
185int Point_set_y(lua_State* L)
186{
187 auto pt = get_obj<gfx::Point>(L, 1);
188 pt->y = lua_tointeger(L, 2);
189 return 0;
190}
191
192const luaL_Reg Point_methods[] = {
193 { "__gc", Point_gc },
194 { "__eq", Point_eq },
195 { "__tostring", Point_tostring },
196 { "__unm", Point_unm },
197 { "__add", Point_add },
198 { "__sub", Point_sub },
199 { "__mul", Point_mul },
200 { "__div", Point_div },
201 { "__mod", Point_mod },
202 { "__pow", Point_pow },
203 { "__idiv", Point_div },
204 { nullptr, nullptr }
205};
206
207const Property Point_properties[] = {
208 { "x", Point_get_x, Point_set_x },
209 { "y", Point_get_y, Point_set_y },
210 { nullptr, nullptr, nullptr }
211};
212
213} // anonymous namespace
214
215DEF_MTNAME(gfx::Point);
216
217void register_point_class(lua_State* L)
218{
219 using gfx::Point;
220 REG_CLASS(L, Point);
221 REG_CLASS_NEW(L, Point);
222 REG_CLASS_PROPERTIES(L, Point);
223}
224
225gfx::Point convert_args_into_point(lua_State* L, int index)
226{
227 return Point_new(L, index);
228}
229
230} // namespace script
231} // namespace app
232