propane 2.9.0-java → 2.9.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }