mirror of
https://github.com/processing/processing4.git
synced 2026-04-17 09:49:39 +02:00
add "visualizing data" examples
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
// Code from Visualizing Data, First Edition, Copyright 2008 Ben Fry.
|
||||
|
||||
|
||||
public class ColorIntegrator extends Integrator {
|
||||
|
||||
float r0, g0, b0, a0;
|
||||
float rs, gs, bs, as;
|
||||
|
||||
int colorValue;
|
||||
|
||||
|
||||
public ColorIntegrator(int color0, int color1) {
|
||||
int a1 = (color0 >> 24) & 0xff;
|
||||
int r1 = (color0 >> 16) & 0xff;
|
||||
int g1 = (color0 >> 8) & 0xff;
|
||||
int b1 = (color0 ) & 0xff;
|
||||
|
||||
int a2 = (color1 >> 24) & 0xff;
|
||||
int r2 = (color1 >> 16) & 0xff;
|
||||
int g2 = (color1 >> 8) & 0xff;
|
||||
int b2 = (color1 ) & 0xff;
|
||||
|
||||
r0 = (float)r1 / 255.0f;
|
||||
g0 = (float)g1 / 255.0f;
|
||||
b0 = (float)b1 / 255.0f;
|
||||
a0 = (float)a1 / 255.0f;
|
||||
|
||||
rs = (r2 - r1) / 255.0f;
|
||||
gs = (g2 - g1) / 255.0f;
|
||||
bs = (b2 - b1) / 255.0f;
|
||||
as = (a2 - a1) / 255.0f;
|
||||
}
|
||||
|
||||
|
||||
public boolean update() {
|
||||
boolean updated = super.update();
|
||||
if (updated) {
|
||||
colorValue =
|
||||
(((int) ((a0 + as*value) * 255f) << 24) |
|
||||
((int) ((r0 + rs*value) * 255f) << 16) |
|
||||
((int) ((g0 + gs*value) * 255f) << 8) |
|
||||
((int) ((b0 + bs*value) * 255f)));
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
|
||||
public int get() {
|
||||
return colorValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
// Code from Visualizing Data, First Edition, Copyright 2008 Ben Fry.
|
||||
|
||||
|
||||
public class Integrator {
|
||||
|
||||
static final float DAMPING = 0.5f; // formerly 0.9f
|
||||
static final float ATTRACTION = 0.2f; // formerly 0.1f
|
||||
|
||||
float value = 0;
|
||||
float vel = 0;
|
||||
float accel = 0;
|
||||
float force = 0;
|
||||
float mass = 1;
|
||||
|
||||
float damping; // = DAMPING;
|
||||
float attraction; // = ATTRACTION;
|
||||
|
||||
boolean targeting; // = false;
|
||||
float target; // = 0;
|
||||
|
||||
|
||||
public Integrator() {
|
||||
this.value = 0;
|
||||
this.damping = DAMPING;
|
||||
this.attraction = ATTRACTION;
|
||||
}
|
||||
|
||||
|
||||
public Integrator(float value) {
|
||||
this.value = value;
|
||||
this.damping = DAMPING;
|
||||
this.attraction = ATTRACTION;
|
||||
}
|
||||
|
||||
|
||||
public Integrator(float value, float damping, float attraction) {
|
||||
this.value = value;
|
||||
this.damping = damping;
|
||||
this.attraction = attraction;
|
||||
}
|
||||
|
||||
|
||||
public void set(float v) {
|
||||
value = v;
|
||||
//targeting = false ?
|
||||
}
|
||||
|
||||
|
||||
public boolean update() { // default dtime = 1.0
|
||||
if (targeting) {
|
||||
force += attraction * (target - value);
|
||||
}
|
||||
|
||||
accel = force / mass;
|
||||
vel = (vel + accel) * damping; /* e.g. 0.90 */
|
||||
value += vel;
|
||||
|
||||
force = 0; // implicit reset
|
||||
|
||||
return (vel > 0.0001f);
|
||||
}
|
||||
|
||||
|
||||
public void target(float t) {
|
||||
targeting = true;
|
||||
target = t;
|
||||
}
|
||||
|
||||
|
||||
public void noTarget() {
|
||||
targeting = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
public void attraction(float targetValue, float a) {
|
||||
force += attraction * (targetValue - value);
|
||||
}
|
||||
|
||||
public void attract(float target, float a) {
|
||||
attraction(target, a);
|
||||
update();
|
||||
}
|
||||
|
||||
public void setDecay(float d) {
|
||||
kDecay = d;
|
||||
}
|
||||
|
||||
public void decay() {
|
||||
force -= kDecay * value;
|
||||
}
|
||||
|
||||
public void decay(float d) {
|
||||
force -= d * value;
|
||||
}
|
||||
|
||||
public void setImpulse(float i) {
|
||||
kImpulse = i;
|
||||
}
|
||||
|
||||
public void impulse() {
|
||||
//printf("kimpulse is %f\n", kImpulse);
|
||||
force += kImpulse;
|
||||
//decay(-kImpulse); // lazy
|
||||
}
|
||||
|
||||
public void impulse(float i) {
|
||||
force += i;
|
||||
//decay(-i); // lazy
|
||||
}
|
||||
|
||||
public void setDamping(float d) {
|
||||
kDamping = d;
|
||||
}
|
||||
|
||||
public void noise(float amount) {
|
||||
force += (float) ((Math.random() * 2) - 1) * amount;
|
||||
}
|
||||
|
||||
public void add(float v) {
|
||||
value += v;
|
||||
}
|
||||
|
||||
public void add(Integrator integrator) {
|
||||
value += integrator.value;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
void Integrator1f::updateRK() { // default dtime = 1.0
|
||||
#define H 0.001
|
||||
float f1 = force;
|
||||
float f2 = force + H*f1/2;
|
||||
float f3 = force + H*f2/2;
|
||||
float f4 = force + H*f3;
|
||||
velocity = velocity + (H/6)*(f1 + 2*f2 + 2*f3 + f4);
|
||||
}
|
||||
|
||||
eval(x) is the force
|
||||
i think x should be time, so x is normally 1.0.
|
||||
if dtime were incorporated, that would probably work
|
||||
>> need correct function for force and dtime
|
||||
|
||||
double f1 = fn.evalX(x);
|
||||
double f2 = fn.evalX(x + h*f1/2);
|
||||
double f3 = fn.evalX(x + h*f2/2);
|
||||
double f4 = fn.evalX(x + h*f3);
|
||||
|
||||
out = x + (h/6)*(f1 + 2*f2 + 2*f3 + f4);
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
public void step(double t, double x, double y,
|
||||
Function fn, double h, double out[]) {
|
||||
double f1 = fn.evalX(t, x, y);
|
||||
double g1 = fn.evalY(t, x, y);
|
||||
|
||||
double f2 = fn.evalX(t + h/2, x + h*f1/2, y + h*g1/2);
|
||||
double g2 = fn.evalY(t + h/2, x + h*f1/2, y + h*g1/2);
|
||||
|
||||
double f3 = fn.evalX(t + h/2, x + h*f2/2, y + h*g2/2);
|
||||
double g3 = fn.evalY(t + h/2, x + h*f2/2, y + h*g2/2);
|
||||
|
||||
double f4 = fn.evalX(t + h, x + h*f3, y + h*g3);
|
||||
double g4 = fn.evalY(t + h, x + h*f3, y + h*g3);
|
||||
|
||||
out[0] = x + (h/6)*(f1 + 2*f2 + 2*f3 + f4);
|
||||
out[1] = y + (h/6)*(g1 + 2*g2 + 2*g3 + g4);
|
||||
}
|
||||
*/
|
||||
|
||||
//void Integrator1f::update(float dtime) {
|
||||
//velocity += force * dtime;
|
||||
// value += velocity*dtime + 0.5f*force*dtime*dtime;
|
||||
//force = 0;
|
||||
//}
|
||||
@@ -0,0 +1,131 @@
|
||||
// Code from Visualizing Data, First Edition, Copyright 2008 Ben Fry.
|
||||
|
||||
|
||||
class Place {
|
||||
int code;
|
||||
String name;
|
||||
float x;
|
||||
float y;
|
||||
|
||||
int partial[];
|
||||
int matchDepth;
|
||||
|
||||
|
||||
public Place(int code, String name, float lon, float lat) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.x = lon;
|
||||
this.y = lat;
|
||||
|
||||
partial = new int[6];
|
||||
partial[5] = code;
|
||||
partial[4] = partial[5] / 10;
|
||||
partial[3] = partial[4] / 10;
|
||||
partial[2] = partial[3] / 10;
|
||||
partial[1] = partial[2] / 10;
|
||||
}
|
||||
|
||||
|
||||
void check() {
|
||||
// default to zero levels of depth that match
|
||||
matchDepth = 0;
|
||||
|
||||
if (typedCount != 0) {
|
||||
// Start from the greatest depth, and work backwards to see how many
|
||||
// items match. Want to figure out the maximum match, so better to
|
||||
// begin from the end.
|
||||
// The multiple levels of matching are important because more than one
|
||||
// depth level might be fading at a time.
|
||||
for (int j = typedCount; j > 0; --j) {
|
||||
if (typedPartials[j] == partial[j]) {
|
||||
matchDepth = j;
|
||||
break; // since starting at end, can stop now
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if (partial[typedCount] == partialCode) {
|
||||
if (matchDepth == typedCount) {
|
||||
foundCount++;
|
||||
if (typedCount == 5) {
|
||||
chosen = this;
|
||||
}
|
||||
|
||||
if (x < boundsX1) boundsX1 = x;
|
||||
if (y < boundsY1) boundsY1 = y;
|
||||
if (x > boundsX2) boundsX2 = x;
|
||||
if (y > boundsY2) boundsY2 = y;
|
||||
}
|
||||
}
|
||||
|
||||
void draw() {
|
||||
float xx = TX(x);
|
||||
float yy = TY(y);
|
||||
|
||||
if ((xx < 0) || (yy < 0) || (xx >= width) || (yy >= height)) return;
|
||||
|
||||
if ((zoomDepth.value < 2.8f) || !zoomEnabled) { // show simple dots
|
||||
//pixels[((int) yy) * width + ((int) xx)] = faders[matchDepth].cvalue;
|
||||
set((int)xx, (int)yy, faders[matchDepth].colorValue);
|
||||
|
||||
} else { // show slightly more complicated dots
|
||||
noStroke();
|
||||
|
||||
fill(faders[matchDepth].colorValue);
|
||||
//rect(TX(nlon), TY(nlat), depther.value-1, depther.value-1);
|
||||
|
||||
if (matchDepth == typedCount) {
|
||||
if (typedCount == 4) { // on the fourth digit, show nums for the 5th
|
||||
text(code % 10, TX(x), TY(y));
|
||||
} else { // show a larger box for selections
|
||||
rect(xx, yy, zoomDepth.value, zoomDepth.value);
|
||||
}
|
||||
} else { // show a slightly smaller box for unselected
|
||||
rect(xx, yy, zoomDepth.value-1, zoomDepth.value-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void drawChosen() {
|
||||
noStroke();
|
||||
fill(faders[matchDepth].colorValue);
|
||||
// the chosen point has to be a little larger when zooming
|
||||
int size = zoomEnabled ? 6 : 4;
|
||||
rect(TX(x), TY(y), size, size);
|
||||
|
||||
// calculate position to draw the text, slightly offset from the main point
|
||||
float textX = TX(x);
|
||||
float textY = TY(y) - size - 4;
|
||||
|
||||
// don't go off the top.. (e.g. 59544)
|
||||
if (textY < 20) {
|
||||
textY = TY(y) + 20;
|
||||
}
|
||||
|
||||
// don't run off the bottom.. (e.g. 33242)
|
||||
if (textY > height - 5) {
|
||||
textY = TY(y) - 20;
|
||||
}
|
||||
|
||||
String location = name + " " + nf(code, 5);
|
||||
|
||||
if (zoomEnabled) {
|
||||
textAlign(CENTER);
|
||||
text(location, textX, textY);
|
||||
|
||||
} else {
|
||||
float wide = textWidth(location);
|
||||
|
||||
if (textX > width/3) {
|
||||
textX -= wide + 8;
|
||||
} else {
|
||||
textX += 8;
|
||||
}
|
||||
|
||||
textAlign(LEFT);
|
||||
fill(highlightColor);
|
||||
text(location, textX, textY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Code from Visualizing Data, First Edition, Copyright 2008 Ben Fry.
|
||||
|
||||
|
||||
class Slurper implements Runnable {
|
||||
|
||||
Slurper() {
|
||||
Thread thread = new Thread(this);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
InputStream input = openStream("zips.gz");
|
||||
BufferedReader reader = createReader(input);
|
||||
|
||||
// first get the info line
|
||||
String line = reader.readLine();
|
||||
parseInfo(line);
|
||||
|
||||
places = new Place[totalCount];
|
||||
|
||||
// parse each of the rest of the lines
|
||||
while ((line = reader.readLine()) != null) {
|
||||
places[placeCount] = parsePlace(line);
|
||||
placeCount++;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
This book is here to help you get your job done. In general, you may use the
|
||||
code in this book in your programs and documentation. You do not need to contact
|
||||
us for permission unless youÕre reproducing a significant portion of the code.
|
||||
For example, writing a program that uses several chunks of code from this book
|
||||
does not require permission. Selling or distributing a CD-ROM of examples from
|
||||
OÕReilly books does require permission. Answering a question by citing this book
|
||||
and quoting example code does not require permission. Incorporating a significant
|
||||
amount of example code from this book into your productÕs documentation does
|
||||
require permission.
|
||||
|
||||
We appreciate, but do not require, attribution. An attribution usually includes
|
||||
the title, author, publisher, and ISBN. For example: ÒVisualizing Data, First
|
||||
Edition by Ben Fry. Copyright 2008 Ben Fry, 9780596514556.Ó
|
||||
|
||||
If you feel your use of code examples falls outside fair use or the permission
|
||||
given above, feel free to contact us at permissions@oreilly.com.
|
||||
*/
|
||||
|
||||
color backgroundColor = #333333; // dark background color
|
||||
color dormantColor = #999966; // initial color of the map
|
||||
color highlightColor = #CBCBCB; // color for selected points
|
||||
color unhighlightColor = #66664C; // color for points that are not selected
|
||||
color waitingColor = #CBCBCB; // "please type a zip code" message
|
||||
color badColor = #FFFF66; // text color when nothing found
|
||||
|
||||
ColorIntegrator faders[];
|
||||
|
||||
// border of where the map should be drawn on screen
|
||||
float mapX1, mapY1;
|
||||
float mapX2, mapY2;
|
||||
|
||||
// column numbers in the data file
|
||||
static final int CODE = 0;
|
||||
static final int X = 1;
|
||||
static final int Y = 2;
|
||||
static final int NAME = 3;
|
||||
|
||||
int totalCount; // total number of places
|
||||
Place[] places;
|
||||
int placeCount; // number of places loaded
|
||||
|
||||
// min/max boundary of all points
|
||||
float minX, maxX;
|
||||
float minY, maxY;
|
||||
|
||||
// typing and selection
|
||||
PFont font;
|
||||
String typedString = "";
|
||||
char typedChars[] = new char[5];
|
||||
int typedCount;
|
||||
int typedPartials[] = new int[6];
|
||||
|
||||
float messageX, messageY;
|
||||
|
||||
int foundCount;
|
||||
Place chosen;
|
||||
|
||||
// smart updates
|
||||
int notUpdatedCount = 0;
|
||||
|
||||
// zoom
|
||||
boolean zoomEnabled = false;
|
||||
Integrator zoomDepth = new Integrator();
|
||||
|
||||
Integrator zoomX1;
|
||||
Integrator zoomY1;
|
||||
Integrator zoomX2;
|
||||
Integrator zoomY2;
|
||||
|
||||
float targetX1[] = new float[6];
|
||||
float targetY1[] = new float[6];
|
||||
float targetX2[] = new float[6];
|
||||
float targetY2[] = new float[6];
|
||||
|
||||
// boundary of currently valid points at this typedCount
|
||||
float boundsX1, boundsY1;
|
||||
float boundsX2, boundsY2;
|
||||
|
||||
|
||||
public void setup() {
|
||||
size(720, 453, P3D);
|
||||
|
||||
mapX1 = 30;
|
||||
mapX2 = width - mapX1;
|
||||
mapY1 = 20;
|
||||
mapY2 = height - mapY1;
|
||||
|
||||
font = loadFont("ScalaSans-Regular-14.vlw");
|
||||
textFont(font);
|
||||
textMode(SCREEN);
|
||||
|
||||
messageX = 40;
|
||||
messageY = height - 40;
|
||||
|
||||
faders = new ColorIntegrator[6];
|
||||
|
||||
// When nothing is typed, all points are shown with a color called
|
||||
// "dormant," which is brighter than when not highlighted, but
|
||||
// not as bright as the highlight color for a selection.
|
||||
faders[0] = new ColorIntegrator(unhighlightColor, dormantColor);
|
||||
faders[0].attraction = 0.5f;
|
||||
faders[0].target(1);
|
||||
|
||||
for (int i = 1; i < 6; i++) {
|
||||
faders[i] = new ColorIntegrator(unhighlightColor, highlightColor);
|
||||
faders[i].attraction = 0.5;
|
||||
faders[i].target(1);
|
||||
}
|
||||
|
||||
readData();
|
||||
|
||||
zoomX1 = new Integrator(minX);
|
||||
zoomY1 = new Integrator(minY);
|
||||
zoomX2 = new Integrator(maxX);
|
||||
zoomY2 = new Integrator(maxY);
|
||||
|
||||
targetX1[0] = minX;
|
||||
targetX2[0] = maxX;
|
||||
targetY1[0] = minY;
|
||||
targetY2[0] = maxY;
|
||||
|
||||
rectMode(CENTER);
|
||||
ellipseMode(CENTER);
|
||||
frameRate(15);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void readData() {
|
||||
new Slurper();
|
||||
noLoop(); // done loading, can stop updating
|
||||
}
|
||||
|
||||
|
||||
void parseInfo(String line) {
|
||||
String infoString = line.substring(2); // remove the #
|
||||
String[] infoPieces = split(infoString, ',');
|
||||
totalCount = int(infoPieces[0]);
|
||||
minX = float(infoPieces[1]);
|
||||
maxX = float(infoPieces[2]);
|
||||
minY = float(infoPieces[3]);
|
||||
maxY = float(infoPieces[4]);
|
||||
}
|
||||
|
||||
|
||||
Place parsePlace(String line) {
|
||||
String pieces[] = split(line, TAB);
|
||||
|
||||
int zip = int(pieces[CODE]);
|
||||
float x = float(pieces[X]);
|
||||
float y = float(pieces[Y]);
|
||||
String name = pieces[NAME];
|
||||
|
||||
return new Place(zip, name, x, y);
|
||||
}
|
||||
|
||||
|
||||
// change message from 'click inside the window'
|
||||
public void focusGained() {
|
||||
redraw();
|
||||
}
|
||||
|
||||
// change message to 'click inside the window'
|
||||
public void focusLost() {
|
||||
redraw();
|
||||
}
|
||||
|
||||
// this method is empty in p5
|
||||
public void mouseEntered() {
|
||||
requestFocus();
|
||||
}
|
||||
|
||||
|
||||
public void draw() {
|
||||
background(backgroundColor);
|
||||
|
||||
updateAnimation();
|
||||
|
||||
for (int i = 0; i < placeCount; i++) {
|
||||
places[i].draw();
|
||||
}
|
||||
|
||||
if (typedCount == 0) {
|
||||
fill(waitingColor);
|
||||
textAlign(LEFT);
|
||||
String message = "zipdecode by ben fry";
|
||||
// if all places are loaded
|
||||
if (placeCount == totalCount) {
|
||||
if (focused) {
|
||||
message = "type the digits of a zip code";
|
||||
} else {
|
||||
message = "click the map image to begin";
|
||||
}
|
||||
}
|
||||
text(message, messageX, messageY);
|
||||
|
||||
} else {
|
||||
if (foundCount > 0) {
|
||||
if (!zoomEnabled && (typedCount == 4)) {
|
||||
// re-draw the chosen ones, because they're often occluded
|
||||
// by the non-selected points
|
||||
for (int i = 0; i < placeCount; i++) {
|
||||
if (places[i].matchDepth == typedCount) {
|
||||
places[i].draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (chosen != null) {
|
||||
chosen.drawChosen();
|
||||
}
|
||||
|
||||
fill(highlightColor);
|
||||
textAlign(LEFT);
|
||||
text(typedString, messageX, messageY);
|
||||
|
||||
} else {
|
||||
fill(badColor);
|
||||
text(typedString, messageX, messageY);
|
||||
}
|
||||
}
|
||||
|
||||
// draw "zoom" text toggle
|
||||
textAlign(RIGHT);
|
||||
fill(zoomEnabled ? highlightColor : unhighlightColor);
|
||||
text("zoom", width - 40, height - 40);
|
||||
textAlign(LEFT);
|
||||
}
|
||||
|
||||
|
||||
void updateAnimation() {
|
||||
boolean updated = false;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
updated |= faders[i].update();
|
||||
}
|
||||
|
||||
if (foundCount > 0) {
|
||||
zoomDepth.target(typedCount);
|
||||
} else {
|
||||
zoomDepth.target(typedCount-1);
|
||||
}
|
||||
updated |= zoomDepth.update();
|
||||
|
||||
updated |= zoomX1.update();
|
||||
updated |= zoomY1.update();
|
||||
updated |= zoomX2.update();
|
||||
updated |= zoomY2.update();
|
||||
|
||||
// if the data is loaded, can optionally call noLoop() to save cpu
|
||||
if (placeCount == totalCount) { // if fully loaded
|
||||
if (!updated) {
|
||||
notUpdatedCount++;
|
||||
// after 20 frames of no updates, shut off the loop
|
||||
if (notUpdatedCount > 20) {
|
||||
noLoop();
|
||||
notUpdatedCount = 0;
|
||||
}
|
||||
} else {
|
||||
notUpdatedCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float TX(float x) {
|
||||
if (zoomEnabled) {
|
||||
return map(x, zoomX1.value, zoomX2.value, mapX1, mapX2);
|
||||
|
||||
} else {
|
||||
return map(x, minX, maxX, mapX1, mapX2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float TY(float y) {
|
||||
if (zoomEnabled) {
|
||||
return map(y, zoomY1.value, zoomY2.value, mapY2, mapY1);
|
||||
|
||||
} else {
|
||||
return map(y, minY, maxY, mapY2, mapY1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mousePressed() {
|
||||
if ((mouseX > width-100) && (mouseY > height - 50)) {
|
||||
zoomEnabled = !zoomEnabled;
|
||||
redraw();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void keyPressed() {
|
||||
if ((key == BACKSPACE) || (key == DELETE)) {
|
||||
if (typedCount > 0) {
|
||||
typedCount--;
|
||||
}
|
||||
updateTyped();
|
||||
|
||||
} else if ((key >= '0') && (key <= '9')) {
|
||||
if (typedCount != 5) { // only 5 digits
|
||||
if (foundCount > 0) { // don't allow to keep typing bad
|
||||
typedChars[typedCount++] = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
updateTyped();
|
||||
}
|
||||
|
||||
|
||||
void updateTyped() {
|
||||
typedString = new String(typedChars, 0, typedCount);
|
||||
|
||||
// Un-highlight areas already typed past
|
||||
for (int i = 0; i < typedCount; i++) faders[i].target(0);
|
||||
// Highlight potential dots not yet selected by keys
|
||||
for (int i = typedCount; i < 6; i++) faders[i].target(1);
|
||||
|
||||
typedPartials[typedCount] = int(typedString);
|
||||
for (int j = typedCount-1; j > 0; --j) {
|
||||
typedPartials[j] = typedPartials[j + 1] / 10;
|
||||
}
|
||||
|
||||
foundCount = 0;
|
||||
chosen = null;
|
||||
|
||||
boundsX1 = maxX;
|
||||
boundsY1 = maxY;
|
||||
boundsX2 = minX;
|
||||
boundsY2 = minY;
|
||||
|
||||
for (int i = 0; i < placeCount; i++) {
|
||||
// update boundaries of selection
|
||||
// and identify whether a particular place is chosen
|
||||
places[i].check();
|
||||
}
|
||||
calcZoom();
|
||||
|
||||
loop(); // re-enable updates
|
||||
}
|
||||
|
||||
|
||||
void calcZoom() {
|
||||
if (foundCount != 0) {
|
||||
// given a set of min/max coords, expand in one direction so that the
|
||||
// selected area includes the range with the proper aspect ratio
|
||||
|
||||
float spanX = (boundsX2 - boundsX1);
|
||||
float spanY = (boundsY2 - boundsY1);
|
||||
|
||||
float midX = (boundsX1 + boundsX2) / 2;
|
||||
float midY = (boundsY1 + boundsY2) / 2;
|
||||
|
||||
if ((spanX != 0) && (spanY != 0)) {
|
||||
float screenAspect = width / float(height);
|
||||
float spanAspect = spanX / spanY;
|
||||
|
||||
if (spanAspect > screenAspect) {
|
||||
spanY = (spanX / width) * height; // wide
|
||||
|
||||
} else {
|
||||
spanX = (spanY / height) * width; // tall
|
||||
}
|
||||
} else { // if span is zero
|
||||
// use the span from one level previous
|
||||
spanX = targetX2[typedCount-1] - targetX1[typedCount-1];
|
||||
spanY = targetY2[typedCount-1] - targetY1[typedCount-1];
|
||||
}
|
||||
targetX1[typedCount] = midX - spanX/2;
|
||||
targetX2[typedCount] = midX + spanX/2;
|
||||
targetY1[typedCount] = midY - spanY/2;
|
||||
targetY2[typedCount] = midY + spanY/2;
|
||||
|
||||
} else if (typedCount != 0) {
|
||||
// nothing found at this level, so set the zoom identical to the previous
|
||||
targetX1[typedCount] = targetX1[typedCount-1];
|
||||
targetY1[typedCount] = targetY1[typedCount-1];
|
||||
targetX2[typedCount] = targetX2[typedCount-1];
|
||||
targetY2[typedCount] = targetY2[typedCount-1];
|
||||
}
|
||||
|
||||
zoomX1.target(targetX1[typedCount]);
|
||||
zoomY1.target(targetY1[typedCount]);
|
||||
zoomX2.target(targetX2[typedCount]);
|
||||
zoomY2.target(targetY2[typedCount]);
|
||||
|
||||
if (!zoomEnabled) {
|
||||
zoomX1.set(zoomX1.target);
|
||||
zoomY1.set(zoomY1.target);
|
||||
zoomX2.set(zoomX2.target);
|
||||
zoomY2.set(zoomY2.target);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user