-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHGBLocator.java
427 lines (367 loc) · 16 KB
/
HGBLocator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
package hgb;
import android.annotation.SuppressLint;
import android.util.SparseArray;
import java.util.Arrays;
/**
* HGBLocator (with HGBLocatorAid) is used to quickly locate the cell
* for user interaction. (A user click on a cell)
*
* @see "HBGLocatorAid
* @see "Development comments"
* Comment on using CellPack: I thought it not a good idea to store the cell ID in HGBVector2D.
* That it would be much better to store the vector in CellPack. I wrote the code. It works.
* But, it is slower than the original (cell ID stored in the vector). I did not go to the effort
* finding out if I made some looping error. I did not research why slower. I abandoned the idea
* of using CellPack and left the cell ID in the vector.
* 150714 Rewrite locator -- Finished and renamed (twice) 150729 (works much faster and better)
* I renamed Locater, thinking I had spelled incorrectly. Then found Locator was OK.
* Dictionaries say Locator may be spelled either Locator OR Locater.
* I choose and prefer Locator.
*
* 200418 in preperation for publication to Code Project I found an unacceptable bug in Locator:
* touches near the horizontal vertex of a cell might return an incorrect cellID (or -1). I tried
* to find and correct the problem; but in the ended decided the current was simply unworkable.
* Hence, I reworte locater a third time (see new class HGBLocatorAid). I think I have it right
* now. The new locator does a good fast job of finding the touched cellID without error.
*/
public class HGBLocator
{
@SuppressLint("UseSparseArrays")
public HGBLocator(HGBShared hgbShared)
{
this.hgbShared = hgbShared;
this.hgbProgressions = new HGBProgressions();
this.hgbLocatorAid = new HGBLocatorAid(hgbShared);
tmpVec = new HGBVector2D();
touchXY = new float[2];
}
@SuppressWarnings("unused")
private final String TAG = "HGBLocator";
private HGBShared hgbShared = null;
private HGBProgressions hgbProgressions = null;
private HGBLocatorAid hgbLocatorAid = null;
private HGBVector2D tmpVec = null;
private HGBVector2D tmpVector2D = null;
private float[] touchXY;
private float[] hiveOrigin;
private double vectorRadius = 0;
private int[] orderedvecXKeysAry;
private SparseArray<HGBVector2D[]> sortedColKeysAS;
// ------------------------------------------------
private int roseRings = 0;
private int zeroColIndex;
private int zeroRowIndex;
private double hiveMaximumMagnitude;
// ------------------------------------------------
/**
* Initialize once for the class to be used with each touch to be located.
* (Note: reinitialized when cell size change or number of cells change.
* see: initHive() which calls generatedHive())
*/
public void locatorInitialize()
{
roseRings = hgbShared.getRoseRings();
tmpVector2D = new HGBVector2D();
hiveOrigin = hgbShared.getHiveOrigin();
hiveMaximumMagnitude = this.determineHiveMaxMagnitude();
/**
* Most of the work for locator is done in HGBLocatorAid(). Here
* initialize once for the class to be used with each touch to be
* located.
*
* Get HGBVector2D's from the hive origin to each cell Store them as a
* column of cells in a array keyed by vecX of each HGBVector2D Order
* the keys Sort each column by vecY
*/
hgbLocatorAid.Initialize();
hiveOrigin = hgbShared.getHiveOrigin();
int roseRings = hgbShared.getRoseRings();
vectorRadius = hgbShared.getVertexRadius();
int[] cellIndices = hgbProgressions.getCellIndices(roseRings);
hgbShared.setCellIndices(cellIndices);
SparseArray<Integer> vecXCountSA = new SparseArray<Integer>();
SparseArray<Integer> vecYCountSA = new SparseArray<Integer>();
// Create vectors from the hive origin to each cell origin
// and count the number of (cast to int) repeated x coordinates
SparseArray<HGBVector2D> cellVector2DSA = createCellVector2Ds(cellIndices,
vecXCountSA, vecYCountSA);
// The return is a sparseArray of arrays of all hive vectors with the same VecX or vecY
// (in the same column or row of the hive) keyed by (cast to in) vecX or vecY
SparseArray<HGBVector2D[]> keyByVecXColSA = keyByVecXorY(cellVector2DSA,
vecXCountSA,true);
// columns of vecX cells sorted by vecY (still keyed by vecX)
sortedColKeysAS = sortByVecXorY(keyByVecXColSA, true);
// The x offset, from the origin of the hive,
// to the origin of each cell of each column of the hive
orderedvecXKeysAry = hgbLocatorAid.orderKeys(vecXCountSA);
}
// ===============================================
public int getCellIndex(float touchX, float touchY)
{
touchXY[0] = touchX;
touchXY[1] = touchY;
return getCellIndex(touchXY);
}
public int getCellIndex(float[] touchXY)
{
// The vector from the hive origin to the touch point
HGBVector2D touchVec = tmpVec.vCartesianVector(hiveOrigin, touchXY);
// This is the first check to see if the touch is out of the hive.
// However, it will not catch all. If an out of bounds gets by here...
// it will make it all the way to the end. I chose not to figure out
// how it makes it so far... I just catch out of bounds when I test
// to see if the touch is within the chosen cell.
if (touchVec.getMagnitude() > hiveMaximumMagnitude) return -1;
int cellIndex = hgbLocatorAid.locateCellIndex(touchXY, touchVec, sortedColKeysAS);
return cellIndex;
}
// ===============================================
// Entry to followCellIndices
private float[] tmpTouchXY = new float[2];
// See HiveView.OnGestureListener.onScroll()
HGBCellPack cellPack = null;
// ===============================================
/**
* Create an HGBVector2D for each cell from the hive origin to each cell
* origin. And counts the number in each set of equal X (cast to int) (via
* indirect preallocate vecXCountSA).
*
* @param cellIndices
* @return SparseArray<HGBVector2D>. The vector created via vCartesianVector
* from the hive Origin to the rose origin, keyed by cell id.
* vecXCountSA filled and return indirectly
*/
protected SparseArray<HGBVector2D> createCellVector2Ds(int[] cellIndices,
SparseArray<Integer> vecXCountSA,
SparseArray<Integer> vecYCountSA)
{
SparseArray<HGBVector2D> cellVector2DSA = new SparseArray<HGBVector2D>(cellIndices.length);
float[] cellOrg = null;
int vecX = -1;
int vecY = -1;
int id = -1;
int xIndex = -1;
int yIndex = -1;
final Integer notFound = -1;
for (int inx = 0; inx < cellIndices.length; inx++)
{
HGBCellPack hgbCellPack = hgbShared.getCellPack(cellIndices[inx]);
if (hgbCellPack != null)
{
// Create a vector between the hive origin and the cell origin
// (There, when the vector is created, magnitude is calculated.)
cellOrg = hgbCellPack.getOrigin();
id = hgbCellPack.getIndex();
HGBVector2D vec = tmpVector2D.vCartesianVector(hiveOrigin, cellOrg);
vec.setID(id);
cellVector2DSA.put(id, vec);
// Count the cast to int vecX's (storing the quanity of equal vec's)
vecX = (int) vec.getX();
xIndex = vecXCountSA.get(vecX, notFound);
if (xIndex == -1)
{
// This is a new occurrence of the x value, start counting
xIndex = 0;
}
vecXCountSA.put(vecX, ++xIndex);
// Count the cast to in vexY's (storing the quanity of equal vecY's)
vecY = (int) vec.getY();
yIndex = vecYCountSA.get(vecY, notFound);
if (yIndex == -1)
{
// This is a new occurrence of the x value, start counting
yIndex = 0;
}
vecYCountSA.put(vecY, ++yIndex);
}
}
return cellVector2DSA;
}
/**
* Separates the input hive origin to cell origin vectors
* int arrays of the same vecX or vecY component (cast to int) stored
* in a sparse array keyed by the working vector (vecX or vecY).
*
* @param cellVector2DSA
* HGBVector2D instance defined for every rose via
* vCartesianVector(), the hiveOrigin to each cell origin.
* @param vecCountSA
* Count of the number of cells in each column of VecX or row of VecY
* (This count is used to allocate returned arrays)
* @param xyFlg
* process (true) xVec (false) yVec
* @return SparseArray of array of vecX or vecY, keyed by vecX or vecY
* of all hive vectors with the same (cast to int) vecX or vecY
*/
protected SparseArray<HGBVector2D[]> keyByVecXorY(SparseArray<HGBVector2D> cellVector2DSA,
SparseArray<Integer> vecCountSA,
boolean xyFlg)
{
// Arrays of vectors of the same Vector XorY (cast to int)
SparseArray<HGBVector2D[]> keyByVecSA = new SparseArray<HGBVector2D[]>();
// Index into allocated vector XorY (cast to int) arrays
// Somewhere to store the indexes to all the arrays
SparseArray<Integer> indexSA = new SparseArray<Integer>();
HGBVector2D[] vecAry = null;
int vec = -1;
int inx = -1;
int cnt = -1;
// parse though all vectors from the hive origin to each cell
// Hunt for (cast to int) vecX or VecY that are equal: count them and
// store the vectors in a sparse array keyed by vecX or vecY
for (int index = 0; index < cellVector2DSA.size(); index++)
{
int key = cellVector2DSA.keyAt(index);
HGBVector2D tmpVec = cellVector2DSA.get(key);
vec = (xyFlg) ? (int) tmpVec.getX() : (int) tmpVec.getY();
vecAry = keyByVecSA.get(vec);
if (vecAry == null)
{
// The the key has not yet been stored
cnt = vecCountSA.get(vec);
vecAry = new HGBVector2D[cnt];
inx = 0;
vecAry[inx] = tmpVec;
indexSA.put(vec, inx);
keyByVecSA.put(vec, vecAry);
}
else
{
// Then this key was previously stored, get it and add to it.
// indexSA stores the index into this vecX's array
inx = indexSA.get(vec);
vecAry[++inx] = tmpVec;
// store the incremented new index into vecAry in indexSA
// Overwrites the previously stored index for this vecX
indexSA.put(vec, inx);
}
}
return keyByVecSA;
}
/**
* Sorts all vecX columns form top to bottom or vecY rows left to right cells vecY or vecX.
* Loops through input SA and feed each individual array to sortCellVecAry() which
* does the sort.
*
* @param keyByVecSA
* : SparseArray keyed by vecX of all cells with the same
* HGBVector2D X value (cast to int) or vecY
* @return sortedVec_KeyByVecXorYSA
*
*/
public SparseArray<HGBVector2D[]> sortByVecXorY(SparseArray<HGBVector2D[]> keyByVecSA, boolean xyFlg)
{
SparseArray<HGBVector2D[]> sortedVec_KeyByVecXorYSA = new SparseArray<HGBVector2D[]>(keyByVecSA.size());
int key;
HGBVector2D[] sortedbyVec;
for (int inx = 0; inx < keyByVecSA.size(); inx++)
{
key = keyByVecSA.keyAt(inx);
HGBVector2D[] vec = keyByVecSA.get(key);
sortedbyVec = sortCellVecAry(vec, xyFlg);
sortedVec_KeyByVecXorYSA.put(key, sortedbyVec);
}
return sortedVec_KeyByVecXorYSA;
}
/**
* Sorts the input vecX (or vecY) arrays by each cells vecY (or vecX)
* from top to bottom (or left to right).
*
* @param cellVecAry
* : A none sorted vecX (or vecY) array of cells from
* keyByVecSA (or keyByVecYRowSA)
* @param xyFlg
* : true: sort by X, else by Y
* @return The input array stored into a new sorted array.
*
*
* @see class Pair
*/
private HGBVector2D[] sortCellVecAry(HGBVector2D[] cellVecAry, boolean xyFlg)
{
HGBVector2D[] sortedCellVecAry = new HGBVector2D[cellVecAry.length];
// Pair is an internal private class to allow an Arrays.sort()
Pair[] tmpAry = new Pair[cellVecAry.length];
// store the vector Y value and it's position in the cellVecAry
for (int inx = 0; inx < cellVecAry.length; inx++)
{
// I needed to sort on the vectors Y val but retain the
// index. To do so, I had to create a comparable class
// (I could not sort on a 2 dimensional int array. Just
// have to eat the overhead; but is done during initialize.)
Pair pair = (xyFlg)
? new Pair((int) cellVecAry[inx].getY(), inx)
: new Pair((int) cellVecAry[inx].getX(), inx);
tmpAry[inx] = pair;
}
// Sort from smallest to largest (the negatives are on top)
// A simple Arrays.sort() works perfect!
Arrays.sort(tmpAry);
// Fill the sortedCellVecAry with the column of cells sorted from top to
// bottom
for (int inx = 0; inx < tmpAry.length; inx++)
{
Pair pair = tmpAry[inx];
sortedCellVecAry[inx] = cellVecAry[pair.index];
}
return sortedCellVecAry;
}
/**
* Internal class to allow the use of Arrays.sort() I need to sort a two
* dimensional array; but Arrays.sort() will not. I need to keep the cell
* index and it's vecY together as I sorted an array of vecY. To do so I had
* to create this class which implements Comparable(Pair)
*/
private class Pair implements Comparable<Pair>
{
public Pair(int Y, int index)
{
this.Y = Y;
this.index = index;
}
private int Y;
private int index;
@Override
public int compareTo(Pair pair)
{
return this.Y - pair.Y;
}
}
/**
* I define (here) the hive magnitude as the distance to the furthest
* vertex in the hive.
*
* There are a set of 6 "hive vertices". After the center rose, these
* vertices appear in pairs at the extremes of the hive. The pair off
* of the first rose in the last rose ring are easiest to find. Use
* Petal 1 vertex 0 to find the hive magnitude.
*
* @return double hive maximum magnitude Returns -1 on error.
*/
protected double determineHiveMaxMagnitude()
{
int roseRings = hgbShared.getRoseRings();
// Confession: I am at a loss as to why I need to subtract 1.
// I suppose it has to do with a confusion early on about whether to
// count rose 0 as a ring. At any rate... the right answer for the
// last ring is one less.
int[] range = hgbProgressions.roseRingRange(roseRings - 1);
int petal1Index = range[0] + 1;
float[][] baseVertices = hgbShared.getBaseVertices();
HGBCellPack hgbCellPack = hgbShared.getCellPack(petal1Index);
if (hgbCellPack != null)
{
// Get the origin, offset to vertex 0 and create a vector2D form the
// hive origin to vertex 0 of petal 1 and get the magnitude.
float[] org = hgbCellPack.getOrigin();
float[] vertexCoordinate = new float[2];
vertexCoordinate[0] = baseVertices[0][0] + org[0];
vertexCoordinate[1] = baseVertices[0][1] + org[1];
HGBVector2D vec = tmpVector2D.vCartesianVector(hiveOrigin,
vertexCoordinate);
double mag = vec.getMagnitude();
return mag;
}
return -1;
}
}