private static final float IDEAL_FRAME_RATE = 40.0;
final float ambientLightBrightness = 208.0;
final float directionalLightBrightness = 48.0;
NaiveActorSystem myActorSystem;
ViewAngleController viewer;
frameRate(IDEAL_FRAME_RATE);
ambientLight(ambientLightBrightness, ambientLightBrightness, ambientLightBrightness);
directionalLight(directionalLightBrightness, directionalLightBrightness, directionalLightBrightness, 0, 0, -1);
viewer.applyPerspective();
viewer.translateCoordinates();
viewer.rotateCoordinates();
stroke(224.0, 160.0, 224.0);
viewer.processMouseDragged();
if(key == CODED && keyCode == 123) initialize();
final ForceFieldPhysicsSystem myPhysicsSystem = new ForceFieldPhysicsSystem();
myPhysicsSystem.forceFieldList.add(new AizawaField(0.36234542326327834, 0.5802488849206496, 0.3954677594122402, 3.3793866809512525, 0.2240981262099423, 0.018774755037479406, 0.0, 0.0, -0.2, IDEAL_FRAME_RATE, 0.39));
myActorSystem = new NaiveActorSystem(myPhysicsSystem, 1024);
viewer = new ViewAngleController(radians(75.0), radians(-15.0), PI / 30.0, QUARTER_PI, IDEAL_FRAME_RATE, 0.0);
viewer.addMouseDragChecker(new HorizontalMouseDragChecker(new AddZRotation(), -0.3));
viewer.addMouseDragChecker(new VerticalMouseDragChecker(new AddXRotation(), -0.3));
final ArrayList<PhysicsBodyDisplayer> displayerList = new ArrayList<PhysicsBodyDisplayer>();
final int anglePartitionCount = 24;
final float lightness = 88.0;
final float distanceFromZero = 64.0;
for(int i = 0; i < anglePartitionCount; i++) {
final float hueAngle = i * TWO_PI / anglePartitionCount;
displayerList.add(new SphereDisplayer(0.02, cielabColor(lightness, distanceFromZero * cos(hueAngle), distanceFromZero * sin(hueAngle))));
final int particleNumber = 800;
for (int i = 0; i < particleNumber; i++) {
final NaiveBodyActor3D newActor = new NaiveBodyActor3D();
final float distance = pow(random(1.0), 0.5) * 10.0;
final float theta = random(TWO_PI);
final float phi = random(TWO_PI);
newActor.xPosition = distance * cos(theta) * cos(phi);
newActor.yPosition = distance * cos(theta) * sin(phi);
newActor.zPosition = distance * sin(theta);
newActor.displayer = displayerList.get(int(map(theta, 0.0, TWO_PI, 0.0, displayerList.size())));;
myActorSystem.registerNewActor(newActor);
public void addToPhysicsSystem(PhysicsSystem sys);
abstract class ActorSystem
final ArrayList<IActor> actorList;
final PhysicsSystem currentPhysicsSystem;
ActorSystem(PhysicsSystem sys, int initialCapacity) {
currentPhysicsSystem = sys;
actorList = new ArrayList<IActor>(initialCapacity);
ActorSystem(PhysicsSystem sys) {
abstract void registerNewActor(IActor obj);
final class NaiveActorSystem
NaiveActorSystem(PhysicsSystem sys, int initialCapacity) {
super(sys, initialCapacity);
NaiveActorSystem(PhysicsSystem sys) {
void registerNewActor(IActor obj) {
obj.addToPhysicsSystem(currentPhysicsSystem);
currentPhysicsSystem.update();
for (IActor currentObject : actorList) {
final class NaiveBodyActor3D
PhysicsBodyDisplayer displayer;
void addToPhysicsSystem(PhysicsSystem sys) {
public void initialize() {
abstract class PhysicsBodyDisplayer
abstract void display(PhysicsBody parentBody);
final class SphereDisplayer
extends PhysicsBodyDisplayer
SphereDisplayer(float sz, color col) {
void display(PhysicsBody parentBody) {
translate(parentBody.xPosition, parentBody.yPosition, parentBody.zPosition);
color cielabColor(float lValue, float aValue, float bValue) {
final float tristimulusValueX = 0.95047;
final float tristimulusValueY = 1.00000;
final float tristimulusValueZ = 1.08883;
final float yFactor = (lValue + 16.0) / 116.0;
final float xFactor = yFactor + aValue / 500.0;
final float zFactor = yFactor - bValue / 200.0;
final float xValue = constrain(tristimulusValueX * toXyzColorFunc(xFactor), 0.0, 1.0);
final float yValue = constrain(tristimulusValueY * toXyzColorFunc(yFactor), 0.0, 1.0);
final float zValue = constrain(tristimulusValueZ * toXyzColorFunc(zFactor), 0.0, 1.0);
final float redFactor = ( 3.2404542 * xValue - 1.5371385 * yValue - 0.4985314 * zValue);
final float greenFactor = (-0.9692660 * xValue + 1.8760108 * yValue + 0.0415560 * zValue);
final float blueFactor = ( 0.0556434 * xValue - 0.2040259 * yValue + 1.0572252 * zValue);
return color(degamma(constrain(redFactor, 0.0, 1.0)) * 255.0, degamma(constrain(greenFactor, 0.0, 1.0)) * 255.0, degamma(constrain(blueFactor, 0.0, 1.0)) * 255.0);
float toXyzColorFunc(float value) {
final float delta = 6.0 / 29.0;
if (value > delta) return value * value * value;
else return (value - 16.0 / 116.0) * 3.0 * delta * delta;
float degamma(float value) {
if (value <= 0.0031308) return 12.92 * value;
else return 1.055 * pow(value, 1.0 / 2.4) - 0.055;
float xPosition, yPosition;
float xVelocity, yVelocity;
float xAcceleration, yAcceleration;
boolean isPositionLocked;
PhysicsBody(float x, float y, float m, float r) {
PhysicsBody(float x, float y) {
this(0.0, 0.0, 1.0, 1.0);
isPositionLocked = false;
void applyForce(float xForce, float yForce, float zForce) {
if (this.isPositionLocked) return;
xAcceleration += xForce / mass;
yAcceleration += yForce / mass;
if (this.isPositionLocked) return;
xVelocity += xAcceleration;
yVelocity += yAcceleration;
final void unlockPosition() {
isPositionLocked = false;
PhysicsBody3D(float x, float y, float z, float m, float r) {
PhysicsBody3D(float x, float y, float z) {
this(0.0, 0.0, 0.0, 1.0, 1.0);
void applyForce(float xForce, float yForce, float zForce) {
super.applyForce(xForce, yForce, zForce);
zAcceleration += zForce / mass;
if (this.isPositionLocked) return;
zVelocity += zAcceleration;
final ArrayList<PhysicsBody> bodyList;
PhysicsSystem(int initialCapacity) {
bodyList = new ArrayList<PhysicsBody>(initialCapacity);
for (PhysicsBody currentObject : bodyList) {
final class ForceFieldPhysicsSystem
final ArrayList<PhysicsForceField> forceFieldList;
ForceFieldPhysicsSystem(int initialCapacity) {
forceFieldList = new ArrayList<PhysicsForceField>(initialCapacity / 16);
ForceFieldPhysicsSystem() {
for (PhysicsForceField currentObject : forceFieldList) {
currentObject.update(bodyList);
abstract class PhysicsForceField
void update(ArrayList<PhysicsBody> bodyList) {
for (PhysicsBody eachBody : bodyList) {
abstract void affect(PhysicsBody body);
final class GravityForceField
extends PhysicsForceField
final float gravityAcceleration;
GravityForceField(float g) {
this(9.80665 / sq(60.0));
void affect(PhysicsBody body) {
body.yAcceleration += gravityAcceleration;
extends PhysicsForceField
final float a, b, c, d, e, f;
final float referenceXPosition, referenceYPosition, referenceZPosition;
AizawaField(float _a, float _b, float _c, float _d, float _e, float _f, float refX, float refY, float refZ, float fRate, float speedFactor) {
referenceXPosition = refX;
referenceYPosition = refY;
referenceZPosition = refZ;
unitSpeed = speedFactor / fRate;
this(0.36234542326327834, 0.5802488849206496, 0.3954677594122402, 3.3793866809512525, 0.2240981262099423, 0.018774755037479406, 0.0, 0.0, 0.0, 60.0, 1.0);
void affect(PhysicsBody body) {
final float x = body.xPosition - referenceXPosition;
final float y = body.yPosition - referenceYPosition;
final float z = body.zPosition - referenceZPosition;
body.xVelocity = ((z-b) * x - d*y) * unitSpeed;
body.yVelocity = (d * x + (z-b) * y) * unitSpeed;
body.zVelocity = (c + a*z - z * z * z /3 -( x * x +y * y )*(1+ e * z)+ f * z * x * x * x) * unitSpeed;
final class ViewAngleController
final float unitAngleVelocity;
final float initialFieldOfViewAngle;
final float cameraXPosition, cameraYPosition;
float nearestClippingPlaneZPosition, farthestClippingPlaneZPosition;
final float centerXPosition, centerYPosition, centerZPosition;
float xRotationAngle, yRotationAngle, zRotationAngle;
final float initialXRotationAngle, initialZRotationAngle;
final ArrayList<UncodedKeyChecker> keyCheckerList = new ArrayList<UncodedKeyChecker>();
final ArrayList<KeyCodeChecker> keyCodeCheckerList = new ArrayList<KeyCodeChecker>();
final ArrayList<MouseDragChecker> mouseDragCheckerList = new ArrayList<MouseDragChecker>();
boolean checksUncodedKey, checksCodedKey, checksMouseDrag;
ViewAngleController(float initXRot, float initZRot, float initFov, float angleVelocityPerSecond, float idealFrameRate, float centerYPositionOffset) {
unitAngleVelocity = angleVelocityPerSecond / idealFrameRate;
aspectRatio = float(width) / float(height);
cameraXPosition = width * 0.5;
cameraYPosition = height * 0.5;
centerXPosition = width * 0.5;
centerYPosition = height * 0.5 + centerYPositionOffset;
initialXRotationAngle = initXRot;
initialZRotationAngle = initZRot;
initialFieldOfViewAngle = initFov;
this(QUARTER_PI, 0.0, PI / 3.0, QUARTER_PI, 60.0, 0.0);
setFieldOfViewAngle(initialFieldOfViewAngle);
xRotationAngle = initialXRotationAngle;
zRotationAngle = initialZRotationAngle;
void addKeyChecker(UncodedKeyChecker checker) {
keyCheckerList.add(checker);
void addKeyCodeChecker(KeyCodeChecker checker) {
keyCodeCheckerList.add(checker);
void addMouseDragChecker(MouseDragChecker checker) {
mouseDragCheckerList.add(checker);
void addFieldOfViewAngle(float v) {
setFieldOfViewAngle(fieldOfViewAngle + v * unitAngleVelocity);
void setFieldOfViewAngle(float v) {
cameraZPosition = cameraYPosition / tan(fieldOfViewAngle * 0.5);
nearestClippingPlaneZPosition = cameraZPosition * 0.05;
farthestClippingPlaneZPosition = cameraZPosition * 10.0;
void addXRotationAngle(float v) {
xRotationAngle += v * unitAngleVelocity;
void addYRotationAngle(float v) {
yRotationAngle += v * unitAngleVelocity;
void addZRotationAngle(float v) {
zRotationAngle += v * unitAngleVelocity;
void applyPerspective() {
perspective(fieldOfViewAngle, aspectRatio, nearestClippingPlaneZPosition, farthestClippingPlaneZPosition);
void translateCoordinates() {
translate(centerXPosition, centerYPosition, centerZPosition);
void rotateCoordinates() {
if (checksCodedKey && key == CODED) {
for (KeyCodeChecker eachChecker : keyCodeCheckerList) {
eachChecker.checkKeyCode(keyCode, this);
for (UncodedKeyChecker eachChecker : keyCheckerList) {
eachChecker.checkKey(key, this);
void processMouseDragged() {
if (checksMouseDrag == false) return;
for (MouseDragChecker eachChecker : mouseDragCheckerList) {
abstract class InputChecker
final ViewAngleKeyEventHandler handler;
InputChecker(ViewAngleKeyEventHandler _handler) {
void execute(ViewAngleController controller) {
execute(controller, 1.0);
void execute(ViewAngleController controller, float angleVelocityFactor) {
handler.execute(controller, angleVelocityFactor);
final class UncodedKeyChecker
UncodedKeyChecker(char _triggerKey, ViewAngleKeyEventHandler _handler) {
triggerKey = _triggerKey;
void checkKey(char currentKey, ViewAngleController controller) {
if (currentKey == triggerKey) execute(controller);
final class KeyCodeChecker
final int triggerKeyCode;
KeyCodeChecker(int _triggerKeyCode, ViewAngleKeyEventHandler _handler) {
triggerKeyCode = _triggerKeyCode;
void checkKeyCode(int currentKeyCode, ViewAngleController controller) {
if (currentKeyCode == triggerKeyCode) execute(controller);
abstract class MouseDragChecker
MouseDragChecker(ViewAngleKeyEventHandler _handler, float _sensitivity) {
sensitivity = _sensitivity;
abstract void check(ViewAngleController controller);
final class VerticalMouseDragChecker
VerticalMouseDragChecker(ViewAngleKeyEventHandler _handler, float _sensitivity) {
super(_handler, _sensitivity);
void check(ViewAngleController controller) {
execute(controller, (mouseY - pmouseY) * sensitivity);
final class HorizontalMouseDragChecker
HorizontalMouseDragChecker(ViewAngleKeyEventHandler _handler, float _sensitivity) {
super(_handler, _sensitivity);
void check(ViewAngleController controller) {
execute(controller, (mouseX - pmouseX) * sensitivity);
abstract class ViewAngleKeyEventHandler
abstract void execute(ViewAngleController controller, float angleVelocityFactor);
void execute(ViewAngleController controller) {
execute(controller, 1.0);
extends ViewAngleKeyEventHandler
void execute(ViewAngleController controller, float angleVelocityFactor) {
controller.addXRotationAngle(angleVelocityFactor);
extends ViewAngleKeyEventHandler
void execute(ViewAngleController controller, float angleVelocityFactor) {
controller.addYRotationAngle(angleVelocityFactor);
extends ViewAngleKeyEventHandler
void execute(ViewAngleController controller, float angleVelocityFactor) {
controller.addZRotationAngle(angleVelocityFactor);
class AddFieldOfViewAngle
extends ViewAngleKeyEventHandler
void execute(ViewAngleController controller, float angleVelocityFactor) {
controller.addFieldOfViewAngle(angleVelocityFactor);