class Bowstring implements Entity { final float looseLength, springiness0, springiness1; final Stringable end0, end1; final float x0, y0, x1, y1; ArrayList stringables, strung; Bowstring(float x0, float y0, float x1, float y1, float looseLength, float springiness0, float springiness1) { this.x0 = x0; this.x1 = x1; this.y0 = y0; this.y1 = y1; this.looseLength = looseLength; this.springiness0 = springiness0; this.springiness1 = springiness1; this.end0 = new StringEnd(x0, y0); this.end1 = new StringEnd(x1, y1); this.stringables = new ArrayList(); this.strung = new ArrayList(); } void doPhysics0() { } void doPhysics1() { // Figure out which things are strung by taking a convex hull. float x = x0, y = y0; strung.clear(); strung.add(end0); for (int i = 0; i < stringables.size(); ++ i) { Stringable s = (Stringable)stringables.get(i); s.setTemp(0); } while (true) { float bestDX = x1 - x; float bestDY = y1 - y; Stringable bestStringable = null; for (int i = 0; i < stringables.size(); ++ i) { Stringable s = (Stringable)stringables.get(i); float dX = s.getX() - x; float dY = s.getY() - y; if (dY * bestDX > dX * bestDY) { //boolean alreadyAdded = false; //for (int j = 0; j < strung.size(); ++ j) if (s == strung.get(j)) alreadyAdded = true; //if (alreadyAdded) continue; bestDX = dX; bestDY = dY; bestStringable = s; } } if (bestStringable == null) break; if (!bestStringable.wasOnString() && bestStringable.shouldPlay()) { //playSound(chirp); } bestStringable.setOnString(true); bestStringable.setTemp(1); strung.add(bestStringable); x = bestStringable.getX(); y = bestStringable.getY(); } strung.add(end1); for (int i = 0; i < stringables.size(); ++ i) { Stringable s = (Stringable)stringables.get(i); if (s.getTemp() == 0) { s.setOnString(false); } } float stringLength = 0; for (int i = 0; i + 1 < strung.size(); ++ i) stringLength += dist(((Stringable)strung.get(i)).getX(), ((Stringable)strung.get(i)).getY(), ((Stringable)strung.get(i + 1)).getX(), ((Stringable)strung.get(i + 1)).getY()); float springiness = mousePressed ? springiness1 : springiness0; float a = max(0, stringLength - looseLength) * springiness; Stringable s0 = (Stringable)strung.get(0); for (int i = 0; i + 1 < strung.size(); ++ i) { Stringable s1 = (Stringable)strung.get(i + 1); float dX = s1.getX() - s0.getX(); float dY = s1.getY() - s0.getY(); float d = dist(0, 0, dX, dY); if (d < 1e-4) continue; dX *= a / d; dY *= a / d; s0.push(dX, dY); s1.push(- dX, - dY); s0 = s1; } } void draw() { stroke(0, 0, 0); fill(0, 0, 0); for (int i = 0; i + 1 < strung.size(); ++ i) { blockyLine(((Stringable)strung.get(i)).getX(), ((Stringable)strung.get(i)).getY(), ((Stringable)strung.get(i + 1)).getX(), ((Stringable)strung.get(i + 1)).getY()); } } void levelStart() { } } interface Stringable { float getX(); float getY(); void push(float dX, float dY); boolean wasOnString(); void setOnString(boolean isOn); void setTemp(int t); int getTemp(); boolean shouldPlay(); } class StringEnd implements Stringable { float x, y; boolean wasOnString() { return false; } void setOnString(boolean isOn) {} void setTemp(int t) {} int getTemp() { return 0; } boolean shouldPlay() { return false; } StringEnd(float x, float y) { this.x = x; this.y = y; } float getX() { return x; } float getY() { return y; } void push(float dX, float dY) { } } class MouseStringable implements Stringable, Entity { float x, y; float lasty, vy; boolean wasOnString() { return false; } void setOnString(boolean isOn) {} void setTemp(int t) {} int getTemp() { return 0; } boolean shouldPlay() { return false; } void doPhysics0() { if (mousePressed) { x = min(arenaWidth - 1 - rightBorder, max(leftBorder, mouseX)); y = min(mouseMaxY, mouseY); lasty = y; vy = 0; } else { lasty = y; vy -= 3; if (vy < -50) vy = -50; y += vy; if (y < 0) y = 0; } } void doPhysics1() { } void draw() { } float getX() { return x; } float getY() { return y; } void push(float dX, float dY) { } void levelStart() { } }