├── lib ├── junit.jar ├── javaosc.jar ├── guava-r09.jar └── javaoscfull.jar ├── resources_default ├── code │ ├── ant-contrib-1.0b3.jar │ └── doc.sh ├── build.properties └── stylesheet.css ├── examples ├── PlatformExplorer │ ├── data │ │ ├── skeilert_walk_final1.png │ │ ├── skeilert_walk_final10.png │ │ ├── skeilert_walk_final11.png │ │ ├── skeilert_walk_final12.png │ │ ├── skeilert_walk_final13.png │ │ ├── skeilert_walk_final14.png │ │ ├── skeilert_walk_final15.png │ │ ├── skeilert_walk_final16.png │ │ ├── skeilert_walk_final17.png │ │ ├── skeilert_walk_final18.png │ │ ├── skeilert_walk_final19.png │ │ ├── skeilert_walk_final2.png │ │ ├── skeilert_walk_final20.png │ │ ├── skeilert_walk_final21.png │ │ ├── skeilert_walk_final22.png │ │ ├── skeilert_walk_final23.png │ │ ├── skeilert_walk_final24.png │ │ ├── skeilert_walk_final3.png │ │ ├── skeilert_walk_final4.png │ │ ├── skeilert_walk_final5.png │ │ ├── skeilert_walk_final6.png │ │ ├── skeilert_walk_final7.png │ │ ├── skeilert_walk_final8.png │ │ └── skeilert_walk_final9.png │ ├── PlatformCamera.pde │ ├── PlatformCollider.pde │ ├── Platform.pde │ ├── PlatformWorld.pde │ ├── PlatformGenerator.pde │ ├── PlatformExplorer.pde │ ├── PlatformGroup.pde │ ├── Sector.pde │ ├── SectorGrid.pde │ └── Player.pde ├── Level0 │ ├── BallGoalCollider.pde │ ├── Zero.pde │ ├── Goal.pde │ ├── RandomButton.pde │ ├── Bubble.pde │ ├── Ball.pde │ ├── HelperFunctions.pde │ ├── RunButton.pde │ ├── Cell.pde │ ├── LevelWorld.pde │ ├── Level0.pde │ ├── constants.pde │ └── ToolBox.pde ├── bouncingpolygonsdemo │ ├── Box.pde │ ├── BoxGroup.pde │ ├── oscControl.pd │ ├── DemoWorld.pde │ ├── Balls.pde │ ├── bouncingpolygonsdemo.pde │ └── BallGroup.pde ├── template │ ├── TemplateWorld.pde │ ├── TemplateBeing.pde │ ├── TemplateInteractor.pde │ └── template.pde ├── tutorialD │ ├── SquareInteractor.pde │ ├── TutorialWorld.pde │ ├── GlitchyGroup.pde │ ├── tutorialD.pde │ └── GlitchySquare.pde ├── tutorialCcollider │ ├── SquareInteractor.pde │ ├── TutorialWorld.pde │ ├── GlitchyGroup.pde │ ├── GlitchySquare.pde │ └── tutorialCcollider.pde ├── BulletCurtain │ ├── Other.pde │ ├── ShotOtherCollider.pde │ ├── ShotGroup.pde │ ├── Shot.pde │ ├── Subject.pde │ ├── OtherGroup.pde │ └── CharacterGraphicsGenerator.pde ├── tutorialA │ ├── SquareInteractor.pde │ ├── TutorialWorld.pde │ ├── GlitchySquare.pde │ └── tutorialA.pde ├── tutorialB │ ├── SquareInteractor.pde │ ├── TutorialWorld.pde │ ├── GlitchyGroup.pde │ ├── GlitchySquare.pde │ └── tutorialB.pde └── tutorialC │ ├── SquareInteractor.pde │ ├── TutorialWorld.pde │ ├── GlitchyGroup.pde │ ├── GlitchySquare.pde │ └── tutorialC.pde ├── hermes ├── postoffice │ ├── Message.java │ ├── KeySubscriber.java │ ├── OscSubscriber.java │ ├── MouseSubscriber.java │ ├── MouseWheelSubscriber.java │ ├── MouseWheelMessage.java │ ├── KeyMessage.java │ └── MouseMessage.java ├── animation │ ├── AnimationConstants.java │ └── Tileset.java ├── CameraBeingInteractor.java ├── physics │ ├── MassedCollider.java │ ├── MassedMergeCollider.java │ ├── GravityInteractor.java │ ├── GenericMassedCollider.java │ ├── InverseSquareInteractor.java │ ├── Physics.java │ ├── InsideMassedCollider.java │ └── ImpulseCollision.java ├── BoundingBoxCollider.java ├── Collider.java ├── DetectedInteraction.java ├── Pair.java ├── Optimizer.java ├── Interaction.java ├── hshape │ ├── HCompoundShape.java │ ├── HShape.java │ └── HCircle.java ├── SelfInteractionOptimizer.java ├── Group.java ├── Hermes.java ├── InteractionHandler.java ├── HObject.java ├── Interactor.java ├── GenericGroup.java └── HermesMath.java ├── LICENSE ├── README.markdown ├── hermesTest ├── physicsTest │ ├── GravityInteractorTest.java │ ├── PhysicsTest.java │ ├── InsideMassedColliderTest.java │ └── ImpulseCollisionTest.java ├── animationTests │ └── AnimationJUnitTests.java ├── core │ ├── BeingTest.java │ └── OptimizerTest.java ├── postOfficeTests │ ├── oscTest.maxpat │ └── OSCMessageJUnitTests.java └── shapeTests │ └── RectangleTest.java └── library.properties /lib/junit.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/lib/junit.jar -------------------------------------------------------------------------------- /lib/javaosc.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/lib/javaosc.jar -------------------------------------------------------------------------------- /lib/guava-r09.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/lib/guava-r09.jar -------------------------------------------------------------------------------- /lib/javaoscfull.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/lib/javaoscfull.jar -------------------------------------------------------------------------------- /resources_default/code/ant-contrib-1.0b3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/resources_default/code/ant-contrib-1.0b3.jar -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final1.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final10.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final11.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final12.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final13.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final14.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final15.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final16.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final17.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final18.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final19.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final2.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final20.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final21.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final22.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final23.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final24.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final3.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final4.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final5.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final6.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final7.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final8.png -------------------------------------------------------------------------------- /examples/PlatformExplorer/data/skeilert_walk_final9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdlester/hermes/HEAD/examples/PlatformExplorer/data/skeilert_walk_final9.png -------------------------------------------------------------------------------- /hermes/postoffice/Message.java: -------------------------------------------------------------------------------- 1 | package hermes.postoffice; 2 | 3 | /** 4 | * Basic interface to connect all message types. 5 | */ 6 | public interface Message { 7 | 8 | } -------------------------------------------------------------------------------- /hermes/animation/AnimationConstants.java: -------------------------------------------------------------------------------- 1 | package hermes.animation; 2 | /** 3 | * Constants used for Animations... 4 | */ 5 | public interface AnimationConstants { 6 | 7 | public static final int INFINITE_LOOPS = -1; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /examples/Level0/BallGoalCollider.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * If the ball hits the goal, then change mode to COMPLETED 3 | */ 4 | class BallGoalCollider extends Collider { 5 | public void handle(Ball being1, Goal being2) { 6 | setMode(COMPLETED); 7 | } 8 | } 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/PlatformExplorer/PlatformCamera.pde: -------------------------------------------------------------------------------- 1 | class PlatformCamera extends HCamera { 2 | PlatformCamera() { 3 | super(); 4 | } 5 | 6 | void draw() { 7 | setPosition(player.getPosition().x - WINDOW_WIDTH / 2, 8 | player.getPosition().y - WINDOW_HEIGHT / 2); 9 | super.draw(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/bouncingpolygonsdemo/Box.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Container box to the world 3 | */ 4 | class Box extends MassedBeing { 5 | Box() { 6 | super(new HRectangle(new PVector(0,0), new PVector(0,0), new PVector((float)WIDTH,(float)HEIGHT)), new PVector(0,0), Float.POSITIVE_INFINITY, 1); 7 | } 8 | 9 | void draw() {} 10 | } 11 | -------------------------------------------------------------------------------- /examples/bouncingpolygonsdemo/BoxGroup.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents the program frame 3 | * Keeps balls from escaping frame 4 | */ 5 | class BoxGroup extends Group { 6 | BoxGroup(World world) { 7 | super(world); 8 | Box boite = new Box(); 9 | getWorld().register(boite); 10 | this.add(boite); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/template/TemplateWorld.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Template World 3 | * You'll need to add stuff to setup(). 4 | */ 5 | class TemplateWorld extends World { 6 | TemplateWorld(int portIn, int portOut) { 7 | super(portIn, portOut); 8 | } 9 | 10 | void setup() { 11 | //IMPORTANT: put all other setup hereterBeing(TemplateBeing); 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /examples/template/TemplateBeing.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Template being 3 | */ 4 | class TemplateBeing extends Being { 5 | TemplateBeing(HShape shape) { 6 | super(shape); 7 | //Add your constructor info here 8 | } 9 | 10 | public void update() { 11 | // Add update method here 12 | } 13 | 14 | public void draw() { 15 | // Add your draw method here 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /examples/tutorialD/SquareInteractor.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Template interactor between a MyBeing and another MyBeing 3 | */ 4 | class SquareInteractor extends Collider { 5 | SquareInteractor() { 6 | super(); 7 | } 8 | 9 | void handle(GlitchySquare being1, GlitchySquare being2) { 10 | being1.drawStroke(); 11 | being2.drawStroke(); 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /hermes/postoffice/KeySubscriber.java: -------------------------------------------------------------------------------- 1 | package hermes.postoffice; 2 | 3 | /** 4 | * Must be implemented by any object that wants to listen for and receive Key messages. 5 | */ 6 | public interface KeySubscriber { 7 | /** 8 | * Receives and handles Key message sent to object by PostOffice. 9 | * @param m the message sent by the PostOffice 10 | */ 11 | public void receive(KeyMessage m); 12 | } 13 | -------------------------------------------------------------------------------- /examples/tutorialCcollider/SquareInteractor.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Template interactor between a MyBeing and another MyBeing 3 | */ 4 | class SquareInteractor extends Collider { 5 | SquareInteractor() { 6 | super(); 7 | } 8 | 9 | void handle(GlitchySquare being1, GlitchySquare being2) { 10 | being1.drawStroke(); 11 | being2.drawStroke(); 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /hermes/postoffice/OscSubscriber.java: -------------------------------------------------------------------------------- 1 | package hermes.postoffice; 2 | 3 | /** 4 | * Must be implemented by any object that wants to listen for and receive Osc messages. 5 | */ 6 | public interface OscSubscriber { 7 | /** 8 | * Receives and handles Osc message sent to object by PostOffice. 9 | * @param m the message sent by the PostOffice 10 | */ 11 | public void receive(OscMessage m); 12 | } 13 | -------------------------------------------------------------------------------- /hermes/postoffice/MouseSubscriber.java: -------------------------------------------------------------------------------- 1 | package hermes.postoffice; 2 | 3 | /** 4 | * Must be implemented by any object that wants to listen for and receive Mouse messages 5 | */ 6 | public interface MouseSubscriber { 7 | /** 8 | * Receives and handles Mouse message sent to object by PostOffice. 9 | * @param m the message sent by the PostOffice 10 | */ 11 | public void receive(MouseMessage m); 12 | } 13 | -------------------------------------------------------------------------------- /hermes/CameraBeingInteractor.java: -------------------------------------------------------------------------------- 1 | package hermes; 2 | 3 | /** 4 | * Used by World. Adds Beings to the Camera's draw list if they are on screen. 5 | *

s 6 | * Internal -- you do not need to understand this class! 7 | */ 8 | class CameraBeingInteractor extends BoundingBoxCollider { 9 | 10 | public void handle(HCamera being1, Being being2) { 11 | being1.addBeing(being2); 12 | } 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /hermes/postoffice/MouseWheelSubscriber.java: -------------------------------------------------------------------------------- 1 | package hermes.postoffice; 2 | 3 | /** 4 | * Must be implemented by any object that wants to listen for and receive MouseWheel messages 5 | */ 6 | public interface MouseWheelSubscriber { 7 | /** 8 | * Receives and handles MouseWheel message sent to object by PostOffice. 9 | * @param m the message sent by the PostOffice 10 | */ 11 | public void receive(MouseWheelMessage m); 12 | } 13 | -------------------------------------------------------------------------------- /examples/BulletCurtain/Other.pde: -------------------------------------------------------------------------------- 1 | class Other extends Actor { 2 | 3 | OtherGroup parentGroup; 4 | 5 | Float howManyPixelsToTravel = 1.0; 6 | 7 | Other(float x, float y, float bodyWidth, float bodyHeight, AnimatedSprite animatedSprite) { 8 | super(x, y, bodyWidth, bodyHeight, animatedSprite); 9 | } 10 | 11 | void update() { 12 | setX(getX() - (howManyPixelsToTravel * otherTravelMultiplier)); 13 | 14 | if (getX() + bodyWidth < 0) { 15 | world.delete(this); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /hermes/physics/MassedCollider.java: -------------------------------------------------------------------------------- 1 | package hermes.physics; 2 | 3 | /** 4 | * This is a collision handler for MassedBeing which handles collisions 5 | * using both projection (instantly separating the bodies) and impulse (changing their velocities). 6 | * 7 | */ 8 | public class MassedCollider extends GenericMassedCollider { 9 | 10 | public MassedCollider() { 11 | super(); 12 | } 13 | 14 | public MassedCollider(float elasticity) { 15 | super(elasticity); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/tutorialA/SquareInteractor.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Template interactor between a MyBeing and another MyBeing 3 | */ 4 | class SquareInteractor extends Interactor { 5 | SquareInteractor() { 6 | //Add your constructor info here 7 | } 8 | 9 | boolean detect(GlitchySquare being1, GlitchySquare being2) { 10 | //Add your detect method here 11 | return true; 12 | } 13 | 14 | void handle(GlitchySquare being1, GlitchySquare being2) { 15 | //Add your handle method here 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /examples/tutorialB/SquareInteractor.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Template interactor between a MyBeing and another MyBeing 3 | */ 4 | class SquareInteractor extends Interactor { 5 | SquareInteractor() { 6 | //Add your constructor info here 7 | } 8 | 9 | boolean detect(GlitchySquare being1, GlitchySquare being2) { 10 | //Add your detect method here 11 | return true; 12 | } 13 | 14 | void handle(GlitchySquare being1, GlitchySquare being2) { 15 | //Add your handle method here 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /examples/tutorialC/SquareInteractor.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * When two GlitchySquares overlap, 3 | * draw a border around them 4 | */ 5 | class SquareInteractor extends Interactor { 6 | SquareInteractor() { 7 | super(); 8 | } 9 | 10 | boolean detect(GlitchySquare being1, GlitchySquare being2) { 11 | return being1.getShape().collide(being2.getShape()); 12 | } 13 | 14 | void handle(GlitchySquare being1, GlitchySquare being2) { 15 | being1.drawStroke(); 16 | being2.drawStroke(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /resources_default/code/doc.sh: -------------------------------------------------------------------------------- 1 | # a shell script to create a java documentation 2 | # for a processing library. 3 | # 4 | # make changes to the variables below so they 5 | # fit the structure of your library 6 | 7 | # the package name of your library 8 | package=template; 9 | 10 | # source folder location 11 | src=../src; 12 | 13 | # the destination folder of your documentation 14 | dest=../documentation; 15 | 16 | 17 | # compile the java documentation 18 | javadoc -d $dest -stylesheetfile ./stylesheet.css -sourcepath ${src} ${package} 19 | -------------------------------------------------------------------------------- /examples/PlatformExplorer/PlatformCollider.pde: -------------------------------------------------------------------------------- 1 | // Handles player-platform collisions 2 | class PlatformCollider extends GenericMassedCollider { 3 | 4 | PlatformCollider(float elasticity) { 5 | super(elasticity); 6 | } 7 | 8 | // reset the player's jump when he hits a platform, then do the normal projection/impulse collision stuff 9 | void handle(Player player, Platform platform) { 10 | player.resetJump(); // reset the jump 11 | super.handle(player, platform); // have GenericMassedCollider do the rest 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /hermes/BoundingBoxCollider.java: -------------------------------------------------------------------------------- 1 | package hermes; 2 | 3 | /** 4 | * A basic collision detector. detect will return true if the bounding boxes of the beings overlap. 5 | * The implementation of handle is left to the child classes. 6 | */ 7 | public abstract class BoundingBoxCollider extends Interactor { 8 | 9 | // returns true if the bounding boxes of A and B collide 10 | public boolean detect(A being1, B being2) { 11 | return being1.getBoundingBox().collide(being2.getBoundingBox()); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /examples/tutorialB/TutorialWorld.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Tutorial World 3 | * A World filled with squares 4 | */ 5 | class TutorialWorld extends World { 6 | int _squareNum; 7 | 8 | TutorialWorld(int squareNum/*, int portIn, int portOut*/) { 9 | super(/*portIn, portOut*/); 10 | _squareNum = squareNum; 11 | } 12 | 13 | void setup() { 14 | GlitchyGroup g = new GlitchyGroup(this); 15 | register(g); 16 | 17 | for (int i = 0; i < _squareNum; i++) { 18 | g.addSquare(); 19 | } 20 | } 21 | 22 | void draw() { 23 | background(0); 24 | super.draw(); 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /examples/tutorialA/TutorialWorld.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Tutorial World 3 | * A World filled with squares 4 | */ 5 | class TutorialWorld extends World { 6 | int _squareNum; 7 | 8 | TutorialWorld(int squareNum/*, int portIn, int portOut*/) { 9 | super(/*portIn, portOut*/); 10 | _squareNum = squareNum; 11 | } 12 | 13 | void setup() { 14 | for (int i = 0; i < _squareNum; i++) { 15 | int x = (int) random(WINDOW_WIDTH - 50); 16 | int y = (int) random(WINDOW_HEIGHT - 50); 17 | register(new GlitchySquare(x, y)); 18 | } 19 | } 20 | 21 | void draw() { 22 | background(0); 23 | super.draw(); 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /hermes/Collider.java: -------------------------------------------------------------------------------- 1 | package hermes; 2 | 3 | /** 4 | * An Interactor that detects collisions between Beings based on their shape. 5 | * A collision will be detected if the HShapes of the Beings overlap. 6 | * Implementation of handle() (which gets called if a collision is detected) is left to the child class. 7 | */ 8 | public abstract class Collider extends Interactor{ 9 | 10 | // returns true if A and B collide 11 | public boolean detect(A being1, B being2) { 12 | return being1.getShape().collide(being2.getShape()); 13 | } 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /examples/BulletCurtain/ShotOtherCollider.pde: -------------------------------------------------------------------------------- 1 | class ShotOtherCollider extends BoundingBoxCollider { 2 | 3 | 4 | //Interaction sends OSC message when an "Other" is destroyed. 5 | void handle(Shot shot, Other other) { 6 | 7 | world.getPostOffice().sendFloat("/"+SYSTEM_NAME+"/"+"OtherDestroyed", 1.0); 8 | 9 | world.getPostOffice().sendFloat("/"+SYSTEM_NAME+"/"+"OtherDestroyedAtX", map(other.getX(), 0.0, width, 0.0, 1.0)); 10 | world.getPostOffice().sendFloat("/"+SYSTEM_NAME+"/"+"OtherDestroyedAtY", map(other.getY(), 0.0, height, 0.0, 1.0)); 11 | 12 | world.delete(shot); 13 | world.delete(other); 14 | } 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/tutorialC/TutorialWorld.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Tutorial World 3 | * A World filled with squares 4 | */ 5 | class TutorialWorld extends World { 6 | int _squareNum; 7 | 8 | TutorialWorld(int squareNum/*, int portIn, int portOut*/) { 9 | super(/*portIn, portOut*/); 10 | _squareNum = squareNum; 11 | } 12 | 13 | void setup() { 14 | GlitchyGroup g = new GlitchyGroup(this); 15 | register(g); 16 | 17 | for (int i = 0; i < _squareNum; i++) { 18 | g.addSquare(); 19 | } 20 | 21 | register(g,g,new SquareInteractor()); 22 | } 23 | 24 | void draw() { 25 | background(0); 26 | super.draw(); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /examples/template/TemplateInteractor.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Template interactor between a TemplateBeing and another TemplateBeing 3 | * Don't forget to change TemplateBeing-s to 4 | * the names of the Being-types you want to interact 5 | */ 6 | class TemplateInteractor extends Interactor { 7 | TemplateInteractor() { 8 | super(); 9 | //Add your constructor info here 10 | } 11 | 12 | boolean detect(TemplateBeing being1, TemplateBeing being2) { 13 | //Add your detect method here 14 | return true; 15 | } 16 | 17 | void handle(TemplateBeing being1, TemplateBeing being2) { 18 | //Add your handle method here 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/tutorialCcollider/TutorialWorld.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Tutorial World 3 | * A World filled with squares 4 | */ 5 | class TutorialWorld extends World { 6 | int _squareNum; 7 | 8 | TutorialWorld(int squareNum/*, int portIn, int portOut*/) { 9 | super(/*portIn, portOut*/); 10 | _squareNum = squareNum; 11 | } 12 | 13 | void setup() { 14 | GlitchyGroup g = new GlitchyGroup(this); 15 | register(g); 16 | 17 | for (int i = 0; i < _squareNum; i++) { 18 | g.addSquare(); 19 | } 20 | 21 | register(g,g,new SquareInteractor()); 22 | } 23 | 24 | void draw() { 25 | background(0); 26 | super.draw(); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /examples/tutorialD/TutorialWorld.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Tutorial World 3 | * A World filled with squares 4 | */ 5 | class TutorialWorld extends World { 6 | int _squareNum; 7 | 8 | TutorialWorld(int squareNum/*, int portIn, int portOut*/) { 9 | super(/*portIn, portOut*/); 10 | _squareNum = squareNum; 11 | } 12 | 13 | void setup() { 14 | GlitchyGroup g = new GlitchyGroup(this); 15 | register(g); 16 | subscribe(g, POCodes.Key.A); 17 | 18 | for (int i = 0; i < _squareNum; i++) { 19 | g.addSquare(); 20 | } 21 | 22 | register(g,g,new SquareInteractor()); 23 | } 24 | 25 | void draw() { 26 | background(0); 27 | super.draw(); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /examples/BulletCurtain/ShotGroup.pde: -------------------------------------------------------------------------------- 1 | class ShotGroup extends Group { 2 | 3 | ShotGroup(World world) { 4 | super(world); 5 | } 6 | 7 | float initialTravel; 8 | 9 | void receive(OscMessage message) { 10 | String[] msgSplit = message.getAddress().split("/"); 11 | 12 | if (msgSplit[1].equals(SYSTEM_NAME)) { 13 | if (message.hasRemainingArguments()) { 14 | if (msgSplit[2].equals("SetTravelMultiplierForAllShots")) { 15 | float newMultiplier = constrain(message.getAndRemoveFloat(), 0.0, 1.0); 16 | newMultiplier = map(newMultiplier, 0.0, 1.0, 0.0, 10); 17 | shotTravelMultiplier = newMultiplier; 18 | } 19 | } 20 | } 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /examples/PlatformExplorer/Platform.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents the stationary platforms in the game 3 | */ 4 | class Platform extends MassedBeing { 5 | 6 | static final float HEIGHT = 40.0f; // the platform's height 7 | final color COLOR = color(125,125,125); // the platform's color 8 | 9 | float width; // width of this platform 10 | 11 | /** 12 | * makes a Platform with given center and width 13 | */ 14 | Platform(PVector center, float width) { 15 | super(new HRectangle(center, width, HEIGHT), HermesMath.zeroVector(), HermesMath.INFINITY, 1); 16 | 17 | this.width = width; 18 | } 19 | 20 | void draw() { 21 | fill(COLOR); 22 | rect(0, 0, width, HEIGHT); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /examples/tutorialB/GlitchyGroup.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Synchronizes the color of the GlitchySquares 3 | */ 4 | class GlitchyGroup extends Group { 5 | 6 | GlitchyGroup(World w) { 7 | super(w); 8 | } 9 | 10 | public void update() { 11 | color c = pickColor(); 12 | for (GlitchySquare s : getObjects()) { 13 | s.setColor(c); 14 | } 15 | } 16 | 17 | private color pickColor() { 18 | return color(int(random(256)), int(random(256)), int(random(256))); 19 | } 20 | 21 | public void addSquare() { 22 | int x = (int) random(WINDOW_WIDTH - 50); 23 | int y = (int) random(WINDOW_HEIGHT - 50); 24 | GlitchySquare s = new GlitchySquare(x, y); 25 | _world.register(s); 26 | this.add(s); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /examples/tutorialC/GlitchyGroup.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Synchronizes the color of the GlitchySquares 3 | */ 4 | class GlitchyGroup extends Group { 5 | 6 | GlitchyGroup(World w) { 7 | super(w); 8 | } 9 | 10 | public void update() { 11 | color c = pickColor(); 12 | for (GlitchySquare s : getObjects()) { 13 | s.setColor(c); 14 | } 15 | } 16 | 17 | private color pickColor() { 18 | return color(int(random(256)), int(random(256)), int(random(256))); 19 | } 20 | 21 | public void addSquare() { 22 | int x = (int) random(WINDOW_WIDTH - 50); 23 | int y = (int) random(WINDOW_HEIGHT - 50); 24 | GlitchySquare s = new GlitchySquare(x, y); 25 | _world.register(s); 26 | this.add(s); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/tutorialCcollider/GlitchyGroup.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Synchronizes the color of the GlitchySquares 3 | */ 4 | class GlitchyGroup extends Group { 5 | 6 | GlitchyGroup(World w) { 7 | super(w); 8 | } 9 | 10 | public void update() { 11 | color c = pickColor(); 12 | for (GlitchySquare s : getObjects()) { 13 | s.setColor(c); 14 | } 15 | } 16 | 17 | private color pickColor() { 18 | return color(int(random(256)), int(random(256)), int(random(256))); 19 | } 20 | 21 | public void addSquare() { 22 | int x = (int) random(WINDOW_WIDTH - 50); 23 | int y = (int) random(WINDOW_HEIGHT - 50); 24 | GlitchySquare s = new GlitchySquare(x, y); 25 | _world.register(s); 26 | this.add(s); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/tutorialA/GlitchySquare.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * An immobile flashing square --- DANGEROUS 3 | */ 4 | class GlitchySquare extends Being { 5 | static final int WIDTH = 50; 6 | static final int HEIGHT = 50; 7 | color _c; 8 | static final int SHAKE_STEP = 10; 9 | 10 | GlitchySquare(int x, int y) { 11 | super(new HRectangle(x, y, WIDTH, HEIGHT)); 12 | _c = pickColor(); 13 | } 14 | 15 | public void update() { 16 | _c = pickColor(); 17 | _position.x += round(random(SHAKE_STEP * 2)) - SHAKE_STEP; 18 | } 19 | 20 | public void draw() { 21 | fill(_c); 22 | noStroke(); 23 | _shape.draw(); 24 | } 25 | 26 | private color pickColor() { 27 | return color(int(random(256)), int(random(256)), int(random(256))); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /examples/Level0/Zero.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * The 0 at the top-left corner of the screen 3 | */ 4 | class Zero { 5 | 6 | int vertBarWidth = 30; 7 | int vertBarHeight = 60; 8 | 9 | int horizBarWidth = 60; 10 | int horizBarHeight = 10; 11 | 12 | int vertBarLeftX = 0; 13 | int vertBarRightX = 70; 14 | int vertBarY = horizBarHeight; 15 | 16 | int horizBarX = 20; 17 | int horizBarBottomY = horizBarHeight + vertBarHeight; 18 | int horizBarTopY = 0; 19 | 20 | void draw() { 21 | fill(255); 22 | noStroke(); 23 | rect(horizBarX, horizBarTopY, horizBarWidth, horizBarHeight); 24 | rect(horizBarX, horizBarBottomY, horizBarWidth, horizBarHeight); 25 | rect(vertBarLeftX, vertBarY, vertBarWidth, vertBarHeight); 26 | rect(vertBarRightX, vertBarY, vertBarWidth, vertBarHeight); 27 | } 28 | 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/tutorialB/GlitchySquare.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * An immobile flashing square --- DANGEROUS 3 | */ 4 | class GlitchySquare extends Being { 5 | static final int WIDTH = 50; 6 | static final int HEIGHT = 50; 7 | color _c; 8 | static final int SHAKE_STEP = 10; 9 | 10 | GlitchySquare(int x, int y) { 11 | super(new HRectangle(x, y, WIDTH, HEIGHT)); 12 | _c = color(0,0,0); 13 | } 14 | 15 | public void update() { 16 | //_c = pickColor(); 17 | _position.x += round(random(SHAKE_STEP * 2)) - SHAKE_STEP; 18 | } 19 | 20 | public void draw() { 21 | fill(_c); 22 | noStroke(); 23 | _shape.draw(); 24 | } 25 | 26 | public void setColor(color c) { 27 | _c = c; 28 | } 29 | 30 | private color pickColor() { 31 | return color(int(random(256)), int(random(256)), int(random(256))); 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /examples/BulletCurtain/Shot.pde: -------------------------------------------------------------------------------- 1 | class Shot extends Being { 2 | 3 | 4 | float travel, shotWidth, shotHeight; 5 | 6 | Shot(float x, float y, float shotWidth, float shotHeight, float travel) { 7 | 8 | super(new HRectangle(x, y, shotWidth, shotHeight)); 9 | this.travel = travel; 10 | this.shotWidth = shotWidth; 11 | this.shotHeight = shotHeight; 12 | } 13 | 14 | void update() { 15 | //This game doesn't use Hermes's built in physics, so the update needs to determine how much each shot travels 16 | setX(getX() + (travel * shotTravelMultiplier)); 17 | 18 | //if the shot has traveled offscreen, delete it from the groups 19 | if (getX() > width) { 20 | world.delete(this); 21 | } 22 | } 23 | 24 | 25 | void draw() { 26 | noStroke(); 27 | fill(255); 28 | rect(0, 0, shotWidth, shotHeight); 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /hermes/DetectedInteraction.java: -------------------------------------------------------------------------------- 1 | package hermes; 2 | 3 | /** 4 | * Used to store detected interactions when applyImmediate method of Interactor returns false. 5 | *

6 | * Internal -- you do not need to understand this class! 7 | */ 8 | class DetectedInteraction { 9 | A _being1; 10 | B _being2; 11 | Interaction _interaction; 12 | 13 | DetectedInteraction(A b1, B b2, Interaction interaction) { 14 | _being1 = b1; 15 | _being2 = b2; 16 | _interaction = interaction; 17 | } 18 | 19 | public A get_being1() { 20 | return _being1; 21 | } 22 | public B get_being2() { 23 | return _being2; 24 | } 25 | public Interactor get_interactor() { 26 | return _interaction.getInteractor(); 27 | } 28 | 29 | public Interaction getInteraction() { 30 | return _interaction; 31 | } 32 | } -------------------------------------------------------------------------------- /hermes/postoffice/MouseWheelMessage.java: -------------------------------------------------------------------------------- 1 | package hermes.postoffice; 2 | 3 | /** 4 | * Message representing a movement of the Mouse Wheel. 5 | * All MouseWheelMessages are of the same type. 6 | */ 7 | public class MouseWheelMessage implements Message { 8 | 9 | //Represents amount to scroll 10 | private int _wheelRotation; 11 | 12 | /** 13 | * Creates a new MouseWheelMessage. 14 | * @param wheelRotation 15 | */ 16 | public MouseWheelMessage(int wheelRotation) { 17 | _wheelRotation = wheelRotation; 18 | } 19 | 20 | /** 21 | * Gets the amount the wheel has been rotated. 22 | */ 23 | public int getWheelRotation() { 24 | return _wheelRotation; 25 | } 26 | 27 | /** 28 | * Checks for the equality of MouseWheelMessage types. 29 | */ 30 | public boolean equals(Object o) { 31 | if(o instanceof MouseWheelMessage) return true; 32 | return false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /hermes/Pair.java: -------------------------------------------------------------------------------- 1 | package hermes; 2 | 3 | /** 4 | * Utility for wrapping a pair of objects. 5 | * 6 | * @param the type of the first object in the pair 7 | * @param the type of the second object in the pair 8 | */ 9 | public class Pair { 10 | 11 | public A first; // the first object in the pair 12 | public B second; // the second object in the pair 13 | 14 | /** 15 | * Creates a Pair with given values for in its first and second fields. 16 | * @param first the first object to store 17 | * @param second the second object to store 18 | */ 19 | public Pair(A first, B second) { 20 | this.first = first; 21 | this.second = second; 22 | } 23 | 24 | /** 25 | * @return the first object in the pair 26 | */ 27 | public A getFirst() { 28 | return first; 29 | } 30 | 31 | /** 32 | * @return the second object in the pair 33 | */ 34 | public B getSecond() { 35 | return second; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /hermes/physics/MassedMergeCollider.java: -------------------------------------------------------------------------------- 1 | package hermes.physics; 2 | 3 | /** 4 | * this is a collision handler that merges beings who's shapes intersect 5 | * 6 | * NOT YET IMPLEMENTED 7 | * 8 | * @author Sam 9 | * 10 | * @param 11 | */ 12 | /* 13 | public class MassedMergeCollider extends Collider{ 14 | 15 | public abstract class Merger { 16 | 17 | public abstract A merge(A being1, A being2); 18 | 19 | public void mergeMassedBeings(MassedBeing being1, MassedBeing being2) { 20 | 21 | } 22 | 23 | } 24 | 25 | public boolean detect(A being1, A being2) { 26 | if(!super.detect(being1, being2)) { 27 | return false; 28 | } else { 29 | MassedBeing.addMergeCollision(being1, being2); 30 | 31 | return true; 32 | } 33 | } 34 | 35 | public void handle(A being1, A being2) { 36 | 37 | } 38 | 39 | public MassedBeing mergeCollide(A being1, A being2) { 40 | return null; 41 | } 42 | 43 | } 44 | */ -------------------------------------------------------------------------------- /hermes/physics/GravityInteractor.java: -------------------------------------------------------------------------------- 1 | package hermes.physics; 2 | 3 | /** 4 | * An interactor for gravitational attractions using the standard inverse-square force model. 5 | * F = G * m1 * m2 / r ^ 2 6 | * 7 | */ 8 | public class GravityInteractor extends InverseSquareInteractor { 9 | 10 | /** 11 | * Sets up the gravity interactor with a maximum interaction range. 12 | * @param gravityConstant the gravity constant G (should be positive) 13 | * @param maxRange the maximum range of gravitational interactions 14 | */ 15 | public GravityInteractor(float gravityConstant, float maxRange) { 16 | super(-gravityConstant, maxRange); 17 | } 18 | 19 | /** 20 | * Sets up a gravity interactor with no range limit. 21 | * @param gravityConstant the gravity constant G (should be positive) 22 | */ 23 | public GravityInteractor(float gravityConstant) { 24 | super(-gravityConstant); 25 | } 26 | 27 | protected float beingFactor(MassedBeing being) { 28 | return being.getMass(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /examples/bouncingpolygonsdemo/oscControl.pd: -------------------------------------------------------------------------------- 1 | #N canvas 222 283 744 338 10; 2 | #X obj 229 302 sendOSC; 3 | #X obj 9 16 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 4 | -1; 5 | #X msg 9 41 connect localhost 8080; 6 | #X obj 100 128 hsl 128 15 0.01 0.99 0 0 empty empty elasticity -2 -8 7 | 0 10 -262144 -1 -1 0 1; 8 | #X msg 115 148 send /BouncingBalls/SetElasticity \$1; 9 | #X obj 52 76 hsl 128 15 0.05 0.99 0 0 empty empty mass -2 -8 0 10 -262144 10 | -1 -1 0 1; 11 | #X msg 63 95 send /BouncingBalls/SetMass \$1; 12 | #X obj 146 180 nbx 5 14 3 1e+37 0 0 empty empty sides 0 -8 0 10 -262144 13 | -1 -1 3 256; 14 | #X msg 159 197 send /BouncingBalls/SetSides \$1; 15 | #X obj 190 229 hsl 128 15 0.01 0.99 0 0 empty empty rotate -2 -8 0 16 | 10 -262144 -1 -1 0 1; 17 | #X msg 204 248 send /BouncingBalls/SetRotate \$1; 18 | #X connect 1 0 2 0; 19 | #X connect 2 0 0 0; 20 | #X connect 3 0 4 0; 21 | #X connect 4 0 0 0; 22 | #X connect 5 0 6 0; 23 | #X connect 6 0 0 0; 24 | #X connect 7 0 8 0; 25 | #X connect 8 0 0 0; 26 | #X connect 9 0 10 0; 27 | #X connect 10 0 0 0; 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Ryan Lester, Chris Novello 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /examples/Level0/Goal.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Where player is trying to get the ball 3 | */ 4 | class Goal extends Being { 5 | Goal() { 6 | super(new HRectangle(new PVector((canvasLeftX+goali*cellSideLength), 7 | (containerTopY+goalj*cellSideLength)), 8 | new PVector(cellSideLength, cellSideLength)), 9 | new PVector(0, 0)); 10 | world.register(this, false); 11 | } 12 | 13 | void draw() { 14 | if (mode == RUN) { 15 | //Draw X 16 | strokeWeight(3); 17 | stroke(189, 0, 0); 18 | line(10, 10, cellSideLength-10, cellSideLength-10); // \ 19 | line(cellSideLength-10, 10, 10, cellSideLength-10); // / 20 | } 21 | else if (mode == COMPLETED) { 22 | //Draw check mark 23 | strokeWeight(2); 24 | stroke(255); 25 | fill(62, 67, 71, 130); 26 | rect(0, 0, cellSideLength, cellSideLength); 27 | strokeWeight(3); 28 | stroke(0, 240, 0); 29 | line(15, 20, 20, 30); // \ 30 | line(20, 30, 30, 10); // / 31 | } 32 | } 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /examples/tutorialC/GlitchySquare.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * An immobile flashing square --- DANGEROUS 3 | */ 4 | class GlitchySquare extends Being { 5 | static final int WIDTH = 50; 6 | static final int HEIGHT = 50; 7 | static final int SHAKE_STEP = 10; 8 | color _c; 9 | boolean _stroke; 10 | 11 | GlitchySquare(int x, int y) { 12 | super(new HRectangle(x, y, WIDTH, HEIGHT)); 13 | _c = color(0,0,0); 14 | _stroke = false; 15 | } 16 | 17 | public void update() { 18 | //_c = pickColor(); 19 | _position.x += round(random(SHAKE_STEP * 2)) - SHAKE_STEP; 20 | _stroke = false; 21 | } 22 | 23 | public void draw() { 24 | fill(_c); 25 | if(_stroke) { 26 | strokeWeight(5); 27 | stroke(255); 28 | } else { 29 | noStroke(); 30 | } 31 | _shape.draw(); 32 | } 33 | 34 | public void setColor(color c) { 35 | _c = c; 36 | } 37 | 38 | public void drawStroke() { 39 | _stroke = true; 40 | } 41 | 42 | private color pickColor() { 43 | return color(int(random(256)), int(random(256)), int(random(256))); 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /examples/bouncingpolygonsdemo/DemoWorld.pde: -------------------------------------------------------------------------------- 1 | class DemoWorld extends World { 2 | DemoWorld(PostOffice po, HCamera cam) { 3 | super(po, cam); 4 | } 5 | 6 | void setup() { 7 | _ballGroup = new BallGroup(_world); 8 | //ball group handles all messages 9 | _postOffice.subscribe(_ballGroup, POLY_KEY); 10 | _postOffice.subscribe(_ballGroup, CIRCLE_KEY); 11 | _postOffice.subscribe(_ballGroup, RECT_KEY); 12 | _postOffice.subscribe(_ballGroup, DELETE_KEY); 13 | _postOffice.subscribe(_ballGroup, Button.LEFT); 14 | _postOffice.subscribe(_ballGroup, "/BouncingBalls/SetElasticity"); 15 | _postOffice.subscribe(_ballGroup, "/BouncingBalls/SetMass"); 16 | _postOffice.subscribe(_ballGroup, "/BouncingBalls/SetSides"); 17 | _postOffice.subscribe(_ballGroup, "/BouncingBalls/SetRotate"); 18 | 19 | _boxGroup = new BoxGroup(_world); 20 | 21 | //Set up the interactions 22 | _world.register(_ballGroup, _ballGroup, new MassedCollider(), new SelfInteractionOptimizer()); 23 | _world.register(_boxGroup, _ballGroup, new InsideMassedCollider()); 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /examples/tutorialCcollider/GlitchySquare.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * An immobile flashing square --- DANGEROUS 3 | */ 4 | class GlitchySquare extends Being { 5 | static final int WIDTH = 50; 6 | static final int HEIGHT = 50; 7 | static final int SHAKE_STEP = 10; 8 | color _c; 9 | boolean _stroke; 10 | 11 | GlitchySquare(int x, int y) { 12 | super(new HRectangle(x, y, WIDTH, HEIGHT)); 13 | _c = color(0,0,0); 14 | _stroke = false; 15 | } 16 | 17 | public void update() { 18 | //_c = pickColor(); 19 | _position.x += round(random(SHAKE_STEP * 2)) - SHAKE_STEP; 20 | _stroke = false; 21 | } 22 | 23 | public void draw() { 24 | fill(_c); 25 | if(_stroke) { 26 | strokeWeight(5); 27 | stroke(255); 28 | } else { 29 | noStroke(); 30 | } 31 | _shape.draw(); 32 | } 33 | 34 | public void setColor(color c) { 35 | _c = c; 36 | } 37 | 38 | public void drawStroke() { 39 | _stroke = true; 40 | } 41 | 42 | private color pickColor() { 43 | return color(int(random(256)), int(random(256)), int(random(256))); 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /hermes/Optimizer.java: -------------------------------------------------------------------------------- 1 | package hermes; 2 | 3 | /** 4 | * Determines which beings are compared and potentially interacted by Interactor. 5 | * Unoptimized interactions check all members of A against all the members of B (order n^2). 6 | * Optimizers allow for the creation of data structures which will speed interactions. 7 | *

8 | * Group B is given as argument to constructor and organized according to optimizer type. 9 | * Detect checks body A against the structure formed from Group B. 10 | *

11 | * Sample advanced optimizers include quadtree. 12 | */ 13 | public interface Optimizer, GroupB extends GenericGroup> { 15 | 16 | /** 17 | * This should detect all possible interactions between the members of groups A and B, using handler. 18 | * @param group1 a group of beings 19 | * @param group2 a group of beings 20 | * @param handler interaction handler that will detect and handle interactions 21 | */ 22 | public void detect(GroupA group1, GroupB group2, InteractionHandler handler); 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /examples/PlatformExplorer/PlatformWorld.pde: -------------------------------------------------------------------------------- 1 | class PlatformWorld extends World { 2 | PlatformWorld(PostOffice po, HCamera cam) { 3 | super(po,cam); 4 | } 5 | 6 | void setup() { 7 | // set up the platforms 8 | platforms = new PlatformGroup(world); 9 | Sector first = new Sector(0, 0, platforms, 0.8); 10 | SectorGrid grid = new SectorGrid(first, platforms); 11 | 12 | // set up platform generation 13 | world.register(grid, cam, new PlatformGenerator()); 14 | 15 | // set up the player 16 | player = new Player(0, 60); 17 | println("player created"); 18 | world.register(player, true); 19 | po.subscribe(player, POCodes.Key.W); 20 | po.subscribe(player, POCodes.Key.A); 21 | po.subscribe(player, POCodes.Key.S); 22 | po.subscribe(player, POCodes.Key.D); 23 | po.subscribe(player, POCodes.Key.UP); 24 | po.subscribe(player, POCodes.Key.DOWN); 25 | po.subscribe(player, POCodes.Key.LEFT); 26 | po.subscribe(player, POCodes.Key.RIGHT); 27 | 28 | // make player collide with platforms 29 | world.register(player, platforms, new PlatformCollider(0)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/tutorialD/GlitchyGroup.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Synchronizes the color of the GlitchySquares 3 | */ 4 | class GlitchyGroup extends Group { 5 | 6 | GlitchyGroup(World w) { 7 | super(w); 8 | } 9 | 10 | public void update() { 11 | color c = pickColor(); 12 | for (GlitchySquare s : getObjects()) { 13 | if (!s.usingKey()) { 14 | s.setColor(c); 15 | } 16 | } 17 | } 18 | 19 | public color pickColor() { 20 | return color(int(random(256)), int(random(256)), int(random(256))); 21 | } 22 | 23 | public void addSquare() { 24 | int x = (int) random(WINDOW_WIDTH - 50); 25 | int y = (int) random(WINDOW_HEIGHT - 50); 26 | GlitchySquare s = new GlitchySquare(x, y); 27 | s.useKey(); 28 | _world.register(s); 29 | _world.subscribe(s, POCodes.Key.UP); 30 | _world.subscribe(s, POCodes.Key.RIGHT); 31 | _world.subscribe(s, POCodes.Key.DOWN); 32 | _world.subscribe(s, POCodes.Key.LEFT); 33 | _world.subscribe(s, POCodes.Button.LEFT, s.getShape()); 34 | add(s); 35 | } 36 | 37 | public void receive(KeyMessage m) { 38 | if (m.isPressed()) { 39 | addSquare(); 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /hermes/Interaction.java: -------------------------------------------------------------------------------- 1 | package hermes; 2 | 3 | /** 4 | * Wraps all the elements comprising an interaction. 5 | *

6 | * Internal -- you do not need to understand this class! 7 | * See {@link hermes.World World} for information on registering Interactions. 8 | */ 9 | class Interaction { 10 | 11 | private GenericGroup a; // the first group in the interaction 12 | private GenericGroup b; // the second group in the interaction 13 | private Interactor interactor; // the interactor 14 | private Optimizer optimizer; // the optimizer, if there is one 15 | 16 | public Interaction(GenericGroup a, GenericGroup b, Interactor interactor, 17 | Optimizer optimizer) { 18 | this.a = a; 19 | this.b = b; 20 | this.interactor = interactor; 21 | this.optimizer = optimizer; 22 | } 23 | 24 | public GenericGroup getA() { 25 | return a; 26 | } 27 | 28 | public GenericGroup getB() { 29 | return b; 30 | } 31 | 32 | public Interactor getInteractor() { 33 | return interactor; 34 | } 35 | 36 | public Optimizer getOptimizer() { 37 | return optimizer; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /hermes/hshape/HCompoundShape.java: -------------------------------------------------------------------------------- 1 | package hermes.hshape; 2 | 3 | import java.util.*; 4 | import processing.core.PVector; 5 | 6 | /** 7 | * NOTE: do not use, does not work. 8 | *

9 | * Represents a compound shape containing any number of shapes. 10 | * The position of contained shapes will be defined relative to the position. 11 | * 12 | *//* 13 | public class CompoundShape extendsHShape { 14 | 15 | private ListHShape> _shapes; 16 | 17 | *//** 18 | * creates a new compound shape 19 | * @param position the shape's position 20 | *//* 21 | public CompoundShape(PVector position) { 22 | super(position); 23 | _shapes = new ArrayListHShape>(); 24 | } 25 | 26 | *//** 27 | * adds a shape to the compound 28 | * @param shape 29 | *//* 30 | public void addShapeHShape shape) { 31 | _shapes.add(shape); 32 | } 33 | 34 | @Override 35 | public boolean collideHShape other) { 36 | assert other != null : "In CompoundShape.collide: other must be a valid shape"; 37 | 38 | // collide individually with each of our shapes 39 | for(IteratorHShape> iter = _shapes.iterator(); iter.hasNext(); ) { 40 | if(other.collide(iter.next())) 41 | return true; 42 | } 43 | return false; 44 | } 45 | 46 | } 47 | */ -------------------------------------------------------------------------------- /examples/PlatformExplorer/PlatformGenerator.pde: -------------------------------------------------------------------------------- 1 | // Generates platforms when the camera enters an unexplored area 2 | class PlatformGenerator extends Interactor { 3 | 4 | boolean detect(SectorGrid grid, HCamera cam) { 5 | // we only need to do anything if the camera's box is not completely contained by the current sector 6 | return !grid.getCurrentSector().getBoundingBox().contains(cam.getBoundingBox()); 7 | } 8 | 9 | void handle(SectorGrid grid, HCamera cam) { 10 | Sector current = grid.getCurrentSector(); 11 | // see which neighbors of this sector the camera overlaps with 12 | HRectangle[] neighbors = current.getNeighborRects(); 13 | for(int i = 0; i < 8; i++) { 14 | if(cam.getBoundingBox().collide(neighbors[i])) { 15 | int x = (int)neighbors[i].getPosition().x / Sector.SECTOR_SIZE; 16 | int y = (int)neighbors[i].getPosition().y / Sector.SECTOR_SIZE; 17 | // make sure the overlapped sector exists 18 | Sector sector = grid.getSector(x, y); 19 | // if the camera center is in a new bounding box, we need to change the current sector 20 | if(neighbors[i].contains(cam.getBoundingBox().getCenter())) 21 | grid.setCurrentSector(sector); 22 | } 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /examples/tutorialA/tutorialA.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Tutorial example 1 3 | * Beings doing stuff! 4 | * https://github.com/rdlester/hermes/wiki/Tutorial-Pt.-2:-A-Simple-World 5 | */ 6 | 7 | import hermes.*; 8 | import hermes.hshape.*; 9 | import hermes.animation.*; 10 | import hermes.physics.*; 11 | import hermes.postoffice.*; 12 | 13 | /////////////////////////////////////////////////// 14 | // CONSTANTS 15 | /////////////////////////////////////////////////// 16 | /** 17 | * Constants should go up here 18 | * Making more things constants makes them easier to adjust and play with! 19 | */ 20 | static final int WINDOW_WIDTH = 600; 21 | static final int WINDOW_HEIGHT = 600; 22 | static final int PORT_IN = 8080; 23 | static final int PORT_OUT = 8000; 24 | 25 | World currentWorld; 26 | 27 | /** 28 | * Add groups here if you need custom group behavoir 29 | */ 30 | 31 | /////////////////////////////////////////////////// 32 | // PAPPLET 33 | /////////////////////////////////////////////////// 34 | 35 | void setup() { 36 | size(600, 600); 37 | background(0); 38 | Hermes.setPApplet(this); 39 | 40 | currentWorld = new TutorialWorld(300); 41 | 42 | //Important: don't forget to add setup to TemplateWorld! 43 | 44 | currentWorld.start(); // this should be the last line in setup() method 45 | } 46 | 47 | void draw() { 48 | currentWorld.draw(); 49 | } 50 | -------------------------------------------------------------------------------- /examples/tutorialCcollider/tutorialCcollider.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Tutorial 3.5 3 | * Demo of a built-in interaction 4 | * https://github.com/rdlester/hermes/wiki/Tutorial-Pt.-4:-Interactors 5 | */ 6 | 7 | import hermes.*; 8 | import hermes.hshape.*; 9 | import hermes.animation.*; 10 | import hermes.physics.*; 11 | import hermes.postoffice.*; 12 | 13 | /////////////////////////////////////////////////// 14 | // CONSTANTS 15 | /////////////////////////////////////////////////// 16 | /** 17 | * Constants should go up here 18 | * Making more things constants makes them easier to adjust and play with! 19 | */ 20 | static final int WINDOW_WIDTH = 600; 21 | static final int WINDOW_HEIGHT = 600; 22 | static final int PORT_IN = 8080; 23 | static final int PORT_OUT = 8000; 24 | 25 | World currentWorld; 26 | 27 | /** 28 | * Add groups here if you need custom group behavoir 29 | */ 30 | 31 | /////////////////////////////////////////////////// 32 | // PAPPLET 33 | /////////////////////////////////////////////////// 34 | 35 | void setup() { 36 | size(600, 600); 37 | background(0); 38 | Hermes.setPApplet(this); 39 | 40 | currentWorld = new TutorialWorld(10); 41 | 42 | //Important: don't forget to add setup to TemplateWorld! 43 | 44 | currentWorld.start(); // this should be the last line in setup() method 45 | } 46 | 47 | void draw() { 48 | currentWorld.draw(); 49 | } 50 | -------------------------------------------------------------------------------- /examples/tutorialB/tutorialB.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Tutorial example 2 3 | * Groups - you'll have more fun together! 4 | * https://github.com/rdlester/hermes/wiki/Tutorial-Pt.-3:-Groups 5 | */ 6 | 7 | import hermes.*; 8 | import hermes.hshape.*; 9 | import hermes.animation.*; 10 | import hermes.physics.*; 11 | import hermes.postoffice.*; 12 | 13 | /////////////////////////////////////////////////// 14 | // CONSTANTS 15 | /////////////////////////////////////////////////// 16 | /** 17 | * Constants should go up here 18 | * Making more things constants makes them easier to adjust and play with! 19 | */ 20 | static final int WINDOW_WIDTH = 600; 21 | static final int WINDOW_HEIGHT = 600; 22 | static final int PORT_IN = 8080; 23 | static final int PORT_OUT = 8000; 24 | 25 | World currentWorld; 26 | 27 | /** 28 | * Add groups here if you need custom group behavoir 29 | */ 30 | 31 | /////////////////////////////////////////////////// 32 | // PAPPLET 33 | /////////////////////////////////////////////////// 34 | 35 | void setup() { 36 | size(600, 600); 37 | background(0); 38 | Hermes.setPApplet(this); 39 | 40 | currentWorld = new TutorialWorld(int(random(500))); 41 | 42 | //Important: don't forget to add setup to TemplateWorld! 43 | 44 | currentWorld.start(); // this should be the last line in setup() method 45 | } 46 | 47 | void draw() { 48 | currentWorld.draw(); 49 | } 50 | -------------------------------------------------------------------------------- /examples/tutorialC/tutorialC.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Tutorial example 3 3 | * Interactions - for every action there's always a... 4 | * https://github.com/rdlester/hermes/wiki/Tutorial-Pt.-4:-Interactors 5 | */ 6 | 7 | import hermes.*; 8 | import hermes.hshape.*; 9 | import hermes.animation.*; 10 | import hermes.physics.*; 11 | import hermes.postoffice.*; 12 | 13 | /////////////////////////////////////////////////// 14 | // CONSTANTS 15 | /////////////////////////////////////////////////// 16 | /** 17 | * Constants should go up here 18 | * Making more things constants makes them easier to adjust and play with! 19 | */ 20 | static final int WINDOW_WIDTH = 600; 21 | static final int WINDOW_HEIGHT = 600; 22 | static final int PORT_IN = 8080; 23 | static final int PORT_OUT = 8000; 24 | 25 | World currentWorld; 26 | 27 | /** 28 | * Add groups here if you need custom group behavoir 29 | */ 30 | 31 | /////////////////////////////////////////////////// 32 | // PAPPLET 33 | /////////////////////////////////////////////////// 34 | 35 | void setup() { 36 | size(600, 600); 37 | background(0); 38 | Hermes.setPApplet(this); 39 | 40 | currentWorld = new TutorialWorld(10); 41 | 42 | //Important: don't forget to add setup to TemplateWorld! 43 | 44 | currentWorld.start(); // this should be the last line in setup() method 45 | } 46 | 47 | void draw() { 48 | currentWorld.draw(); 49 | } 50 | -------------------------------------------------------------------------------- /hermes/SelfInteractionOptimizer.java: -------------------------------------------------------------------------------- 1 | package hermes; 2 | 3 | /** 4 | * This Optimizer reduces the time needed to handle interactions between all members of 5 | * the same group, and also eliminates duplicate interactions. Each member will be checked 6 | * against each other member of the group exactly once. This works only if the interactions 7 | * are commutative, ie handle(A,B) is equivalent to handle(B,A). 8 | *
9 | * Detection under this optimizer is still an O(n^2) operation, but using it will reduce 10 | * total calculation time by a factor of approximately 1/2 (the number of computations 11 | * performed will be n(n-1) / 2, where n is the number of beings in the group). 12 | * 13 | * @author Sam 14 | * 15 | * @param
the type of Being the group contains 16 | */ 17 | public class SelfInteractionOptimizer implements Optimizer, Group> { 18 | 19 | public void detect(Group group1, Group group2, 20 | InteractionHandler handler) { 21 | 22 | assert group1 == group2 : "SelfInteractionOptimizer.detect: group1 and group2 must be the same group"; 23 | 24 | int length = group1.size(); 25 | for(int i = 0; i < length - 1; i++) { 26 | A being1 = group1.get(i); 27 | for(int j = i + 1; j < length; j++) { 28 | A being2 = group2.get(j); 29 | handler.interactionHandler(being1, being2); 30 | } 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /examples/template/template.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * A template to get you started 3 | * Define your beings, groups, interactors and worlds in separate tabs 4 | * Put the pieces together in this top-level file! 5 | * 6 | * See the tutorial for details: 7 | * https://github.com/rdlester/hermes/wiki/Tutorial-Pt.-1:-Worlds-and-Beings 8 | */ 9 | 10 | import hermes.*; 11 | import hermes.hshape.*; 12 | import hermes.animation.*; 13 | import hermes.physics.*; 14 | import hermes.postoffice.*; 15 | 16 | /////////////////////////////////////////////////// 17 | // CONSTANTS 18 | /////////////////////////////////////////////////// 19 | /** 20 | * Constants should go up here 21 | * Making more things constants makes them easier to adjust and play with! 22 | */ 23 | static final int WINDOW_WIDTH = 600; 24 | static final int WINDOW_HEIGHT = 600; 25 | static final int PORT_IN = 8080; 26 | static final int PORT_OUT = 8000; 27 | 28 | World currentWorld; 29 | 30 | /////////////////////////////////////////////////// 31 | // PAPPLET 32 | /////////////////////////////////////////////////// 33 | 34 | void setup() { 35 | size(600, 600); 36 | Hermes.setPApplet(this); 37 | 38 | currentWorld = new TemplateWorld(PORT_IN, PORT_OUT); 39 | 40 | //Important: don't forget to add setup to TemplateWorld! 41 | 42 | currentWorld.start(); // this should be the last line in setup() method 43 | } 44 | 45 | void draw() { 46 | currentWorld.draw(); 47 | } 48 | -------------------------------------------------------------------------------- /examples/PlatformExplorer/PlatformExplorer.pde: -------------------------------------------------------------------------------- 1 | import processing.opengl.*; 2 | import java.util.Hashtable; 3 | 4 | import hermes.*; 5 | import hermes.hshape.*; 6 | import hermes.animation.*; 7 | import hermes.physics.*; 8 | import hermes.postoffice.*; 9 | 10 | /////////////////////////////////////////////////// 11 | // DEFINE 12 | /////////////////////////////////////////////////// 13 | 14 | static final int WINDOW_WIDTH = 600; 15 | static final int WINDOW_HEIGHT = 600; 16 | 17 | static final float GRAVITY = -200; // acceleration due to gravity 18 | 19 | /////////////////////////////////////////////////// 20 | // GLOBAL VARS 21 | /////////////////////////////////////////////////// 22 | 23 | World world; 24 | PlatformCamera cam; 25 | PostOffice po; 26 | PlatformGroup platforms; 27 | Player player; 28 | 29 | /////////////////////////////////////////////////// 30 | // PAPPLET 31 | /////////////////////////////////////////////////// 32 | 33 | void setup() { 34 | size(600, 600); // set window size 35 | Hermes.setPApplet(this); // give the library the PApplet 36 | 37 | // set up the world, camera, and post office 38 | cam = new PlatformCamera(); 39 | po = new PostOffice(); 40 | world = new PlatformWorld(po, cam); 41 | 42 | rectMode(CENTER); 43 | 44 | frameRate(60); 45 | 46 | //Sets up and starts world 47 | world.start(); 48 | } 49 | 50 | void draw() { 51 | background(230); 52 | 53 | world.draw(); 54 | } 55 | -------------------------------------------------------------------------------- /hermes/Group.java: -------------------------------------------------------------------------------- 1 | package hermes; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Groups together Beings that share common attributes 7 | * and interact with other HObjects. 8 | *

9 | * Group uses an ArrayList to keep track of Beings. 10 | * Use GenericGroup if you want to use a different data structure 11 | * or keep track of things that aren't Beings. 12 | *

13 | * Groups are used for the same reasons as GenericGroups. 14 | * See JavaDocs for GenericGroup for more details. 15 | * 16 | * @param the type of being to be stored 17 | */ 18 | public class Group extends GenericGroup> { 19 | 20 | /** 21 | * Constructs a new empty group. 22 | * @param world the world containing the group 23 | */ 24 | public Group(World world) { 25 | super(new ArrayList(), world); 26 | } 27 | 28 | /** 29 | * Retrieves the being at a specific index. 30 | * @param index the index to fetch from 31 | * @return the being at index 32 | */ 33 | public A get(int index) { 34 | return getObjects().get(index); 35 | } 36 | 37 | /** 38 | * Finds the location of being in the group. 39 | * @param being the being 40 | * @return the index at which the being is stored 41 | */ 42 | public int getIndex(A being) { 43 | return getObjects().indexOf(being); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /examples/tutorialD/tutorialD.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Tutorial 4 3 | * PostOffice / mouse+keyboard inputs - speak to me! 4 | * https://github.com/rdlester/hermes/wiki/Tutorial-Pt.-5:-Post-Office-&-User-Input 5 | */ 6 | 7 | import hermes.*; 8 | import hermes.hshape.*; 9 | import hermes.animation.*; 10 | import hermes.physics.*; 11 | import hermes.postoffice.*; 12 | 13 | /////////////////////////////////////////////////// 14 | // CONSTANTS 15 | /////////////////////////////////////////////////// 16 | /** 17 | * Constants should go up here 18 | * Making more things constants makes them easier to adjust and play with! 19 | */ 20 | static final int WINDOW_WIDTH = 600; 21 | static final int WINDOW_HEIGHT = 600; 22 | static final int PORT_IN = 8080; 23 | static final int PORT_OUT = 8000; 24 | 25 | World currentWorld; 26 | 27 | /** 28 | * Add groups here if you need custom group behavoir 29 | */ 30 | 31 | /////////////////////////////////////////////////// 32 | // PAPPLET 33 | /////////////////////////////////////////////////// 34 | 35 | void setup() { 36 | size(600, 600); 37 | background(0); 38 | Hermes.setPApplet(this); 39 | 40 | currentWorld = new TutorialWorld(10); 41 | 42 | currentWorld.start(); // this should be the last line in setup() method 43 | } 44 | 45 | void draw() { 46 | currentWorld.draw(); 47 | } 48 | 49 | /*void keyPressed() { 50 | currentWorld.deactivate(); 51 | currentWorld = new TutorialWorld((int)random(1000)); 52 | currentWorld.start(); 53 | }*/ 54 | -------------------------------------------------------------------------------- /examples/Level0/RandomButton.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * When pressed, randomizes the vector field on the canvas 3 | */ 4 | class RandomButton extends Being { 5 | 6 | Canvas _c; 7 | PostOffice _po; 8 | boolean _hover; 9 | boolean _keyPressed = false; 10 | 11 | RandomButton(Canvas c, PostOffice po) { 12 | super(new HRectangle(randomButtonX,randomButtonY,randomButtonSide,randomButtonSide)); 13 | _c = c; 14 | _hover = false; 15 | _po = po; 16 | } 17 | 18 | /** 19 | * Tells canvas to randomize vector field on key press or mouse press 20 | */ 21 | void receive(KeyMessage m) { 22 | if(m.isPressed() && !_keyPressed) { 23 | _c.randomize(); 24 | _keyPressed = true; 25 | } 26 | else { 27 | _keyPressed = false; 28 | } 29 | } 30 | 31 | void receive(MouseMessage m) { 32 | _hover = true; 33 | if(m.getAction() == POCodes.Click.PRESSED) { 34 | _c.randomize(); 35 | } 36 | } 37 | 38 | 39 | boolean getHover() {return _hover;} 40 | void setHover(boolean hover) {_hover = hover;} 41 | 42 | void draw() { 43 | if(_hover) { 44 | fill(buttonHover); 45 | _hover = _po.isMouseInRegion(this.getShape()); 46 | } 47 | else { 48 | noFill(); 49 | } 50 | getShape().draw(); 51 | //Draw star in the center 52 | translate(randomButtonSide/2,randomButtonSide/2); 53 | for(int i = 0; i < starNum; i++) { 54 | rotate(PI/starNum); 55 | line(randomButtonSide/4,randomButtonSide/4,-randomButtonSide/4,-randomButtonSide/4); 56 | } 57 | } 58 | } 59 | 60 | 61 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # HERMES 2 | 3 | Hermes is an experimental game engine leveraging Java's object-oriented systems and the simplicity of Processing to provide an architecture for rapid sketching and prototyping of games and simulations. 4 | 5 | ## Install 6 | 7 | ### For Processing 2: 8 | [Hermes.zip](https://rdlester.github.io/hermes/downloads/hermes.zip) contains a completely built version of the library with examples and documentation. To install, drag the unzipped folder into the 'libraries' folder in your Processing sketchbook. 9 | 10 | ### For Processing 3: 11 | A beta release of Hermes supporting Processing 3 is [available here](https://github.com/rdlester/hermes/releases/download/v3.0beta/hermes-3.0.zip), but please check [the release page for caveats](https://github.com/rdlester/hermes/releases/tag/v3.0beta). 12 | 13 | ## Getting Started 14 | 15 | See the [Tutorial](https://github.com/rdlester/hermes/wiki/An-Introduction-to-Hermes) for an introduction to making games with Hermes. See [the javadocs](https://rdlester.github.io/hermes/doc) for full code and API documentation. 16 | 17 | ## Source 18 | 19 | The full source code of the library is in the hermes/ folder; unit tests are in hermesTest/. The library itself is in the package `hermes`, and testing code is in `hermesTest`. In the pre-built download, source is included in the src/ folder. See [the wiki](https://github.com/rdlester/hermes/wiki/Building-from-Source) for details on building the library from source. 20 | 21 | ## License 22 | 23 | Hermes is available under the MIT license. See 'LICENSE' for full details. 24 | -------------------------------------------------------------------------------- /hermes/Hermes.java: -------------------------------------------------------------------------------- 1 | package hermes; 2 | 3 | import processing.core.PApplet; 4 | 5 | /** 6 | * Library class for broad scale static helpers, storage of PApplet 7 | */ 8 | public class Hermes { 9 | 10 | private static PApplet _parentApplet = null; //Storage of sketch's PApplet. 11 | private static float _timeScale = 1.0f; // the time scale used by Hermes motion and physics calculations 12 | 13 | /** 14 | * Sets the time scale for calculating motion and physics. This is seconds/unit, so a value of 2 will mean 15 | * each 2 seconds correspond to one time unit. 16 | * @param scale 17 | */ 18 | public static void setTimeScale(float scale) { 19 | _timeScale = scale; 20 | } 21 | 22 | /** 23 | * Returns the Hermes time scale. 24 | * @return the time scale 25 | */ 26 | public static float getTimeScale() { 27 | return _timeScale; 28 | } 29 | 30 | /** 31 | * Returns the PApplet that Hermes is running in. 32 | */ 33 | public static PApplet getPApplet() { 34 | synchronized(_parentApplet) { 35 | return _parentApplet; 36 | } 37 | } 38 | 39 | /** 40 | * Set the PApplet that all utilities use. Called Hermes.setPApplet(this) before doing anything else! 41 | */ 42 | public static void setPApplet(PApplet parentApplet) { 43 | _parentApplet = parentApplet; 44 | } 45 | 46 | /** 47 | * Causes the calling thread to sleep, catches InterruptedException. 48 | * @param time the time (in milliseconds) to sleep 49 | */ 50 | public static void unsafeSleep(long time) { 51 | try { 52 | Thread.sleep(time); 53 | } catch (InterruptedException e) {} 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /examples/Level0/Bubble.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Translucent balls demonstrating flow of canvas to player 3 | * Do not interact with the goal 4 | */ 5 | class Bubble extends MassedBeing { 6 | 7 | Bubble(PVector position) { 8 | super(new HCircle(position, ballRadius/2), new PVector(0, 0), ballMass, ballElasticity, 35, 8); 9 | world.register(this, true); 10 | } 11 | 12 | void draw() { 13 | fill(146, 239, 233, 120); 14 | noStroke(); 15 | ellipse(0, 0, ballRadius, ballRadius); 16 | } 17 | 18 | void update() { 19 | //Get the cell the bubble is in 20 | int x = (int)getX(); 21 | int y = (int)getY(); 22 | x -= canvasLeftX; 23 | y -= containerTopY; 24 | int i = x / cellSideLength; 25 | int j = y / cellSideLength; 26 | 27 | //check to make sure did not escape //TODO: remove this hack if possible! 28 | if (i < 0) { 29 | i=0; 30 | setX(canvasLeftX); 31 | setVelocityX(-getVelocityX()); 32 | } 33 | if (i >= canvasNumCellsX) { 34 | i=canvasNumCellsX-1; 35 | setX(canvasRightX); 36 | setVelocityX(-getVelocityX()); 37 | } 38 | if (j < 0) { 39 | j=0; 40 | setY(containerTopY); 41 | setVelocityY(-getVelocityY()); 42 | } 43 | if (j >= canvasNumCellsY) { 44 | j=canvasNumCellsY-1; 45 | setY(containerBottomY); 46 | setVelocityY(-getVelocityY()); 47 | } 48 | 49 | //Add the flow to the bubble 50 | Cell[][] grid = canvas.getGrid(); 51 | Cell in = grid[i][j]; 52 | if (!in.hasTool()) { //appply the flow if the cell has no tool within 53 | setVelocity(PVector.add(getVelocity(), in.getFlowDir())); 54 | } 55 | } 56 | } 57 | 58 | 59 | -------------------------------------------------------------------------------- /examples/Level0/Ball.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Central ball of the game 3 | * This is what player tries to get into the goal 4 | */ 5 | class Ball extends MassedBeing { 6 | 7 | Ball() { 8 | super(new HCircle(new PVector((canvasLeftX+balli*cellSideLength)+cellSideLength/2, 9 | (containerTopY+ballj*cellSideLength)+cellSideLength/2), ballRadius), 10 | new PVector(0, 0), ballMass, ballElasticity, 35, 8); 11 | world.register(this, true); 12 | } 13 | 14 | void update() { 15 | //determine cell containing ball 16 | int x = (int)getX(); 17 | int y = (int)getY(); 18 | x -= canvasLeftX; 19 | y -= containerTopY; 20 | int i = x / cellSideLength; 21 | int j = y / cellSideLength; 22 | 23 | //check to make sure did not escape //TODO: remove this hack if possible! 24 | if (i < 0) { 25 | i=0; 26 | setX(canvasLeftX); 27 | setVelocityX(-getVelocityX()); 28 | } 29 | if (i >= canvasNumCellsX) { 30 | i=canvasNumCellsX-1; 31 | setX(canvasRightX); 32 | setVelocityX(-getVelocityX()); 33 | } 34 | if (j < 0) { 35 | j=0; 36 | setY(containerTopY); 37 | setVelocityY(-getVelocityY()); 38 | } 39 | if (j >= canvasNumCellsY) { 40 | j=canvasNumCellsY-1; 41 | setY(containerBottomY); 42 | setVelocityY(-getVelocityY()); 43 | } 44 | 45 | //Add cell's flow to ball 46 | Cell[][] grid = canvas.getGrid(); 47 | Cell in = grid[i][j]; 48 | if (!in.hasTool()) { //appply the flow if the cell has no tool within 49 | setVelocity(PVector.add(getVelocity(), in.getFlowDir())); 50 | } 51 | } 52 | 53 | void draw() { 54 | fill(189, 0, 0); 55 | noStroke(); 56 | ellipse(0, 0, ballRadius*2, ballRadius*2); 57 | } 58 | } 59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/bouncingpolygonsdemo/Balls.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Abstract class for balls, contains basic functionality 3 | * Only difference between type of balls is the type of shape used 4 | */ 5 | abstract class Ball extends MassedBeing { 6 | 7 | Group _group; 8 | color _color; 9 | 10 | Ball(HShape shape, PVector velocity, float mass, float elasticity) { 11 | super(shape, velocity, mass, elasticity, 35, 8); 12 | _color = color(random(255), random(255), random(255)); 13 | } 14 | 15 | void update() { 16 | if(getX() < 0) 17 | setX(0); 18 | if(getY() < 0) 19 | setY(0); 20 | if(getX() > (float)WIDTH) 21 | setX((float)WIDTH); 22 | if(getY() > (float)HEIGHT) 23 | setY((float)HEIGHT); 24 | } 25 | 26 | void draw() { 27 | fill(_color); 28 | getShape().draw(); 29 | } 30 | } 31 | 32 | /** 33 | * Creates a polygonal ball 34 | * Uses factory method inside of HPolygon to create the polygons 35 | */ 36 | class PolyBall extends Ball { 37 | PolyBall(PVector center, PVector velocity, float mass, float elasticity) { 38 | super(HPolygon.createRegularHPolygon(center,polyPoint,ballSize * mass), velocity, mass, elasticity); 39 | ((HPolygon) getShape()).rotate(polyRot); 40 | } 41 | } 42 | 43 | /** 44 | * Creates a circular ball 45 | */ 46 | class HCircleBall extends Ball { 47 | HCircleBall(PVector center, PVector velocity, float mass, float elasticity) { 48 | super(new HCircle(center, ballSize * mass), velocity, mass, elasticity); 49 | } 50 | } 51 | 52 | /** 53 | * Creates a square ball 54 | */ 55 | class RectBall extends Ball { 56 | RectBall(PVector center, PVector velocity, float mass, float elasticity) { 57 | super(new HRectangle(center, ballSize * mass, ballSize * mass), velocity, mass, elasticity); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /hermesTest/physicsTest/GravityInteractorTest.java: -------------------------------------------------------------------------------- 1 | package hermesTest.physicsTest; 2 | 3 | import hermes.hshape.HRectangle; 4 | import hermes.physics.GravityInteractor; 5 | import hermes.physics.MassedBeing; 6 | 7 | import org.junit.*; 8 | 9 | import static hermes.HermesMath.*; 10 | import static org.junit.Assert.*; 11 | import processing.core.PVector; 12 | 13 | public class GravityInteractorTest { 14 | 15 | public class TestBeing extends MassedBeing { 16 | 17 | public TestBeing(PVector position, PVector velocity, float mass) { 18 | super(new HRectangle(position, 4, 4), velocity, mass, 1); 19 | } 20 | 21 | public void draw() {} 22 | 23 | } 24 | 25 | TestBeing being1, being2, being3; 26 | GravityInteractor inter; 27 | 28 | @Before 29 | public void setup() { 30 | being1 = new TestBeing(zeroVector(), zeroVector(), 2); 31 | being2 = new TestBeing(makeVector(1, 1), zeroVector(), 1); 32 | being3 = new TestBeing(makeVector(0, 2.1), zeroVector(), 0.5f); 33 | inter = new GravityInteractor(1, 2); 34 | } 35 | 36 | @Test 37 | public void test_detect() { 38 | assertTrue(inter.detect(being1, being2)); 39 | assertFalse(inter.detect(being1, being1)); 40 | assertFalse(inter.detect(being1, being3)); 41 | assertTrue(inter.detect(being2, being3)); 42 | assertTrue(inter.detect(being3, being2)); 43 | } 44 | 45 | @Test 46 | public void test_handle() { 47 | inter.handle(being1, being2); 48 | assertEquals(being1.getForce().mag(), 1, 1e-6); 49 | assertEquals(being1.getForce().x, 1 / Math.sqrt(2), 1e-6); 50 | assertEquals(being1.getForce().y, 1 / Math.sqrt(2), 1e-6); 51 | assertEquals(being2.getForce().mag(), 1, 1e-6); 52 | assertEquals(being2.getForce().x, -1 / Math.sqrt(2), 1e-6); 53 | assertEquals(being2.getForce().y, -1 / Math.sqrt(2), 1e-6); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /examples/PlatformExplorer/PlatformGroup.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Generates and stores the platforms in the game 3 | */ 4 | class PlatformGroup extends Group { 5 | 6 | PlatformGroup(World world) { 7 | super(world); 8 | 9 | } 10 | 11 | /** 12 | * adds a platform at given center and width 13 | */ 14 | void addPlatform(PVector center, float width) { 15 | Platform platform = new Platform(center, width); 16 | world.register(platform, false); 17 | this.add(platform); 18 | } 19 | 20 | /** 21 | * fills an area with randomly-placed platforms 22 | */ 23 | void generatePlatforms(HRectangle area, float verticalStep, float density) { 24 | float boxWidth = area.getWidth(); 25 | float boxHeight = area.getHeight(); 26 | float maxPlatWidth = boxWidth / 2; 27 | float minPlatWidth = Platform.HEIGHT * 2; 28 | // iterate through rows 29 | for(float y = area.getAbsMin().y + verticalStep; y <= area.getAbsMax().y - verticalStep; y += verticalStep) { 30 | float x = area.getAbsMin().x; // start at the left end of the rect 31 | float platWidth = random(minPlatWidth, maxPlatWidth); // width of the new platform 32 | float baseDist = random(minPlatWidth, maxPlatWidth); // distance before the new platform 33 | x += platWidth / 2 + baseDist / density; // move to the midpoint of the new platform 34 | while(x < area.getAbsMax().x - platWidth / 2) { // keep going right until a platform would leave the rect 35 | addPlatform(new PVector(x, y, 0), platWidth); 36 | x += platWidth / 2; 37 | platWidth = random(minPlatWidth, maxPlatWidth); 38 | baseDist = random(minPlatWidth, maxPlatWidth); 39 | x += platWidth / 2 + baseDist / density; 40 | } 41 | } 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /examples/Level0/HelperFunctions.pde: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////// 2 | // HELPER METHODS 3 | /////////////////////////////////////////////////// 4 | 5 | /** 6 | * Changing mode of game between BUILD, RUN, and COMPLETED 7 | */ 8 | void setMode(int newMode) { 9 | if(newMode == BUILD) { 10 | if(bubbleGroup!=null) {//delete the bubbles 11 | bubbleGroup.destroy(); 12 | } 13 | if(ball!=null) { //delete the ball 14 | world.delete(ball); 15 | ball = null; 16 | } 17 | if(goal!=null) { // delete the goal 18 | world.delete(goal); 19 | goal = null; 20 | } 21 | } else if(newMode == RUN) { 22 | //clean up global vars 23 | templateTool = null; 24 | dragTool = null; 25 | selectedTool = null; 26 | 27 | makeBubbles();//make the bubbles (note: I put this before make the ball so that ball will be drawn overtop) 28 | ball = new Ball(); //make the ball 29 | world.addToGroup(ball, ballGroup); 30 | goal = new Goal(); //make the goal 31 | world.addToGroup(goal, goalGroup); 32 | } else if(newMode == COMPLETED) { 33 | //TODO: fill in 34 | if(ball!=null) { 35 | world.delete(ball); 36 | ball = null; 37 | } 38 | } 39 | 40 | mode = newMode; 41 | } 42 | 43 | /** 44 | * Factory helper method for filling canvas with bubbles 45 | */ 46 | void makeBubbles() { 47 | Cell[][] grid = canvas.getGrid(); 48 | for(int i=0; i sectors; // all the sectors, hashed by a number derived from their coordinates 9 | Sector currentSector; // the current sector containing the character 10 | 11 | PlatformGroup platformGroup; // group to add platforms to 12 | 13 | // sets up the grid with a starting sector 14 | SectorGrid(Sector first, PlatformGroup platforms) { 15 | super(first.rectangle); 16 | sectors = new Hashtable(); 17 | currentSector = first; 18 | platformGroup = platforms; 19 | sectors.put(packCoors(first.x, first.y), first); 20 | } 21 | 22 | // returns the sector where the camera is currently centered 23 | Sector getCurrentSector() { 24 | return currentSector; 25 | } 26 | 27 | // changes the current sector where the camera is centered 28 | void setCurrentSector(Sector sector) { 29 | currentSector = sector; 30 | } 31 | 32 | // used for generating the hash code for sectors 33 | Integer packCoors(int x, int y) { 34 | return new Integer(x ^ y << 16); 35 | } 36 | 37 | // gets the sector at the given x,y sector coordinates 38 | // if the sector is not yet in the hastable, it is generated and populated 39 | Sector getSector(int x, int y) { 40 | Integer coors = packCoors(x, y); 41 | Sector sector = sectors.get(coors); // retrieve the sector 42 | if(sector == null) { // if it does not yet exist, generate it 43 | sector = new Sector(x, y, platformGroup, random(0.5, 1)); // generate with random density 44 | sectors.put(coors, sector); // add the new sector to the hastable 45 | } 46 | return sector; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /hermes/postoffice/KeyMessage.java: -------------------------------------------------------------------------------- 1 | package hermes.postoffice; 2 | 3 | /** 4 | * Message representing a key stroke. 5 | * The type of the key message is determined by the key it corresponds to. 6 | */ 7 | public class KeyMessage implements Message { 8 | 9 | //key code of message 10 | private int _keyCode; 11 | 12 | //Char contained by message 13 | private char _keyChar; 14 | 15 | //Boolean indicating if key is pressed or released 16 | private boolean _isPressed; 17 | 18 | /** 19 | * Creates a new KeyMessage. 20 | * @param keyCode int corresponding to key, use PostOffice constants to determine key 21 | * @param keyChar char corresponding to key, PostOffice.CHAR_UNDEFINED if key is non-unicode 22 | * @param isPressed true when key is pressed, false when key is released 23 | */ 24 | public KeyMessage(int keyCode, char keyChar, boolean isPressed) { 25 | _keyCode = keyCode; 26 | _keyChar = keyChar; 27 | _isPressed = isPressed; 28 | } 29 | 30 | /** 31 | * Gets code listed in POConstants corresponding to key 32 | * @return integer corresponding to key 33 | */ 34 | public int getKeyCode() { 35 | return _keyCode; 36 | } 37 | 38 | /** 39 | * Gets character representing the key acted upon if key is unicode. 40 | * @return char representing the key acted upon if key is unicode 41 | */ 42 | public char getKeyChar() { 43 | assert _keyChar != POCodes.Key.CHAR_UNDEFINED : "KeyMessage.getKeyChar() - Cannot get char of non-unicode key"; 44 | return _keyChar; 45 | } 46 | 47 | /** 48 | * Tells if key is pressed or not. 49 | * @return true is key is pressed, false if key is released 50 | */ 51 | public boolean isPressed() { 52 | return _isPressed; 53 | } 54 | 55 | /** 56 | * Checks for equality of KeyMessage by comparing key codes. 57 | */ 58 | public boolean equals(Object o) { 59 | if(o instanceof KeyMessage) { 60 | KeyMessage m = (KeyMessage) o; 61 | return this.getKeyCode() == m.getKeyCode(); 62 | } 63 | return false; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /examples/Level0/RunButton.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * The user pushes the RunButton to run the simulation, changing mode to RUN 3 | */ 4 | class RunButton extends Being { 5 | 6 | PostOffice _po; 7 | float runButtonDiameter = runButtonRadius*2; 8 | float innerSymbolLength = runButtonDiameter/3; 9 | boolean _hover = false; 10 | boolean _keyPressed = false; 11 | 12 | RunButton(PostOffice po) { 13 | super(new HCircle(new PVector(runButtonCenterX, runButtonCenterY), runButtonRadius)); 14 | _po = po; 15 | } 16 | 17 | /** 18 | * Switches run mode on key press or mouse press 19 | */ 20 | void receive(KeyMessage m) { 21 | if(m.isPressed() && !_keyPressed) { 22 | if(mode == BUILD) { 23 | setMode(RUN); 24 | } 25 | else { 26 | setMode(BUILD); 27 | } 28 | _keyPressed = true; 29 | } 30 | else { 31 | _keyPressed = false; 32 | } 33 | } 34 | 35 | void receive(MouseMessage m) { 36 | _hover = true; 37 | if(m.getAction() == POCodes.Click.PRESSED) { 38 | //switch modes 39 | if(mode == BUILD) { 40 | setMode(RUN); 41 | } else if(mode == RUN || mode == COMPLETED) { 42 | setMode(BUILD); 43 | } 44 | } 45 | } 46 | 47 | boolean getHover() {return _hover;} 48 | void setHover(boolean hover) {_hover = hover;} 49 | 50 | void draw() { 51 | strokeWeight(3); 52 | stroke(62, 67, 71); 53 | if(_hover) { //Add light highlighting when mouse is hovering over button 54 | fill(buttonHover); 55 | _hover = _po.isMouseInRegion(this.getShape()); 56 | } else { 57 | fill(bgColor); 58 | } 59 | ellipse(0, 0, runButtonDiameter, runButtonDiameter); 60 | fill(62, 67, 71); 61 | //Draw inner symbol 62 | if(mode == BUILD) { 63 | triangle(-runButtonRadius/3.5+3, -runButtonRadius/3.5, runButtonRadius/4+3, 0, -runButtonRadius/3.5+3, runButtonRadius/3.5); 64 | } else { 65 | rect(-innerSymbolLength/2, -innerSymbolLength/2, innerSymbolLength, innerSymbolLength); 66 | } 67 | } 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /hermesTest/physicsTest/PhysicsTest.java: -------------------------------------------------------------------------------- 1 | package hermesTest.physicsTest; 2 | 3 | import static hermes.HermesMath.*; 4 | import static org.junit.Assert.*; 5 | import hermes.hshape.HRectangle; 6 | import hermes.physics.*; 7 | 8 | import org.junit.*; 9 | import processing.core.*; 10 | 11 | public class PhysicsTest { 12 | 13 | class MyBeing extends MassedBeing { 14 | 15 | public MyBeing(float mass, float elasticity) { 16 | super(new HRectangle(zeroVector(),1.0f,1.0f), 17 | zeroVector(), mass, elasticity); 18 | } 19 | 20 | public void draw() {} 21 | 22 | } 23 | 24 | @Test 25 | public void test_calculateImpulse() { 26 | MassedBeing being1 = new MyBeing(1.0f, 1.0f); 27 | MassedBeing being2 = new MyBeing(1.0f, 1.0f); 28 | 29 | // very simple case (body hits body of equal mass at rest, transferring velocity) 30 | being1.setPosition(zeroVector()); 31 | being1.setVelocity(zeroVector()); 32 | being2.setPosition(makeVector(-1.0f,0.0f)); 33 | being2.setVelocity(makeVector(1.0f,0.0f)); 34 | PVector impulse = Physics.calculateImpulse(being1, being2, 1.0f, makeVector(-1.0f,0.0f)); 35 | assertEquals(impulse.x, -1.0f, 1e-8); 36 | assertEquals(impulse.y, 0.0f, 1e-8); 37 | impulse = Physics.calculateImpulse(being2, being1, 1.0f, makeVector(-1.0f,0.0f)); 38 | assertEquals(impulse.x, 1.0f, 1e-8); 39 | assertEquals(impulse.y, 0.0f, 1e-8); 40 | impulse = Physics.calculateImpulse(being1, being2, 1.0f, makeVector(-10.0f,0.0f)); 41 | assertEquals(impulse.x, -1.0f, 1e-8); 42 | assertEquals(impulse.y, 0.0f, 1e-8); 43 | 44 | // beings of differing mass 45 | being1.setMass(2.0f); 46 | impulse = Physics.calculateImpulse(being1, being2, 1.0f, makeVector(-1.0f,0.0f)); 47 | assertEquals(impulse.x, -4.0 / 3.0, 1e-5); 48 | assertEquals(impulse.y, 0.0f, 1e-5); 49 | 50 | // relativity! 51 | being1.setVelocity(makeVector(1,0)); 52 | being2.setVelocity(makeVector(2,0)); 53 | impulse = Physics.calculateImpulse(being1, being2, 1.0f, makeVector(-1.0f,0.0f)); 54 | assertEquals(impulse.x, -4.0 / 3.0, 1e-5); 55 | assertEquals(impulse.y, 0.0f, 1e-5); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /hermes/physics/GenericMassedCollider.java: -------------------------------------------------------------------------------- 1 | package hermes.physics; 2 | 3 | import hermes.*; 4 | import processing.core.PVector; 5 | 6 | /** 7 | *

8 | * This is a collision handler for MassedBeing and its subclasses which handles collisions 9 | * using both projection (instantly separating the bodies) and impulse (changing their velocities). 10 | *

11 | * Use GenericMassedCollider, as opposed to MassedCollider, only if you intend to override the behavior 12 | * and methods specific to a subclass of MassedBeing. 13 | * 14 | * @param the first type of MassedBeing 15 | * @param the second type of MassedBeing 16 | */ 17 | public class GenericMassedCollider extends Interactor { 18 | 19 | private Float _elasticity = null; 20 | 21 | /** 22 | *

23 | * Default MassedCollider constructor. In this case, collision elasticity will be the average of the colliding being elasticities. 24 | */ 25 | public GenericMassedCollider() { 26 | super(false, true); 27 | } 28 | 29 | /** 30 | * Creates a MassedCollider with specified collision elasticity. 31 | * @param elasticity collision elasticity 32 | */ 33 | public GenericMassedCollider(float elasticity) { 34 | super(); 35 | _elasticity = new Float(elasticity); 36 | } 37 | 38 | public boolean detect(A being1, B being2) { 39 | // find the projection vector between the beings 40 | PVector projection = being1.getShape().projectionVector(being2.getShape()); 41 | if(projection == null) 42 | return false; // if they aren't colliding 43 | // store the collision 44 | if(_elasticity == null) 45 | MassedBeing.addImpulseCollision(being1, being2, projection); 46 | else 47 | MassedBeing.addImpulseCollision(being1, being2, projection, _elasticity); 48 | return true; 49 | } 50 | 51 | public void handle(A being1, B being2) { 52 | ImpulseCollision collision = being1.getImpulseCollisionWith(being2); 53 | collision.applyImpulses(); 54 | collision.applyDisplacement(); 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /hermesTest/animationTests/AnimationJUnitTests.java: -------------------------------------------------------------------------------- 1 | package hermesTest.animationTests; 2 | //package src.hermesTest.animationTests; 3 | //import org.junit.After; 4 | //import org.junit.Before; 5 | //import org.junit.Test; 6 | // 7 | //import processing.core.*; 8 | //import src.hermes.Hermes; 9 | //import src.hermes.animation.Tileset; 10 | // 11 | //import static org.junit.Assert.*; 12 | // 13 | //public class AnimationJUnitTests { 14 | // 15 | // testerProcessingSketch _sketch; 16 | // 17 | // @Before 18 | // public void setUp() { 19 | // _sketch = new testerProcessingSketch(); 20 | // Hermes.setPApplet(_sketch); 21 | // } 22 | // 23 | // 24 | // @After 25 | // public void tearDown() { 26 | // _sketch = null; 27 | // Hermes.setPApplet(null); 28 | // } 29 | // 30 | // 31 | // //create an image of 4 tiles, each 50(row) by 100(col) pixels 32 | // @Test 33 | // public void test_all() { 34 | // 35 | // //create the image 36 | // PImage img = _sketch.createImage(200, 100, _sketch.ARGB); 37 | // img.loadPixels(); 38 | // for(int i=0; iOptimizer, where you use 8 | * this class to handle the interactions for you. 9 | * 10 | * @author Sam 11 | * 12 | */ 13 | @SuppressWarnings({ "rawtypes" }) 14 | public class InteractionHandler { 15 | 16 | Interaction _interaction; 17 | LinkedList _detectedInteractionsQueue; 18 | 19 | /** 20 | * Constructs a new InteractionHandler. 21 | * @param interaction the interaction being handled 22 | * @param detectedInteractionsQ the detected interactions queue where non-immediate interactions are stored 23 | */ 24 | InteractionHandler(Interaction interaction, LinkedList detectedInteractionsQ) { 25 | _interaction = interaction; 26 | _detectedInteractionsQueue = detectedInteractionsQ; 27 | } 28 | 29 | /** 30 | * Checks if an interaction is detected between being1 and being2. 31 | * If the interaction is immediate, 32 | * synchronizes on the beings and handles the interaction, 33 | * otherwise adds a new DetectedInteraction object to the detectedInteractionsQueue. 34 | * @param being1 the first interacting Being 35 | * @param being2 the second interacting Being 36 | * @return whether an interaction was detected 37 | */ 38 | public boolean interactionHandler(A being1, B being2) { 39 | // see if an interaction was detected 40 | if(being1 != being2 && _interaction.getInteractor().detect(being1, being2)) { 41 | if(_interaction.getInteractor().appliedImmediately()) { // if immediate, handle it now 42 | synchronized(being1) { 43 | synchronized(being2) { 44 | _interaction.getInteractor().handle(being1, being2); 45 | } 46 | } 47 | } else {//if not immediate, queue detection to handle later 48 | _detectedInteractionsQueue.add(new DetectedInteraction(being1, being2, _interaction)); 49 | } 50 | return true; 51 | } 52 | return false; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /hermesTest/physicsTest/InsideMassedColliderTest.java: -------------------------------------------------------------------------------- 1 | package hermesTest.physicsTest; 2 | 3 | import hermes.hshape.*; 4 | import hermes.physics.*; 5 | import processing.core.PVector; 6 | 7 | import org.junit.*; 8 | 9 | import static hermes.HermesMath.*; 10 | import static org.junit.Assert.*; 11 | 12 | public class InsideMassedColliderTest { 13 | 14 | class TestBeing extends MassedBeing { 15 | 16 | public TestBeing(PVector position, float width, float height, float mass) { 17 | super(new HRectangle(position, width, height), zeroVector(), mass, 1); 18 | } 19 | 20 | public void draw() {} 21 | 22 | } 23 | 24 | TestBeing big; 25 | TestBeing small; 26 | InsideMassedCollider collider; 27 | 28 | @Before 29 | public void setup() { 30 | big = new TestBeing(zeroVector(), 10, 10, INFINITY); 31 | small = new TestBeing(zeroVector(), 3, 3, 1); 32 | collider = new InsideMassedCollider(); 33 | } 34 | 35 | @Test 36 | public void test_detect() { 37 | assertFalse(collider.detect(big, small)); 38 | small.setPosition(4,0); 39 | assertTrue(collider.detect(big, small)); 40 | small.setPosition(0,-3.6f); 41 | assertTrue(collider.detect(big, small)); 42 | small.setPosition(0, 6); 43 | assertTrue(collider.detect(big, small)); 44 | small.setPosition(10,10); 45 | assertFalse(collider.detect(big, small)); 46 | } 47 | 48 | @Test 49 | public void test_handle() { 50 | small.setPosition(4,0); 51 | small.setVelocity(makeVector(1,0)); 52 | collider.detect(big, small); 53 | collider.handle(big, small); 54 | assertEquals(small.getImpulse().x, -2, 1e-8); 55 | assertEquals(small.getDisplacement().x, -0.5, 1e-5); 56 | setup(); 57 | small.setPosition(0,-3.6f); 58 | small.setVelocity(makeVector(0,-1)); 59 | collider.detect(big, small); 60 | collider.handle(big, small); 61 | assertEquals(small.getImpulse().y, 2, 1e-5); 62 | assertEquals(small.getDisplacement().y, 0.1f, 1e-5); 63 | setup(); 64 | small.setPosition(0,6); 65 | small.setVelocity(makeVector(0,1)); 66 | collider.detect(big, small); 67 | collider.handle(big, small); 68 | assertEquals(small.getImpulse().y, -2, 1e-5); 69 | assertEquals(small.getDisplacement().y, -2.5f, 1e-5); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /examples/Level0/Cell.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Helper class for Canvas, ToolBox, defines a cell in the vector field 3 | * Location is defined by which cell of the 2D array they are stored in inside Canvas, ToolBox 4 | */ 5 | class Cell extends Being { 6 | 7 | PVector _flowDir; //Any normalized vector 8 | float _flowStr; //Cannot be negative or greater than flowMax 9 | Tool _tool; 10 | boolean _hover; 11 | //Keeps track of cell's location in grid 12 | int _i; 13 | int _j; 14 | 15 | Cell(PVector cellTopLeft, int i, int j) { 16 | super(new HRectangle(cellTopLeft, new PVector(cellSideLength, cellSideLength))); 17 | _flowDir = INIT_DIR; 18 | _flowStr = INIT_STR; 19 | _tool = null; 20 | _hover = false; 21 | _i = i; 22 | _j = j; 23 | } 24 | 25 | PVector getFlowDir() {return _flowDir;} 26 | void setFlowDir(PVector direction) {_flowDir = direction;} 27 | 28 | float getFlowStr() {return _flowStr;} 29 | void setFlowStr(float strength) {_flowStr = strength;} 30 | 31 | boolean hasTool() {return _tool != null;} 32 | void setTool(Tool tool) {_tool = tool;} 33 | Tool getTool() {return _tool;} 34 | 35 | boolean getHover() {return _hover;} 36 | void setHover(boolean hover) {_hover = hover;} 37 | 38 | int geti() {return _i;} 39 | int getj() {return _j;} 40 | 41 | void draw() { 42 | if(_hover && mode == BUILD) { 43 | fill(137, 148, 158); 44 | } 45 | else { 46 | noFill(); 47 | } 48 | //Draw cell square 49 | stroke(255); 50 | strokeWeight(2); 51 | rect(0, 0, cellSideLength, cellSideLength); 52 | if(mode == BUILD && (_i!=goali || _j!=goalj)) { // in BUILD mode 53 | if(!hasTool() && _i>=0 && _j>=0) { // the cell does not contain a tool draw the arrow 54 | //TODO: figure out algorithm for representing flow strength 55 | translate(cellSideLength/2, cellSideLength/2); 56 | float angle = HermesMath.angle(_flowDir); 57 | rotate(angle - PI/2); 58 | stroke(0,0,255,70); 59 | translate(0, cellSideLength/4); 60 | line(0, 0, 0, -cellSideLength/2); 61 | rotate(PI/4.0); 62 | line(0,0,0,-cellSideLength/4); 63 | rotate(3*PI/2.0); 64 | line(0,0,0,-cellSideLength/4); 65 | } 66 | } else if(mode == RUN) { // in RUN mode 67 | //TODO: fill in 68 | } 69 | } 70 | } 71 | 72 | 73 | -------------------------------------------------------------------------------- /hermes/postoffice/MouseMessage.java: -------------------------------------------------------------------------------- 1 | package hermes.postoffice; 2 | 3 | import processing.core.PVector; 4 | 5 | /** 6 | * Message representing mouse actions. 7 | *

8 | * Type of MouseMessage determined by the button pressed. 9 | *

10 | * Subscription to a mouse button gets you press, release, and drag events; 11 | * subscription to POConstants.Button.NO gets you mouse moved messages. 12 | */ 13 | public class MouseMessage implements Message { 14 | 15 | //Button clicked in message 16 | //Get buttons from MouseEvent, 1 is button 1, etc. 17 | //0 is no buttons 18 | private POCodes.Button _buttonClicked; 19 | //Type of action designated by message 20 | private POCodes.Click _actionType; 21 | //Locations 22 | private int _x, _y; 23 | 24 | /** 25 | * Creates a new MouseMessage. 26 | * @param buttonClicked 27 | * @param x 28 | * @param y 29 | */ 30 | public MouseMessage(POCodes.Button buttonClicked, POCodes.Click actionType, int x, int y) { 31 | _buttonClicked = buttonClicked; 32 | _actionType = actionType; 33 | _x = x; 34 | _y = y; 35 | } 36 | 37 | /** 38 | * Gets the button pressed on the mouse. 39 | * Use constants defined in POCodes.Button 40 | * to figure out what button has been pressed 41 | * @return button type, compare against POCodes.Button 42 | */ 43 | public POCodes.Button getButton() { 44 | return _buttonClicked; 45 | } 46 | /** 47 | * Gets the action designated by the message. 48 | * Compare w/ POCodes.Click to determined actions. 49 | * @return action type, compare against POCodes.Click 50 | */ 51 | public POCodes.Click getAction() { 52 | return _actionType; 53 | } 54 | 55 | /** 56 | * Gets the x location of the mouse 57 | * @return int corresponding to x location 58 | */ 59 | public int getX() { 60 | return _x; 61 | } 62 | 63 | /** 64 | * Gets the y location of the mouse 65 | * @return int corresponding to y location 66 | */ 67 | public int getY() { 68 | return _y; 69 | } 70 | 71 | /** 72 | * Gets position of mouse as PVector 73 | * @return PVector w/ x,y coordinates 74 | */ 75 | public PVector getPosition() { 76 | return new PVector(_x, _y); 77 | } 78 | 79 | /** 80 | * Checks for equality of MouseMessages by comparing mouse button. 81 | */ 82 | public boolean equals(Object o) { 83 | if(o instanceof MouseMessage) { 84 | MouseMessage m = (MouseMessage) o; 85 | return m.getButton() == this.getButton(); 86 | } 87 | return false; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /examples/Level0/LevelWorld.pde: -------------------------------------------------------------------------------- 1 | public class LevelWorld extends World { 2 | 3 | public LevelWorld() { 4 | super(); 5 | } 6 | 7 | public void setup() { 8 | //instantiate groups 9 | ballGroup = new Group(world); 10 | goalGroup = new Group(world); 11 | toolGroup = new Group(world); 12 | canvasGroup = new Group(world); 13 | bubbleGroup = new Group(world); 14 | 15 | //containers 16 | canvas = new Canvas(_postOffice); 17 | println("canvas created"); 18 | register(canvas, true); 19 | addToGroup(canvas, canvasGroup); 20 | _postOffice.subscribe(canvas,Button.NO,canvas.getShape()); 21 | _postOffice.subscribe(canvas,Button.LEFT,canvas.getShape()); 22 | toolBox = new ToolBox(canvas); 23 | register(toolBox, false); 24 | _postOffice.subscribe(toolBox,Button.NO); 25 | _postOffice.subscribe(toolBox,Button.LEFT); 26 | 27 | //buttons 28 | RunButton runButton = new RunButton(_postOffice); 29 | register(runButton, true); 30 | _postOffice.subscribe(runButton,Key.R); 31 | _postOffice.subscribe(runButton,Button.NO,runButton.getShape()); 32 | _postOffice.subscribe(runButton,Button.LEFT,runButton.getShape()); 33 | RandomButton randomButton = new RandomButton(canvas,_postOffice); 34 | register(randomButton,false); 35 | _postOffice.subscribe(randomButton,Key.SPACE); 36 | _postOffice.subscribe(randomButton,Button.NO,randomButton.getShape()); 37 | _postOffice.subscribe(randomButton,Button.LEFT,randomButton.getShape()); 38 | 39 | //key for SelectedToolAttributeSwitcher 40 | SelectedToolAttributeSwitcher selectedToolAttributeSwitcher = new SelectedToolAttributeSwitcher(); 41 | _postOffice.subscribe(selectedToolAttributeSwitcher,Key.E); 42 | _postOffice.subscribe(selectedToolAttributeSwitcher,Key.W); 43 | 44 | //make the mousehandler and register subscriptions with the postoffice 45 | /*MouseHandler mouseHandler = new MouseHandler(canvas, toolBox, runButton, randomButton); 46 | _postOffice.subscribe(mouseHandler, LEFT_BUTTON); 47 | _postOffice.subscribe(mouseHandler, NO_BUTTON);*/ 48 | 49 | //register interactions 50 | register(canvasGroup, ballGroup, new InsideMassedCollider()); 51 | register(canvasGroup, bubbleGroup, new InsideMassedCollider()); 52 | register(ballGroup, goalGroup, new BallGoalCollider()); 53 | register(toolGroup, ballGroup, new MassedCollider()); 54 | register(toolGroup, bubbleGroup, new MassedCollider()); 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /examples/Level0/Level0.pde: -------------------------------------------------------------------------------- 1 | /** 2 | Level0 is a prototype level-editor 3 | for exploring the use of arbitrary force/vector fields in physics-based games. 4 | 5 | The goal of the game is to get a red ball into the goal. 6 | 7 | To do so, the player will have to guide the ball 8 | through an electric field that pushes and pulls the ball in various directions. 9 | 10 | The grid on the bottom right represents the main playing field. 11 | The ball starts on the top right, and the goal is located on the bottom right. 12 | The arrows in each cell represent the direction of the force applied by the electric field at that location. 13 | 14 | The giant 'play' button will start and stop the ball's motion. 15 | When playing, small blue balls starting from the centers of all but the ball-start and goal cell 16 | will appear in order to help visualize the flow produced by the current electric field. 17 | To change the electric field, click the star button or hit 'space'. 18 | 19 | The grid on the bottom left is the "toolbox". 20 | There are four types of tools that can be placed in the game: 21 | - diamond 22 | - triangle 23 | - hexagon 24 | - circle 25 | - right trianle (wedge) 26 | To add a block to the playing field, first click on the tool to select it, 27 | then click on a grid cell in the playing field to add it. 28 | Clicking and dragging on the playing field will 'paint' the tool across the cells. 29 | To delete a tool from the playing field, simply drag it off the playing field and release. 30 | To rotate a tool, select it and press the 'w' key. 31 | To change the elasticity of the tool, select it and press the 'e' key. 32 | (Note: the clear elasticity adds speed to the ball on a bounce, 33 | potentially causing it to move too fast for the physics engine to handle! Use alternate elasticities with caution) 34 | **/ 35 | 36 | import hermes.physics.*; 37 | import hermes.animation.*; 38 | import hermes.hshape.*; 39 | import hermes.postoffice.*; 40 | import hermes.*; 41 | import static hermes.postoffice.POCodes.*; 42 | import java.util.LinkedList; 43 | 44 | /////////////////////////////////////////////////// 45 | // PAPPLET 46 | /////////////////////////////////////////////////// 47 | 48 | void setup() { 49 | size(600, 600); 50 | Hermes.setPApplet(this); 51 | 52 | world = new LevelWorld(); 53 | world.lockUpdateRate(50); 54 | 55 | rectMode(CORNER); 56 | setMode(BUILD); 57 | 58 | smooth(); 59 | 60 | world.start(); // gets the World thread running 61 | } 62 | 63 | void draw() { 64 | background(bgColor); 65 | world.draw(); // World handles drawing 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | # UTF-8 supported. 2 | 3 | # The name of your library as you want it formatted 4 | name = Hermes 5 | 6 | # List of authors. Links can be provided using the syntax [author name](url) 7 | authorList = Ryan Lester, Chris Novello, Sam Eilertsen, and Jen Kovnats 8 | 9 | # A web page for your library, NOT a direct link to where to download it 10 | url = http://rdlester.github.com/hermes 11 | 12 | # The category of your library, must be one (or many) of the following: 13 | # "3D" "Animation" "Compilations" "Data" 14 | # "Fabrication" "Geometry" "GUI" "Hardware" 15 | # "I/O" "Language" "Math" "Simulation" 16 | # "Sound" "Utilities" "Typography" "Video & Vision" 17 | # 18 | # If a value other than those listed is used, your library will listed as "Other." 19 | category = Simulation Animation Geometry Compilation 20 | 21 | # A short sentence (or fragment) to summarize the library's function. This will be 22 | # shown from inside the PDE when the library is being installed. Avoid repeating 23 | # the name of your library here. Also, avoid saying anything redundant like 24 | # mentioning that its a library. This should start with a capitalized letter, and 25 | # end with a period. 26 | sentence = Experimental game framework and engine for rapid prototyping of games and simulations. 27 | 28 | # Additional information suitable for the Processing website. The value of 29 | # 'sentence' always will be prepended, so you should start by writing the 30 | # second sentence here. If your library only works on certain operating systems, 31 | # mention it here. 32 | paragraph = Leverges the strengths of Object-Oriented Design and the ease of Processing to make it easy to bring game mechanics to life. Built-in OSC support makes it easy to enable communication between your games and external data sources. Also includes animation/sprite and physics libraries that can be used outside the framework. 33 | 34 | # Links in the 'sentence' and 'paragraph' attributes can be inserted using the 35 | # same syntax as for authors. That is, [here is a link to Processing](http://processing.org/) 36 | 37 | 38 | # A version number that increments once with each release. This 39 | # is used to compare different versions of the same library, and 40 | # check if an update is available. You should think of it as a 41 | # counter, counting the total number of releases you've had. 42 | version = 5 43 | 44 | # The version as the user will see it. If blank, the version attribute will be used here 45 | prettyVersion = 2.0 46 | -------------------------------------------------------------------------------- /hermes/physics/InverseSquareInteractor.java: -------------------------------------------------------------------------------- 1 | package hermes.physics; 2 | 3 | import static hermes.HermesMath.reverse; 4 | import hermes.*; 5 | import processing.core.PVector; 6 | 7 | /** 8 | *

9 | * A general inverse square-law force interactor. 10 | * Each being in the interaction will receive an equal and opposite force F = k * q1 * q2 / r^2 11 | * where k is a factor set in the constructor, q1 and q2 are determined by beingFactor 12 | * for the first and second beings respectively, and r is the distance between the beings. It can also be given a maximum range. 13 | * 14 | */ 15 | public abstract class InverseSquareInteractor extends Interactor { 16 | 17 | private float _maxRangeSquared; // the maximum interaction range 18 | private float _k; // the gravity constant 19 | 20 | /** 21 | * Sets up a ColoumbInteractor with a range limit. 22 | * @param factor the force constant factor (k in the Coloumb equation) 23 | * @param maxRange the maximum range of the interaction. Beings separated by a distance greater than this range will not interact. 24 | */ 25 | public InverseSquareInteractor(float factor, float maxRange) { 26 | _k = factor; 27 | _maxRangeSquared = maxRange * maxRange; 28 | } 29 | 30 | /** 31 | * Sets up a ColoumbInteractor with no range limit. 32 | * @param factor the force constant factor (k in the Coloumb equation) 33 | */ 34 | public InverseSquareInteractor(float factor) { 35 | _k = factor; 36 | _maxRangeSquared = Float.POSITIVE_INFINITY; 37 | } 38 | 39 | public boolean detect(MassedBeing being1, MassedBeing being2) { 40 | if(being1 == being2) // no self-interaction 41 | return false; 42 | PVector r = PVector.sub(being1.getPosition(), being2.getPosition()); 43 | float d_squared = r.dot(r); 44 | // check if the distance is within the maximum range 45 | return d_squared <= _maxRangeSquared && d_squared != 0; 46 | } 47 | 48 | public void handle(MassedBeing being1, MassedBeing being2) { 49 | // F = k * q1 * q2 / r^2 50 | PVector r = PVector.sub(being2.getPosition(), being1.getPosition()); 51 | double d_squared = (double)r.dot(r); 52 | double F = _k * beingFactor(being1) * beingFactor(being2) / d_squared; 53 | r.normalize(); 54 | PVector force = PVector.mult(r, (float)F); 55 | being2.addForce(force); 56 | being1.addForce(reverse(force)); 57 | } 58 | 59 | /** 60 | * The factor from each being used in the numerator of the Coloumb equation 61 | * for example, charge for an electric force or mass for gravity. 62 | * @param being a being 63 | * @return the being's factor 64 | */ 65 | abstract protected float beingFactor(MassedBeing being); 66 | 67 | } 68 | -------------------------------------------------------------------------------- /examples/tutorialD/GlitchySquare.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * An immobile flashing square --- DANGEROUS 3 | */ 4 | class GlitchySquare extends Being { 5 | static final int WIDTH = 50; 6 | static final int HEIGHT = 50; 7 | static final int SHAKE_STEP = 10; 8 | color _c; 9 | boolean _stroke; 10 | boolean _keyOn; 11 | 12 | // tracking direction 13 | boolean _up; 14 | boolean _right; 15 | boolean _down; 16 | boolean _left; 17 | 18 | GlitchySquare(int x, int y) { 19 | super(new HRectangle(x, y, WIDTH, HEIGHT)); 20 | _c = color(0, 0, 0); 21 | _stroke = false; 22 | _keyOn = false; 23 | } 24 | 25 | public void update() { 26 | if (!_keyOn) { 27 | _position.x += round(random(SHAKE_STEP * 2)) - SHAKE_STEP; 28 | } 29 | else { 30 | if (_up) { 31 | _position.y -= SHAKE_STEP; 32 | } 33 | if (_right) { 34 | _position.x += SHAKE_STEP; 35 | } 36 | if (_down) { 37 | _position.y += SHAKE_STEP; 38 | } 39 | if (_left) { 40 | _position.x -= SHAKE_STEP; 41 | } 42 | } 43 | _stroke = false; 44 | } 45 | 46 | public void draw() { 47 | fill(_c); 48 | if (_stroke) { 49 | stroke(255); 50 | } 51 | else { 52 | noStroke(); 53 | } 54 | _shape.draw(); 55 | } 56 | 57 | public void setColor(color c) { 58 | _c = c; 59 | } 60 | 61 | public void useKey() { 62 | _c = color(119, 205, 231); 63 | _keyOn = true; 64 | } 65 | 66 | public boolean usingKey() { 67 | return _keyOn; 68 | } 69 | 70 | public void drawStroke() { 71 | _stroke = true; 72 | } 73 | 74 | public color pickColor() { 75 | return color(int(random(256)), int(random(256)), int(random(256))); 76 | } 77 | 78 | public void receive(KeyMessage m) { 79 | int code = m.getKeyCode(); 80 | if (m.isPressed()) { 81 | if (code == POCodes.Key.UP) { 82 | _up = true; 83 | } 84 | else if (code == POCodes.Key.RIGHT) { 85 | _right = true; 86 | } 87 | else if (code == POCodes.Key.DOWN) { 88 | _down = true; 89 | } 90 | else if (code == POCodes.Key.LEFT) { 91 | _left = true; 92 | } 93 | } 94 | else { 95 | if (code == POCodes.Key.UP) { 96 | _up = false; 97 | } 98 | else if (code == POCodes.Key.RIGHT) { 99 | _right = false; 100 | } 101 | else if (code == POCodes.Key.DOWN) { 102 | _down = false; 103 | } 104 | else if (code == POCodes.Key.LEFT) { 105 | _left = false; 106 | } 107 | } 108 | } 109 | 110 | public void receive(MouseMessage m) { 111 | currentWorld.delete(this); 112 | } 113 | } 114 | 115 | -------------------------------------------------------------------------------- /examples/PlatformExplorer/Player.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents the player 3 | */ 4 | class Player extends MassedBeing { 5 | 6 | final static float PLAYER_WIDTH = 16; 7 | final static float PLAYER_HEIGHT = 36; 8 | final static float PLAYER_SPEED = 150; 9 | 10 | // constants used to indicate the direction the player is facing 11 | final static int FACING_LEFT = 1; 12 | final static int FACING_RIGHT = 2; 13 | 14 | int direction = FACING_RIGHT; // the direction the player is facing 15 | boolean jumped = false; // whether the player can jump 16 | 17 | AnimatedSprite sprite; 18 | int animIndex; 19 | 20 | Player(float x, float y) { 21 | super(new HRectangle(HermesMath.makeVector(x, y), PLAYER_WIDTH, PLAYER_HEIGHT), HermesMath.zeroVector(), 1.0f, 1.0f); 22 | 23 | // load the animated character walk cycle 24 | sprite = new AnimatedSprite(); 25 | Animation anim = new Animation("skeilert_walk_final", 1, 24, ".png", (int)(1000.0f / 24.0f)); 26 | animIndex = sprite.addAnimation(anim); 27 | sprite.setActiveAnimation(animIndex); 28 | sprite.pause(); 29 | } 30 | 31 | void draw() { 32 | scale(0.2); 33 | imageMode(CENTER); 34 | // if the character is facing left, invert the image 35 | if(direction == FACING_LEFT) { 36 | scale(-1,1); 37 | translate(20, 0); 38 | } 39 | image(sprite.animate(), 0, 0); // draw the current animation frame 40 | } 41 | 42 | // when this is called the player can jump again 43 | void resetJump() { 44 | jumped = false; 45 | } 46 | 47 | // we use update() to apply gravity 48 | void update() { 49 | addForce(new PVector(0, -GRAVITY * getMass(), 0)); 50 | if(abs(getVelocity().y) >= 5) 51 | sprite.pause(); 52 | } 53 | 54 | void receive(KeyMessage m) { 55 | int nKey = m.getKeyCode(); 56 | if(m.isPressed()) { // the player's movement is controlled by w/a/s/d or the arrows 57 | if(nKey == POCodes.Key.D || nKey == POCodes.Key.RIGHT) { 58 | getVelocity().x = PLAYER_SPEED; 59 | direction = FACING_RIGHT; 60 | } 61 | if(nKey == POCodes.Key.A || nKey == POCodes.Key.LEFT) { 62 | getVelocity().x = -PLAYER_SPEED; 63 | direction = FACING_LEFT; 64 | } 65 | if((nKey == POCodes.Key.W || nKey == POCodes.Key.UP) && !jumped) { 66 | addImpulse(new PVector(0, -PLAYER_SPEED, 0)); 67 | jumped = true; 68 | } 69 | if(nKey == POCodes.Key.S || nKey == POCodes.Key.DOWN) { 70 | getVelocity().y = 2*PLAYER_SPEED; 71 | } 72 | if(abs(getVelocity().y) <= 5) sprite.unpause(); 73 | } else { // when a key is released, we stop the player 74 | if(nKey == POCodes.Key.D || nKey == POCodes.Key.A || nKey == POCodes.Key.LEFT || nKey == POCodes.Key.RIGHT) { 75 | getVelocity().x = 0; 76 | sprite.pause(); 77 | } 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /hermes/animation/Tileset.java: -------------------------------------------------------------------------------- 1 | package hermes.animation; 2 | 3 | import hermes.Hermes; 4 | import processing.core.PImage; 5 | 6 | public class Tileset { 7 | 8 | private PImage _tileImage; //a tile set aka a tile map aka a sprite sheet 9 | private int _tileWidth; //width of tiles in the sheet, in pixels 10 | private int _tileHeight;//height of tiles in the sheet, in pixels 11 | 12 | private PImage[][] _slicedTiles; //we preprocess the images by cutting them up and storing them in this grid 13 | 14 | /** 15 | * 16 | * @param tileImage PImage of tileset to cut up 17 | * @param tileWidth width of tiles in the set, in pixels 18 | * @param tileHeight height of tiles in the set, in pixels 19 | */ 20 | @SuppressWarnings("static-access") 21 | public Tileset(PImage tileImage, int tileWidth, int tileHeight) { 22 | _tileImage = tileImage; 23 | _tileWidth = tileWidth; 24 | _tileHeight = tileHeight; 25 | 26 | //initialize array, figure out how many rows and cols (note: _slicedTiles[y][x]) 27 | _slicedTiles = new PImage[tileImage.height / _tileHeight][tileImage.width / _tileWidth]; 28 | 29 | //cut the image into slices 30 | for (int row = 0; row < _tileImage.height / _tileHeight; row++) { 31 | for (int col = 0; col < _tileImage.width / _tileWidth; col++) { 32 | //Create a new image slice that corresponds to the size of the tiles in this set 33 | PImage slice = Hermes.getPApplet().createImage(_tileWidth, _tileHeight, Hermes.getPApplet().ARGB); 34 | //Copy pixels from the originally sourced image 35 | slice.copy(_tileImage, col * _tileWidth, row * _tileHeight, _tileWidth, _tileHeight, 0, 0, _tileWidth, _tileHeight); 36 | 37 | _slicedTiles[row][col] = slice; //add the slice to our array of slices 38 | } 39 | } 40 | } 41 | 42 | /** 43 | * Get a single tile from the Tilemap 44 | * @param row row that the tile is in 45 | * @param col col that the tile is in 46 | * @return a PImage of the tile 47 | */ 48 | public PImage getTile (int row, int col) { 49 | return _slicedTiles[row][col]; 50 | } 51 | 52 | /** 53 | * Get the width of each tile 54 | * @return the width of each tile 55 | */ 56 | public int getTileWidth() { 57 | return _tileWidth; 58 | } 59 | 60 | /** 61 | * Get the height of each tile 62 | * @return the height of each tile 63 | */ 64 | public int getTileHeight() { 65 | return _tileHeight; 66 | } 67 | 68 | public int getNumberOfCols() { 69 | return _slicedTiles[0].length; 70 | } 71 | 72 | public int getNumberOfRows() { 73 | return _slicedTiles.length; 74 | } 75 | 76 | /** 77 | * Get a row of PImages from the Tileset 78 | *
Note: You might find this handy for building an Animation for an AnimatedSprite 79 | * @param row the index of the row to be returned 80 | * @return the row of PImages 81 | */ 82 | public PImage[] getRowOfTiles(int row) { 83 | return _slicedTiles[row]; 84 | } 85 | 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /examples/bouncingpolygonsdemo/bouncingpolygonsdemo.pde: -------------------------------------------------------------------------------- 1 | /* 2 | * Physics demo: 3 | * Click and release mouse to create shapes 4 | * Drag to give polygon velocity 5 | * Use the '1', '2', and '3' keys to choose shape 6 | * '1' = HRectangle, '2' = HCircle, '3' = HPolygon 7 | * Use the 'd' key to delete shapes 8 | * 9 | * OSC demo: 10 | * Open 'oscControl.pd' in puredata 11 | * Click the button attached to the 'connect' message 12 | * While this sketch is running, 13 | * use sliders to create polygons of different shapes and sizes 14 | */ 15 | 16 | //import src.template.library.*; 17 | import hermes.*; 18 | import hermes.hshape.*; 19 | import hermes.animation.*; 20 | import hermes.physics.*; 21 | import hermes.postoffice.*; 22 | import java.util.Random; 23 | import static hermes.HermesMath.*; 24 | import static hermes.postoffice.POCodes.*; 25 | 26 | ////////////// 27 | // Constants 28 | ////////////// 29 | 30 | //Engine parts 31 | World _world; 32 | PostOffice _postOffice; 33 | HCamera _camera; 34 | 35 | //Groups 36 | BallGroup _ballGroup; 37 | BoxGroup _boxGroup; 38 | 39 | //Tracking the mouse 40 | boolean _mousePressed; 41 | float _origX, _origY; 42 | float _dX, _dY; 43 | 44 | //Screen/World size 45 | final int WIDTH = 400; 46 | final int HEIGHT = 400; 47 | 48 | //Used in creating polygons, updated by OSC 49 | static int polyPoint = 3; //Number of points in PolyBalls 50 | static float polyRot = 0; //Amount to rotate new PolyBalls by 51 | 52 | //Constant determining ball size 53 | static final int ballSize = 25; //Size of ball, scaled by ball mass 54 | 55 | //Used for creating different shapes 56 | static int mode = 0; //Mode dictating which type of ball will get created 57 | static final int POLY_MODE = 0; 58 | static final int POLY_KEY = Key.VK_1; 59 | static final int CIRCLE_MODE = 1; 60 | static final int CIRCLE_KEY = Key.VK_2; 61 | static final int RECT_MODE = 2; 62 | static final int RECT_KEY = Key.VK_3; 63 | static final int DELETE_KEY = Key.D; 64 | 65 | // instructions 66 | String instruct = "Click and drag!"; 67 | 68 | void setup() { 69 | size(WIDTH, HEIGHT); 70 | Hermes.setPApplet(this); 71 | 72 | //Set up the engine 73 | _camera = new HCamera(); 74 | try { 75 | _postOffice = new PostOffice(8080, 8000); 76 | } catch(Exception e) { 77 | _postOffice = new PostOffice(); 78 | } 79 | _world = new DemoWorld(_postOffice, _camera); 80 | _world.lockUpdateRate(50); 81 | 82 | smooth(); 83 | 84 | _world.start(); // gets the World thread running 85 | } 86 | 87 | void draw() { 88 | background(230); //Overwrite what's already been drawn 89 | 90 | if(_mousePressed) { 91 | //Draw line indicating velocity of created ball 92 | line(_origX, _origY, _dX, _dY); 93 | } 94 | 95 | _camera.draw(); //Camera object handles drawing all the appropriate Beings 96 | 97 | // draw instructions 98 | fill(0); 99 | text(instruct, 10, 20); 100 | } 101 | -------------------------------------------------------------------------------- /examples/bouncingpolygonsdemo/BallGroup.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Holds and keeps track of balls 3 | * Handles messages and the creation of new balls 4 | */ 5 | class BallGroup extends Group { 6 | 7 | //Used in creating balls, updated via OSC 8 | float _newMass = 1; 9 | float _newElasticity = 1; 10 | 11 | BallGroup(World world) { 12 | super(world); 13 | _mousePressed = false; 14 | } 15 | 16 | //Updates ball creation mode depending on key press 17 | void receive(KeyMessage m) { 18 | int key = m.getKeyCode(); 19 | switch(key) { 20 | case POLY_KEY: 21 | mode = POLY_MODE; 22 | break; 23 | case CIRCLE_KEY: 24 | mode = CIRCLE_MODE; 25 | break; 26 | case RECT_KEY: 27 | mode = RECT_MODE; 28 | break; 29 | case DELETE_KEY: 30 | destroy(); 31 | break; 32 | } 33 | } 34 | 35 | //Handles mouse messages for line drawing and ball creation 36 | void receive(MouseMessage m) { 37 | POCodes.Click action = m.getAction(); 38 | switch(action) { 39 | case PRESSED: //Register mouse press and initialize variables 40 | if(!_mousePressed) { 41 | _mousePressed = true; 42 | _origX = m.getX(); 43 | _origY = m.getY(); 44 | _dX = m.getX(); 45 | _dY = m.getY(); 46 | } 47 | break; 48 | case DRAGGED: //Update mouse location 49 | _dX = m.getX(); 50 | _dY = m.getY(); 51 | break; 52 | case RELEASED: //Deregister mouse press and create new ball 53 | _mousePressed = false; 54 | Ball ball; 55 | switch(mode) { 56 | case POLY_MODE: 57 | ball = new PolyBall(new PVector(_origX, _origY), new PVector(_origX-_dX, _origY-_dY), _newMass, _newElasticity); 58 | break; 59 | case CIRCLE_MODE: 60 | ball = new HCircleBall(new PVector(_origX, _origY), new PVector(_origX-_dX, _origY-_dY), _newMass, _newElasticity); 61 | break; 62 | case RECT_MODE: 63 | ball = new RectBall(new PVector(_origX, _origY), new PVector(_origX-_dX, _origY-_dY), _newMass, _newElasticity); 64 | break; 65 | default: 66 | System.out.println("In an invalid mode"); 67 | ball = new HCircleBall(new PVector(_origX, _origY), new PVector(_origX-_dX, _origY-_dY), _newMass, _newElasticity); 68 | break; 69 | } 70 | getWorld().register(ball); 71 | this.add(ball); 72 | break; 73 | } 74 | } 75 | 76 | //Updates variables influencing ball creation 77 | void receive(OscMessage m) { 78 | String[] messages = m.getAddress().split("/"); 79 | if(messages[1].equals("BouncingBalls")) { 80 | if(messages[2].equals("SetMass")) { 81 | _newMass = constrain(m.getAndRemoveFloat(), 0, 1); 82 | } 83 | else if(messages[2].equals("SetElasticity")) { 84 | _newElasticity = constrain(m.getAndRemoveFloat(), 0, 1); 85 | } 86 | else if(messages[2].equals("SetSides")) { 87 | polyPoint = (int) m.getAndRemoveInt(); 88 | } 89 | else if(messages[2].equals("SetRotate")) { 90 | polyRot = constrain(m.getAndRemoveFloat(), 0, 2*PI); 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /hermesTest/core/BeingTest.java: -------------------------------------------------------------------------------- 1 | package hermesTest.core; 2 | 3 | import hermes.*; 4 | import hermes.hshape.*; 5 | 6 | import org.junit.*; 7 | 8 | import processing.core.PApplet; 9 | import processing.core.PGraphics; 10 | 11 | import static hermes.HermesMath.*; 12 | import static org.junit.Assert.*; 13 | 14 | /** 15 | * test class for Hermes.Being 16 | * extends Being for protected method access 17 | * @author Sam 18 | * 19 | */ 20 | public class BeingTest { 21 | 22 | public class TestBeing extends Being { 23 | 24 | public TestBeing() { 25 | super(new HRectangle(zeroVector(),1.0f,1.0f), zeroVector()); 26 | } 27 | 28 | public long updateTime() { 29 | return super.updateTime(); 30 | } 31 | 32 | public boolean processUpdate() { 33 | return super.processUpdate(); 34 | } 35 | 36 | public void step() { 37 | super.step(); 38 | } 39 | 40 | public void EulerIntegratePosition(double dt) { 41 | super.EulerIntegratePosition(dt); 42 | } 43 | 44 | public void draw() {} 45 | } 46 | 47 | @Before 48 | public void setup() { 49 | PApplet applet = new PApplet(); 50 | applet.g = new PGraphics(); 51 | Hermes.setPApplet(applet); 52 | applet.rectMode(PApplet.CENTER); 53 | } 54 | 55 | /** 56 | * tests Being.updateTime() 57 | */ 58 | @Test 59 | public void test_updateTime() { 60 | TestBeing being = new TestBeing(); 61 | being.updateTime(); 62 | try { 63 | Thread.sleep(10); 64 | } catch (InterruptedException e) { assertTrue(false); } 65 | long elapsed = being.updateTime(); 66 | assertEquals(elapsed, 1e7,1e6); // this COULD fail if the system is running slow 67 | } 68 | 69 | @Test 70 | public void test_EulerIntegratePosition() { 71 | TestBeing being = new TestBeing(); 72 | being.setVelocity(makeVector(0.5f,0.0f,0.0f)); 73 | being.EulerIntegratePosition(0.5); 74 | assertEquals(being.getPosition().x, 0.25f, 1e-8); 75 | assertEquals(being.getPosition().y, 0.0f, 1e-8); 76 | assertEquals(being.getPosition().z, 0.0f, 1e-8); 77 | } 78 | 79 | @Test 80 | public void test_step() { 81 | TestBeing being = new TestBeing(); 82 | being.setVelocity(makeVector(0.5f,0.0f,0.0f)); 83 | being.updateTime(); 84 | try { 85 | Thread.sleep(1000); 86 | } catch (InterruptedException e) { assertTrue(false); } 87 | being.step(); 88 | assertEquals(being.getPosition().x, 0.5f, 1e-2); 89 | assertEquals(being.getPosition().y, 0.0f, 1e-2); 90 | assertEquals(being.getPosition().z, 0.0f, 1e-2); 91 | } 92 | 93 | @Test 94 | public void test_processUpdate() { 95 | TestBeing being = new TestBeing(); 96 | being.setVelocity(makeVector(0.5f,0.0f,0.0f)); 97 | being.updateTime(); 98 | try { 99 | Thread.sleep(1000); 100 | } catch (InterruptedException e) { assertTrue(false); } 101 | being.processUpdate(); 102 | assertEquals(being.getPosition().x, 0.5f, 1e-2); 103 | assertEquals(being.getPosition().y, 0.0f, 1e-2); 104 | assertEquals(being.getPosition().z, 0.0f, 1e-2); 105 | } 106 | 107 | 108 | } 109 | -------------------------------------------------------------------------------- /hermes/physics/Physics.java: -------------------------------------------------------------------------------- 1 | package hermes.physics; 2 | 3 | import processing.core.PVector; 4 | 5 | /** 6 | * A utility class for physics. 7 | */ 8 | public class Physics { 9 | 10 | /** 11 | * Calculates the impulse from a collision between objects, without accounting for rotation. 12 | * @param v1 the velocity of the first object 13 | * @param v2 the velocity of the second object 14 | * @param m1 the mass of the first object 15 | * @param m2 the mass of the second object 16 | * @param elasticity the elasticity of collision, 1.0 is perfectly elastic 17 | * (objects will bounce, all energy is conserved), 0.0 is completely inelastic 18 | * (they will stop, no conservation of kinetic energy) 19 | * @param normal vector pointing from body 1 into body 2 along the axis of collision 20 | * does NOT need to be of unit length (use the projection vector) 21 | * @return the impulse from body 1 on body 2 (impulse from 2 on 1 is merely the reverse of this) 22 | * 23 | * this is calculated by the equation: 24 | *

25 | * -(1-e)(v2 - v1) . n
26 | * j = -----------------------------------
27 | * n . n(1/m1 + 1/m2)
28 | *

29 | * 30 | * where e is elasticity and n is the normal 31 | * 32 | */ 33 | public static PVector calculateImpulse(PVector v1, PVector v2, 34 | float m1, float m2, float elasticity, PVector normal) { 35 | assert v1 != null : "Physics.calculateImpulse: v1 must be a valid PVector"; 36 | assert v2 != null : "Physics.calculateImpulse: v2 must be a valid PVector"; 37 | assert normal != null : "Physics.calculateImpulse: normal must be a valid PVector"; 38 | assert !(normal.x == 0 && normal.y == 0) : "Physics.calculateImpulse: normal must be nonzero"; 39 | 40 | PVector numerator = PVector.sub(v2, v1); // calculate relative velocity 41 | numerator.mult(-1 - elasticity); // factor by elasticity 42 | float result = numerator.dot(normal); // find normal component 43 | result /= normal.dot(normal); // normalize 44 | result /= (1 / m1 + 1 / m2); // factor in mass 45 | 46 | return PVector.mult(normal, result); 47 | } 48 | 49 | /** 50 | * Calculates the impulse from a collision between Massed Beings, without accounting for rotation. 51 | * @param being1 the first being 52 | * @param being2 the second being 53 | * @param elasticity the elasticity of collision, 1.0 is perfectly elastic 54 | * (objects will bounce, all energy is conserved), 0.0 is completely inelastic 55 | * (they will stop, no conservation of kinetic energy) 56 | * @param normal vector pointing from being1 to being2 along the axis of collision 57 | * (use the projection vector between their shapes) 58 | * @return impulse from being1 on being2 59 | */ 60 | public static PVector calculateImpulse(MassedBeing being1, MassedBeing being2, 61 | float elasticity, PVector normal) { 62 | return calculateImpulse(being1.getVelocity(), being2.getVelocity(), 63 | being1.getMass(), being2.getMass(), elasticity, normal); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /hermesTest/core/OptimizerTest.java: -------------------------------------------------------------------------------- 1 | package hermesTest.core; 2 | 3 | import hermes.*; 4 | import hermes.hshape.*; 5 | import hermes.postoffice.PostOffice; 6 | 7 | import java.util.Random; 8 | import org.junit.*; 9 | 10 | import processing.core.PApplet; 11 | import processing.core.PGraphics; 12 | import static org.junit.Assert.*; 13 | 14 | 15 | public class OptimizerTest { 16 | 17 | static class OptTestBeing extends Being { 18 | 19 | static Random r = new Random(); 20 | 21 | int compares = 0; 22 | int handles = 0; 23 | 24 | OptTestBeing() { 25 | super(makeRect()); 26 | } 27 | 28 | private static HRectangle makeRect() { 29 | float x = r.nextFloat() * 100; 30 | float y = r.nextFloat() * 100; 31 | return new HRectangle(x, y, 1, 1); 32 | } 33 | 34 | } 35 | 36 | class OptTestInter extends Interactor { 37 | 38 | public boolean detect(OptTestBeing being1, OptTestBeing being2) { 39 | being1.compares++; 40 | being2.compares++; 41 | return true; 42 | } 43 | 44 | public void handle(OptTestBeing being1, OptTestBeing being2) { 45 | being1.handles++; 46 | being2.handles++; 47 | } 48 | 49 | } 50 | 51 | class OptTestCollider extends Collider { 52 | 53 | public void handle(OptTestBeing being1, OptTestBeing being2) {} 54 | 55 | } 56 | 57 | @Before 58 | public void setup() { 59 | PApplet applet = new PApplet(); 60 | applet.g = new PGraphics(); 61 | Hermes.setPApplet(applet); 62 | applet.rectMode(PApplet.CENTER); 63 | } 64 | 65 | @Test 66 | public void test_SelfInteractionOptimizer() { 67 | World world = new World(new PostOffice(), new HCamera()); 68 | Group group = new Group(world); 69 | for(int i = 0; i < 100; i++) { 70 | group.add(new OptTestBeing()); 71 | } 72 | world.update(); 73 | world.register(group, group, new OptTestInter(), 74 | new SelfInteractionOptimizer()); 75 | world.update(); 76 | for(OptTestBeing element : group.getObjects()) { 77 | assertEquals(element.compares, 99); 78 | assertEquals(element.handles, 99); 79 | } 80 | } 81 | 82 | @Test 83 | public void test_SelfInterOptPerformance() { 84 | World world = new World(new PostOffice(), new HCamera()); 85 | Group group = new Group(world); 86 | for(int i = 0; i < 10000; i++) { 87 | group.add(new OptTestBeing()); 88 | } 89 | world.update(); 90 | world.register(group, group, new OptTestInter()); 91 | long time = System.nanoTime(); 92 | world.update(); 93 | long elapsed = System.nanoTime() - time; 94 | System.out.println("Time for 10000 unoptimized rectangle collisions: " + elapsed); 95 | 96 | world = new World(new PostOffice(), new HCamera()); 97 | group.setWorld(world); 98 | world.register(group, group, new OptTestInter(), 99 | new SelfInteractionOptimizer()); 100 | time = System.nanoTime(); 101 | world.update(); 102 | elapsed = System.nanoTime() - time; 103 | System.out.println("Time for 10000 optimized rectangle collisions: " + elapsed); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /examples/BulletCurtain/OtherGroup.pde: -------------------------------------------------------------------------------- 1 | class OtherGroup extends Group { 2 | 3 | float spawnX; 4 | float spawnY; 5 | int animationIndexToUseOnSpawn = 0; 6 | 7 | float otherTravelSpeed = 1.0; 8 | float otherTravelSpeedMultiplier = 2; 9 | 10 | float bodyWidth, bodyHeight; 11 | 12 | 13 | OtherGroup(World world, float bodyWidth, float bodyHeight) { 14 | super(world); 15 | 16 | this.bodyWidth = bodyWidth; 17 | this.bodyHeight = bodyHeight; 18 | 19 | //Default spawn position (on the right side of the screen) 20 | spawnX = width; 21 | spawnY = height/2 - bodyHeight/2; 22 | } 23 | 24 | void receive(OscMessage message) { 25 | String[] msgSplit = message.getAddress().split("/"); 26 | 27 | if (msgSplit[1].equals(SYSTEM_NAME)) { 28 | if (msgSplit[2].equals("GenerateAnOther")) { 29 | if (message.hasRemainingArguments()) { 30 | if (message.getAndRemoveFloat() == 1.0) { 31 | Other other = new Other(spawnX, spawnY, bodyWidth, bodyHeight, createAnimatedSpriteForOther()); 32 | other.animatedSprite.setActiveAnimation(animationIndexToUseOnSpawn % other.animatedSprite.getNumberOfAnimations()); 33 | other.animatedSprite.overrideMillisecondsPerFrame(numberOfMillisecondsFramePlaysFor); 34 | other.howManyPixelsToTravel = otherTravelSpeed; 35 | add(other); 36 | 37 | world.register(other); 38 | } 39 | } 40 | } 41 | 42 | else if (msgSplit[2].equals("SetOtherSpawnX")) { 43 | if (message.hasRemainingArguments()) { 44 | 45 | float constrainedX = constrain(message.getAndRemoveFloat(), 0.0, 1.0); 46 | float remappedX = map(constrainedX, 0.0, 1.0, (width/3), width - BODY_WIDTH); 47 | spawnX = remappedX; 48 | } 49 | } 50 | 51 | 52 | else if (msgSplit[2].equals("SetOtherSpawnY")) { 53 | if (message.hasRemainingArguments()) { 54 | float constrainedY = constrain(message.getAndRemoveFloat(), 0.0, 1.0); 55 | float remappedY = map(constrainedY, 0.0, 1.0, 0.0, height - BODY_HEIGHT); 56 | spawnY = remappedY; 57 | } 58 | } 59 | 60 | else if (msgSplit[2].equals("SetOtherTravelSpeed")) { 61 | if (message.hasRemainingArguments()) { 62 | float travel = constrain(message.getAndRemoveFloat(), 0.0, 1.0); 63 | travel = map(travel, 0.0, 1.0, 1.0, 20); 64 | otherTravelSpeed = travel; 65 | } 66 | } 67 | 68 | else if (msgSplit[2].equals("NewAnimationForSpawnedOthers")) { 69 | if (message.hasRemainingArguments()) { 70 | if (message.getAndRemoveFloat() == 1.0) { 71 | animationIndexToUseOnSpawn++; 72 | } 73 | } 74 | } 75 | 76 | else if (msgSplit[2].equals("SetTravelMultiplierForAllOthers")) { 77 | if (message.hasRemainingArguments()) { 78 | if (message.getAndRemoveFloat() == 1.0) { 79 | float newMultiplier = constrain(message.getAndRemoveFloat(), 0.0, 1.0); 80 | newMultiplier = map(newMultiplier, 0.0, 1.0, 0.0, 5); 81 | otherTravelMultiplier = newMultiplier; 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /hermesTest/physicsTest/ImpulseCollisionTest.java: -------------------------------------------------------------------------------- 1 | package hermesTest.physicsTest; 2 | 3 | import hermes.hshape.*; 4 | import hermes.physics.*; 5 | import processing.core.PVector; 6 | import static hermes.HermesMath.*; 7 | import static org.junit.Assert.*; 8 | import org.junit.*; 9 | 10 | 11 | public class ImpulseCollisionTest { 12 | 13 | class MyBeing extends MassedBeing { 14 | 15 | public MyBeing(PVector pos, PVector vel, float mass, float elasticity) { 16 | super(new HCircle(pos, 1.0f), 17 | vel, mass, elasticity); 18 | } 19 | 20 | public void draw() {} 21 | 22 | } 23 | 24 | /** 25 | * tests ImpulseCollision.addImpulse(); 26 | */ 27 | @Test 28 | public void test_addImpulse() { 29 | MyBeing being1 = new MyBeing(zeroVector(), zeroVector(), 1.0f, 1.0f); 30 | MyBeing being2 = new MyBeing(zeroVector(), zeroVector(), 1.0f, 1.0f); 31 | ImpulseCollision collision = new ImpulseCollision(being1, being2, makeVector(1.0f,1.0f), 1.0f); 32 | collision.addImpulse(makeVector(1.0f,2.0f), being1); 33 | assertEquals(collision.getImpulse().x, 1.0f, 1e-8); 34 | assertEquals(collision.getImpulse().y, 2.0f, 1e-8); 35 | collision.addImpulse(makeVector(-1,0), being2); 36 | assertEquals(collision.getImpulse().x, 2.0f, 1e-8); 37 | assertEquals(collision.getImpulse().y, 2.0f, 1e-8); 38 | } 39 | 40 | /** 41 | * tests ImpulseCollision.applyProjection() 42 | */ 43 | @Test 44 | public void test_applyProjection() { 45 | MyBeing being1 = new MyBeing(zeroVector(), zeroVector(), 1, 1); 46 | MyBeing being2 = new MyBeing(makeVector(1, 0), zeroVector(), 1, 1); 47 | PVector projection = being1.getShape().projectionVector(being2.getShape()); 48 | assertEquals(projection.x, 1, 1e-8); 49 | assertEquals(projection.y, 0, 1e-8); 50 | ImpulseCollision collision = new ImpulseCollision(being1, being2, projection, 1.0f); 51 | collision.calculateDisplacement(); 52 | collision.applyDisplacement(); 53 | assertEquals(being1.getDisplacement().x, -0.5f, 1e-8); 54 | assertEquals(being2.getDisplacement().x, 0.5f, 1e-8); 55 | // check infinite masses 56 | being1 = new MyBeing(zeroVector(), zeroVector(), INFINITY, 1); 57 | being2 = new MyBeing(makeVector(1, 0), zeroVector(), 1, 1); 58 | collision = new ImpulseCollision(being1, being2, projection, 1.0f); 59 | collision.calculateDisplacement(); 60 | collision.applyDisplacement(); 61 | assertEquals(being1.getDisplacement().x, 0, 1e-8); 62 | assertEquals(being2.getDisplacement().x, 1, 1e-8); 63 | being1 = new MyBeing(zeroVector(), zeroVector(), 1, 1); 64 | being2 = new MyBeing(makeVector(1, 0), zeroVector(), INFINITY, 1); 65 | collision = new ImpulseCollision(being1, being2, projection, 1.0f); 66 | collision.calculateDisplacement(); 67 | collision.applyDisplacement(); 68 | assertEquals(being1.getDisplacement().x, -1, 1e-8); 69 | assertEquals(being2.getDisplacement().x, 0, 1e-8); 70 | being1 = new MyBeing(zeroVector(), zeroVector(), INFINITY, 1); 71 | being2 = new MyBeing(makeVector(1, 0), zeroVector(), INFINITY, 1); 72 | collision = new ImpulseCollision(being1, being2, projection, 1.0f); 73 | collision.calculateDisplacement(); 74 | collision.applyDisplacement(); 75 | assertEquals(being1.getDisplacement().x, -0.5f, 1e-8); 76 | assertEquals(being2.getDisplacement().x, 0.5f, 1e-8); 77 | 78 | } 79 | 80 | 81 | } 82 | -------------------------------------------------------------------------------- /hermes/HObject.java: -------------------------------------------------------------------------------- 1 | package hermes; 2 | 3 | import hermes.postoffice.*; 4 | import java.util.Iterator; 5 | import java.util.LinkedList; 6 | 7 | /** 8 | * Basic game object class. 9 | *

10 | * All parts of game are represented as HObjects. 11 | *

12 | * For most uses, extend the Being class 13 | *

14 | * Extend HObject directly when you want to represent something more abstract, 15 | * like game state. 16 | */ 17 | public abstract class HObject implements KeySubscriber, MouseSubscriber, MouseWheelSubscriber, OscSubscriber { 18 | 19 | private LinkedList _groups; // groups the being is a member of 20 | 21 | protected HObject() { 22 | _groups = new LinkedList(); 23 | } 24 | 25 | /** 26 | * adds the HObject to the group 27 | * @param group the group to add to 28 | */ 29 | @SuppressWarnings({ "unchecked", "rawtypes" }) 30 | protected void addToGroup(GenericGroup group) { 31 | // need to lock on the group 32 | synchronized(group) { 33 | group.getObjects().add(this); 34 | _groups.add(group); 35 | } 36 | } 37 | 38 | /** 39 | * removes the HObject from this group 40 | * @param group the group to remove from 41 | */ 42 | @SuppressWarnings("rawtypes") 43 | protected void removeFromGroup(GenericGroup group) { 44 | // need to lock on the group 45 | synchronized(group) { 46 | group.getObjects().remove(this); 47 | _groups.remove(group); 48 | } 49 | } 50 | 51 | /** 52 | * removes the HObject from all containing groups 53 | */ 54 | protected void delete() { 55 | // go through all the groups, deleting this being 56 | for(Iterator iter = _groups.iterator(); iter.hasNext(); ) { 57 | GenericGroup group = iter.next(); 58 | // need to lock on the group 59 | synchronized(group) { 60 | group.getObjects().remove(this); 61 | iter.remove(); 62 | } 63 | } 64 | } 65 | 66 | /** 67 | * Returns an iterator over all the groups the object is a member of. 68 | * @return iterators over the groups containing this object 69 | */ 70 | protected Iterator getGroups() { 71 | return _groups.iterator(); 72 | } 73 | 74 | /** 75 | * Used for multisampling -- if true the object needs to be sampled more on the current update. 76 | * @return whether the object needs more samples this update 77 | */ 78 | public boolean needsMoreSamples() { 79 | return false; 80 | } 81 | 82 | //Methods for receiving methods from PostOffice, defined in subscriber interfaces 83 | //Left blank here, must be overridden by user to add functionality 84 | /** 85 | * Override if you want your Being to handle Key messages 86 | */ 87 | public void receive(KeyMessage m) { 88 | //VOID 89 | } 90 | /** 91 | * Override if you want your Being to handle Mouse messages 92 | */ 93 | public void receive(MouseMessage m) { 94 | //VOID 95 | } 96 | /** 97 | * Override if you want your Being to handle Mouse Wheel messages 98 | */ 99 | public void receive(MouseWheelMessage m) { 100 | //VOID 101 | } 102 | /** 103 | * Override if you want your Being to handle OSC messages 104 | */ 105 | public void receive(OscMessage m) { 106 | //VOID 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /hermes/physics/InsideMassedCollider.java: -------------------------------------------------------------------------------- 1 | package hermes.physics; 2 | 3 | 4 | import hermes.*; 5 | import processing.core.PVector; 6 | import static hermes.HermesMath.*; 7 | 8 | /** 9 | *

10 | * A Collider that keeps a being's bounding box contained inside of another by projection and impulse collision. 11 | * If the smaller of the beings in the interaction travels hits the inside edge of the larger it will bounce back inside. 12 | *

13 | * This is a multisampled interaction and it is not applied immediately. (See Interactor documentation.) 14 | */ 15 | public class InsideMassedCollider extends Interactor { 16 | 17 | public InsideMassedCollider() { 18 | super(false,true); 19 | } 20 | 21 | public boolean detect(MassedBeing being1, MassedBeing being2) { 22 | //figure out which MassedBeing is smaller, which is bigger 23 | float height1 = being1.getBoundingBox().getMax().y - being1.getBoundingBox().getMin().y; 24 | float height2 = being2.getBoundingBox().getMax().y - being2.getBoundingBox().getMin().y; 25 | float width1 = being1.getBoundingBox().getMax().x - being1.getBoundingBox().getMin().x; 26 | float width2 = being2.getBoundingBox().getMax().x - being2.getBoundingBox().getMin().x; 27 | MassedBeing smallerBeing; 28 | MassedBeing biggerBeing; 29 | float smallerBeingHeight, smallerBeingWidth; 30 | if(width1>width2 && height1>height2) { 31 | biggerBeing = being1; 32 | smallerBeing = being2; 33 | smallerBeingHeight = height2; smallerBeingWidth = width2; 34 | } else if(width1 8 | * When the shape is linked to a being, shape's position should be a reference to the being's position. 9 | *

10 | * In order for collision to work, all shapes must know how to collide with all other shapes. 11 | */ 12 | public abstract class HShape { 13 | 14 | protected PVector _position; // the shape's position 15 | // all points in the shape are defined relative to the position 16 | // when a shape represents a Being's collision area, 17 | // this should be a reference to the Being's position 18 | 19 | /** 20 | * Creates a new shape. 21 | * When the shape represents a Being's collision area, 22 | * the shape's position should reference the being's position. 23 | * @param position the shape's position 24 | */ 25 | protected HShape(PVector position) { 26 | assert position != null : "In HShape constructor: position must be a valid PVector"; 27 | 28 | _position = position; 29 | } 30 | 31 | /** 32 | * Detects a collision with another shape 33 | * @param other the shape to collide with 34 | * @return whether the shapes have collided 35 | */ 36 | public boolean collide(HShape other) { 37 | assert other != null : "HShape.collide: other must be a valid HShape"; 38 | return projectionVector(other) != null; 39 | } 40 | 41 | /** 42 | * Finds the projection vector for a collision with another shape. 43 | *

44 | * The projection vector is the vector by which one body could be displaced such that 45 | * it no longer intersects the other body, by the shortest possible distance. 46 | *

47 | * Each shape must contain methods for collision with all other types of HShapes. 48 | * If you create your own type of shape, you will have to modify HShape 49 | * (as well as its subclasses) 50 | * to contain methods colliding it with the new type. 51 | * @param other the shape to collide with 52 | * @return the projection vector from this body to other, or null if they aren't colliding 53 | */ 54 | public abstract PVector projectionVector(HShape other); 55 | public abstract PVector projectionVector(HRectangle other); 56 | public abstract PVector projectionVector(HCircle other); 57 | public abstract PVector projectionVector(HPolygon other); 58 | 59 | /** 60 | * Whether the Shape contains the given point coordinates (boundary is inclusive). 61 | * @param point the point vector 62 | * @return true if (x,y) lies within the Shape 63 | */ 64 | public abstract boolean contains(PVector point); 65 | 66 | /** 67 | * @param x the x coordinate 68 | * @param y the y coordinate 69 | * @return true if (x,y) lies within the Shape 70 | */ 71 | public abstract boolean contains(float x, float y); 72 | 73 | /** 74 | * Finds the smallest Rectangle that encloses the shape. 75 | * @return the bounding box 76 | */ 77 | public abstract HRectangle getBoundingBox(); 78 | 79 | /** 80 | * Returns the shape's position. 81 | * Note, this is a reference, if you change this vector it will move the shape. 82 | * @return the shape's position 83 | */ 84 | public PVector getPosition() { 85 | return _position; 86 | } 87 | 88 | /** 89 | * Shortcut method for drawing the shape on screen 90 | */ 91 | public abstract void draw(); 92 | 93 | } 94 | 95 | -------------------------------------------------------------------------------- /examples/Level0/constants.pde: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////// 2 | // GLOBAL VARS AND CONSTANSTS 3 | /////////////////////////////////////////////////// 4 | 5 | World world; 6 | 7 | 8 | final int BUILD = 0; 9 | final int RUN = 1; 10 | final int COMPLETED = 2; 11 | int mode = BUILD; // 0 is setup; 1 is run 12 | 13 | //Container sizes and locations 14 | //Y location and size is same for both 15 | int containerHeight = 440; 16 | int containerTopY = 120; 17 | int containerBottomY = containerTopY + containerHeight; 18 | //Canvas X and width 19 | Canvas canvas = null; 20 | int canvasWidth = 360; 21 | int canvasLeftX = 30; 22 | int canvasRightX = canvasLeftX + canvasWidth; 23 | //Toolbox X and width 24 | ToolBox toolBox; 25 | int toolBoxWidth = 120; 26 | int toolBoxLeftX = 430; 27 | int toolBoxRightX = toolBoxLeftX + toolBoxWidth; 28 | 29 | int cellSideLength = 40; //gives us 9 across, 12 down in canvas ; 3 across, 12 down in toolbox 30 | 31 | //Frame size 32 | int frameWidth = 700; 33 | int frameHeight = 590; 34 | int bgColor = color(122, 131, 139); 35 | 36 | static final int PORT_IN = 8080; 37 | static final int PORT_OUT = 8000; 38 | 39 | //groups 40 | Group ballGroup = null; 41 | Group goalGroup = null; 42 | Group canvasGroup = null; 43 | Group toolGroup = null; 44 | Group bubbleGroup = null; 45 | 46 | /////////////////////////////////////////////////// 47 | // Button constants 48 | /////////////////////////////////////////////////// 49 | 50 | //button stuff 51 | int buttonHover = 240; 52 | 53 | //run button 54 | int starNum = 8; 55 | int runButtonRadius = cellSideLength; 56 | int runButtonCenterX = canvasLeftX + cellSideLength; 57 | int runButtonCenterY = containerTopY - 10 - runButtonRadius; 58 | 59 | //random button 60 | int randomButtonSide = cellSideLength; 61 | int randomButtonX = canvasRightX - 3*randomButtonSide/2; 62 | int randomButtonY = containerTopY - 10 - 3*randomButtonSide/2; 63 | 64 | /////////////////////////////////////////////////// 65 | // Container constants 66 | /////////////////////////////////////////////////// 67 | 68 | //Cell constants 69 | static final PVector INIT_DIR = new PVector(0,1); 70 | static final float INIT_STR = 1; 71 | float flowMax = 10; 72 | int canvasNumCellsX = canvasWidth / cellSideLength; 73 | int canvasNumCellsY = containerHeight / cellSideLength; 74 | int toolBoxNumCellsX = toolBoxWidth / cellSideLength; 75 | int toolBoxNumCellsY = containerHeight / cellSideLength; 76 | 77 | /////////////////////////////////////////////////// 78 | // Ball and Goal constants 79 | /////////////////////////////////////////////////// 80 | 81 | //ball 82 | Ball ball = null; 83 | int balli = 0; //index in canvas 84 | int ballj = 0; 85 | int ballRadius = 10; 86 | int ballMass = 100; 87 | int ballElasticity = 1; 88 | //goal 89 | Goal goal = null; 90 | int goali = 0; //index in canvas 91 | int goalj = canvasNumCellsY-1; 92 | 93 | /////////////////////////////////////////////////// 94 | // Tool constants 95 | /////////////////////////////////////////////////// 96 | 97 | //Constants defining the tools 98 | final int NOTOOL = 0; 99 | final int QUADRANGLE = 1; 100 | final int TRIANGLE = 2; 101 | final int HEXAGON = 3; 102 | final int CIRCLETOOL = 4; 103 | final int WEDGE = 5; 104 | final int PUNCHER = 6; //to be added 105 | final int BATON = 7; //to be added 106 | final int FUSE = 8; //to be added 107 | //Tool stored by dragging, used for placing tools on the board 108 | Tool templateTool = null; 109 | Tool dragTool = null; 110 | Tool selectedTool = null; 111 | int dragIniti = -1; // set to -1 when from toolbox, 112 | int dragInitj = -1; // real values when from canvas 113 | 114 | //tool elasticity 115 | final float SPRINGY = 1.5; //TODO: ??????set? 116 | final float PERFECT = 1; 117 | final float STICKY = 0.5; 118 | 119 | //cilia 120 | int ciliaNum = 16; //number of silia 121 | int csize = 3; //size of the silia 122 | 123 | 124 | -------------------------------------------------------------------------------- /hermes/Interactor.java: -------------------------------------------------------------------------------- 1 | package hermes; 2 | 3 | /** 4 | * An Interactor defines some conditional relationship between two HObjects. 5 | * It provides a method of detecting whether the objects are interacting, and 6 | * a method of executing/handling the resultant interaction. 7 | *

8 | * This is the primary means of writing 9 | * conditional logic into the World game loop. 10 | * 11 | * @param The first type of HObject in the interaction. 12 | * @param The second type of HObject in the interaction. 13 | */ 14 | public abstract class Interactor { 15 | 16 | private boolean _applyImmediate; // whether handle will be called upon detection, or deferred 17 | private boolean _multisample; // whether the Interactor will be multisampled 18 | 19 | /** 20 | * Constructs a new Interactor, setting whether the Interactor will be handled upon 21 | * detection or deferred, and whether the Interactor will be multisampled. Note that 22 | * these flags are only meaningful when the Interactor has been registered with a World. 23 | * @param applyImmediate If true, the Interactor will be applied by World as soon as 24 | * detect has returned true. If false, it will 25 | * be applied only when all possible interactions have been checked 26 | * for the step, for the entire World. 27 | * @param multisample If true, the Interactor will be checked for and applied 28 | * for each interacting object for each step, against all 29 | * all objects it may interact with. If false, it will be 30 | * checked for and applied only once per update of the loop, 31 | * on the first step. 32 | */ 33 | protected Interactor(boolean applyImmediate, boolean multisample) { 34 | _applyImmediate = applyImmediate; 35 | _multisample = multisample; 36 | } 37 | 38 | /** 39 | * Constructs a new Interactor using default settings. 40 | * It will be applied as soon as detect 41 | * has returned true, and will be applied on once per update (not multisampled). 42 | *

43 | * Use this constructor unless you understand multisampling and delayed application 44 | * and need it for a specific purpose. 45 | */ 46 | protected Interactor() { 47 | this(true, false); 48 | } 49 | 50 | /** 51 | * Whether the Interactor is applied upon detection, or deferred until all possible 52 | * interactions have been checked for the step. 53 | * @return If true, the World will call handle immediately upon 54 | * detect returning true. If false, it will wait until the 55 | * end of the loop, after all interactions have been checked. 56 | */ 57 | public boolean appliedImmediately() { 58 | return _applyImmediate; 59 | } 60 | 61 | /** 62 | * Whether the Interactor is checked for each step of the objects it is registered with, 63 | * or only once per update. 64 | * @return If true, for each object this Interactor is registered with, the World 65 | * will apply it for all the object's steps, against all objects the object could 66 | * interact with. If false, it will be checked only once per object per update, on 67 | * the object's first step. 68 | */ 69 | public boolean multisampled() { 70 | return _multisample; 71 | } 72 | 73 | /** 74 | * Determines whether an interaction between two objects has occurred. 75 | * @param object1 the first interacting object 76 | * @param object2 the second interacting object 77 | * @return Whether or not an interaction has occurred. 78 | */ 79 | public abstract boolean detect(A object1, B object2); 80 | 81 | /** 82 | * Handles the result of an interaction that has been detected (by detect return true) 83 | * for a pair of objects. 84 | * @param object1 the first object 85 | * @param object2 the second object 86 | */ 87 | public abstract void handle(A object1, B object2); 88 | } 89 | 90 | -------------------------------------------------------------------------------- /resources_default/build.properties: -------------------------------------------------------------------------------- 1 | # Create libraries for the open source programming language 2 | # and environment processing (http://www.processing.org) 3 | # 4 | # Customize the build properties to make the ant-build-process 5 | # work for your environment. How? Please read the comments below. 6 | # 7 | # The default properties are set for OSX, for Windows-settings 8 | # please refer to comments made under (1) and (2). 9 | 10 | 11 | 12 | # (1) 13 | # Where is your processing sketchbook located? 14 | # If you are not sure, check the sketchbook location in your 15 | # processing application preferences. 16 | # ${user.home} points the compiler to your home directory 17 | # For windows the default path to your sketchbook would be 18 | # ${user.home}/My Documents/Processing (make adjustments below). 19 | 20 | sketchbook.location=${user.home}/Documents/Processing 21 | 22 | 23 | 24 | # (2) 25 | # Where are the jar files located that are required for compiling 26 | # your library such as e.g. core.jar? 27 | # by default the local classpath location points to folder libs 28 | # inside eclipse's workspace (by default found in your home directory). 29 | # For Windows the default path would be ${user.home}/workspace/libs 30 | # (make adjustments below) 31 | 32 | classpath.local.location=${user.home}/Documents/hermes/lib 33 | 34 | 35 | 36 | # For OSX users. 37 | # The following path will direct you into processing's 38 | # application source code folder in case you put processing inside your 39 | # Applications folder. Uncommenting the line below will overwrite the 40 | # classpath.local.location from above. 41 | 42 | classpath.local.location=/Applications/Processing.app/Contents/Java/core/library 43 | 44 | 45 | # Add all jar files that are required for compiling your project 46 | # to the local and project classpath, use a comma as delimiter. 47 | # These jar files must be inside your classpath.local.location folder. 48 | 49 | classpath.local.include=core.jar 50 | 51 | 52 | # Add processing's libraries folder to the classpath. 53 | # If you don't need to include the libraries folder to your classpath, 54 | # comment out the following line. 55 | 56 | classpath.libraries.location=${user.home}/Documents/hermes/lib 57 | 58 | 59 | 60 | # (3) set the java version that should be used to compile your library. 61 | 62 | java.target.version=1.7 63 | 64 | 65 | # set the description of the ant build.xml file 66 | 67 | ant.description=processingLibs. ant build file. 68 | 69 | 70 | 71 | # (4) 72 | # project details. 73 | # Give your library a name. 74 | 75 | project.name=hermes 76 | 77 | 78 | # use normal or fast as value for project.compile 79 | # (fast will only compile the project into your sketchbook, 80 | # using normal as property will compile the distribution including the 81 | # javadoc-reference and all web-files - the compile process here 82 | # takes longer) 83 | 84 | project.compile=normal 85 | 86 | # all files compiled with project.compile=normal are stored 87 | # in folder distribution 88 | 89 | 90 | 91 | # (5) 92 | # the following items are properties that will be used to 93 | # make changes to the html document template. values of properties 94 | # will be implemented ito the document automatically. 95 | 96 | # set the current version of your project. 97 | 98 | project.version=2.0 99 | project.copyright=(c) 2014 100 | project.dependencies=None 101 | project.keywords=Simulation Animation Geometry Compilation 102 | 103 | author.name=Ryan Lester 104 | author.url=https://github.com/rdlester 105 | 106 | tested.platform=osx,windows 107 | tested.processingversion=2.0+ 108 | 109 | # recommendations for storing your source code online are: code.google or github. 110 | 111 | source.host=github 112 | source.url=https://github.com/rdlester/hermes 113 | source.repository=git://github.com/rdlester/hermes.git 114 | 115 | 116 | # include javadoc references into your project's javadoc 117 | 118 | javadoc.java.href= 119 | #http://java.sun.com/javase/6/docs/api/ 120 | javadoc.processing.href= 121 | #http://dev.processing.org/reference/everything/javadoc/ 122 | 123 | 124 | -------------------------------------------------------------------------------- /hermesTest/postOfficeTests/oscTest.maxpat: -------------------------------------------------------------------------------- 1 | { 2 | "patcher" : { 3 | "fileversion" : 1, 4 | "rect" : [ 127.0, 115.0, 640.0, 480.0 ], 5 | "bglocked" : 0, 6 | "defrect" : [ 127.0, 115.0, 640.0, 480.0 ], 7 | "openrect" : [ 0.0, 0.0, 0.0, 0.0 ], 8 | "openinpresentation" : 0, 9 | "default_fontsize" : 12.0, 10 | "default_fontface" : 0, 11 | "default_fontname" : "Arial", 12 | "gridonopen" : 0, 13 | "gridsize" : [ 15.0, 15.0 ], 14 | "gridsnaponopen" : 0, 15 | "toolbarvisible" : 1, 16 | "boxanimatetime" : 200, 17 | "imprint" : 0, 18 | "enablehscroll" : 1, 19 | "enablevscroll" : 1, 20 | "devicewidth" : 0.0, 21 | "boxes" : [ { 22 | "box" : { 23 | "maxclass" : "newobj", 24 | "text" : "print", 25 | "patching_rect" : [ 47.0, 256.0, 34.0, 20.0 ], 26 | "numinlets" : 1, 27 | "id" : "obj-10", 28 | "fontname" : "Arial", 29 | "numoutlets" : 0, 30 | "fontsize" : 12.0 31 | } 32 | 33 | } 34 | , { 35 | "box" : { 36 | "maxclass" : "newobj", 37 | "text" : "route /test", 38 | "patching_rect" : [ 380.0, 284.0, 64.0, 20.0 ], 39 | "numinlets" : 1, 40 | "id" : "obj-9", 41 | "fontname" : "Arial", 42 | "numoutlets" : 2, 43 | "outlettype" : [ "", "" ], 44 | "fontsize" : 12.0 45 | } 46 | 47 | } 48 | , { 49 | "box" : { 50 | "maxclass" : "message", 51 | "text" : "1", 52 | "patching_rect" : [ 143.0, 124.0, 32.5, 18.0 ], 53 | "numinlets" : 2, 54 | "id" : "obj-8", 55 | "fontname" : "Arial", 56 | "numoutlets" : 1, 57 | "outlettype" : [ "" ], 58 | "fontsize" : 12.0 59 | } 60 | 61 | } 62 | , { 63 | "box" : { 64 | "maxclass" : "newobj", 65 | "text" : "prepend /test", 66 | "patching_rect" : [ 146.0, 168.0, 81.0, 20.0 ], 67 | "numinlets" : 1, 68 | "id" : "obj-7", 69 | "fontname" : "Arial", 70 | "numoutlets" : 1, 71 | "outlettype" : [ "" ], 72 | "fontsize" : 12.0 73 | } 74 | 75 | } 76 | , { 77 | "box" : { 78 | "maxclass" : "newobj", 79 | "text" : "print", 80 | "patching_rect" : [ 386.0, 317.0, 34.0, 20.0 ], 81 | "numinlets" : 1, 82 | "id" : "obj-6", 83 | "fontname" : "Arial", 84 | "numoutlets" : 0, 85 | "fontsize" : 12.0 86 | } 87 | 88 | } 89 | , { 90 | "box" : { 91 | "maxclass" : "newobj", 92 | "text" : "udpsend 127.0.0.1 8000", 93 | "patching_rect" : [ 143.0, 297.0, 140.0, 20.0 ], 94 | "numinlets" : 1, 95 | "id" : "obj-4", 96 | "fontname" : "Arial", 97 | "numoutlets" : 0, 98 | "fontsize" : 12.0 99 | } 100 | 101 | } 102 | , { 103 | "box" : { 104 | "maxclass" : "newobj", 105 | "text" : "udpreceive 8080", 106 | "patching_rect" : [ 382.0, 226.0, 99.0, 20.0 ], 107 | "numinlets" : 1, 108 | "id" : "obj-2", 109 | "fontname" : "Arial", 110 | "numoutlets" : 1, 111 | "outlettype" : [ "" ], 112 | "fontsize" : 12.0 113 | } 114 | 115 | } 116 | ], 117 | "lines" : [ { 118 | "patchline" : { 119 | "source" : [ "obj-7", 0 ], 120 | "destination" : [ "obj-4", 0 ], 121 | "hidden" : 0, 122 | "midpoints" : [ ] 123 | } 124 | 125 | } 126 | , { 127 | "patchline" : { 128 | "source" : [ "obj-2", 0 ], 129 | "destination" : [ "obj-9", 0 ], 130 | "hidden" : 0, 131 | "midpoints" : [ ] 132 | } 133 | 134 | } 135 | , { 136 | "patchline" : { 137 | "source" : [ "obj-8", 0 ], 138 | "destination" : [ "obj-7", 0 ], 139 | "hidden" : 0, 140 | "midpoints" : [ ] 141 | } 142 | 143 | } 144 | , { 145 | "patchline" : { 146 | "source" : [ "obj-9", 0 ], 147 | "destination" : [ "obj-6", 0 ], 148 | "hidden" : 0, 149 | "midpoints" : [ ] 150 | } 151 | 152 | } 153 | , { 154 | "patchline" : { 155 | "source" : [ "obj-7", 0 ], 156 | "destination" : [ "obj-10", 0 ], 157 | "hidden" : 0, 158 | "midpoints" : [ ] 159 | } 160 | 161 | } 162 | ] 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /examples/BulletCurtain/CharacterGraphicsGenerator.pde: -------------------------------------------------------------------------------- 1 | /* 2 | This makes generative patterns for use as blocky pixel character graphics. 3 | Visually, the hope is to make something in between look classic video game aliens/spaceships, and patterns from cellular automata. 4 | */ 5 | 6 | class CharacterGraphicsGenerator { 7 | 8 | //Pallette of colors used for characters 9 | color[] colorPalette = { 10 | color(255, 51, 52), color(255, 145, 48), color(255, 254, 136), color(0, 184, 48), color(0, 210, 255) 11 | }; 12 | 13 | /* 14 | Makes a generative PImage[] to be used for creatures/ships. 15 | Each PImage in the the returned PImage[] is a unique pattern, symmetrical about its horizontal axis 16 | patterns are built out of "blocks," where each block has a number of pixels per block 17 | this is to basically scaling - the character generated can be any size, but still retain a classic pixelated/blocky look 18 | */ 19 | public PImage[] generate(int numberOfWidthBlocks, int numberOfHeightBlocks, int pixelsPerBlock, int numberOfAnimationFrames) { 20 | 21 | //make a PImage[] of frames for the animation 22 | PImage[] generatedFrames = new PImage[numberOfAnimationFrames]; 23 | 24 | //use colors from the colorPalette 25 | color characterColor = colorPalette[int(random(colorPalette.length))]; 26 | 27 | //for every frame.. 28 | for (int frameIndex = 0; frameIndex < generatedFrames.length; frameIndex++) { 29 | //generate a random frame of desired width&height. 30 | generatedFrames[frameIndex] = createRandomFrame(numberOfWidthBlocks, numberOfHeightBlocks, pixelsPerBlock, characterColor); 31 | } 32 | 33 | return generatedFrames; 34 | } 35 | 36 | private PImage createRandomFrame (int numberOfWidthBlocks, int numberOfHeightBlocks, int pixelsPerBlock, color characterColor) { 37 | 38 | //Make a pattern, return it as a PImage 39 | PImage currentFrame = createImage(numberOfWidthBlocks*pixelsPerBlock, numberOfHeightBlocks*pixelsPerBlock, ARGB); 40 | 41 | //call loadPixels() to prep pixel buffer for writing 42 | currentFrame.loadPixels(); 43 | 44 | int pixelRow = 0; 45 | int pixelCol = 0; 46 | 47 | //for every column of pixels in this image.. (which is the same as the frameWidth) 48 | for (int col = 0; col < numberOfWidthBlocks; col++) { 49 | 50 | //generate a pattern, 1/2 the height of the image, as it will be made symetrical about the horizontal axis 51 | int[] pattern = new int[numberOfHeightBlocks / 2]; 52 | 53 | //for every position in the pattern... 54 | for (int patternIndex = 0; patternIndex < pattern.length; patternIndex++) { 55 | //build a random pattern.. 56 | //***this might be more fun as a Cellular Automata eventually... or something the user can codebend somehow 57 | pattern[patternIndex] = int(random(2)); //generate a random number that is either 0 or 1 58 | } 59 | 60 | //now, set all the pixels 61 | for (int row = 0; row < numberOfHeightBlocks/2 ; row++) { 62 | 63 | color colorToUse = color (0, 0, 0, 0); //transparent 64 | //If the generated pattern has a 1, 65 | if (pattern[row] == 1) { 66 | colorToUse = characterColor; 67 | } // otherwise leave it as transparent; 68 | 69 | //Spice the colors up a bit... 70 | if (int(random(10)) == 0) { 71 | //Make a random secondary color 72 | colorToUse = color(random(255), random(255), random(255)); 73 | } 74 | 75 | //now iterate through all the pixels in this block and set their color 76 | for (int i = 0; i < pixelsPerBlock; i++) { 77 | for (int j = 0; j < pixelsPerBlock; j++) { 78 | 79 | currentFrame.set(col * pixelsPerBlock + i, row * pixelsPerBlock + j, colorToUse); // for the top half of the PImage 80 | int symmetricalRow = numberOfHeightBlocks - row - 1; //and the bottom half, symmetrically 81 | currentFrame.set(col * pixelsPerBlock + i, symmetricalRow * pixelsPerBlock + j, colorToUse); // for the top half of the PImage 82 | } 83 | } 84 | } 85 | } 86 | 87 | //Consider a mod: maybe make sure the leftmost col has a few pixels in it.. more convincing collisions ?? 88 | 89 | //Lastly, update the pixels of the PImage that was generated 90 | currentFrame.updatePixels(); 91 | return currentFrame; 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /hermesTest/shapeTests/RectangleTest.java: -------------------------------------------------------------------------------- 1 | package hermesTest.shapeTests; 2 | 3 | import static org.junit.Assert.*; 4 | import hermes.Hermes; 5 | import hermes.hshape.*; 6 | 7 | import org.junit.*; 8 | 9 | import processing.core.*; 10 | 11 | /** 12 | * unit tests for Hermes.shape.HRectangle 13 | * @author Sam 14 | * 15 | */ 16 | public class HRectangleTest { 17 | 18 | @Before 19 | public void setup() { 20 | PApplet applet = new PApplet(); 21 | applet.g = new PGraphics(); 22 | Hermes.setPApplet(applet); 23 | applet.rectMode(PApplet.CENTER); 24 | } 25 | 26 | /** 27 | * tests the collision of two HRectangles 28 | */ 29 | @Test 30 | public void test_HRectangleCollide() { 31 | // check one rectangle containing another 32 | HShape r1 = new HRectangle(new PVector(0,0), 5, 5); 33 | assertFalse(r1.collide(r1)); // self-collision 34 | HShape r2 = new HRectangle(new PVector(0,0), 3, 3); 35 | assertTrue(r1.collide(r2)); 36 | assertTrue(r2.collide(r1)); 37 | // corner overlap 38 | r2 = new HRectangle(new PVector(-3,-3),1,1); 39 | assertTrue(r1.collide(r2)); 40 | assertTrue(r2.collide(r1)); 41 | r2 = new HRectangle(new PVector(3,-3),1,1); 42 | assertTrue(r1.collide(r2)); 43 | assertTrue(r2.collide(r1)); 44 | r2 = new HRectangle(new PVector(-3,3),1,1); 45 | assertTrue(r1.collide(r2)); 46 | assertTrue(r2.collide(r1)); 47 | r2 = new HRectangle(new PVector(3,3),1,1); 48 | assertTrue(r1.collide(r2)); 49 | assertTrue(r2.collide(r1)); 50 | // one side overlap 51 | r2 = new HRectangle(new PVector(4,0),3,1); 52 | assertTrue(r1.collide(r2)); 53 | assertTrue(r2.collide(r1)); 54 | r2 = new HRectangle(new PVector(-4,0),3,1); 55 | assertTrue(r1.collide(r2)); 56 | assertTrue(r2.collide(r1)); 57 | r2 = new HRectangle(new PVector(0,4),1,3); 58 | assertTrue(r1.collide(r2)); 59 | assertTrue(r2.collide(r1)); 60 | r2 = new HRectangle(new PVector(0,-4),1,3); 61 | assertTrue(r1.collide(r2)); 62 | assertTrue(r2.collide(r1)); 63 | // center overlap 64 | r1 = new HRectangle(new PVector(0,0),1,3); 65 | r2 = new HRectangle(new PVector(0,1),3,1); 66 | assertTrue(r1.collide(r2)); 67 | assertTrue(r2.collide(r1)); 68 | // check for false positives 69 | r2 = new HRectangle(new PVector(0,3),3,1); 70 | assertFalse(r1.collide(r2)); 71 | assertFalse(r2.collide(r1)); 72 | } 73 | 74 | /** 75 | * tests HRectangle.projectionVector() 76 | */ 77 | @Test 78 | public void test_projectionVector() { 79 | // check one rectangle containing another 80 | HRectangle r1 = new HRectangle(new PVector(0,0), 5, 5); 81 | assertEquals(r1.projectionVector(r1), null); // self-collision 82 | HRectangle r2 = new HRectangle(new PVector(0,0), 3, 3); 83 | assertEquals(r1.projectionVector(r2).x, 0, 1e-8); 84 | assertEquals(r1.projectionVector(r2).y, -4, 1e-8); 85 | // corner overlap 86 | r2 = new HRectangle(new PVector(-3,-3),1,1); 87 | assertEquals(r1.projectionVector(r2).x, 0, 1e-8); 88 | assertEquals(r1.projectionVector(r2).y, 0, 1e-8); 89 | // one side overlap 90 | r2 = new HRectangle(new PVector(3,0),3,1); 91 | assertEquals(r1.projectionVector(r2).x, 1, 1e-8); 92 | assertEquals(r1.projectionVector(r2).y, 0, 1e-8); 93 | assertEquals(r2.projectionVector(r1).x, -1, 1e-8); 94 | assertEquals(r2.projectionVector(r1).y, 0, 1e-8); 95 | r2 = new HRectangle(new PVector(-3,0),3,1); 96 | assertEquals(r1.projectionVector(r2).x, -1, 1e-8); 97 | assertEquals(r1.projectionVector(r2).y, 0, 1e-8); 98 | r2 = new HRectangle(new PVector(0,3),1,3); 99 | assertEquals(r1.projectionVector(r2).x, 0, 1e-8); 100 | assertEquals(r1.projectionVector(r2).y, 1, 1e-8); 101 | r2 = new HRectangle(new PVector(0,-3),1,3); 102 | assertEquals(r1.projectionVector(r2).x, 0, 1e-8); 103 | assertEquals(r1.projectionVector(r2).y, -1, 1e-8); 104 | } 105 | 106 | /** 107 | * tests HRectangle.getCenter() 108 | */ 109 | @Test 110 | public void test_getCenter() { 111 | HRectangle rect = new HRectangle(new PVector(3,3), 5.0f, 1.2f); 112 | assertEquals(rect.getCenter().x, 3.0f, 1e-8); 113 | assertEquals(rect.getCenter().y, 3.0f, 1e-8); 114 | rect = new HRectangle(new PVector(0,0), new PVector(-1,-1), new PVector(3,4)); 115 | assertEquals(rect.getCenter().x, 1.0f, 1e-8); 116 | assertEquals(rect.getCenter().y, 1.5f, 1e-8); 117 | } 118 | 119 | /** 120 | * tests HRectangle.contains(HRectangle) 121 | */ 122 | @Test 123 | public void test_contains() { 124 | HRectangle r1 = new HRectangle(new PVector(0,0), 6, 6); 125 | assertTrue(r1.contains(r1)); 126 | HRectangle r2 = new HRectangle(new PVector(0,0), 4, 4); 127 | assertTrue(r1.contains(r2)); 128 | assertFalse(r2.contains(r1)); 129 | r2 = new HRectangle(new PVector(0, 2), 2.5f, 2.5f); 130 | assertFalse(r1.contains(r2)); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /hermesTest/postOfficeTests/OSCMessageJUnitTests.java: -------------------------------------------------------------------------------- 1 | package hermesTest.postOfficeTests; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.fail; 5 | import hermes.Hermes; 6 | import hermes.postoffice.OscMessage; 7 | 8 | import org.junit.After; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | 12 | import processing.core.PApplet; 13 | 14 | public class OSCMessageJUnitTests { 15 | 16 | PApplet _sketch; 17 | 18 | @Before 19 | public void setUp() { 20 | _sketch = new PApplet(); 21 | Hermes.setPApplet(_sketch); 22 | } 23 | 24 | @After 25 | public void tearDown() { 26 | _sketch = null; 27 | Hermes.setPApplet(null); 28 | } 29 | 30 | 31 | @Test 32 | public void testGoodData() { 33 | 34 | //make an object array of all valid arguments 35 | Object[] testObjects = new Object[3]; 36 | testObjects[0] = new Integer(10); 37 | testObjects[1] = new String("Oh hai"); 38 | testObjects[2] = new Float(1.0); 39 | 40 | //don't need a valid address.. just testing the argument retrieval functionality 41 | OscMessage message = new OscMessage("/DummyAddress", testObjects); 42 | 43 | //make sure the number of arguments is correct 44 | assertEquals(message.getNumberOfRemainingArguments(), 3); 45 | //Should be an int 46 | assertEquals(message.getTypeTagOfNextArgument(), "i"); 47 | //check the value 48 | int intValue = message.getAndRemoveInt(); 49 | assertEquals((Integer)intValue, (Integer)testObjects[0]); 50 | 51 | //make sure the number of arguments is correct 52 | assertEquals(message.getNumberOfRemainingArguments(), 2); 53 | //Should be a string 54 | assertEquals(message.getTypeTagOfNextArgument(), "s"); 55 | //check the value 56 | String stringValue = message.getAndRemoveString(); 57 | assertEquals((String)stringValue, (String)testObjects[1]); 58 | 59 | //make sure the number of arguments is correct 60 | assertEquals(message.getNumberOfRemainingArguments(), 1); 61 | //Should be a float 62 | assertEquals(message.getTypeTagOfNextArgument(), "f"); 63 | //check the value 64 | Float floatValue= message.getAndRemoveFloat(); 65 | assertEquals((Float)floatValue, (Float)testObjects[2]); 66 | 67 | assertEquals(message.getNumberOfRemainingArguments(), 0); 68 | 69 | } 70 | 71 | @Test 72 | public void textBadMethodCalls() { 73 | 74 | //make an object array of all valid arguments 75 | Object[] testObjects = new Object[3]; 76 | testObjects[0] = new Integer(10); 77 | testObjects[1] = new String("Oh hai"); 78 | testObjects[2] = new Float(1.0); 79 | 80 | //don't need a valid address.. just testing the argument retrieval functionality 81 | OscMessage message = new OscMessage("/DummyAddress", testObjects); 82 | 83 | try { 84 | message.getAndRemoveFloat(); 85 | fail( "Should throw AssertionError" ); 86 | } catch (AssertionError expectedException) { 87 | } 88 | 89 | try { 90 | message.getAndRemoveString(); 91 | fail( "Should throw AssertionError" ); 92 | } catch (AssertionError expectedException) { 93 | } 94 | 95 | //remove the element to check Int 96 | message.getAndRemoveInt(); 97 | 98 | try { 99 | message.getAndRemoveInt(); 100 | fail( "Should throw AssertionError" ); 101 | } catch (AssertionError expectedException) { 102 | } 103 | 104 | //empty the OSCMessage and then check errors.. 105 | message.getAndRemoveString(); 106 | message.getAndRemoveFloat(); 107 | 108 | 109 | try { 110 | message.getAndRemoveInt(); 111 | fail( "Should throw AssertionError because the OSCMessage is empty" ); 112 | } catch (AssertionError expectedException) { 113 | } 114 | 115 | try { 116 | message.getAndRemoveFloat(); 117 | fail( "Should throw AssertionError because the OSCMessage is empty" ); 118 | } catch (AssertionError expectedException) { 119 | } 120 | 121 | 122 | try { 123 | message.getAndRemoveString(); 124 | fail( "Should throw AssertionError because the OSCMessage is empty" ); 125 | } catch (AssertionError expectedException) { 126 | } 127 | 128 | try { 129 | message.getTypeTagOfNextArgument(); 130 | fail( "Should throw AssertionError because the OSCMessage is empty" ); 131 | } catch (AssertionError expectedException) { 132 | } 133 | 134 | //Lastly, check nonexistent typetag 135 | class Impossible { 136 | } 137 | //make a message that contains an 'Impossible' 138 | testObjects = new Object[1]; 139 | testObjects[0] = new Impossible(); 140 | message = new OscMessage("/DummyAddress", testObjects); 141 | 142 | try { 143 | message.getTypeTagOfNextArgument(); 144 | fail( "Should throw AssertionError because the tag does not exist" ); 145 | } catch (AssertionError expectedException) { 146 | } 147 | 148 | } 149 | 150 | 151 | } 152 | -------------------------------------------------------------------------------- /resources_default/stylesheet.css: -------------------------------------------------------------------------------- 1 | /* Javadoc style sheet */ 2 | /* Define colors, fonts and other style attributes here to override the defaults */ 3 | /* processingLibs style by andreas schlegel, sojamo */ 4 | 5 | 6 | body { 7 | margin : 0; 8 | padding : 0; 9 | padding-left : 10px; 10 | padding-right : 8px; 11 | background-color : #FFFFFF; 12 | font-family : Verdana, Geneva, Arial, Helvetica, sans-serif; 13 | font-size : 100%; 14 | font-size : 0.7em; 15 | font-weight : normal; 16 | line-height : normal; 17 | margin-bottom:30px; 18 | } 19 | 20 | 21 | 22 | 23 | /* Headings */ 24 | h1, h2, h3, h4, h5, th { 25 | font-family :Arial, Helvetica, sans-serif; 26 | font-size:1.2em; 27 | } 28 | 29 | 30 | p { 31 | font-size : 1em; 32 | width:80%; 33 | } 34 | 35 | pre, code { 36 | font-family : "Courier New", Courier, monospace; 37 | font-size : 12px; 38 | line-height : normal; 39 | } 40 | 41 | 42 | 43 | table { 44 | border:0; 45 | margin-bottom:10px; 46 | margin-top:10px; 47 | } 48 | 49 | 50 | tr, td { 51 | border-top: 0px solid; 52 | border-left: 0px solid; 53 | padding-top:8px; 54 | padding-bottom:8px; 55 | } 56 | 57 | 58 | 59 | hr { 60 | border:0; 61 | height:1px; 62 | padding:0; 63 | margin:0; 64 | margin-bottom:4px; 65 | 66 | } 67 | 68 | 69 | 70 | dd, th, td, font { 71 | font-size:1.0em; 72 | line-height:1.0em; 73 | } 74 | 75 | 76 | 77 | dt { 78 | margin-bottom:0px; 79 | } 80 | 81 | 82 | 83 | dd { 84 | margin-top:2px; 85 | margin-bottom:4px; 86 | } 87 | 88 | 89 | 90 | a { 91 | text-decoration: underline; 92 | font-weight: normal; 93 | } 94 | 95 | a:hover, 96 | a:active { 97 | text-decoration: underline; 98 | font-weight: normal; 99 | } 100 | 101 | a:visited, 102 | a:link:visited { 103 | text-decoration: underline; 104 | font-weight: normal; 105 | } 106 | 107 | 108 | img { 109 | border: 0px solid #000000; 110 | } 111 | 112 | 113 | 114 | /* Navigation bar fonts */ 115 | .NavBarCell1 { 116 | border:0; 117 | } 118 | 119 | .NavBarCell1Rev { 120 | border:0; 121 | } 122 | 123 | .NavBarFont1 { 124 | font-family: Arial, Helvetica, sans-serif; 125 | font-size:1.1em; 126 | } 127 | 128 | 129 | .NavBarFont1 b { 130 | font-weight:normal; 131 | } 132 | 133 | 134 | 135 | .NavBarFont1:after, .NavBarFont1Rev:after { 136 | font-weight:normal; 137 | content: " \\"; 138 | } 139 | 140 | 141 | .NavBarFont1Rev { 142 | font-family: Arial, Helvetica, sans-serif; 143 | font-size:1.1em; 144 | } 145 | 146 | .NavBarFont1Rev b { 147 | font-family: Arial, Helvetica, sans-serif; 148 | font-size:1.1em; 149 | font-weight:normal; 150 | } 151 | 152 | .NavBarCell2 { 153 | font-family: Arial, Helvetica, sans-serif; 154 | } 155 | 156 | .NavBarCell3 { 157 | font-family: Arial, Helvetica, sans-serif; 158 | } 159 | 160 | 161 | 162 | font.FrameItemFont { 163 | font-family: Helvetica, Arial, sans-serif; 164 | font-size:1.1em; 165 | line-height:1.1em; 166 | } 167 | 168 | font.FrameHeadingFont { 169 | font-family: Helvetica, Arial, sans-serif; 170 | line-height:32px; 171 | } 172 | 173 | /* Font used in left-hand frame lists */ 174 | .FrameTitleFont { 175 | font-family: Helvetica, Arial, sans-serif 176 | } 177 | 178 | 179 | .toggleList { 180 | padding:0; 181 | margin:0; 182 | margin-top:12px; 183 | } 184 | 185 | .toggleList dt { 186 | font-weight:bold; 187 | font-size:12px; 188 | font-family:arial,sans-serif; 189 | padding:0px; 190 | margin:10px 0px 10px 0px; 191 | } 192 | 193 | .toggleList dt span { 194 | font-family: monospace; 195 | padding:0; 196 | margin:0; 197 | } 198 | 199 | 200 | .toggleList dd { 201 | margin:0; 202 | padding:0; 203 | } 204 | 205 | html.isjs .toggleList dd { 206 | display: none; 207 | } 208 | 209 | .toggleList pre { 210 | padding: 4px 4px 4px 4px; 211 | } 212 | 213 | 214 | 215 | 216 | 217 | /* COLORS */ 218 | 219 | pre, code { 220 | color: #000000; 221 | } 222 | 223 | 224 | body { 225 | color : #333333; 226 | background-color :#FFFFFF; 227 | } 228 | 229 | 230 | h1, h2, h3, h4, h5, h6 { 231 | color:#555; 232 | } 233 | 234 | a, 235 | .toggleList dt { 236 | color: #1a7eb0; 237 | } 238 | 239 | a:hover, 240 | a:active { 241 | color: #1a7eb0; 242 | } 243 | 244 | a:visited, 245 | a:link:visited { 246 | color: #1a7eb0; 247 | } 248 | 249 | td,tr { 250 | border-color: #999999; 251 | } 252 | 253 | hr { 254 | color:#999999; 255 | background:#999999; 256 | } 257 | 258 | 259 | .TableHeadingColor { 260 | background: #dcdcdc; 261 | color: #555; 262 | } 263 | 264 | 265 | .TableSubHeadingColor { 266 | background: #EEEEFF 267 | } 268 | 269 | .TableRowColor { 270 | background: #FFFFFF 271 | } 272 | 273 | 274 | .NavBarCell1 { 275 | background-color:#dcdcdc; 276 | color:#000; 277 | } 278 | 279 | .NavBarCell1 a { 280 | color:#333; 281 | } 282 | 283 | 284 | .NavBarCell1Rev { 285 | background-color:transparent; 286 | } 287 | 288 | .NavBarFont1 { 289 | color:#333; 290 | } 291 | 292 | 293 | .NavBarFont1Rev { 294 | color:#fff; 295 | } 296 | 297 | .NavBarCell2 { 298 | background-color:#999; 299 | } 300 | 301 | .NavBarCell2 a { 302 | color:#fff; 303 | } 304 | 305 | 306 | 307 | .NavBarCell3 { 308 | background-color:#dcdcdc; 309 | } 310 | 311 | -------------------------------------------------------------------------------- /hermes/physics/ImpulseCollision.java: -------------------------------------------------------------------------------- 1 | package hermes.physics; 2 | 3 | import processing.core.*; 4 | import static hermes.HermesMath.*; 5 | 6 | /** 7 | *

8 | * This class is used to store the data for an impulse-based collision between two beings.
9 | * Intended for internal use, you do not need to understand or use this class!
10 | * It will accumulate an impulse for a collision between two beings, and apply it equally to each 11 | * when the collision is resolved. 12 | * 13 | */ 14 | public class ImpulseCollision { 15 | 16 | private MassedBeing _being1, _being2; // the two beings colliding 17 | private PVector _projection; // the projection vector from being1 to being2 18 | private PVector _impulse; // the impulse on _being2 from _being1 19 | private float _elasticity; // the elasticity of the collision 20 | private PVector _being1Displacement, 21 | _being2Displacement; // the displacement on each being to project them out of collision 22 | 23 | /** 24 | * Sets up a collision between beings 25 | * @param being1 the first being (impulses and projections are from being1 to being2) 26 | * @param being2 the second being 27 | * @param projection the projection vector from being1 to being2 28 | */ 29 | public ImpulseCollision(MassedBeing being1, MassedBeing being2, 30 | PVector projection, float elasticity) { 31 | assert being1 != null : "ImpulseCollision contructor: being1 must be a valid being"; 32 | assert being2 != null : "ImpulseCollision contructor: being2 must be a valid being"; 33 | 34 | _being1 = being1; 35 | _being2 = being2; 36 | _projection = projection; 37 | _elasticity = elasticity; 38 | _impulse = zeroVector(); 39 | _being1Displacement = zeroVector(); 40 | _being2Displacement = zeroVector(); 41 | } 42 | 43 | /** 44 | * the projection vector from being1 to being2 45 | * @return the projection vector 46 | */ 47 | public PVector getProjection() { 48 | return _projection; 49 | } 50 | 51 | /** 52 | * sets the projection vector for the collision 53 | * @param projection the projection vector 54 | */ 55 | public void setProjection(PVector projection) { 56 | _projection = projection; 57 | } 58 | 59 | /** 60 | * returns the accumulated impulse vector 61 | * @return the impulse 62 | */ 63 | public PVector getImpulse() { 64 | return _impulse; 65 | } 66 | 67 | /** 68 | * add an impulse to the collision 69 | * @param impulse the impulse to add 70 | * @param origin the origin of the impulse (should be being1 or being2) 71 | */ 72 | public void addImpulse(PVector impulse, MassedBeing origin) { 73 | assert impulse != null : "ImpulseCollision.addImpulse: impulse must be a valid PVector"; 74 | 75 | if(origin == _being1) // if the impulse is from being1 76 | _impulse.add(impulse); // add it 77 | else if(origin == _being2) // if its from being2 78 | _impulse.sub(impulse); // subtract it 79 | else 80 | assert false : "ImpulseCollision.addImpulse: origin of collision force must be being1 or being2"; 81 | } 82 | 83 | /** 84 | * add an impulse to the beings, calculated between them, based on their current mass and velocity, to the beings 85 | */ 86 | public void addImpulse() { 87 | if(!(_projection.x == 0 && _projection.y == 0)) 88 | addImpulse(Physics.calculateImpulse(_being1, _being2, _elasticity, _projection), _being1); 89 | } 90 | 91 | /** 92 | * applies the stored impulse to each being 93 | * clears the impulse vector 94 | */ 95 | public void applyImpulses() { 96 | _being1.addImpulse(getReverse(_impulse)); 97 | _being2.addImpulse(_impulse); 98 | zeroVector(_impulse); 99 | } 100 | 101 | /** 102 | * calculates the projective displacement on each being 103 | */ 104 | public void calculateDisplacement() { 105 | float m1 = _being1.getMass(); 106 | float m2 = _being2.getMass(); 107 | float M = m1 + m2;// need to deal with infinite masses 108 | if(m1 == Float.POSITIVE_INFINITY && m2 == Float.POSITIVE_INFINITY) { 109 | _being1Displacement = PVector.mult(getReverse(_projection), 0.5f); 110 | _being2Displacement = PVector.mult(_projection, 0.5f); 111 | } else if (m1 == Float.POSITIVE_INFINITY) { 112 | _being1Displacement = zeroVector(); 113 | _being2Displacement = cloneVector(_projection); 114 | } else if (m2 == Float.POSITIVE_INFINITY) { 115 | _being1Displacement = getReverse(_projection); 116 | _being2Displacement = zeroVector(); 117 | } else { 118 | _being1Displacement = PVector.mult(getReverse(_projection), m2 / M); 119 | _being2Displacement = PVector.mult(_projection, m1 / M); 120 | } 121 | } 122 | 123 | /** 124 | * applies the projective displacement to each being 125 | */ 126 | public void applyDisplacement() { 127 | _being1.addDisplacement(_being1Displacement); 128 | _being2.addDisplacement(_being2Displacement); 129 | zeroVector(_being1Displacement); 130 | zeroVector(_being2Displacement); 131 | } 132 | 133 | /** 134 | * whether the one of the beings in this collision is the specified being 135 | * @param being the being to check for 136 | * @return whether being is in this collision 137 | */ 138 | public boolean hasBeing(MassedBeing being) { 139 | return _being1 == being || _being2 == being; 140 | } 141 | } -------------------------------------------------------------------------------- /examples/Level0/ToolBox.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * The ToolBox contains the available tools for level-buidling 3 | */ 4 | class ToolBox extends Being { 5 | 6 | Cell[][] _grid; 7 | 8 | //Used for controlling the dragging of tools 9 | Canvas _canvas; 10 | 11 | //label 12 | Zero zero; 13 | 14 | ToolBox(Canvas canvas) { 15 | super(new HRectangle(new PVector(toolBoxLeftX, containerTopY), 16 | new PVector(toolBoxWidth, containerHeight))); 17 | _grid = new Cell[toolBoxNumCellsX][toolBoxNumCellsY]; 18 | _canvas = canvas; 19 | initialize(); 20 | } 21 | 22 | void initialize() { 23 | //make all the cells 24 | for(int i=0; iHObject
s using the specified collection 12 | * that share common attributes and interact with another group of HObjects. 13 | *

14 | * Like Beings, GenericGroups can have their own update methods. 15 | *

16 | * The primary purpose of grouping is for use in interactions. 17 | * However, GenericGroups can also be used to store data about or provide 18 | * access to the contained objects. 19 | * For example, groups can keep track of the ages of the objects it contains 20 | * and direct messages to either the oldest or the newest object. 21 | *

22 | * See {@link hermes.World World} for more details on registering interactions or Updates. 23 | * 24 | * @see hermes.Interactor Interactor 25 | * @see hermes.HObject HObject 26 | * 27 | * @param the type of the objects in the group 28 | * @param the type of underlying collection used 29 | */ 30 | public class GenericGroup> 31 | implements KeySubscriber, MouseSubscriber, MouseWheelSubscriber, OscSubscriber { 32 | 33 | private B _objects; // the underlying collection 34 | private LinkedList _needsMoreSamples; // keeps track of any beings that need 35 | // more samples this update 36 | protected World _world; // the world containing the groups 37 | 38 | /** 39 | * Instantiates a group storing HObjects in the given collection. 40 | * @param objects the collection objects will be stored in 41 | * @param world the world where the group will be used 42 | */ 43 | public GenericGroup(B objects, World world) { 44 | _objects = objects; 45 | _world = world; 46 | _needsMoreSamples = new LinkedList(); 47 | } 48 | 49 | /** 50 | * Returns the underlying collection containing all objects in the group. 51 | * WARNING -- DO NOT ADD TO OR REMOVE FROM THIS COLLECTION DIRECTLY 52 | * @return the data structure containing all objects in the group 53 | */ 54 | public B getObjects() { 55 | return _objects; 56 | } 57 | 58 | /** 59 | * An iterator over the underlying collection. 60 | * WARNING -- DO NOT REMOVE BEINGS USING Iterator.remove() 61 | * @return an iterator over all elements in the group 62 | */ 63 | public Iterator iterator() { 64 | return getObjects().iterator(); 65 | } 66 | 67 | /** 68 | * Performs an update on the group. Override to use. 69 | */ 70 | public void update() {} 71 | 72 | /** 73 | * Adds a being to the group at the end of the next update loop. 74 | * @param being the being to add 75 | * @return the added object 76 | */ 77 | public A add(A being) { 78 | _world.addToGroup(being, this); 79 | return being; 80 | } 81 | 82 | /** 83 | * Removes an object from the group at the end of the next update loop. 84 | * @param object the object to remove 85 | * @return the removed object 86 | */ 87 | public A remove(A object) { 88 | _world.remove(object, this); 89 | if(hasNeedsMoreSamples() && object.needsMoreSamples()) { 90 | _needsMoreSamples.remove(object); 91 | } 92 | return object; 93 | } 94 | 95 | /** 96 | * Adds the contents of another group to this group. 97 | * Will always be O(n) regardless of the underlying collection. 98 | * @param group the objects to add 99 | */ 100 | public void addAll(GenericGroup group) { 101 | for(Iterator iter = group.iterator(); iter.hasNext(); ) { 102 | _world.addToGroup(iter.next(), this); 103 | } 104 | } 105 | 106 | /** 107 | * Removes the contents of a group from this group. 108 | * @param group the objects to remove 109 | */ 110 | public void removeAll(GenericGroup group) { 111 | for(Iterator iter = group.iterator(); iter.hasNext(); ) { 112 | A object = iter.next(); 113 | if(hasNeedsMoreSamples() && object.needsMoreSamples()) { 114 | _needsMoreSamples.remove(object); 115 | } 116 | _world.remove(object, this); 117 | } 118 | } 119 | 120 | /** 121 | * Clears everything from the group at the end of the update. 122 | * This will always be O(n), regardless of the underlying collection. 123 | */ 124 | public void clear() { 125 | for(Iterator iter = iterator(); iter.hasNext(); ){ 126 | _world.remove(iter.next(), this); 127 | } 128 | _needsMoreSamples.clear(); 129 | } 130 | 131 | /** 132 | * Deletes everything in the group from the world at the end of the update. 133 | * Note: this means the group and its beings are totally destroyed! 134 | * They will be removed from any other groups they are in. 135 | * This will always be O(n), regardless of the underlying collection. 136 | */ 137 | public void destroy() { 138 | for(Iterator iter = iterator(); iter.hasNext(); ) { 139 | _world.delete(iter.next()); 140 | } 141 | _needsMoreSamples.clear(); 142 | } 143 | 144 | /** 145 | * @return the number of beings contained by the group 146 | */ 147 | public int size() { 148 | return _objects.size(); 149 | } 150 | 151 | /** 152 | * @param world the world the group should be contained by 153 | */ 154 | public void setWorld(World world) { 155 | this._world = world; 156 | } 157 | 158 | /** 159 | * @return the world currently containing the group 160 | */ 161 | public World getWorld() { 162 | return _world; 163 | } 164 | 165 | void addNeedsMoreSamples(A object) { 166 | _needsMoreSamples.addLast(object); 167 | } 168 | 169 | Iterator getNeedsMoreSamples() { 170 | return _needsMoreSamples.iterator(); 171 | } 172 | 173 | /** 174 | * Used internally. 175 | * Whether the group contains objects that need more samples on this update. 176 | * @return true if objects within the group need more samples, false if no objects do 177 | */ 178 | public boolean hasNeedsMoreSamples() { 179 | return !_needsMoreSamples.isEmpty(); 180 | } 181 | 182 | void clearNeedsMoreSamples() { 183 | _needsMoreSamples.clear(); 184 | } 185 | 186 | //Methods for receiving methods from PostOffice, defined in subscriber interfaces 187 | //Left blank here, must be overrided by user to add functionality 188 | /** 189 | * Override if you want your group to handle Key messages 190 | */ 191 | public void receive(KeyMessage m) { 192 | //VOID 193 | } 194 | /** 195 | * Override if you want your group to handle Mouse messages 196 | */ 197 | public void receive(MouseMessage m) { 198 | //VOID 199 | } 200 | /** 201 | * Override if you want your group to handle Mouse Wheel messages 202 | */ 203 | public void receive(MouseWheelMessage m) { 204 | //VOID 205 | } 206 | /** 207 | * Override if you want your group to handle OSC messages 208 | */ 209 | public void receive(OscMessage m) { 210 | //VOID 211 | } 212 | 213 | } 214 | -------------------------------------------------------------------------------- /hermes/HermesMath.java: -------------------------------------------------------------------------------- 1 | package hermes; 2 | 3 | import processing.core.*; 4 | 5 | /** 6 | * A collection of helpful math utilities. 7 | *

8 | * HINT: Use import static hermes.HermesMath.*; 9 | * to access these methods like you would Processing library functions. 10 | */ 11 | public class HermesMath { 12 | 13 | public static final float INFINITY = Float.POSITIVE_INFINITY; 14 | public static final float MINUS_INFINITY = Float.NEGATIVE_INFINITY; 15 | 16 | /** 17 | * Rotates vector counter-clockwise by an angle theta; 18 | * mutates the vector and returns it. 19 | * @param vector the vector to rotate 20 | * @param theta the angle to rotate counter-clockwise by 21 | * @return the rotated vector (not a new vector, merely a reference to the vector passed in) 22 | */ 23 | public static PVector rotate(PVector vector, double theta) { 24 | float x = (float) (vector.x * Math.cos(theta) - vector.y * Math.sin(theta)); 25 | float y = (float) (vector.x * Math.sin(theta) + vector.y * Math.cos(theta)); 26 | vector.set(x, y, 0); 27 | return vector; 28 | } 29 | 30 | /** 31 | * Same as rotate() but returns an entirely new vector 32 | * (does not mutate). 33 | * @param vector the vector to rotate 34 | * @param theta the angle to rotate counter-clockwise by 35 | * @return the new rotated vector 36 | * @see hermes.HermesMath#rotate(PVector, double) 37 | */ 38 | public static PVector getRotate(PVector vector, double theta) { 39 | float x = (float) (vector.x * Math.cos(theta) - vector.y * Math.sin(theta)); 40 | float y = (float) (vector.x * Math.sin(theta) + vector.y * Math.cos(theta)); 41 | return new PVector(x,y); 42 | } 43 | 44 | /** 45 | * Reverses the direction of a PVector in the coordinate system, 46 | * so the signs of each component are inverted; 47 | * returns a mutated vector. 48 | *

49 | * Example: 50 | * 51 | * PVector v = new PVector(3,-2); 52 | * Math.reverse(v); 53 | * // v.x will now be -3, and v.y will be 2 54 | * 55 | * @param vector the vector to invert 56 | * @return the vector (not a new vector, merely a reference to the vector passed in) 57 | */ 58 | public static PVector reverse(PVector vector) { 59 | vector.x = -vector.x; 60 | vector.y = -vector.y; 61 | vector.z = -vector.z; 62 | return vector; 63 | } 64 | 65 | /** 66 | * Same as reverse(), but does not modify vector. 67 | * @param vector the vector to invert 68 | * @return a new vector that points in the opposite direction of vector 69 | * @see hermes.HermesMath#reverse(PVector) 70 | */ 71 | public static PVector getReverse(PVector vector) { 72 | return new PVector(-vector.x, -vector.y, -vector.z); 73 | } 74 | 75 | /** 76 | * Creates a new PVector at (0,0,0). 77 | * @return zero vector 78 | */ 79 | public static PVector zeroVector() { 80 | return new PVector(0,0,0); 81 | } 82 | 83 | /** 84 | * Mutates a vector to the zero vector 85 | * @param vector the vector 86 | * @return the altered vector 87 | */ 88 | public static PVector zeroVector(PVector vector) { 89 | vector.set(0,0,0); 90 | return vector; 91 | } 92 | 93 | /** 94 | * Factory method for creating PVectors 95 | * @param x 96 | * @param y 97 | * @param z 98 | * @return PVector 99 | */ 100 | public static PVector makeVector(float x, float y, float z) { 101 | return new PVector(x,y,z); 102 | } 103 | 104 | public static PVector makeVector(float x, float y) { 105 | return new PVector(x,y,0.0f); 106 | } 107 | 108 | public static PVector makeVector(double x, double y, double z) { 109 | return new PVector((float)x,(float)y,(float)z); 110 | } 111 | 112 | public static PVector makeVector(double x, double y) { 113 | return new PVector((float)x,(float)y,0.0f); 114 | } 115 | 116 | /** 117 | * Instantiates a new vector that is a carbon copy of a given vector. 118 | * @param vector the vector to copy 119 | * @return the new copy 120 | */ 121 | public static PVector cloneVector(PVector vector) { 122 | return new PVector(vector.x, vector.y, vector.z); 123 | } 124 | 125 | /** 126 | * Gets the square of the magnitude of a PVector. 127 | * Useful when taking the square root to find the true magnitude is not important, 128 | * ie. when trying to identify the largest or smallest PVector in a set 129 | * (saves time). 130 | * @param vector 131 | * @return The square of the magnitude of the vector 132 | */ 133 | public static float mag2(PVector vector) { 134 | return vector.x * vector.x + vector.y * vector.y + vector.z * vector.z; 135 | } 136 | 137 | /** 138 | * Returns the sign of a float, assigns zero a sign of one. 139 | * @param x the float 140 | * @return 1 if x is positive or zero, -1 if x is negative 141 | */ 142 | public static float sign(float x) { 143 | return (x < 0 ? -1.0f : 1.0f); 144 | } 145 | 146 | /** 147 | * Averages two float values. 148 | * @param v1 the first value 149 | * @param v2 the second value 150 | * @return the average 151 | */ 152 | public static float average(float v1, float v2) { 153 | return (v1 + v2) / 2.0f; 154 | } 155 | 156 | /** 157 | * Averages an array of floats. 158 | * @param values the values to average 159 | * @return the average 160 | */ 161 | public static float average(float[] values) { 162 | float acc = 0; 163 | for(int i = 0; i < values.length; i++) 164 | acc += values[i]; 165 | return acc / values.length; 166 | } 167 | 168 | /** 169 | * Returns the angle of the vector. 170 | *

171 | * (1,0) is 0, (0,1) is PI/2, etc. 172 | * @param dir the vector 173 | * @return angle 174 | */ 175 | public static float angle(PVector dir) { 176 | float angle = (float) Math.atan2(dir.y, dir.x); 177 | return (angle < 0 ? angle + (float)Math.PI * 2 : angle); 178 | } 179 | 180 | /** 181 | * Determines if a point is inside a circle. 182 | * @param x x location of point 183 | * @param y y location of point 184 | * @param cirX x location of center of circle 185 | * @param cirY y location of center of circle 186 | * @param radius radius of circle 187 | * @return true if point is in circle, false otherwise 188 | */ 189 | public static boolean inCircle(float x, float y, float cirX, float cirY, float radius) { 190 | float distX = x - cirX; 191 | float distY = y - cirY; 192 | return distX*distX + distY*distY <= radius*radius; 193 | } 194 | 195 | /** 196 | * Determines if a point is inside a circle. 197 | * @param point location of point 198 | * @param circleCenter location of center of circle 199 | * @param radius radius of circle 200 | * @return true if point is in circle, false otherwise 201 | */ 202 | public static boolean inCircle(PVector point, PVector circleCenter, float radius) { 203 | float distX = point.x - circleCenter.x; 204 | float distY = point.y - circleCenter.y; 205 | return distX*distX + distY*distY <= radius*radius; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /hermes/hshape/HCircle.java: -------------------------------------------------------------------------------- 1 | package hermes.hshape; 2 | 3 | import hermes.Hermes; 4 | import processing.core.PVector; 5 | import static hermes.HermesMath.*; 6 | 7 | /** 8 | * Represents a circle. 9 | */ 10 | public class HCircle extends HShape { 11 | 12 | private PVector _center; 13 | private float _radius; 14 | 15 | /** 16 | * Constructor defining center of circle 17 | * as position of object. 18 | * @param position 19 | * @param radius 20 | */ 21 | public HCircle(PVector position, float radius) { 22 | super(position); 23 | 24 | assert radius >= 0 : "In HCircle constructor, radius must be positive"; //TODO can radius be 0? 25 | 26 | _center = new PVector(0,0); 27 | _radius = radius; 28 | } 29 | 30 | /** 31 | * Constructor defining center of circle 32 | * to be a certain distance away from the position. 33 | * @param position 34 | * @param center 35 | * @param radius 36 | */ 37 | public HCircle(PVector position, PVector center, float radius) { 38 | super(position); 39 | 40 | assert center != null : "In HCircle constructor, center must be a valid PVector"; 41 | assert radius >= 0 : "In HCircle constructor, radius must be non-negative"; //TODO can radius be 0? 42 | 43 | _center = center; 44 | _radius = radius; 45 | } 46 | 47 | /** 48 | * Getter for center 49 | */ 50 | public PVector getCenter() { 51 | return _center; 52 | } 53 | 54 | /** 55 | * Getter for radius 56 | */ 57 | public float getRadius() { 58 | return _radius; 59 | } 60 | 61 | @Override 62 | public boolean contains(PVector point) { 63 | float distX = point.x - _position.x; 64 | float distY = point.y - _position.y; 65 | return distX*distX + distY*distY <= _radius*_radius; 66 | } 67 | 68 | @Override 69 | public boolean contains(float x, float y) { 70 | float distX = x - _position.x; 71 | float distY = y - _position.y; 72 | return distX*distX + distY*distY <= _radius*_radius; 73 | } 74 | 75 | @Override 76 | public PVector projectionVector(HShape other) { 77 | assert other != null : "HCircle.collide: other must be a validHShape"; 78 | PVector opposite = other.projectionVector(this); 79 | return opposite == null ? null : reverse(opposite); 80 | } 81 | 82 | @Override 83 | public PVector projectionVector(HPolygon other) { 84 | PVector opposite = other.projectionVector(this); 85 | return opposite == null ? null : reverse(opposite); 86 | } 87 | 88 | @Override 89 | public PVector projectionVector(HCircle other) { 90 | //Get the center of this circle 91 | PVector worldCenterThis = PVector.add(_position, _center); 92 | //Get the center of the other circle 93 | PVector worldCenterOther = PVector.add(other.getPosition(), other.getCenter()); 94 | 95 | //HCircles are colliding if distance between them is less than sum of radii 96 | PVector dir = PVector.sub(worldCenterOther, worldCenterThis); 97 | float distance = dir.mag(); 98 | float sumRadii = _radius + other._radius; 99 | boolean collides = distance <= sumRadii; 100 | 101 | //Projection vector is the unit vector pointing from this circle to other scaled by overlap 102 | if(collides) { 103 | float magnitude = sumRadii - distance; 104 | dir.normalize(); 105 | dir.mult(magnitude); 106 | return dir; 107 | } 108 | else return null; 109 | } 110 | 111 | @Override 112 | public PVector projectionVector(HRectangle other) { 113 | //Get the center of this circle 114 | PVector worldCenter = PVector.add(_center, _position); 115 | //Figure out what voronoi region of the rectangle the circle is in 116 | PVector min = PVector.add(other._position, other.getMin()); 117 | PVector max = PVector.add(other._position, other.getMax()); 118 | if(min.x <= worldCenter.x) { 119 | if(worldCenter.x <= max.x) { 120 | //In regions above or below rectangle, 121 | //compare y projections 122 | float minProject = worldCenter.y - _radius; 123 | float maxProject = worldCenter.y + _radius; 124 | if(min.y <= maxProject && minProject <= max.y) { 125 | float topCollide = max.y - minProject; 126 | float bottomCollide = maxProject - min.y; 127 | return (topCollide >= bottomCollide ? 128 | new PVector(0,bottomCollide): 129 | new PVector(0,-topCollide)); 130 | 131 | } 132 | } 133 | else if(min.y <= worldCenter.y) { 134 | if(worldCenter.y <= max.y) { 135 | //In region directly to right of rectangle 136 | //Compare x projections 137 | float minProject = worldCenter.x - _radius; 138 | if(minProject <= max.x) { 139 | return new PVector(minProject - max.x,0); 140 | } 141 | } 142 | else { 143 | //In region to the right&up of rectangle 144 | //Get projection of both along up-right vertex (max) 145 | return getOverlap(worldCenter,max); 146 | } 147 | } 148 | else { 149 | //In region to the right&down of rectangle 150 | //Get projection of both along bottom-right vertex 151 | PVector brVertex = new PVector(max.x, min.y); 152 | return getOverlap(worldCenter, brVertex); 153 | } 154 | } 155 | else if(min.y <= worldCenter.y) { 156 | if(worldCenter.y <= max.y) { 157 | //In region directly to the left of rectangle 158 | //Compare x projections 159 | float maxProject = worldCenter.x + _radius; 160 | if(min.x <= maxProject) { 161 | return new PVector(maxProject - min.x,0); 162 | } 163 | } 164 | else { 165 | //In region to the left&up of rectangle 166 | //Get projection of both along top-left vertex 167 | PVector tlVertex = new PVector(min.x, max.y); 168 | return getOverlap(worldCenter, tlVertex); 169 | } 170 | } 171 | else { 172 | //In region to the left&down of rectangle 173 | //Get projection of both along bottom-left vertex (min) 174 | return getOverlap(worldCenter, min); 175 | } 176 | 177 | return null; 178 | } 179 | 180 | /** 181 | * Helper method. 182 | * Finds overlap between a circle and the corner of a rectangle. 183 | * @param worldCenter 184 | * @param vertex 185 | * @return projection vector when colliding, null when not 186 | */ 187 | private PVector getOverlap(PVector worldCenter, PVector vertex) { 188 | //Get vector from circle to vertex and overlap of shapes 189 | PVector axis = PVector.sub(vertex, worldCenter); 190 | float overlap = _radius - axis.mag(); 191 | if(overlap >= 0) { 192 | //Get projection vector 193 | axis.normalize(); 194 | axis.mult(overlap); 195 | return axis; 196 | } 197 | else return null; 198 | } 199 | 200 | @Override 201 | public HRectangle getBoundingBox() { 202 | return new HRectangle(PVector.sub(PVector.add(_position, _center),new PVector(_radius,_radius)), 2*_radius, 2*_radius); 203 | } 204 | 205 | @Override 206 | public void draw() { 207 | Hermes.getPApplet().ellipse(_center.x, _center.y, 2*_radius, 2*_radius); 208 | } 209 | 210 | @Override 211 | public String toString() { 212 | return "Position:" + _position + "\nCenter:" + _center + "\nRadius:" + _radius; 213 | } 214 | 215 | } 216 | --------------------------------------------------------------------------------