1// Aseprite
2// Copyright (C) 2019-2020 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#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/snap_to_grid.h"
13
14#include "gfx/point.h"
15#include "gfx/rect.h"
16
17#include <cstdlib>
18
19namespace app {
20
21gfx::Point snap_to_grid(const gfx::Rect& grid,
22 const gfx::Point& point,
23 const PreferSnapTo prefer)
24{
25 if (grid.isEmpty())
26 return point;
27
28 div_t d, dx, dy;
29 dx = std::div(grid.x, grid.w);
30 dy = std::div(grid.y, grid.h);
31
32 gfx::Point newPoint(point.x-dx.rem,
33 point.y-dy.rem);
34 if (prefer != PreferSnapTo::ClosestGridVertex) {
35 if (newPoint.x < 0) newPoint.x -= grid.w;
36 if (newPoint.y < 0) newPoint.y -= grid.h;
37 }
38
39 switch (prefer) {
40
41 case PreferSnapTo::ClosestGridVertex:
42 d = std::div(newPoint.x, grid.w);
43 newPoint.x = dx.rem + d.quot*grid.w + ((d.rem > grid.w/2)? grid.w: 0);
44
45 d = std::div(newPoint.y, grid.h);
46 newPoint.y = dy.rem + d.quot*grid.h + ((d.rem > grid.h/2)? grid.h: 0);
47 break;
48
49 case PreferSnapTo::BoxOrigin:
50 case PreferSnapTo::FloorGrid:
51 d = std::div(newPoint.x, grid.w);
52 newPoint.x = dx.rem + d.quot*grid.w;
53
54 d = std::div(newPoint.y, grid.h);
55 newPoint.y = dy.rem + d.quot*grid.h;
56 break;
57
58 case PreferSnapTo::CeilGrid:
59 d = std::div(newPoint.x, grid.w);
60 newPoint.x = d.rem ? dx.rem + (d.quot+1)*grid.w: newPoint.x;
61
62 d = std::div(newPoint.y, grid.h);
63 newPoint.y = d.rem ? dy.rem + (d.quot+1)*grid.h: newPoint.y;
64 break;
65
66 case PreferSnapTo::BoxEnd:
67 d = std::div(newPoint.x, grid.w);
68 newPoint.x = dx.rem + (d.quot+1)*grid.w;
69
70 d = std::div(newPoint.y, grid.h);
71 newPoint.y = dy.rem + (d.quot+1)*grid.h;
72 break;
73 }
74
75 return newPoint;
76}
77
78} // namespace app
79