propane 2.9.0-java → 2.9.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -0
  3. data/Rakefile +2 -3
  4. data/lib/.gitignore +1 -0
  5. data/lib/export.txt +2 -0
  6. data/lib/propane/version.rb +1 -1
  7. data/pom.rb +2 -49
  8. data/pom.xml +1 -64
  9. data/propane.gemspec +13 -10
  10. data/src/main/java/processing/awt/PGraphicsJava2D.java +29 -20
  11. data/src/main/java/processing/core/PApplet.java +12854 -12803
  12. data/src/main/java/processing/core/PGraphics.java +17 -22
  13. data/src/main/java/processing/core/PShape.java +42 -20
  14. data/src/main/java/processing/core/PShapeSVG.java +20 -5
  15. data/src/main/java/processing/core/PSurfaceNone.java +1 -4
  16. data/src/main/java/processing/data/DoubleDict.java +848 -0
  17. data/src/main/java/processing/data/DoubleList.java +928 -0
  18. data/src/main/java/processing/data/FloatDict.java +18 -2
  19. data/src/main/java/processing/data/FloatList.java +26 -2
  20. data/src/main/java/processing/data/IntDict.java +12 -3
  21. data/src/main/java/processing/data/IntList.java +24 -1
  22. data/src/main/java/processing/data/JSONObject.java +2 -2
  23. data/src/main/java/processing/data/LongDict.java +800 -0
  24. data/src/main/java/processing/data/LongList.java +937 -0
  25. data/src/main/java/processing/data/Sort.java +1 -1
  26. data/src/main/java/processing/data/StringDict.java +12 -3
  27. data/src/main/java/processing/data/StringList.java +25 -2
  28. data/src/main/java/processing/data/Table.java +16 -5
  29. data/src/main/java/processing/data/XML.java +8 -1
  30. data/src/main/java/processing/opengl/PGL.java +23 -16
  31. data/src/main/java/processing/opengl/PGraphicsOpenGL.java +13 -10
  32. data/src/main/java/processing/opengl/PJOGL.java +35 -12
  33. data/src/main/java/processing/opengl/shaders/LightVert-brcm.glsl +154 -0
  34. data/src/main/java/processing/opengl/shaders/LightVert-vc4.glsl +2 -2
  35. data/src/main/java/processing/opengl/shaders/TexLightVert-brcm.glsl +160 -0
  36. data/src/main/java/processing/opengl/shaders/TexLightVert-vc4.glsl +2 -2
  37. metadata +22 -13
