Last active
January 3, 2019 16:44
-
-
Save companje/9ebc00b5511a6571d7d8d1a3bd7802c3 to your computer and use it in GitHub Desktop.
Making progress with TopoGame
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import hypermedia.net.*; | |
UDP udp; | |
PShape sphere; | |
Game game; | |
Globe globe; | |
Ring ring; | |
PFont defaultFont; | |
void setup() { | |
size(800, 800, P3D); | |
udp = new UDP(this, 8888); | |
udp.listen(true); | |
ring = new Ring(); | |
ring.setup(); | |
globe = new Globe(); | |
globe.setup(); | |
game = new Game(this); | |
game.setup(); | |
defaultFont = loadFont("SansSerif.plain-13.vlw"); | |
textFont(defaultFont); | |
} | |
void draw() { | |
background(0); | |
ortho(); | |
translate(width/2, height/2); //center | |
game.draw(); | |
//game.drawDebug(); | |
} | |
void mouseDragged() { | |
PVector from = toSphere(float(pmouseX)/width-.5, float(pmouseY)/width-.5); | |
PVector to = toSphere(float(mouseX)/width-.5, float(mouseY)/width-.5); | |
globe.qRel.mult(new Quaternion(from.dot(to), from.cross(to))); //w,xyz | |
globe.qRel.normalize(); | |
} | |
void receive( byte[] data ) { | |
String message = new String( data ); | |
String values[] = split(message, ','); | |
if (values.length==7) { | |
for (int i=0; i<3; i++) { | |
float offset = .7; | |
float speed = .075 / globe.radius; | |
float localRotation = radians(45); | |
float globalRotation = radians(155) - i/3. * TWO_PI; | |
float x = float(values[i*2+0]); | |
float y = float(values[i*2+1]); | |
PVector from = new PVector(offset, 0).rotate(globalRotation); | |
PVector to = new PVector(x, y).rotate(localRotation); | |
to.y *= -1; | |
to.rotate(globalRotation).mult(speed).add(from); | |
from = toSphere(from); | |
to = toSphere(to); | |
globe.qRel.mult(new Quaternion(from.dot(to), from.cross(to))); //w,xyz | |
globe.qRel.normalize(); | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class City { | |
String title,subtitle; | |
float lat,lon; | |
} | |
class Data { | |
PImage imgOceans, imgCountries, imgContinents; | |
HashMap<Integer, String> oceans = new HashMap(); | |
HashMap<Integer, String> countries = new HashMap(); | |
HashMap<Integer, String> continents = new HashMap(); | |
ArrayList<City> cities = new ArrayList(); | |
void setup() { | |
imgCountries = loadImage("countries.png"); | |
imgContinents = loadImage("continents.png"); | |
imgOceans = loadImage("oceans.png"); | |
for (String s : loadStrings("oceans.txt")) { | |
String items[] = split(s, ","); | |
oceans.put(unhex(items[0]), items[1]); | |
} | |
for (String s : loadStrings("countries.txt")) { | |
String items[] = split(s, ","); | |
countries.put(unhex(items[0]), items[1]); | |
} | |
for (String s : loadStrings("continents.txt")) { | |
String items[] = split(s, ","); | |
continents.put(unhex(items[0]), items[1]); | |
} | |
for (String s : loadStrings("cities.txt")) { | |
String items[] = split(s,","); | |
City c = new City(); | |
c.title = items[0]; | |
c.lat = float(items[1]); | |
c.lon = float(items[2]); | |
c.subtitle = items[3]; | |
} | |
} | |
color getColor(float lat, float lon, PImage img) { | |
int x = (int)map(lon, -180, 180, 0, img.width); | |
int y = (int)map(lat, 90, -90, 0, img.height); | |
return img.get(x, y) & 0xffffff; | |
} | |
String getTitle(float lat, float lon, PImage img, HashMap list) { | |
String s = (String)list.get(getColor(lat, lon, img)); | |
return s!=null ? s : ""; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class GameState { // base class | |
Game game; | |
GameState(Game game) { | |
this.game = game; | |
} | |
void setup() { | |
} | |
void cleanup() { | |
} | |
void pause() { | |
} | |
void resume() { | |
} | |
void update() { | |
} | |
void draw() { | |
} | |
} | |
class Game { | |
Start start = new Start(this); | |
Intro intro = new Intro(this); | |
NewQuestion newQuestion = new NewQuestion(this); | |
FindCity findCity = new FindCity(this); | |
FindArea findArea = new FindArea(this); | |
PlaceFound placeFound = new PlaceFound(this); | |
Finished finished = new Finished(this); | |
GameOver gameOver = new GameOver(this); | |
PApplet app; | |
GameState state; | |
Heli heli = new Heli(); | |
Data data = new Data(); | |
Game(PApplet app) { | |
this.app = app; | |
state = start; | |
} | |
void setup() { | |
data.setup(); | |
heli.setup(); | |
state.setup(); | |
} | |
void draw() { | |
state.draw(); | |
} | |
class Start extends GameState { | |
Start(Game game) { | |
super(game); | |
} | |
void draw() { | |
ring.draw(); | |
globe.draw(); | |
heli.heading = globe.heading; | |
heli.draw(); | |
text("welcome to the game", -width/2+30, -height/2+30); | |
float lon = globe.qNow.getLongitude(); | |
float lat = globe.qNow.getLatitude(); | |
ring.title = data.getTitle(lat, lon, data.imgCountries, data.countries); | |
} | |
} | |
class Intro extends GameState { | |
Intro(Game game) { | |
super(game); | |
} | |
} | |
class NewQuestion extends GameState { | |
NewQuestion(Game game) { | |
super(game); | |
} | |
} | |
class FindCity extends GameState { | |
FindCity(Game game) { | |
super(game); | |
} | |
} | |
class FindArea extends GameState { | |
FindArea(Game game) { | |
super(game); | |
} | |
} | |
class PlaceFound extends GameState { | |
PlaceFound(Game game) { | |
super(game); | |
} | |
} | |
class Finished extends GameState { | |
Finished(Game game) { | |
super(game); | |
} | |
} | |
class GameOver extends GameState { | |
GameOver(Game game) { | |
super(game); | |
} | |
} | |
void drawDebug() { | |
pushStyle(); | |
pushMatrix(); | |
hint(DISABLE_DEPTH_TEST); | |
imageMode(CORNER); | |
translate(-width/2, -height/2); | |
image(globe.texture, 0, 0, 300, 150); | |
image(data.imgOceans, 0, 150, 300, 150); | |
image(data.imgCountries, 0, 300, 300, 150); | |
image(data.imgContinents, 0, 450, 300, 150); | |
float lon = globe.qNow.getLongitude(); | |
float lat = globe.qNow.getLatitude(); | |
float x = map(lon, -180, 180, 0, 300); | |
float y = map(lat, 90, -90, 0, 150); | |
fill(255, 0, 0); | |
noStroke(); | |
ellipse(x, y, 5, 5); | |
ellipse(x, y+150, 5, 5); | |
ellipse(x, y+300, 5, 5); | |
ellipse(x, y+450, 5, 5); | |
fill(255); | |
text(data.getTitle(lat, lon, data.imgContinents, data.continents), 10, 450+140); | |
text(data.getTitle(lat, lon, data.imgCountries, data.countries), 10, 300+140); | |
text(data.getTitle(lat, lon, data.imgOceans, data.oceans), 10, 150+140); | |
fill(255); | |
//textSize(20); | |
float xx=650; | |
float yy=0; | |
text("cartesian: " + globe.qNow.getCartesian(), xx, yy+=20); | |
text("cartesian.length: " +globe.qNow. getCartesian().mag(), xx, yy+=20); | |
text("lat: " + int(globe.qNow.getLatitude()), xx, yy+=20); | |
text("lon: " + int(globe.qNow.getLongitude()), xx, yy+=20); | |
hint(ENABLE_DEPTH_TEST); | |
popMatrix(); | |
popStyle(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Globe { | |
PShape shape; | |
PShader shader; | |
PImage texture; | |
float radius = 315; | |
float heading, toHeading; | |
Quaternion qRel = new Quaternion(); | |
Quaternion qNow = new Quaternion(); | |
void setup() { | |
sphereDetail(100); | |
texture = loadImage("earth2k.jpg"); | |
shape = createShape(SPHERE, radius); | |
shape.setStroke(false); | |
shape.setTexture(texture); | |
println("setup"); | |
shader = loadShader("shader.glsl"); | |
} | |
void update() { | |
if (!qRel.isIdentity()) { //only update when moved | |
qNow.mult(qRel); | |
toHeading = qRel.getHeading(); | |
} | |
heading = angleLerp(heading, toHeading, .1); //always update | |
qRel.reset(); //reset movement of this frame | |
} | |
void draw() { | |
update(); | |
pushMatrix(); | |
rotate(qNow); | |
rotateY(HALF_PI); //rotate texture to latlon=0,0 | |
shader(shader); | |
shape(shape); | |
resetShader(); | |
popMatrix(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Heli { | |
PImage blades,body; | |
int pmillis = 0; | |
float heading; | |
float bladeAngle; | |
void setup() { | |
blades = loadImage("blades.png"); | |
body = loadImage("body.png"); | |
} | |
void draw() { | |
if (millis()-pmillis > 10) { | |
pmillis = millis(); | |
bladeAngle += TWO_PI/30; | |
if (bladeAngle>TWO_PI) bladeAngle-=TWO_PI; | |
} | |
hint(DISABLE_DEPTH_TEST); | |
imageMode(CENTER); | |
pushMatrix(); | |
rotate(heading - HALF_PI); | |
image(body,0,0); | |
popMatrix(); | |
pushMatrix(); | |
rotate(bladeAngle); //heading + HALF_PI); | |
image(blades,0,0); | |
popMatrix(); | |
hint(ENABLE_DEPTH_TEST); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static class Quaternion { | |
float W, X, Y, Z; | |
Quaternion() { | |
set(1, 0, 0, 0); | |
} | |
Quaternion(float w, float x, float y, float z) { | |
set(w, x, y, z); | |
} | |
Quaternion(float w, PVector v) { | |
set(w, v.x, v.y, v.z); | |
} | |
void set(float w, float x, float y, float z) { | |
W = w; | |
X = x; | |
Y = y; | |
Z = z; | |
} | |
Quaternion mult(Quaternion q) { | |
float x = q.W * X + q.X * W + q.Y * Z - q.Z * Y; | |
float y = q.W * Y - q.X * Z + q.Y * W + q.Z * X; | |
float z = q.W * Z + q.X * Y - q.Y * X + q.Z * W; | |
float w = q.W * W - q.X * X - q.Y * Y - q.Z * Z; | |
W = w; | |
X = x; | |
Y = y; | |
Z = z; | |
return this; | |
} | |
Quaternion copy() { | |
return new Quaternion(W, X, Y, Z); | |
} | |
void applyTo(PVector v) { | |
// nVidia SDK implementation | |
PVector uv, uuv; | |
PVector qvec = new PVector(X, Y, Z); //_v.x, _v.y, _v.z); | |
uv = qvec.cross(v); //uv = qvec ^ v; | |
uuv = qvec.cross(uv); //uuv = qvec ^ uv; | |
uv.mult(2.0f * W); | |
uuv.mult(2.0f); | |
v.add(uv); | |
v.add(uuv); | |
} | |
Quaternion normalize() { | |
float norme = sqrt(W*W + X*X + Y*Y + Z*Z); | |
if (norme == 0.0) { | |
W = 1.0; | |
X = Y = Z = 0.0; | |
} else { | |
float recip = 1.0/norme; | |
W *= recip; | |
X *= recip; | |
Y *= recip; | |
Z *= recip; | |
} | |
return this; | |
} | |
float getAngle() { | |
float sinhalfangle = sqrt(X*X+Y*Y+Z*Z); | |
return 2.0 * atan2(sinhalfangle, W); | |
} | |
PVector getAxis() { | |
float sinhalfangle = sqrt(X*X+Y*Y+Z*Z); | |
if (sinhalfangle>0) { | |
PVector axis = new PVector(X, Y, Z); | |
axis.div(sinhalfangle); | |
return axis; | |
} else return new PVector(0, 0, 1); | |
} | |
/// Set the elements of the Quat to represent a rotation of angle | |
/// around the axis (x,y,z) | |
static Quaternion fromRotate( float angle, float x, float y, float z ) { | |
//angle in Radians! | |
//const | |
float epsilon = 0.0000001f; | |
float length = sqrt( x * x + y * y + z * z ); //was sqrtf | |
if (length < epsilon) { | |
// ~zero length axis, so reset rotation to zero. | |
//*this = ofQuaternion(); | |
return new Quaternion(1, 0, 0, 0); | |
} | |
float inversenorm = 1.0f / length; | |
float coshalfangle = cos( 0.5f * angle ); | |
float sinhalfangle = sin( 0.5f * angle ); | |
float _x = x * sinhalfangle * inversenorm; | |
float _y = y * sinhalfangle * inversenorm; | |
float _z = z * sinhalfangle * inversenorm; | |
float _w = coshalfangle; | |
return new Quaternion(_x, _y, _z, _w); | |
} | |
/// Spherical Linear Interpolation | |
/// As t goes from 0 to 1, the Quat object goes from "from" to "to" | |
/// Reference: Shoemake at SIGGRAPH 89 | |
/// See also | |
/// http://www.gamasutra.com/features/programming/19980703/quaternions_01.htm | |
static Quaternion slerp(Quaternion from, Quaternion to, float t) { | |
float epsilon = 0.00001; | |
float omega, cosomega, sinomega, scale_from, scale_to ; | |
Quaternion quatTo = to.copy(); | |
// this is a dot product | |
cosomega = from.X*to.X + from.Y*to.Y + from.Z*to.Z + from.W*to.W; | |
if ( cosomega < 0.0 ) { | |
cosomega = -cosomega; | |
quatTo.X *= -1; | |
quatTo.Y *= -1; | |
quatTo.Z *= -1; | |
quatTo.W *= -1; | |
} | |
if ( (1.0 - cosomega) > epsilon ) { | |
omega = acos(cosomega) ; // 0 <= omega <= Pi (see man acos) | |
sinomega = sin(omega) ; // this sinomega should always be +ve so | |
// could try sinomega=sqrt(1-cosomega*cosomega) to avoid a sin()? | |
scale_from = sin((1.0 - t) * omega) / sinomega ; | |
scale_to = sin(t * omega) / sinomega ; | |
} else { | |
/* -------------------------------------------------- | |
The ends of the vectors are very close | |
we can use simple linear interpolation - no need | |
to worry about the "spherical" interpolation | |
-------------------------------------------------- */ | |
scale_from = 1.0 - t ; | |
scale_to = t ; | |
} | |
//add | |
return new Quaternion( | |
from.W * scale_from + quatTo.W * scale_to, | |
from.X * scale_from + quatTo.X * scale_to, | |
from.Y * scale_from + quatTo.Y * scale_to, | |
from.Z * scale_from + quatTo.Z * scale_to | |
); | |
} | |
String toString() { | |
return W + "," + X + "," + Y + "," + Z; | |
} | |
PVector toEulerAngle() { //const Quaterniond& q, double& roll, double& pitch, double& yaw) { | |
PVector rollPitchYaw = new PVector(); | |
//roll (x-axis rotation) | |
float sinr_cosp = +2.0 * (W*X + Y*Z); | |
float cosr_cosp = +1.0 - 2.0 * (X*X + Y*Y); | |
rollPitchYaw.x = atan2(sinr_cosp, cosr_cosp); | |
// pitch (y-axis rotation) | |
float sinp = +2.0 * (W*Y - Z*X); | |
if (abs(sinp) >= 1) | |
rollPitchYaw.y = sinp<0 ? -HALF_PI : HALF_PI; //copysign(M_PI / 2, sinp); // use 90 degrees if out of range | |
else | |
rollPitchYaw.y = asin(sinp); | |
// yaw (z-axis rotation) | |
float siny_cosp = +2.0 * (W*Z + X*Y); | |
float cosy_cosp = +1.0 - 2.0 * (Y*Y + Z*Z); | |
rollPitchYaw.z = atan2(siny_cosp, cosy_cosp); | |
return rollPitchYaw; | |
} | |
float getYaw() { | |
float siny_cosp = +2.0 * (W*Z + X*Y); | |
float cosy_cosp = +1.0 - 2.0 * (Y*Y + Z*Z); | |
return atan2(siny_cosp, cosy_cosp); | |
} | |
float getLength2() { | |
return X*X + Y*Y + Z*Z + W*W; | |
} | |
PMatrix3D getRotationMatrix() { | |
//see also: | |
//https://github.com/processing/processing/blob/master/core/src/processing/core/PMatrix3D.java#L465 | |
//and: openFrameworks: ofMatrix4x4::setRotate(const ofQuaternion& q) | |
PMatrix3D m = new PMatrix3D(); //new identity matrix created | |
float length2 = this.getLength2(); | |
if (abs(length2) < EPSILON) { | |
// m = identity matrix | |
} else { | |
float rlength2 = length2 != 1.0 ? 2.0/length2 : 2.0; | |
float wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; | |
// calculate coefficients | |
x2 = rlength2*X; | |
y2 = rlength2*Y; | |
z2 = rlength2*Z; | |
xx = X * x2; | |
xy = X * y2; | |
xz = X * z2; | |
yy = Y * y2; | |
yz = Y * z2; | |
zz = Z * z2; | |
wx = W * x2; | |
wy = W * y2; | |
wz = W * z2; | |
m.set( 1.0 - (yy + zz), xy + wz, xz - wy, 0, | |
xy - wz, 1.0 - (xx + zz), yz + wx, 0, | |
xz + wy, yz - wx, 1.0 - (xx + yy), 0, | |
0, 0, 0, 1); | |
} | |
return m; | |
} | |
PVector getCartesian() { | |
PMatrix3D m = getRotationMatrix(); | |
PVector v = new PVector(0, 0, 1); | |
return m.mult(v, null); | |
} | |
float getLatitude() { //degrees | |
return degrees(-asin(getCartesian().y)); | |
} | |
float getLongitude() { //degrees | |
float lon = degrees(-atan2(getCartesian().z, getCartesian().x))+90; | |
return (lon>180) ? lon-360 : lon; | |
} | |
Quaternion div(Quaternion q) { | |
mult(new Quaternion().invert(q)); | |
return this; | |
} | |
Quaternion invert(Quaternion in) { | |
float dot = in.getLength2(); | |
dot = dot == 0.0 ? 0.0 : 1.0 / dot; | |
X = -in.X * dot; | |
Y = -in.Y * dot; | |
Z = -in.Z * dot; | |
W = in.W * dot; | |
return this; | |
} | |
boolean isIdentity() { | |
return W==1 && X==0 && Y==0 && Z==0; | |
} | |
void reset() { | |
set(1, 0, 0, 0); | |
} | |
float getHeading() { | |
PVector v = new PVector(0, 0, 1); | |
applyTo(v); | |
return atan2(v.y, v.x); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Ring { | |
PFont font; | |
float radius = 420; | |
String title = ""; | |
void setup() { | |
font = loadFont("Arial-BoldMT-35.vlw"); | |
} | |
void draw() { | |
pushStyle(); | |
noStroke(); | |
fill(128); | |
ellipse(0, 0, height, height); | |
fill(255); | |
textFont(font); | |
arcTextBoxCenter(title, radius-24-60); | |
textFont(defaultFont); | |
popStyle(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.awt.Rectangle; | |
color hsb(float h, float s, float b) { | |
if (b==0) return color(0); | |
if (s==0) return color(b); | |
h *= 6. / 255; | |
s /= 255.; | |
float x = b * (1-s); | |
float y = b * (1-s*(h-int(h))); | |
float z = b * (1-s*(1-(h-int(h)))); | |
//float r = (h==0||h==5) ? b : h==1 ? y : (h==4) ? z : x; | |
switch (int(h)) { | |
case 0: | |
return color(b, z, x); //red | |
case 1: | |
return color(y, b, x); //green | |
case 2: | |
return color(x, b, z); | |
case 3: | |
return color(x, y, b); //blue | |
case 4: | |
return color(z, x, b); | |
case 5: | |
return color(b, x, y); //back to red | |
} | |
return color(0); | |
} | |
color hsb(float h) { | |
return hsb(h, 255, 255); | |
} | |
void rotate180(PImage img) { | |
img.loadPixels(); | |
int wh = img.width * img.height; | |
for (int i=0; i<wh/2; i++) { | |
color tmp = img.pixels[i]; | |
img.pixels[i] = img.pixels[wh-i-1]; | |
img.pixels[wh-i-1] = tmp; | |
} | |
img.updatePixels(); | |
} | |
PVector clerp(PVector v1, PVector v2, PVector center, float f) { //interpolation on a circle | |
float r = PVector.sub(v1, center).mag(); //use average magnitude? | |
PVector v = PVector.lerp(v1, v2, f); | |
v.sub(center); | |
v.normalize(); | |
v.mult(r); | |
v.add(center); | |
return v; | |
} | |
PVector get3PointCircleCenter(float x1, float y1, float x2, float y2, float x3, float y3) { | |
float ma = (y2-y1)/(x2-x1); //slope | |
float mb = (y3-y2)/(x3-x2); | |
float cx = (ma*mb*(y1-y3) + mb*(x1+x2) - ma*(x2+x3)) / (2*(mb-ma) ); | |
float cy = -1*(cx-(x1+x2)/2)/ma + (y1+y2)/2; | |
return new PVector(cx, cy); | |
} | |
PVector get3PointCircleCenter(PVector p1, PVector p2, PVector p3) { | |
return get3PointCircleCenter(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); | |
} | |
PVector getCenter(ArrayList<PVector> vectors) { | |
PVector center = new PVector(); | |
for (PVector v : vectors) { | |
center.add(v); | |
} | |
center.div(vectors.size()); | |
return center; | |
} | |
ArrayList<PVector> getReducedPoints(ArrayList<PVector> pointsA, float dist) { | |
ArrayList<PVector> pointsB = new ArrayList(); | |
if (pointsA.size()==0) return pointsB; | |
pointsB.add(pointsA.get(0)); //first point | |
for (int i=1; i<pointsA.size(); i++) { | |
Boolean hasNeighbourNearby = false; | |
for (int j=0; j<pointsB.size(); j++) { //check if get(i) far enough from all existing clusters | |
if (pointsA.get(i).dist(pointsB.get(j)) < dist) { | |
hasNeighbourNearby = true; | |
break; | |
} | |
} | |
if (!hasNeighbourNearby) { | |
pointsB.add(pointsA.get(i)); | |
} | |
} | |
return pointsB; | |
} | |
//PVector getFarthestPoint(Contour contour, float x, float y) { | |
// PVector farthest = contour.getConvexHull().getPoints().get(0); //farthest | |
// for (PVector p : contour.getConvexHull().getPoints()) { | |
// if (dist(farthest.x, farthest.y, x, y)<dist(p.x, p.y, x, y)) { | |
// farthest = p; | |
// } | |
// } | |
// return farthest; | |
//} | |
PVector getFarthestPoint(ArrayList<PVector> points, float x, float y) { | |
PVector farthest = points.get(0); //farthest / furthest | |
for (PVector p : points) { | |
if (dist(p.x, p.y, x, y) > dist(farthest.x, farthest.y, x, y)) { | |
farthest = p; | |
} | |
} | |
return farthest; | |
} | |
float getCenterToCenterDistance(Rectangle a, Rectangle b) { | |
return dist((float)a.getCenterX(), (float)a.getCenterY(), (float)b.getCenterX(), (float)b.getCenterY()); | |
} | |
PVector getClosest(ArrayList<PVector> points, PVector t) { | |
if (points.size()==0) return null; | |
PVector closest = points.get(0); | |
for (PVector p : points) { | |
if (t.dist(p) < t.dist(closest)) { | |
closest = p; | |
} | |
} | |
return closest; | |
} | |
void arcStrip(float inner, float outer, float start, float end) { | |
float range=end-start; | |
float a; | |
beginShape(TRIANGLE_STRIP); | |
for (int i=0, n=500; i<n+1; i++) { | |
a=(i+0.5)/n; | |
vertex(sin(a*range+start)*inner, cos(a*range+start)*inner); | |
a=(i+0.0)/n; | |
vertex(sin(a*range+start)*outer, cos(a*range+start)*outer); | |
} | |
endShape(); | |
} | |
float getArcTextBoxWidth(String txt, float radius) { //returns an angle | |
float maxAngle = 0; | |
String lines[] = split(txt, "\n"); | |
for (int l=0; l<lines.length; l++) { | |
float angle = 0; | |
radius += textAscent() + textDescent(); | |
for (int i=0; i<lines[l].length(); i++) { | |
char letter = lines[l].charAt(i); | |
angle += atan(textWidth(letter) / radius) ; | |
} | |
maxAngle = max(angle, maxAngle); | |
} | |
return maxAngle; | |
} | |
void arcTextBoxLeft(String txt, float radius) { | |
textAlign(LEFT); | |
String lines[] = split(txt, "\n"); | |
for (int l=0; l<lines.length; l++) { | |
float angle = 0; | |
radius += textAscent() + textDescent(); //tex | |
for (int i=0; i<lines[l].length(); i++) { | |
char letter = lines[l].charAt(i); | |
pushMatrix(); | |
rotate(-angle); | |
translate(0, radius); | |
//scale(1, globalScaleY); | |
text(letter, 0, 0); | |
popMatrix(); | |
angle += atan(textWidth(letter) / radius) ; | |
} | |
} | |
} | |
void arcTextBoxCenter(String txt, float radius) { | |
textAlign(CENTER); | |
float angle = getArcTextBoxWidth(txt, radius); | |
pushMatrix(); | |
rotate(angle/2); | |
arcTextBoxLeft(txt, radius); | |
popMatrix(); | |
} | |
void arcTextBoxRight(String txt, float radius) { | |
textAlign(CENTER); | |
float angle = getArcTextBoxWidth(txt, radius); | |
pushMatrix(); | |
rotate(angle); | |
arcTextBoxLeft(txt, radius); | |
popMatrix(); | |
} | |
PShape bendShape(PShape shape, int w, int h, float r, Boolean center, float arcArch) { | |
float r1 = r; | |
float r2 = r+h; | |
int numLines = shape.getVertexCount()/2; | |
int numCells = numLines-1; | |
//println("numLines",numLines,numCells); | |
for (int i=0; i<numLines; i++) { | |
float f = map(i, 0, numLines-1, 0, 1); | |
float a1 = f * w/r - (center ? (w/r)/2 : 0); | |
shape.setVertex(i*2+0, sin(a1)*r1, cos(a1)*r1 - r); // -r = 0,0 at top left | |
float r3 = lerp(r1, r2, arcArch); //0=arc, 1=arch, .5=in between | |
float a2 = f * w/r3 - (center ? (w/r3)/2 : 0); | |
shape.setVertex(i*2+1, sin(a2)*r2, cos(a2)*r2 - r); | |
} | |
return shape; | |
} | |
PShape shapeFromImage(PImage img, int cellWidth) { //res=cellWidth, numCells dynamic | |
PShape strip = createShape(); | |
strip.beginShape(QUAD_STRIP); | |
strip.stroke(255, 255, 0, 50); | |
strip.noStroke(); | |
int numCells = ceil(img.width / cellWidth); | |
for (int i=0; i<=numCells; i++) { | |
float f = float(i)/numCells; | |
float x = lerp(0, img.width, f); | |
strip.vertex(x, 0, x, 0); | |
strip.vertex(x, img.height, x, img.width); //why img.width? Bug in Processing? | |
} | |
strip.endShape(); | |
strip.setTexture(img); | |
return strip; | |
} | |
Boolean isClockwise(PVector a, PVector b, PVector c) { | |
//https://gamedev.stackexchange.com/questions/22133/how-to-detect-if-object-is-moving-in-clockwise-or-counterclockwise-direction | |
return (b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x) > 0; | |
} | |
Boolean between(float t, float a, float b) { | |
return (t>=a && t<=b) || (t>=b && t<=a); | |
} | |
float smoothstep(float x, float min, float max) { | |
float t; | |
t = constrain((x - min) / (max - min), 0.0, 1.0); | |
return t * t * (3.0 - 2.0 * t); | |
} | |
PVector toSphere(float x, float y) { //-0.5 ... +0.5 | |
PVector v = new PVector(x, y); | |
if (v.mag()>=1.0f) v.normalize(); | |
else v.z = sqrt(1.0 - v.mag()); | |
return v; | |
} | |
PVector toSphere(PVector v) { //-0.5 ... +0.5 | |
return toSphere(v.x, v.y); | |
} | |
float angleLerp(float from, float to, float t) { | |
float diff = (to - from) % TWO_PI; | |
return from + (2 * diff % TWO_PI - diff) * t; | |
} | |
void rotate(Quaternion q) { | |
rotate(q.getAngle(), q.getAxis().x, q.getAxis().y, q.getAxis().z); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment