1
2
3
4
5
6
7 package ch.colabproject.colab.api.controller.card.grid;
8
9 import java.util.ArrayList;
10 import java.util.Collection;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Set;
16
17
18
19
20
21
22 public final class Grid {
23
24
25 public static int DEFAULT_X_COORDINATE = 1;
26
27
28 public static int DEFAULT_Y_COORDINATE = 1;
29
30
31 public static int DEFAULT_WIDTH = 1;
32
33
34 public static int DEFAULT_HEIGHT = 1;
35
36
37 private Map<Integer, Map<Integer, GridCellWithId>> matrix = new HashMap<>();
38
39
40 private Set<GridCellWithId> cells = new HashSet<>();
41
42
43 private Integer xMin = null;
44
45 private Integer yMin = null;
46
47
48 private Integer xMax = null;
49
50 private Integer yMax = null;
51
52
53
54
55
56
57
58
59
60 public boolean isFree(Integer x, Integer y) {
61 var column = matrix.get(x);
62 if (column != null) {
63 return column.get(y) == null;
64 }
65 return true;
66 }
67
68
69
70
71
72
73
74
75
76 public GridCellWithId getCellAt(Integer x, Integer y) {
77 var column = matrix.get(x);
78 if (column != null) {
79 GridCellWithId cell = column.get(y);
80 if (cell != null) {
81 return cell;
82 }
83 }
84 return null;
85 }
86
87
88
89
90
91
92 private void initCell(GridCellWithId cell) {
93 if (cell.getX() == null) {
94 cell.setX(DEFAULT_X_COORDINATE);
95 }
96
97 if (cell.getY() == null) {
98 cell.setY(DEFAULT_Y_COORDINATE);
99 }
100
101 if (cell.getWidth() == null || cell.getWidth() < 1) {
102 cell.setWidth(DEFAULT_WIDTH);
103 }
104 if (cell.getHeight() == null || cell.getHeight() < 1) {
105 cell.setHeight(DEFAULT_HEIGHT);
106 }
107 }
108
109
110
111
112
113 public void resetCell(GridCell cell) {
114 cell.setX(DEFAULT_X_COORDINATE);
115 cell.setY(DEFAULT_Y_COORDINATE);
116 cell.setWidth(DEFAULT_WIDTH);
117 cell.setHeight(DEFAULT_HEIGHT);
118 }
119
120
121
122
123
124
125
126
127 public boolean isFree(GridCellWithId cell) {
128 initCell(cell);
129 int cxMin = cell.getX();
130 int cxMax = cxMin + cell.getWidth() - 1;
131
132 int cyMin = cell.getY();
133 int cyMax = cyMin + cell.getHeight() - 1;
134
135 for (int x = cxMin; x <= cxMax; x++) {
136 var column = matrix.get(x);
137 if (column != null) {
138 for (int y = cyMin; y <= cyMax; y++) {
139 var c = column.get(y);
140 if (c != null && c != cell) {
141 return false;
142 }
143 }
144 }
145 }
146 return true;
147 }
148
149
150
151
152
153
154 private void markCells(GridCellWithId cell) {
155 initCell(cell);
156 cells.add(cell);
157
158 int cxMin = cell.getX();
159 int cxMax = cxMin + cell.getWidth() - 1;
160
161 int cyMin = cell.getY();
162 int cyMax = cyMin + cell.getHeight() - 1;
163
164 if (this.xMin == null) {
165 this.xMin = cxMin;
166 } else {
167 this.xMin = Math.min(this.xMin, cxMin);
168 }
169
170 if (this.xMax == null) {
171 this.xMax = cxMax;
172 } else {
173 this.xMax = Math.max(this.xMax, cxMax);
174 }
175
176 if (this.yMin == null) {
177 this.yMin = cyMin;
178 } else {
179 this.yMin = Math.min(this.yMin, cyMin);
180 }
181
182 if (this.yMax == null) {
183 this.yMax = cyMax;
184 } else {
185 this.yMax = Math.max(this.yMax, cyMax);
186 }
187
188 for (int x = cxMin; x <= cxMax; x++) {
189 var column = matrix.get(x);
190
191 if (column == null) {
192 column = new HashMap<>();
193 matrix.put(x, column);
194 }
195
196 for (int y = cyMin; y <= cyMax; y++) {
197 column.put(y, cell);
198 }
199 }
200 }
201
202
203
204
205
206
207 private Coord findLastUsedPosition() {
208 for (int y = yMax; y >= yMin; y--) {
209 for (int x = this.xMax; x >= xMin; x--) {
210 var column = matrix.get(x);
211 if (column != null && column.get(y) != null) {
212 return new Coord(x, y);
213 }
214 }
215 }
216 return null;
217 }
218
219
220
221
222
223
224 private void findNewPosition(GridCellWithId cell) {
225 Coord lastCoord = findLastUsedPosition();
226 initCell(cell);
227
228 if (lastCoord != null) {
229
230 cell.setX(DEFAULT_X_COORDINATE);
231 cell.setY(DEFAULT_Y_COORDINATE);
232 }
233 if (cell.getWidth() == 1 && cell.getHeight() == 1) {
234 if (lastCoord.x < xMax || xMax - xMin < 2) {
235
236 cell.setX(lastCoord.x + 1);
237 cell.setY(lastCoord.y);
238 } else {
239
240 cell.setX(xMin);
241 cell.setY(lastCoord.y + 1);
242 }
243 } else if (cell.getWidth() > cell.getHeight()) {
244
245
246 cell.setX(xMin);
247 cell.setY(yMax + 1);
248 } else {
249
250 cell.setX(xMax + 1);
251 cell.setY(yMin);
252 }
253
254 }
255
256
257
258
259
260
261 public void addCell(GridCellWithId cell) {
262 if (isFree(cell)) {
263 markCells(cell);
264 } else {
265 findNewPosition(cell);
266 markCells(cell);
267 }
268 }
269
270
271
272
273 public void shift() {
274
275 Integer shiftX = 1 - xMin;
276 Integer shiftY = 1 - yMin;
277
278 cells.forEach(cell -> {
279 cell.setX(cell.getX() + shiftX);
280 cell.setY(cell.getY() + shiftY);
281 });
282
283 xMin = 1;
284 yMin = 1;
285 xMax += shiftX;
286 yMax += shiftY;
287 }
288
289
290
291
292
293
294
295
296 public static Grid resolveConflicts(Collection<? extends GridCellWithId> cells) {
297 ArrayList<GridCellWithId> list = new ArrayList<>();
298 list.addAll(cells);
299
300 list.forEach(cell -> {
301
302 if (cell.getWidth() == null) {
303 cell.setWidth(1);
304 }
305 if (cell.getHeight() == null) {
306 cell.setHeight(1);
307 }
308 });
309
310 list.sort((a, b) -> {
311 boolean sameWidth = a.getWidth().equals(b.getWidth());
312 boolean sameHeight = a.getHeight().equals(b.getHeight());
313
314 Integer aArea = a.getWidth() * a.getHeight();
315 Integer bArea = b.getWidth() * b.getHeight();
316
317 if (sameHeight && sameWidth) {
318
319 if (a.getId() < b.getId()) {
320 return -1;
321 } else if (a.getId() < b.getId()) {
322 return 1;
323 } else {
324 return 0;
325 }
326 } else if (sameWidth) {
327
328 return a.getHeight() - b.getHeight();
329 } else if (sameHeight) {
330
331 return a.getWidth() - b.getWidth();
332 } else if (aArea.equals(bArea)) {
333
334
335 return a.getHeight() - b.getHeight();
336 } else {
337
338 return aArea - bArea;
339 }
340 });
341
342 List<GridCellWithId> conflictual = new ArrayList<>();
343 Grid grid = new Grid();
344
345 list.forEach(cell -> {
346 if (grid.isFree(cell)) {
347 grid.markCells(cell);
348 } else {
349 conflictual.add(cell);
350 }
351 });
352 conflictual.forEach(cell -> {
353 grid.findNewPosition(cell);
354 grid.markCells(cell);
355 });
356 return grid;
357 }
358
359
360
361
362 public static class Coord {
363
364
365 private Integer x;
366
367
368 private Integer y;
369
370
371
372
373
374
375
376 public Coord(Integer x, Integer y) {
377 this.x = x;
378 this.y = y;
379 }
380
381 @Override
382 public String toString() {
383 return "Coord{" + x + "," + y + '}';
384 }
385 }
386 }