@@ -227,7 +227,7 @@ public class PGraphics extends PImage implements PConstants {
227
227
  * images go out of scope, they will be properly garbage collected.
228
228
  */
229
229
  protected WeakHashMap<PImage, Object> cacheMap =
230
- new WeakHashMap<PImage, Object>();
230
+ new WeakHashMap<>();
231
231
 
232
232
 
233
233
  ////////////////////////////////////////////////////////////
@@ -334,7 +334,7 @@ public class PGraphics extends PImage implements PConstants {
334
334
  /** True if colors are not in the range 0..1 */
335
335
  boolean colorModeScale; // = true;
336
336
 
337
- /**
337
+ /**
338
338
  * True if colorMode(RGB, 255). Defaults to true so that color()
339
339
  * used as part of a field declaration will properly assign values.
340
340
  */
@@ -2016,6 +2016,7 @@ public class PGraphics extends PImage implements PConstants {
2016
2016
  showMissingWarning("clip");
2017
2017
  }
2018
2018
 
2019
+
2019
2020
  /**
2020
2021
  * ( begin auto-generated from noClip.xml )
2021
2022
  *
@@ -2269,9 +2270,9 @@ public class PGraphics extends PImage implements PConstants {
2269
2270
  */
2270
2271
  public void curveVertex(float x, float y) {
2271
2272
  curveVertexCheck();
2272
- float[] vertex = curveVertices[curveVertexCount];
2273
- vertex[X] = x;
2274
- vertex[Y] = y;
2273
+ float[] v = curveVertices[curveVertexCount];
2274
+ v[X] = x;
2275
+ v[Y] = y;
2275
2276
  curveVertexCount++;
2276
2277
 
2277
2278
  // draw a segment if there are enough points
@@ -2292,10 +2293,10 @@ public class PGraphics extends PImage implements PConstants {
2292
2293
  */
2293
2294
  public void curveVertex(float x, float y, float z) {
2294
2295
  curveVertexCheck();
2295
- float[] vertex = curveVertices[curveVertexCount];
2296
- vertex[X] = x;
2297
- vertex[Y] = y;
2298
- vertex[Z] = z;
2296
+ float[] v = curveVertices[curveVertexCount];
2297
+ v[X] = x;
2298
+ v[Y] = y;
2299
+ v[Z] = z;
2299
2300
  curveVertexCount++;
2300
2301
 
2301
2302
  // draw a segment if there are enough points
@@ -3904,6 +3905,7 @@ public class PGraphics extends PImage implements PConstants {
3904
3905
  }
3905
3906
 
3906
3907
 
3908
+
3907
3909
  //////////////////////////////////////////////////////////////
3908
3910
 
3909
3911
  // SHAPE
@@ -3957,7 +3959,6 @@ public class PGraphics extends PImage implements PConstants {
3957
3959
  }
3958
3960
 
3959
3961
 
3960
-
3961
3962
  /**
3962
3963
  * ( begin auto-generated from shape.xml )
3963
3964
  *
@@ -4055,6 +4056,7 @@ public class PGraphics extends PImage implements PConstants {
4055
4056
  }
4056
4057
 
4057
4058
 
4059
+
4058
4060
  //////////////////////////////////////////////////////////////
4059
4061
 
4060
4062
  // TEXT/FONTS
@@ -4918,16 +4920,6 @@ public class PGraphics extends PImage implements PConstants {
4918
4920
  }
4919
4921
 
4920
4922
 
4921
- // public void text(String s, float a, float b, float c, float d, float z) {
4922
- // if (z != 0) translate(0, 0, z); // slowness, badness
4923
- //
4924
- // text(s, a, b, c, d);
4925
- // textZ = z;
4926
- //
4927
- // if (z != 0) translate(0, 0, -z); // TEMPORARY HACK! SLOW!
4928
- // }
4929
-
4930
-
4931
4923
  public void text(int num, float x, float y) {
4932
4924
  text(String.valueOf(num), x, y);
4933
4925
  }
@@ -6169,7 +6161,9 @@ public class PGraphics extends PImage implements PConstants {
6169
6161
  ellipseMode(s.ellipseMode);
6170
6162
  shapeMode(s.shapeMode);
6171
6163
 
6172
- blendMode(s.blendMode);
6164
+ if (blendMode != s.blendMode) {
6165
+ blendMode(s.blendMode);
6166
+ }
6173
6167
 
6174
6168
  if (s.tint) {
6175
6169
  tint(s.tintColor);
@@ -7384,6 +7378,7 @@ public class PGraphics extends PImage implements PConstants {
7384
7378
  pushStyle();
7385
7379
  pushMatrix();
7386
7380
  resetMatrix();
7381
+ noStroke();
7387
7382
  fill(backgroundColor);
7388
7383
  rect(0, 0, width, height);
7389
7384
  popMatrix();
@@ -8128,7 +8123,7 @@ public class PGraphics extends PImage implements PConstants {
8128
8123
  */
8129
8124
  static public void showWarning(String msg) { // ignore
8130
8125
  if (warnings == null) {
8131
- warnings = new HashMap<String, Object>();
8126
+ warnings = new HashMap<>();
8132
8127
  }
8133
8128
  if (!warnings.containsKey(msg)) {
8134
8129
  System.err.println(msg);
@@ -2040,7 +2040,7 @@ public class PShape implements PConstants {
2040
2040
  parent.addName(nom, shape);
2041
2041
  } else {
2042
2042
  if (nameTable == null) {
2043
- nameTable = new HashMap<String,PShape>();
2043
+ nameTable = new HashMap<>();
2044
2044
  }
2045
2045
  nameTable.put(nom, shape);
2046
2046
  }
@@ -2387,14 +2387,14 @@ public class PShape implements PConstants {
2387
2387
  /**
2388
2388
  * ( begin auto-generated from PShape_setFill.xml )
2389
2389
  *
2390
- * The <b>setFill()</b> method defines the fill color of a <b>PShape</b>.
2391
- * This method is used after shapes are created or when a shape is defined explicitly
2392
- * (e.g. <b>createShape(RECT, 20, 20, 80, 80)</b>) as shown in the above example.
2393
- * When a shape is created with <b>beginShape()</b> and <b>endShape()</b>, its
2394
- * attributes may be changed with <b>fill()</b> and <b>stroke()</b> within
2395
- * <b>beginShape()</b> and <b>endShape()</b>. However, after the shape is
2396
- * created, only the <b>setFill()</b> method can define a new fill value for
2397
- * the <b>PShape</b>.
2390
+ * The <b>setFill()</b> method defines the fill color of a <b>PShape</b>.
2391
+ * This method is used after shapes are created or when a shape is defined explicitly
2392
+ * (e.g. <b>createShape(RECT, 20, 20, 80, 80)</b>) as shown in the above example.
2393
+ * When a shape is created with <b>beginShape()</b> and <b>endShape()</b>, its
2394
+ * attributes may be changed with <b>fill()</b> and <b>stroke()</b> within
2395
+ * <b>beginShape()</b> and <b>endShape()</b>. However, after the shape is
2396
+ * created, only the <b>setFill()</b> method can define a new fill value for
2397
+ * the <b>PShape</b>.
2398
2398
  *
2399
2399
  * ( end auto-generated )
2400
2400
  *
@@ -2543,14 +2543,14 @@ public class PShape implements PConstants {
2543
2543
  /**
2544
2544
  * ( begin auto-generated from PShape_setStroke.xml )
2545
2545
  *
2546
- * The <b>setStroke()</b> method defines the outline color of a <b>PShape</b>.
2547
- * This method is used after shapes are created or when a shape is defined
2548
- * explicitly (e.g. <b>createShape(RECT, 20, 20, 80, 80)</b>) as shown in
2549
- * the above example. When a shape is created with <b>beginShape()</b> and
2550
- * <b>endShape()</b>, its attributes may be changed with <b>fill()</b> and
2551
- * <b>stroke()</b> within <b>beginShape()</b> and <b>endShape()</b>.
2552
- * However, after the shape is created, only the <b>setStroke()</b> method
2553
- * can define a new stroke value for the <b>PShape</b>.
2546
+ * The <b>setStroke()</b> method defines the outline color of a <b>PShape</b>.
2547
+ * This method is used after shapes are created or when a shape is defined
2548
+ * explicitly (e.g. <b>createShape(RECT, 20, 20, 80, 80)</b>) as shown in
2549
+ * the above example. When a shape is created with <b>beginShape()</b> and
2550
+ * <b>endShape()</b>, its attributes may be changed with <b>fill()</b> and
2551
+ * <b>stroke()</b> within <b>beginShape()</b> and <b>endShape()</b>.
2552
+ * However, after the shape is created, only the <b>setStroke()</b> method
2553
+ * can define a new stroke value for the <b>PShape</b>.
2554
2554
  *
2555
2555
  * ( end auto-generated )
2556
2556
  *
@@ -2891,13 +2891,24 @@ public class PShape implements PConstants {
2891
2891
  // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2892
2892
 
2893
2893
 
2894
- // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
2894
+ /**
2895
+ * Return true if this x, y coordinate is part of this shape. Only works
2896
+ * with PATH shapes or GROUP shapes that contain other GROUPs or PATHs.
2897
+ */
2895
2898
  public boolean contains(float x, float y) {
2896
2899
  if (family == PATH) {
2900
+ // apply the inverse transformation matrix to the point coordinates
2901
+ PMatrix inverseCoords = matrix.get();
2902
+ inverseCoords.invert(); // maybe cache this?
2903
+ inverseCoords.invert(); // maybe cache this?
2904
+ PVector p = new PVector();
2905
+ inverseCoords.mult(new PVector(x,y),p);
2906
+
2907
+ // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
2897
2908
  boolean c = false;
2898
2909
  for (int i = 0, j = vertexCount-1; i < vertexCount; j = i++) {
2899
- if (((vertices[i][Y] > y) != (vertices[j][Y] > y)) &&
2900
- (x <
2910
+ if (((vertices[i][Y] > p.y) != (vertices[j][Y] > p.y)) &&
2911
+ (p.x <
2901
2912
  (vertices[j][X]-vertices[i][X]) *
2902
2913
  (y-vertices[i][Y]) /
2903
2914
  (vertices[j][1]-vertices[i][Y]) +
@@ -2906,7 +2917,18 @@ public class PShape implements PConstants {
2906
2917
  }
2907
2918
  }
2908
2919
  return c;
2920
+
2921
+ } else if (family == GROUP) {
2922
+ // If this is a group, loop through children until we find one that
2923
+ // contains the supplied coordinates. If a child does not support
2924
+ // contains() throw a warning and continue.
2925
+ for (int i = 0; i < childCount; i++) {
2926
+ if (children[i].contains(x, y)) return true;
2927
+ }
2928
+ return false;
2929
+
2909
2930
  } else {
2931
+ // https://github.com/processing/processing/issues/1280
2910
2932
  throw new IllegalArgumentException("The contains() method is only implemented for paths.");
2911
2933
  }
2912
2934
  }
@@ -32,6 +32,8 @@ import java.awt.geom.Point2D;
32
32
 
33
33
  import java.util.Map;
34
34
  import java.util.HashMap;
35
+ import java.util.regex.Matcher;
36
+ import java.util.regex.Pattern;
35
37
 
36
38
 
37
39
  /**
@@ -452,14 +454,27 @@ public class PShapeSVG extends PShape {
452
454
 
453
455
  String pointsAttr = element.getString("points");
454
456
  if (pointsAttr != null) {
455
- String[] pointsBuffer = PApplet.splitTokens(pointsAttr);
456
- vertexCount = pointsBuffer.length;
457
+ Pattern pattern = Pattern.compile("([+-]?[\\d]+(\\.[\\d]+)?([eE][+-][\\d]+)?)(,?\\s*)([+-]?[\\d]+(\\.[\\d]+)?([eE][+-][\\d]+)?)");
458
+ Matcher matcher = pattern.matcher(pointsAttr);
459
+ vertexCount = 0;
460
+ while (matcher.find()) {
461
+ vertexCount++;
462
+ }
463
+ matcher.reset();
457
464
  vertices = new float[vertexCount][2];
458
465
  for (int i = 0; i < vertexCount; i++) {
459
- String pb[] = PApplet.splitTokens(pointsBuffer[i], ", \t\r\n");
460
- vertices[i][X] = Float.parseFloat(pb[0]);
461
- vertices[i][Y] = Float.parseFloat(pb[1]);
466
+ matcher.find();
467
+ vertices[i][X] = Float.parseFloat(matcher.group(1));
468
+ vertices[i][Y] = Float.parseFloat(matcher.group(5));
462
469
  }
470
+ // String[] pointsBuffer = PApplet.splitTokens(pointsAttr);
471
+ // vertexCount = pointsBuffer.length;
472
+ // vertices = new float[vertexCount][2];
473
+ // for (int i = 0; i < vertexCount; i++) {
474
+ // String pb[] = PApplet.splitTokens(pointsBuffer[i], ", \t\r\n");
475
+ // vertices[i][X] = Float.parseFloat(pb[0]);
476
+ // vertices[i][Y] = Float.parseFloat(pb[1]);
477
+ // }
463
478
  }
464
479
  }
465
480
 
@@ -58,10 +58,7 @@ public class PSurfaceNone implements PSurface {
58
58
 
59
59
 
60
60
  @Override
61
- public void initFrame(PApplet sketch) {/*, int backgroundColor,
62
- int deviceIndex, boolean fullScreen,
63
- boolean spanDisplays) {*/
64
- //this.sketch = sketch;
61
+ public void initFrame(PApplet sketch) {
65
62
  throw new IllegalStateException("initFrame() not available with " +
66
63
  getClass().getSimpleName());
67
64
  }
@@ -0,0 +1,848 @@
1
+ package processing.data;
2
+
3
+ import java.io.*;
4
+ import java.util.HashMap;
5
+ import java.util.Iterator;
6
+ import java.util.Map;
7
+
8
+ import processing.core.PApplet;
9
+
10
+
11
+ /**
12
+ * A simple table class to use a String as a lookup for an double value.
13
+ *
14
+ * @webref data:composite
15
+ * @see IntDict
16
+ * @see StringDict
17
+ */
18
+ public class DoubleDict {
19
+
20
+ /** Number of elements in the table */
21
+ protected int count;
22
+
23
+ protected String[] keys;
24
+ protected double[] values;
25
+
26
+ /** Internal implementation for faster lookups */
27
+ private HashMap<String, Integer> indices = new HashMap<>();
28
+
29
+
30
+ public DoubleDict() {
31
+ count = 0;
32
+ keys = new String[10];
33
+ values = new double[10];
34
+ }
35
+
36
+
37
+ /**
38
+ * Create a new lookup with a specific size. This is more efficient than not
39
+ * specifying a size. Use it when you know the rough size of the thing you're creating.
40
+ *
41
+ * @nowebref
42
+ */
43
+ public DoubleDict(int length) {
44
+ count = 0;
45
+ keys = new String[length];
46
+ values = new double[length];
47
+ }
48
+
49
+
50
+ /**
51
+ * Read a set of entries from a Reader that has each key/value pair on
52
+ * a single line, separated by a tab.
53
+ *
54
+ * @nowebref
55
+ */
56
+ public DoubleDict(BufferedReader reader) {
57
+ String[] lines = PApplet.loadStrings(reader);
58
+ keys = new String[lines.length];
59
+ values = new double[lines.length];
60
+
61
+ for (int i = 0; i < lines.length; i++) {
62
+ String[] pieces = PApplet.split(lines[i], '\t');
63
+ if (pieces.length == 2) {
64
+ keys[count] = pieces[0];
65
+ values[count] = PApplet.parseFloat(pieces[1]);
66
+ indices.put(pieces[0], count);
67
+ count++;
68
+ }
69
+ }
70
+ }
71
+
72
+
73
+ /**
74
+ * @nowebref
75
+ */
76
+ public DoubleDict(String[] keys, double[] values) {
77
+ if (keys.length != values.length) {
78
+ throw new IllegalArgumentException("key and value arrays must be the same length");
79
+ }
80
+ this.keys = keys;
81
+ this.values = values;
82
+ count = keys.length;
83
+ for (int i = 0; i < count; i++) {
84
+ indices.put(keys[i], i);
85
+ }
86
+ }
87
+
88
+
89
+ /**
90
+ * Constructor to allow (more intuitive) inline initialization, e.g.:
91
+ * <pre>
92
+ * new FloatDict(new Object[][] {
93
+ * { "key1", 1 },
94
+ * { "key2", 2 }
95
+ * });
96
+ * </pre>
97
+ */
98
+ public DoubleDict(Object[][] pairs) {
99
+ count = pairs.length;
100
+ this.keys = new String[count];
101
+ this.values = new double[count];
102
+ for (int i = 0; i < count; i++) {
103
+ keys[i] = (String) pairs[i][0];
104
+ values[i] = (Float) pairs[i][1];
105
+ indices.put(keys[i], i);
106
+ }
107
+ }
108
+
109
+
110
+ public DoubleDict(Map<String, Double> incoming) {
111
+ count = incoming.size();
112
+ keys = new String[count];
113
+ values = new double[count];
114
+ int index = 0;
115
+ for (Map.Entry<String, Double> e : incoming.entrySet()) {
116
+ keys[index] = e.getKey();
117
+ values[index] = e.getValue();
118
+ indices.put(keys[index], index);
119
+ index++;
120
+ }
121
+ }
122
+
123
+
124
+ /**
125
+ * @webref doubledict:method
126
+ * @brief Returns the number of key/value pairs
127
+ */
128
+ public int size() {
129
+ return count;
130
+ }
131
+
132
+
133
+ /**
134
+ * Resize the internal data, this can only be used to shrink the list.
135
+ * Helpful for situations like sorting and then grabbing the top 50 entries.
136
+ */
137
+ public void resize(int length) {
138
+ if (length == count) return;
139
+
140
+ if (length > count) {
141
+ throw new IllegalArgumentException("resize() can only be used to shrink the dictionary");
142
+ }
143
+ if (length < 1) {
144
+ throw new IllegalArgumentException("resize(" + length + ") is too small, use 1 or higher");
145
+ }
146
+
147
+ String[] newKeys = new String[length];
148
+ double[] newValues = new double[length];
149
+ PApplet.arrayCopy(keys, newKeys, length);
150
+ PApplet.arrayCopy(values, newValues, length);
151
+ keys = newKeys;
152
+ values = newValues;
153
+ count = length;
154
+ resetIndices();
155
+ }
156
+
157
+
158
+ /**
159
+ * Remove all entries.
160
+ *
161
+ * @webref doubledict:method
162
+ * @brief Remove all entries
163
+ */
164
+ public void clear() {
165
+ count = 0;
166
+ indices = new HashMap<>();
167
+ }
168
+
169
+
170
+ private void resetIndices() {
171
+ indices = new HashMap<>(count);
172
+ for (int i = 0; i < count; i++) {
173
+ indices.put(keys[i], i);
174
+ }
175
+ }
176
+
177
+
178
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
179
+
180
+
181
+ public class Entry {
182
+ public String key;
183
+ public double value;
184
+
185
+ Entry(String key, double value) {
186
+ this.key = key;
187
+ this.value = value;
188
+ }
189
+ }
190
+
191
+
192
+ public Iterable<Entry> entries() {
193
+ return new Iterable<Entry>() {
194
+
195
+ public Iterator<Entry> iterator() {
196
+ return entryIterator();
197
+ }
198
+ };
199
+ }
200
+
201
+
202
+ public Iterator<Entry> entryIterator() {
203
+ return new Iterator<Entry>() {
204
+ int index = -1;
205
+
206
+ public void remove() {
207
+ removeIndex(index);
208
+ index--;
209
+ }
210
+
211
+ public Entry next() {
212
+ ++index;
213
+ Entry e = new Entry(keys[index], values[index]);
214
+ return e;
215
+ }
216
+
217
+ public boolean hasNext() {
218
+ return index+1 < size();
219
+ }
220
+ };
221
+ }
222
+
223
+
224
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
225
+
226
+
227
+ public String key(int index) {
228
+ return keys[index];
229
+ }
230
+
231
+
232
+ protected void crop() {
233
+ if (count != keys.length) {
234
+ keys = PApplet.subset(keys, 0, count);
235
+ values = PApplet.subset(values, 0, count);
236
+ }
237
+ }
238
+
239
+
240
+ public Iterable<String> keys() {
241
+ return new Iterable<String>() {
242
+
243
+ @Override
244
+ public Iterator<String> iterator() {
245
+ return keyIterator();
246
+ }
247
+ };
248
+ }
249
+
250
+
251
+ // Use this to iterate when you want to be able to remove elements along the way
252
+ public Iterator<String> keyIterator() {
253
+ return new Iterator<String>() {
254
+ int index = -1;
255
+
256
+ public void remove() {
257
+ removeIndex(index);
258
+ index--;
259
+ }
260
+
261
+ public String next() {
262
+ return key(++index);
263
+ }
264
+
265
+ public boolean hasNext() {
266
+ return index+1 < size();
267
+ }
268
+ };
269
+ }
270
+
271
+
272
+ /**
273
+ * Return a copy of the internal keys array. This array can be modified.
274
+ *
275
+ * @webref doubledict:method
276
+ * @brief Return a copy of the internal keys array
277
+ */
278
+ public String[] keyArray() {
279
+ crop();
280
+ return keyArray(null);
281
+ }
282
+
283
+
284
+ public String[] keyArray(String[] outgoing) {
285
+ if (outgoing == null || outgoing.length != count) {
286
+ outgoing = new String[count];
287
+ }
288
+ System.arraycopy(keys, 0, outgoing, 0, count);
289
+ return outgoing;
290
+ }
291
+
292
+
293
+ public double value(int index) {
294
+ return values[index];
295
+ }
296
+
297
+
298
+ /**
299
+ * @webref doubledict:method
300
+ * @brief Return the internal array being used to store the values
301
+ */
302
+ public Iterable<Double> values() {
303
+ return new Iterable<Double>() {
304
+
305
+ @Override
306
+ public Iterator<Double> iterator() {
307
+ return valueIterator();
308
+ }
309
+ };
310
+ }
311
+
312
+
313
+ public Iterator<Double> valueIterator() {
314
+ return new Iterator<Double>() {
315
+ int index = -1;
316
+
317
+ public void remove() {
318
+ removeIndex(index);
319
+ index--;
320
+ }
321
+
322
+ public Double next() {
323
+ return value(++index);
324
+ }
325
+
326
+ public boolean hasNext() {
327
+ return index+1 < size();
328
+ }
329
+ };
330
+ }
331
+
332
+
333
+ /**
334
+ * Create a new array and copy each of the values into it.
335
+ *
336
+ * @webref doubledict:method
337
+ * @brief Create a new array and copy each of the values into it
338
+ */
339
+ public double[] valueArray() {
340
+ crop();
341
+ return valueArray(null);
342
+ }
343
+
344
+
345
+ /**
346
+ * Fill an already-allocated array with the values (more efficient than
347
+ * creating a new array each time). If 'array' is null, or not the same
348
+ * size as the number of values, a new array will be allocated and returned.
349
+ */
350
+ public double[] valueArray(double[] array) {
351
+ if (array == null || array.length != size()) {
352
+ array = new double[count];
353
+ }
354
+ System.arraycopy(values, 0, array, 0, count);
355
+ return array;
356
+ }
357
+
358
+
359
+ /**
360
+ * Return a value for the specified key.
361
+ *
362
+ * @webref doubledict:method
363
+ * @brief Return a value for the specified key
364
+ */
365
+ public double get(String key) {
366
+ int index = index(key);
367
+ if (index == -1) {
368
+ throw new IllegalArgumentException("No key named '" + key + "'");
369
+ }
370
+ return values[index];
371
+ }
372
+
373
+
374
+ public double get(String key, double alternate) {
375
+ int index = index(key);
376
+ if (index == -1) {
377
+ return alternate;
378
+ }
379
+ return values[index];
380
+ }
381
+
382
+
383
+ /**
384
+ * @webref doubledict:method
385
+ * @brief Create a new key/value pair or change the value of one
386
+ */
387
+ public void set(String key, double amount) {
388
+ int index = index(key);
389
+ if (index == -1) {
390
+ create(key, amount);
391
+ } else {
392
+ values[index] = amount;
393
+ }
394
+ }
395
+
396
+
397
+ public void setIndex(int index, String key, double value) {
398
+ if (index < 0 || index >= count) {
399
+ throw new ArrayIndexOutOfBoundsException(index);
400
+ }
401
+ keys[index] = key;
402
+ values[index] = value;
403
+ }
404
+
405
+
406
+ /**
407
+ * @webref doubledict:method
408
+ * @brief Check if a key is a part of the data structure
409
+ */
410
+ public boolean hasKey(String key) {
411
+ return index(key) != -1;
412
+ }
413
+
414
+
415
+ /**
416
+ * @webref doubledict:method
417
+ * @brief Add to a value
418
+ */
419
+ public void add(String key, double amount) {
420
+ int index = index(key);
421
+ if (index == -1) {
422
+ create(key, amount);
423
+ } else {
424
+ values[index] += amount;
425
+ }
426
+ }
427
+
428
+
429
+ /**
430
+ * @webref doubledict:method
431
+ * @brief Subtract from a value
432
+ */
433
+ public void sub(String key, double amount) {
434
+ add(key, -amount);
435
+ }
436
+
437
+
438
+ /**
439
+ * @webref doubledict:method
440
+ * @brief Multiply a value
441
+ */
442
+ public void mult(String key, double amount) {
443
+ int index = index(key);
444
+ if (index != -1) {
445
+ values[index] *= amount;
446
+ }
447
+ }
448
+
449
+
450
+ /**
451
+ * @webref doubledict:method
452
+ * @brief Divide a value
453
+ */
454
+ public void div(String key, double amount) {
455
+ int index = index(key);
456
+ if (index != -1) {
457
+ values[index] /= amount;
458
+ }
459
+ }
460
+
461
+
462
+ private void checkMinMax(String functionName) {
463
+ if (count == 0) {
464
+ String msg =
465
+ String.format("Cannot use %s() on an empty %s.",
466
+ functionName, getClass().getSimpleName());
467
+ throw new RuntimeException(msg);
468
+ }
469
+ }
470
+
471
+
472
+ /**
473
+ * @webref doublelist:method
474
+ * @brief Return the smallest value
475
+ */
476
+ public int minIndex() {
477
+ //checkMinMax("minIndex");
478
+ if (count == 0) return -1;
479
+
480
+ // Will still return NaN if there are 1 or more entries, and they're all NaN
481
+ double m = Float.NaN;
482
+ int mi = -1;
483
+ for (int i = 0; i < count; i++) {
484
+ // find one good value to start
485
+ if (values[i] == values[i]) {
486
+ m = values[i];
487
+ mi = i;
488
+
489
+ // calculate the rest
490
+ for (int j = i+1; j < count; j++) {
491
+ double d = values[j];
492
+ if ((d == d) && (d < m)) {
493
+ m = values[j];
494
+ mi = j;
495
+ }
496
+ }
497
+ break;
498
+ }
499
+ }
500
+ return mi;
501
+ }
502
+
503
+
504
+ // return the key for the minimum value
505
+ public String minKey() {
506
+ checkMinMax("minKey");
507
+ int index = minIndex();
508
+ if (index == -1) {
509
+ return null;
510
+ }
511
+ return keys[index];
512
+ }
513
+
514
+
515
+ // return the minimum value, or throw an error if there are no values
516
+ public double minValue() {
517
+ checkMinMax("minValue");
518
+ int index = minIndex();
519
+ if (index == -1) {
520
+ return Float.NaN;
521
+ }
522
+ return values[index];
523
+ }
524
+
525
+
526
+ /**
527
+ * @webref doublelist:method
528
+ * @brief Return the largest value
529
+ */
530
+ // The index of the entry that has the max value. Reference above is incorrect.
531
+ public int maxIndex() {
532
+ //checkMinMax("maxIndex");
533
+ if (count == 0) {
534
+ return -1;
535
+ }
536
+ // Will still return NaN if there is 1 or more entries, and they're all NaN
537
+ double m = Double.NaN;
538
+ int mi = -1;
539
+ for (int i = 0; i < count; i++) {
540
+ // find one good value to start
541
+ if (values[i] == values[i]) {
542
+ m = values[i];
543
+ mi = i;
544
+
545
+ // calculate the rest
546
+ for (int j = i+1; j < count; j++) {
547
+ double d = values[j];
548
+ if (!Double.isNaN(d) && (d > m)) {
549
+ m = values[j];
550
+ mi = j;
551
+ }
552
+ }
553
+ break;
554
+ }
555
+ }
556
+ return mi;
557
+ }
558
+
559
+
560
+ /** The key for a max value; null if empty or everything is NaN (no max). */
561
+ public String maxKey() {
562
+ //checkMinMax("maxKey");
563
+ int index = maxIndex();
564
+ if (index == -1) {
565
+ return null;
566
+ }
567
+ return keys[index];
568
+ }
569
+
570
+
571
+ /** The max value. (Or NaN if no entries or they're all NaN.) */
572
+ public double maxValue() {
573
+ //checkMinMax("maxValue");
574
+ int index = maxIndex();
575
+ if (index == -1) {
576
+ return Float.NaN;
577
+ }
578
+ return values[index];
579
+ }
580
+
581
+
582
+ public double sum() {
583
+ double sum = 0;
584
+ for (int i = 0; i < count; i++) {
585
+ sum += values[i];
586
+ }
587
+ return sum;
588
+ }
589
+
590
+
591
+ public int index(String what) {
592
+ Integer found = indices.get(what);
593
+ return (found == null) ? -1 : found.intValue();
594
+ }
595
+
596
+
597
+ protected void create(String what, double much) {
598
+ if (count == keys.length) {
599
+ keys = PApplet.expand(keys);
600
+ values = PApplet.expand(values);
601
+ }
602
+ indices.put(what, Integer.valueOf(count));
603
+ keys[count] = what;
604
+ values[count] = much;
605
+ count++;
606
+ }
607
+
608
+
609
+ /**
610
+ * @webref doubledict:method
611
+ * @brief Remove a key/value pair
612
+ */
613
+ public int remove(String key) {
614
+ int index = index(key);
615
+ if (index != -1) {
616
+ removeIndex(index);
617
+ }
618
+ return index;
619
+ }
620
+
621
+
622
+ public String removeIndex(int index) {
623
+ if (index < 0 || index >= count) {
624
+ throw new ArrayIndexOutOfBoundsException(index);
625
+ }
626
+ String key = keys[index];
627
+ //System.out.println("index is " + which + " and " + keys[which]);
628
+ indices.remove(keys[index]);
629
+ for (int i = index; i < count-1; i++) {
630
+ keys[i] = keys[i+1];
631
+ values[i] = values[i+1];
632
+ indices.put(keys[i], i);
633
+ }
634
+ count--;
635
+ keys[count] = null;
636
+ values[count] = 0;
637
+ return key;
638
+ }
639
+
640
+
641
+ public void swap(int a, int b) {
642
+ String tkey = keys[a];
643
+ double tvalue = values[a];
644
+ keys[a] = keys[b];
645
+ values[a] = values[b];
646
+ keys[b] = tkey;
647
+ values[b] = tvalue;
648
+
649
+ // indices.put(keys[a], Integer.valueOf(a));
650
+ // indices.put(keys[b], Integer.valueOf(b));
651
+ }
652
+
653
+
654
+ /**
655
+ * Sort the keys alphabetically (ignoring case). Uses the value as a
656
+ * tie-breaker (only really possible with a key that has a case change).
657
+ *
658
+ * @webref doubledict:method
659
+ * @brief Sort the keys alphabetically
660
+ */
661
+ public void sortKeys() {
662
+ sortImpl(true, false, true);
663
+ }
664
+
665
+
666
+ /**
667
+ * @webref doubledict:method
668
+ * @brief Sort the keys alphabetically in reverse
669
+ */
670
+ public void sortKeysReverse() {
671
+ sortImpl(true, true, true);
672
+ }
673
+
674
+
675
+ /**
676
+ * Sort by values in descending order (largest value will be at [0]).
677
+ *
678
+ * @webref doubledict:method
679
+ * @brief Sort by values in ascending order
680
+ */
681
+ public void sortValues() {
682
+ sortValues(true);
683
+ }
684
+
685
+
686
+ /**
687
+ * Set true to ensure that the order returned is identical. Slightly
688
+ * slower because the tie-breaker for identical values compares the keys.
689
+ * @param stable
690
+ */
691
+ public void sortValues(boolean stable) {
692
+ sortImpl(false, false, stable);
693
+ }
694
+
695
+
696
+ /**
697
+ * @webref doubledict:method
698
+ * @brief Sort by values in descending order
699
+ */
700
+ public void sortValuesReverse() {
701
+ sortValuesReverse(true);
702
+ }
703
+
704
+
705
+ public void sortValuesReverse(boolean stable) {
706
+ sortImpl(false, true, stable);
707
+ }
708
+
709
+
710
+ protected void sortImpl(final boolean useKeys, final boolean reverse,
711
+ final boolean stable) {
712
+ Sort s = new Sort() {
713
+ @Override
714
+ public int size() {
715
+ if (useKeys) {
716
+ return count; // don't worry about NaN values
717
+
718
+ } else if (count == 0) { // skip the NaN check, it'll AIOOBE
719
+ return 0;
720
+
721
+ } else { // first move NaN values to the end of the list
722
+ int right = count - 1;
723
+ while (values[right] != values[right]) {
724
+ right--;
725
+ if (right == -1) {
726
+ return 0; // all values are NaN
727
+ }
728
+ }
729
+ for (int i = right; i >= 0; --i) {
730
+ if (Double.isNaN(values[i])) {
731
+ swap(i, right);
732
+ --right;
733
+ }
734
+ }
735
+ return right + 1;
736
+ }
737
+ }
738
+
739
+ @Override
740
+ public int compare(int a, int b) {
741
+ double diff = 0;
742
+ if (useKeys) {
743
+ diff = keys[a].compareToIgnoreCase(keys[b]);
744
+ if (diff == 0) {
745
+ diff = values[a] - values[b];
746
+ }
747
+ } else { // sort values
748
+ diff = values[a] - values[b];
749
+ if (diff == 0 && stable) {
750
+ diff = keys[a].compareToIgnoreCase(keys[b]);
751
+ }
752
+ }
753
+ if (diff == 0) {
754
+ return 0;
755
+ } else if (reverse) {
756
+ return diff < 0 ? 1 : -1;
757
+ } else {
758
+ return diff < 0 ? -1 : 1;
759
+ }
760
+ }
761
+
762
+ @Override
763
+ public void swap(int a, int b) {
764
+ DoubleDict.this.swap(a, b);
765
+ }
766
+ };
767
+ s.run();
768
+
769
+ // Set the indices after sort/swaps (performance fix 160411)
770
+ resetIndices();
771
+ }
772
+
773
+
774
+ /**
775
+ * Sum all of the values in this dictionary, then return a new FloatDict of
776
+ * each key, divided by the total sum. The total for all values will be ~1.0.
777
+ * @return a FloatDict with the original keys, mapped to their pct of the total
778
+ */
779
+ public DoubleDict getPercent() {
780
+ double sum = sum();
781
+ DoubleDict outgoing = new DoubleDict();
782
+ for (int i = 0; i < size(); i++) {
783
+ double percent = value(i) / sum;
784
+ outgoing.set(key(i), percent);
785
+ }
786
+ return outgoing;
787
+ }
788
+
789
+
790
+ /** Returns a duplicate copy of this object. */
791
+ public DoubleDict copy() {
792
+ DoubleDict outgoing = new DoubleDict(count);
793
+ System.arraycopy(keys, 0, outgoing.keys, 0, count);
794
+ System.arraycopy(values, 0, outgoing.values, 0, count);
795
+ for (int i = 0; i < count; i++) {
796
+ outgoing.indices.put(keys[i], i);
797
+ }
798
+ outgoing.count = count;
799
+ return outgoing;
800
+ }
801
+
802
+
803
+ public void print() {
804
+ for (int i = 0; i < size(); i++) {
805
+ System.out.println(keys[i] + " = " + values[i]);
806
+ }
807
+ }
808
+
809
+
810
+ /**
811
+ * Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
812
+ */
813
+ public void save(File file) {
814
+ PrintWriter writer = PApplet.createWriter(file);
815
+ write(writer);
816
+ writer.close();
817
+ }
818
+
819
+
820
+ /**
821
+ * Write tab-delimited entries out to
822
+ * @param writer
823
+ */
824
+ public void write(PrintWriter writer) {
825
+ for (int i = 0; i < count; i++) {
826
+ writer.println(keys[i] + "\t" + values[i]);
827
+ }
828
+ writer.flush();
829
+ }
830
+
831
+
832
+ /**
833
+ * Return this dictionary as a String in JSON format.
834
+ */
835
+ public String toJSON() {
836
+ StringList items = new StringList();
837
+ for (int i = 0; i < count; i++) {
838
+ items.append(JSONObject.quote(keys[i])+ ": " + values[i]);
839
+ }
840
+ return "{ " + items.join(", ") + " }";
841
+ }
842
+
843
+
844
+ @Override
845
+ public String toString() {
846
+ return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
847
+ }
848
+ }