Skip to content

Instantly share code, notes, and snippets.

@ma2saka
Created February 10, 2017 04:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ma2saka/40d546e8c3955e8a95f3fbd5363e895a to your computer and use it in GitHub Desktop.
Save ma2saka/40d546e8c3955e8a95f3fbd5363e895a to your computer and use it in GitHub Desktop.
Scala x Processing
import processing.core.PConstants._
import processing.core.{PApplet, PVector}
import scala.collection.mutable.ListBuffer
class Boid(x: Float,y: Float, private[this] val pApplet: PApplet) {
private[this] val R = 2.0f
private[this] val MAX_FORCE = 0.03f
private[this] val MAX_SPEED = 2.0f
private[this] val NEIGHBORHOOD_DIST = 50f
private[this] val DESIRED_SEPARATION = 25f
private[this] val acceleration = PVector.random2D(pApplet)
private[this] val angle = pApplet.random(TWO_PI)
private[Boid] val position: PVector = new PVector(x, y)
private[Boid] val velocity: PVector = new PVector(Math.cos(angle.toDouble).toFloat, Math.sin(angle.toDouble).toFloat)
def run(boids: ListBuffer[Boid]): Unit = {
this.flock(boids)
this.update()
this.borders()
this.render()
}
private[this] def render() = {
val theta = velocity.heading() + Math.toRadians(90)
pApplet.fill(200)
pApplet.stroke(255)
pApplet.pushMatrix()
pApplet.translate(position.x, position.y)
pApplet.rotate(theta.toFloat)
pApplet.beginShape(TRIANGLES)
pApplet.vertex(0, -R * 2)
pApplet.vertex(-R, R * 2)
pApplet.vertex(R, R * 2)
pApplet.endShape()
pApplet.popMatrix()
}
private[this] def borders() = {
if (position.x < -R) position.x = pApplet.width + R
if (position.y < -R) position.y = pApplet.height + R
if (position.x > pApplet.width + R) position.x = -R
if (position.y > pApplet.height + R) position.y = -R
}
private[this] def update() = {
velocity.add(acceleration)
velocity.limit(MAX_SPEED)
position.add(velocity)
acceleration.mult(0)
}
private[this] def flock(boids: ListBuffer[Boid]) = {
val sep: PVector = this.separate(boids)
val ali: PVector = this.align(boids)
val coh: PVector = this.cohesion(boids)
sep.mult(1.5f)
ali.mult(1.0f)
coh.mult(1.0f)
this.applyForce(sep)
this.applyForce(ali)
this.applyForce(coh)
}
private[this] def applyForce(force: PVector) = {
acceleration.add(force)
}
private[this] def cohesion(boids: ListBuffer[Boid]): PVector = {
val sum = new PVector(0, 0)
// Start with empty vector to accumulate all positions
var count = 0
boids.foreach({ other =>
val d = PVector.dist(position, other.position)
if ((d > 0) && (d < NEIGHBORHOOD_DIST)) {
sum.add(other.position) // Add position
count += 1
}
})
if (count > 0) {
sum.div(count)
seek(sum) // Steer towards the position
}
else {
new PVector(0, 0)
}
}
private[this] def seek(target: PVector): PVector = {
val desired = PVector.sub(target, position)
desired.normalize()
desired.mult(MAX_SPEED)
val steer = PVector.sub(desired, velocity)
steer.limit(MAX_FORCE)
steer
}
private[this] def align(boids: ListBuffer[Boid]): PVector = {
val sum = new PVector(0, 0)
var count = 0
boids.foreach({ other =>
val d = PVector.dist(position, other.position)
if ((d > 0) && (d < NEIGHBORHOOD_DIST)) {
sum.add(other.velocity)
count += 1
}
})
if (count > 0) {
sum.div(count)
sum.normalize()
sum.mult(MAX_SPEED)
val steer = PVector.sub(sum, velocity)
steer.limit(MAX_FORCE)
steer
}
else {
new PVector(0, 0)
}
}
private[this] def separate(boids: ListBuffer[Boid]): PVector = {
val steer = new PVector(0, 0)
var count = 0
boids.foreach({ other =>
val d = PVector.dist(position, other.position)
if ((d > 0) && (d < DESIRED_SEPARATION)) {
val diff = PVector.sub(position, other.position)
diff.normalize()
diff.div(d) // Weight by distance
steer.add(diff)
count += 1 // Keep track of how many
}
})
if (count > 0) {
steer.div(count)
}
if (steer.mag() > 0) {
steer.normalize()
steer.mult(MAX_SPEED)
steer.sub(velocity)
steer.limit(MAX_FORCE)
}
steer
}
}
class Flock {
private[this] val boids: ListBuffer[Boid] = ListBuffer[Boid]()
def run(): Unit = {
boids.foreach({ _.run(boids) })
}
def addBoid(b: Boid): Unit = {
boids.append(b)
}
def size: Int = {
boids.size
}
}
class App extends PApplet {
private[this] val flock: Flock = new Flock()
override def settings(): Unit = {
size(1000, 700, JAVA2D)
}
override def setup(): Unit = {
for (_ <- 1 to 600) {
flock.addBoid(new Boid(width / 2, height / 2, this))
}
}
override def draw(): Unit = {
background(100)
textAlign(LEFT, TOP)
textSize(12)
text("%2.3f fps".format(frameRate), 0, 0)
text("boid size = %d".format(flock.size), 0, 15)
if(this.mousePressed) {
flock.addBoid(new Boid(mouseX, mouseY, this))
}
flock.run()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment