Skip to content
Permalink
Newer
Older
100644 366 lines (348 sloc) 10.4 KB
1
import java.util.*;
2
import java.util.Map.*;
3
import java.lang.Boolean;
5
int reset_x = 140;
6
int reset_y = 460;
Andrew Lawson Andrew Lawson
Nov 17, 2014
7
int reset_w = 100;
8
int reset_h = 25;
10
int calc_x = 250;
11
int calc_y = 460;
12
int calc_w = 100;
13
int calc_h = 25;
Andrew Lawson Andrew Lawson
Nov 17, 2014
15
PShape reset;
16
PShape calculate;
19
ArrayList<PVector> rawInput = new ArrayList<PVector>();
20
Comparator<PVector> compareX, compareXRev, compareY, compareYRev;
22
// Triple used for returning three items
23
private class ReturnTriple {
24
public float sum;
25
public float dist;
26
public HashMap<PVector, Float> memoize;
27
public ReturnTriple(float sum, float dist, HashMap<PVector, Float> memoize) {
28
this.sum = sum;
29
this.dist = dist;
30
this.memoize = memoize;
31
}
32
}
33
Andrew Lawson Andrew Lawson
Dec 1, 2014
34
// Double used for returning centerpoint and sphere
35
private class CenterAndSphere {
36
public PVector center;
37
public PShape sphere;
38
public CenterAndSphere(PVector center, PShape sphere) {
39
this.center = center;
40
this.sphere = sphere;
41
}
42
}
43
Andrew Lawson Andrew Lawson
Nov 17, 2014
45
void setup() {
46
background(255);
Andrew Lawson Andrew Lawson
Nov 17, 2014
47
size(500,500,P2D);
48
// Create buttons
Andrew Lawson Andrew Lawson
Nov 17, 2014
49
reset = createShape(RECT, reset_x, reset_y, reset_w, reset_h);
50
calculate = createShape(RECT, calc_x, calc_y, calc_w, calc_h);
51
sphere = null;
Andrew Lawson Andrew Lawson
Nov 17, 2014
52
noLoop();
53
compareX = new Comparator<PVector>() {
54
public int compare(PVector p1, PVector p2) {
55
if (p1.x < p2.x) {
56
return -1;
57
}
58
else if (p2.x < p1.x) {
59
return 1;
60
}
61
else {
62
return 0;
63
}
64
}
65
};
66
compareXRev = new Comparator<PVector>() {
67
public int compare(PVector p1, PVector p2) {
68
if (p1.x > p2.x) {
69
return -1;
70
}
72
return 1;
73
}
74
else {
75
return 0;
76
}
77
}
78
};
79
compareY = new Comparator<PVector>() {
80
public int compare(PVector p1, PVector p2) {
81
if (p1.y < p2.y) {
84
else if (p2.y < p1.y) {
85
return 1;
86
}
87
else {
88
return 0;
89
}
90
}
91
};
92
compareYRev = new Comparator<PVector>() {
93
public int compare(PVector p1, PVector p2) {
94
if (p1.y > p2.y) {
95
return -1;
96
}
97
else if (p2.y > p1.y) {
98
return 1;
99
}
100
else {
101
return 0;
102
}
103
}
104
};
Andrew Lawson Andrew Lawson
Nov 17, 2014
105
}
106
107
// On mouse press
108
void mousePressed() {
109
// If mouse presses reset button
Andrew Lawson Andrew Lawson
Nov 17, 2014
110
if ((mouseX >= reset_x && mouseX <= (reset_x + reset_w)) &&
111
(mouseY >= reset_y && mouseY <= (reset_y + reset_h))) {
112
// Reset input and background
113
rawInput.clear();
114
centerPoint = null;
Andrew Lawson Andrew Lawson
Dec 1, 2014
115
sphere = null;
Andrew Lawson Andrew Lawson
Nov 17, 2014
116
}
117
// If mouse presses calculate button
118
else if ((mouseX >= calc_x && mouseX <= (calc_x + calc_w)) &&
Andrew Lawson Andrew Lawson
Dec 1, 2014
119
(mouseY >= calc_y && mouseY <= (calc_y + calc_h))) {
120
// Run algorithm
121
CenterAndSphere returnVals = getSeparator(rawInput);
122
sphere = returnVals.sphere;
123
centerPoint = returnVals.center;
Andrew Lawson Andrew Lawson
Nov 17, 2014
125
else {
126
// Create new 2D point from mouse coordinates
127
PVector prelim_point = new PVector(mouseX, mouseY);
128
// Lift to 3D with the squared magnitude of the original point
129
PVector final_point = new PVector(prelim_point.x, prelim_point.y,
Andrew Lawson Andrew Lawson
Dec 1, 2014
130
(float)Math.pow(prelim_point.mag(), 2));
Andrew Lawson Andrew Lawson
Nov 17, 2014
132
}
133
redraw();
134
}
135
136
// Get geometric separator
Andrew Lawson Andrew Lawson
Dec 1, 2014
137
CenterAndSphere getSeparator(ArrayList<PVector> input) {
139
// 1. Get centerpoint
Andrew Lawson Andrew Lawson
Dec 1, 2014
140
PVector centerPoint = approxCenterpoint(input);
141
// 2. Get random unit vector in 3D
Andrew Lawson Andrew Lawson
Dec 1, 2014
142
PVector unitVector = PVector.random3D();
143
// 3. Get radius
144
float radius = getRadius(centerPoint, unitVector);
145
// 4. Output sphere separator
Andrew Lawson Andrew Lawson
Dec 1, 2014
146
float[] sphereAttributes = getSphereAttr(centerPoint, unitVector, radius);
147
PShape separator = createShape(ELLIPSE, sphereAttributes);
148
CenterAndSphere returnVals = new CenterAndSphere(centerPoint, separator);
149
return returnVals;
152
// Get radius for our separator
153
float getRadius(PVector centerPoint, PVector unitVector) {
154
System.out.println(centerPoint.mag());
155
System.out.println(centerPoint.z);
156
System.out.println(centerPoint.z - centerPoint.mag());
157
PVector centerPoint2D = new PVector(centerPoint.x, centerPoint.y);
158
float num = (float)Math.sqrt(centerPoint.z - Math.pow(centerPoint2D.mag(), 2));
159
return num / Math.abs(unitVector.z);
160
}
161
Andrew Lawson Andrew Lawson
Dec 1, 2014
162
// Get the attributes for the sphere separators
163
float[] getSphereAttr(PVector centerPoint, PVector unitVector, float radius) {
164
PVector unitVectorCopy = new PVector(unitVector.x, unitVector.y, unitVector.z);
165
PVector centerPointCopy = new PVector(centerPoint.x, centerPoint.y);
166
unitVectorCopy.mult(radius);
167
centerPointCopy.sub(unitVectorCopy);
168
float x = centerPointCopy.x - radius;
169
float y = centerPointCopy.y - radius;
170
float z = centerPointCopy.z - radius;
Andrew Lawson Andrew Lawson
Dec 1, 2014
171
float[] attributes = {x, y, 2 * radius, 2 * radius};
175
// Get geometric median
176
PVector getGeometricMedian(ArrayList<PVector> input) {
177
if (input.size() == 0) {
178
System.out.println("You don't have any input points!");
181
else if (input.size() == 1) {
183
}
184
else {
185
// Get the point
186
HashMap<PVector, Float> total = new HashMap<PVector, Float>();
187
HashMap<PVector, Float> x = getAxisMin(input, false);
188
HashMap<PVector, Float> y = getAxisMin(input, true);
189
Set<PVector> keys = x.keySet();
190
for (PVector key : keys) {
191
total.put(key, x.get(key) + y.get(key));
192
}
193
ArrayList<Entry<PVector, Float>> sortedList = new ArrayList<Entry<PVector, Float>>(total.entrySet());
194
Collections.sort(sortedList, new Comparator<Entry<PVector, Float>>() {
195
public int compare(Entry<PVector, Float> entry1, Entry<PVector, Float> entry2) {
196
return (int)(entry1.getValue() - entry2.getValue());
197
}
198
});
199
return sortedList.get(0).getKey();
200
}
201
}
202
203
// Sample input points into sets of 4
204
ArrayList<ArrayList<PVector>> samplePoints(ArrayList<PVector> input) {
205
ArrayList<PVector> inputCopy = new ArrayList<PVector>(input);
206
ArrayList<ArrayList<PVector>> setList = new ArrayList<ArrayList<PVector>>();
207
ArrayList<PVector> pointList = new ArrayList<PVector>();
208
// While there are still points, split into sets
209
while (inputCopy.size() > 0) {
Andrew Lawson Andrew Lawson
Nov 30, 2014
210
double rnd = new Random().nextDouble();
211
int index = (int)(rnd * 10) % inputCopy.size();
212
// Add to set
213
pointList.add(inputCopy.get(index));
214
inputCopy.remove(index);
215
// Create new set on max size
216
if (pointList.size() == 4) {
217
setList.add(pointList);
218
pointList = new ArrayList<PVector>();
219
}
220
}
221
// Add most recent unempty set to list
222
if (pointList.size() > 0) {
223
setList.add(pointList);
224
}
225
return setList;
226
}
227
228
// Approximate the centerpoint
229
PVector approxCenterpoint(ArrayList<PVector> input) {
230
if (input.size() == 0) {
231
System.out.println("You don't have any input points!");
Andrew Lawson Andrew Lawson
Nov 30, 2014
232
return null;
235
// Algorithm
236
// Repeat 1 - 3 until one point remains and return that point
Andrew Lawson Andrew Lawson
Nov 30, 2014
237
while (input.size() > 1) {
238
// 1. Sample points into groups of 4
239
ArrayList<ArrayList<PVector>> setList = samplePoints(input);
240
// 2. Compute radon point of each group (geometric median)
241
ArrayList<PVector> radonList = new ArrayList<PVector>();
242
for (ArrayList<PVector> list : setList) {
243
PVector radonPoint = getGeometricMedian(list);
Andrew Lawson Andrew Lawson
Nov 30, 2014
244
radonList.add(radonPoint);
246
// 3. Set input to be new radon points
Andrew Lawson Andrew Lawson
Nov 30, 2014
247
input = radonList;
248
}
249
return input.get(0);
251
}
252
253
// Calculate the distance with the input, memoization
254
float calcDist(int i, PVector currentPoint, ArrayList<PVector> input, boolean isY) {
255
// For each point before i, calculate distance to i
256
PVector prevPoint;
257
float sum = 0;
258
for (int j = 0; j < i; j++) {
259
prevPoint = input.get(j);
260
if (isY) {
261
sum += currentPoint.y - prevPoint.y;
262
}
263
else {
264
sum += currentPoint.x - prevPoint.x;
265
}
270
// Calculate prev sum (i - 1) and the point distance from i
271
ReturnTriple calcLastSum(int i, PVector currentPoint, PVector nextPoint, ArrayList<PVector> input, HashMap<PVector, Float> memoize, boolean isY) {
272
// Calculate for squares
274
// If possible, reuse solution
275
if (memoize.containsKey(currentPoint)) {
276
sum = memoize.get(currentPoint);
277
}
278
// Otherwise, calculate
279
else {
280
sum = calcDist(i, currentPoint, input, isY);
281
memoize.put(currentPoint, sum);
282
}
283
if (isY) {
284
dist = nextPoint.y - currentPoint.y;
286
else {
287
dist = nextPoint.x - currentPoint.x;
288
}
289
return new ReturnTriple(sum, dist, memoize);
290
}
291
292
// Get sums and sums of squares of distances for an input
293
HashMap<PVector, Float> getSums(ArrayList<PVector> input, boolean isY) {
294
// Memoization hash tables
295
HashMap<PVector, Float> sumSquaresMemoize = new HashMap<PVector, Float>();
296
ReturnTriple returnTriple = new ReturnTriple(0, 0, null);
297
PVector currentPoint, nextPoint;
299
for (int i = 0; i < input.size() - 1; i++) {
300
currentPoint = input.get(i);
301
nextPoint = input.get(i + 1);
302
// Calculate sum of squares
303
returnTriple = calcLastSum(i, currentPoint, nextPoint, input, sumSquaresMemoize, isY);
304
sumSquaresMemoize = returnTriple.memoize;
305
sumSquaresMemoize.put(nextPoint, returnTriple.sum + (float)(i * Math.pow(returnTriple.dist, 2)) + (2 * i * returnTriple.dist));
307
return sumSquaresMemoize;
308
}
309
310
// Get minimum point for given axis input
311
HashMap<PVector, Float> getAxisMin(ArrayList<PVector> input, boolean isY) {
312
HashMap<PVector, Float> sumSquaresLeft, sumSquaresRight;
313
HashMap<PVector, Float> totalSumSquares = new HashMap<PVector, Float>();
314
// Get left distances
315
if (isY) {
316
Collections.sort(input, compareY);
317
}
318
else {
319
Collections.sort(input, compareX);
320
}
321
sumSquaresLeft = getSums(input, isY);
322
// Get right distances
323
if (isY) {
324
Collections.sort(input, compareYRev);
325
}
326
else {
327
Collections.sort(input, compareXRev);
328
}
329
sumSquaresRight = getSums(input, isY);
330
// Calculate total sum and store it
331
for (int i = 0; i < sumSquaresLeft.size(); i++) {
332
PVector point = input.get(i);
333
totalSumSquares.put(point, sumSquaresLeft.get(point) + sumSquaresRight.get(point));
334
}
336
}
337
Andrew Lawson Andrew Lawson
Nov 17, 2014
338
// Draw
339
void draw() {
340
background(255);
Andrew Lawson Andrew Lawson
Nov 17, 2014
341
shape(reset);
342
shape(calculate);
344
textSize(20);
345
text("Geometric Separators", 140, 20);
346
textSize(12);
347
text("Reset.", reset_x + 30, reset_y + 15);
348
text("Calculate.", calc_x + 25, calc_y + 15);
349
// Draw input
350
for (PVector point : rawInput) {
Andrew Lawson Andrew Lawson
Dec 1, 2014
351
strokeWeight(4);
352
point(point.x, point.y);
Andrew Lawson Andrew Lawson
Nov 17, 2014
353
}
354
// Draw center point
355
if (centerPoint != null) {
Andrew Lawson Andrew Lawson
Dec 1, 2014
356
strokeWeight(6);
357
point(centerPoint.x, centerPoint.y);
358
System.out.println("x-coordinate: " + Float.toString(centerPoint.x));
359
System.out.println("y-coordinate: " + Float.toString(centerPoint.y));
360
}
361
// Draw separator sphere projected to 2D
362
if (sphere != null) {
363
strokeWeight(6);
364
shape(sphere);
Andrew Lawson Andrew Lawson
Nov 17, 2014
366
}
You can’t perform that action at this time.