During my girlfriend's prom, I found it interesting how quickly everyone in a crowd of people could align to all face the same direction when a certain song came on where everyone danced in unison (like the Macerena). I figured each individual had some field of view in front of them and they were turning toward the average vector of all of the people in front of them, so I tried modeling it. My code gets some alignment, but then some strange wave pattern sweeps through the crowd and I'm not sure why.
read moredancer.java
import java.awt.Polygon;
import java.awt.Rectangle;
import java.lang.reflect.Array;
public class dancer {
double orientation;
int xposition;
int yposition;
int xBounds;
int yBounds;
int size;
Polygon fieldOfView;
public dancer(int i, int j, double orientation,int size,int xBounds, int yBounds){
this.xposition=i;
this.yposition=j;
this.orientation=orientation;
this.yBounds=yBounds;
this.xBounds=xBounds;
this.fieldOfView = new Polygon();
this.updateFieldOfView();
this.size = size;
//if statements that find points for polygon
}
public void updateFieldOfView(){
if(this.orientation>=2*Math.PI){
this.orientation-=2*Math.PI;
}
double leftSideAngle = this.orientation+Math.PI/2;
double rightSideAngle = this.orientation-Math.PI/2;
double maxAdd = 1.5*Math.sqrt((this.xBounds*this.xBounds)+(this.yBounds*this.yBounds));
int xpoints[] = new int[3];
int ypoints[] = new int[3];
//Rectangle boundsOfBox = new Rectangle(0,0,xBounds,yBounds);
double leftxPoint = this.xposition +(int)this.size/2 + maxAdd*Math.cos(leftSideAngle);
double leftyPoint = this.yposition + (int)this.size/2 +maxAdd*Math.sin(leftSideAngle);
double rightxPoint = this.xposition +(int)this.size/2 + maxAdd*Math.cos(rightSideAngle);
double rightyPoint = this.yposition +(int)this.size/2 + maxAdd*Math.sin(rightSideAngle);
double vertexx = this.xposition +(int)this.size/2 + maxAdd*Math.cos(this.orientation);
double vertexy = this.yposition +(int)this.size/2 + maxAdd*Math.sin(this.orientation);
xpoints[0]=(int)leftxPoint;
xpoints[1]=(int)rightxPoint;
xpoints[2]=(int)vertexx;
ypoints[0]=(int)leftyPoint;
ypoints[1]=(int)rightyPoint;
ypoints[2]=(int)vertexy;
this.fieldOfView=new Polygon(xpoints,ypoints,3);
}
public double getOrientation() {
return orientation;
}
public void setOrientation(double orientation) {
this.orientation = orientation;
}
public int getXposition() {
return xposition;
}
public void setXposition(int xposition) {
this.xposition = xposition;
}
public int getYposition() {
return yposition;
}
public void setYposition(int yposition) {
this.yposition = yposition;
}
}
dancers.java
import java.util.Random;
//Perhaps a proximity factor could be introduced. Weight of p1's orientation on p2's orientation is inversely proportional to the distance between them.
//Or inverse square
public class dancers {
boolean drawn = false;
int numberOfDancers;
int size;
int xBounds;
int yBounds;
int speedOfTurn = 5; //higher is slower
dancer dancers[];
Random ran = new Random();
public dancers(int numberOfDancers, int size, int xBounds, int yBounds) {
this.numberOfDancers = numberOfDancers;
this.size = size;
this.dancers = new dancer[numberOfDancers];
this.xBounds = xBounds;
this.yBounds= yBounds;
for (int i = 0; i < numberOfDancers; i++) {
int xPosition = ran.nextInt(xBounds);
int yPosition = ran.nextInt(yBounds);
double orientation = 2*Math.PI*ran.nextDouble();
dancers[i] = new dancer(xPosition,yPosition,orientation,size,xBounds,yBounds);
}
}
public void spin(){
for (int i = 0; i < numberOfDancers; i++) {
dancers[i].orientation+=.1;
if(dancers[i].orientation>2*Math.PI){
dancers[i].orientation-=2*Math.PI;
}
//dancers[i].updateFieldOfView();
//System.out.println(dancers);
}
}
public void updateFieldOfView(){
for (int i = 0; i < numberOfDancers; i++) {
dancers[i].updateFieldOfView();
}
}
public void tryToAlignIncorrectButCool(){
for (int i = 0; i < numberOfDancers; i++) {
double sumOfAngles=0;
int numberOfPeopleSeen = 0;
for (int j = 0; j < numberOfDancers; j++) {
if(dancers[i].fieldOfView.contains(dancers[j].xposition, dancers[j].yposition)){
sumOfAngles+=dancers[j].orientation;
numberOfPeopleSeen++;
}
}
double angleToTendTo=(sumOfAngles)/(numberOfPeopleSeen);
if(angleToTendTo>dancers[i].orientation){
double distanceNeededToGo=angleToTendTo-dancers[i].orientation;
dancers[i].orientation+=(distanceNeededToGo/this.speedOfTurn);
}
if(angleToTendTo<dancers[i].orientation){
double distanceNeededToGo=dancers[i].orientation-angleToTendTo;
dancers[i].orientation+=(distanceNeededToGo/this.speedOfTurn);
}
}
}
public void tryToAlign() {
double orientationToChangeTo[] = new double [dancers.length];
for (int i = 0; i < numberOfDancers; i++) {
double sumOfAngles = 0;
int numberOfPeopleSeen = 0;
for (int j = 0; j < numberOfDancers; j++) {
if (dancers[i].fieldOfView.contains(dancers[j].xposition,
dancers[j].yposition)) {
sumOfAngles += dancers[j].orientation;
numberOfPeopleSeen++;
}
}
double angleToTendTo = (sumOfAngles) / (numberOfPeopleSeen);
if (angleToTendTo > dancers[i].orientation) {
double distanceNeededToGo = angleToTendTo- dancers[i].orientation;
orientationToChangeTo[i] = dancers[i].orientation + (distanceNeededToGo / this.speedOfTurn);
}
if (angleToTendTo < dancers[i].orientation) {
double distanceNeededToGo = dancers[i].orientation - angleToTendTo;
orientationToChangeTo[i] = dancers[i].orientation + (distanceNeededToGo / this.speedOfTurn);
}
}
for (int i = 0; i < numberOfDancers; i++) {
dancers[i].orientation=orientationToChangeTo[i];
}
}
}
player.java
import java.awt.Color;
import java.awt.Graphics;
public class player extends java.applet.Applet implements Runnable {
int frame;
int delay;
int wait=100;
int increment = 1;
int displacement = 20;
int arrowLength = 13;
int halfOfArrowWidth=5;
dancers dancers = new dancers(500,10, 1000,1000);
Thread animator;
/**
* Initialize the applet and compute the delay between frames.
*/
public void init() {
String str = getParameter("fps");
int fps = (str != null) ? Integer.parseInt(str) : 100;
delay = (fps > 0) ? (1000 / fps) : 100;
if (!dancers.drawn) {
dancers.drawn = true;
this.setSize(dancers.xBounds,dancers.yBounds);
}
}
/**
* This method is called when the applet becomes visible on the screen.
* Create a thread and start it.
*/
public void start() {
animator = new Thread(this);
animator.start();
}
/**
* This method is called by the thread that was created in the start method.
* It does the main animation.
*/
public void run() {
while (Thread.currentThread() == animator) {
// Display the next frame of animation.
// Delay for a while
try {
Thread.sleep(wait);
} catch (InterruptedException e) {
break;
}
// Advance the frame
increment++;
repaint();
frame++;
}
}
/**
* This method is called when the applet is no longer visible. Set the
* animator variable to null so that the thread will exit before displaying
* the next frame.
*/
public void stop() {
animator = null;
}
/**
* Paint a frame of animation.
*/
int constantincrementor = 0;
public void paint(Graphics g) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//dancers.spin();
dancers.updateFieldOfView();
dancers.tryToAlign();
//dancers.tryToAlignIncorrectButCool();
//g.drawPolygon(dancers.dancers[4].fieldOfView);
//g.setColor(Color.yellow);
//g.drawString("Here", dancers.dancers[4].xposition, dancers.dancers[4].yposition);
if(dancers.drawn){
int a = 1;
int b = 1;
int c = 1;
int d = 1;
for (int i = 0; i < dancers.numberOfDancers; i++) {
g.fillOval(dancers.dancers[i].xposition, dancers.dancers[i].yposition, dancers.size, dancers.size);
if(dancers.dancers[i].orientation<=Math.PI/2&&dancers.dancers[i].orientation>=0){
a=-1;
b=1;
c=-1;
d=1;
}
if((dancers.dancers[i].orientation>=Math.PI/2&&dancers.dancers[i].orientation<=Math.PI)){
a=-1;
b=1;
c=-1;
d=1;
}
if((dancers.dancers[i].orientation>=Math.PI&&dancers.dancers[i].orientation<=(3*Math.PI)/2)){
a=1;
b=-1;
c=1;
d=-1;
}
if((dancers.dancers[i].orientation>=(3*Math.PI/2)&&dancers.dancers[i].orientation<=2*Math.PI)){
a=1;
b=-1;
c=1;
d=-1;
}
int[] xPoints = {(int)dancers.size/2+dancers.dancers[i].xposition+(int)(arrowLength*Math.cos(dancers.dancers[i].orientation))+(a)*(int)(halfOfArrowWidth*Math.cos(dancers.dancers[i].orientation+(Math.PI/2))),(int)dancers.size/2+dancers.dancers[i].xposition+(int)(arrowLength*Math.cos(dancers.dancers[i].orientation))+(b)*(int)(halfOfArrowWidth*Math.cos(dancers.dancers[i].orientation+(Math.PI/2))),(int)dancers.size/2+dancers.dancers[i].xposition+(int)(arrowLength*Math.cos(dancers.dancers[i].orientation))+(int)(halfOfArrowWidth*Math.cos(dancers.dancers[i].orientation))};
int[] yPoints = {(int)dancers.size/2+dancers.dancers[i].yposition+(int)(arrowLength*Math.sin(dancers.dancers[i].orientation))+(c)*(int)(halfOfArrowWidth*Math.sin(dancers.dancers[i].orientation+(Math.PI/2))),(int)dancers.size/2+dancers.dancers[i].yposition+(int)(arrowLength*Math.sin(dancers.dancers[i].orientation))+(d)*(int)(halfOfArrowWidth*Math.sin(dancers.dancers[i].orientation+(Math.PI/2))),(int)dancers.size/2+dancers.dancers[i].yposition+(int)(arrowLength*Math.sin(dancers.dancers[i].orientation))+(int)(halfOfArrowWidth*Math.sin(dancers.dancers[i].orientation))};
g.fillPolygon(xPoints, yPoints, 3);
g.drawLine(dancers.dancers[i].xposition+(int)dancers.size/2, dancers.dancers[i].yposition+(int)dancers.size/2, (int)dancers.size/2+dancers.dancers[i].xposition+(int)(arrowLength*Math.cos(dancers.dancers[i].orientation)), (int)dancers.size/2+dancers.dancers[i].yposition+(int)(arrowLength*Math.sin(dancers.dancers[i].orientation)));
}
}
}
}