Java tutorial
/* * Phys2D - a 2D physics engine based on the work of Erin Catto. The * original source remains: * * Copyright (c) 2006 Erin Catto http://www.gphysics.com * * This source is provided under the terms of the BSD License. * * Copyright (c) 2006, Phys2D * All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Phys2D/New Dawn Software nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ package com.phys2d.demo.client; import com.google.gwt.dom.client.Document; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.event.dom.client.MouseUpHandler; import com.google.gwt.user.client.Timer; import net.phys2d.client.math.ROVector2f; import net.phys2d.client.math.Vector2f; import net.phys2d.client.raw.Body; import net.phys2d.client.raw.BodyList; import net.phys2d.client.raw.World; import net.phys2d.client.raw.shapes.Box; import net.phys2d.client.raw.strategies.QuadSpaceStrategy; import gwt.g2d.client.graphics.KnownColor; import gwt.g2d.client.graphics.Surface; import gwt.g2d.client.graphics.shapes.ShapeBuilder; /** * A common demo box super class. * * @author Kevin Glass */ public abstract class GwtDemo { /** The title of the current demo */ protected String title; /** The world containing the physics model */ protected World world = new World(new Vector2f(0.0f, 10.0f), 10, new QuadSpaceStrategy(20, 5)); /** True if the simulation is running */ //private boolean running = true; /** gwt-g2d canvas surface */ private final Surface surface; /** counter for click-generated bodies */ private int clickBodyCounter = 0; /** True if we should reset the demo on the next loop */ protected boolean needsReset; /** True if we should render normals */ private boolean normals = true; /** True if we should render contact points */ private boolean contacts = true; /** x position of last mouse down-click */ private int xDown = 0; /** x position of last mouse down-click */ private int yDown = 0; /** * Create a new demo * * @param title * The title of the demo */ public GwtDemo(String title, Surface surface) { this.title = title; this.surface = surface; } /** * Retrieve the title of the demo * * @return The title of the demo */ public String getTitle() { return title; } /** * Notification that a key was pressed * * @param c * The character of key hit */ protected void keyHit(char c) { if (c == 'r') { needsReset = true; } if (c == 'c') { normals = !normals; contacts = !contacts; } } /** * Initialise the GUI */ private void initGUI() { // NOTE: from GWT docs, using anonymous inner classes for this may result in excess memory usage... surface.addMouseDownHandler(new MouseDownHandler() { public void onMouseDown(MouseDownEvent event) { int xCur = event.getNativeEvent().getClientX() - surface.getAbsoluteLeft() + Document.get().getScrollLeft(); int yCur = event.getNativeEvent().getClientY() - surface.getAbsoluteTop() + Document.get().getScrollTop(); xDown = xCur; yDown = yCur; } }); surface.addMouseUpHandler(new MouseUpHandler() { public void onMouseUp(MouseUpEvent event) { int xCur = event.getNativeEvent().getClientX() - surface.getAbsoluteLeft() + Document.get().getScrollLeft(); int yCur = event.getNativeEvent().getClientY() - surface.getAbsoluteTop() + Document.get().getScrollTop(); int xLeft = Math.min(xCur, xDown); int yTop = Math.min(yCur, yDown); float boxDensity = 0.5f; int boxWidth = 30; int boxHeight = 30; if (xCur != xDown || yCur != yDown) { boxWidth = Math.abs(xCur - xDown); boxHeight = Math.abs(yCur - yDown); } Body clickBody = new Body("ClickBody " + clickBodyCounter, new Box(boxWidth, boxHeight), boxWidth * boxHeight * boxDensity); clickBody.setPosition(xLeft + boxWidth / 2, yTop + boxHeight / 2); world.add(clickBody); } }); } /** * Start the simulation running */ public void start() { initGUI(); initDemo(); // float target = 1000 / 60.0f; // float frameAverage = target; // long lastFrame = System.currentTimeMillis(); // float yield = 10000f; // float damping = 0.1f; // long renderTime = 0; // long logicTime = 0; Timer updateTimer = new Timer() { public void run() { update(); } }; updateTimer.scheduleRepeating(50); // update(); } /** * Update the demo - just in case we want to add anything over the top */ protected void update() { // adaptive timing loop from Master Onyx //long timeNow = System.currentTimeMillis(); // frameAverage = (frameAverage * 10 + (timeNow - lastFrame)) / 11; // lastFrame = timeNow; // yield+=yield*((target/frameAverage)-1)*damping+0.05f; // // for(int i=0;i<yield;i++) { // Thread.yield(); // } // render //long beforeRender = System.currentTimeMillis(); // Graphics2D g = (Graphics2D) strategy.getDrawGraphics(); // g.setColor(Color.white); // g.fillRect(0,0,500,500); // // draw(g); // renderGUI(g); // g.setColor(Color.black); // g.drawString("FAv: "+frameAverage,10,50); // g.drawString("FPS: "+(int) (1000 / frameAverage),10,70); // g.drawString("Yield: "+yield,10,90); // g.drawString("Arbiters: "+world.getArbiters().size(),10,110); // g.drawString("Bodies: "+world.getBodies().size(),10,130); // g.drawString("R: "+renderTime,10,150); // g.drawString("L: "+logicTime,10,170); // g.drawString("Energy: "+world.getTotalEnergy(),10,190); // g.dispose(); // strategy.show(); // some temporary code for outputting body values BodyList bodies = world.getBodies(); // refresh canvas surface.fillBackground(KnownColor.LIGHT_BLUE); for (int i = 0; i < bodies.size(); i++) { Body body = bodies.get(i); // System.out.println(body); ROVector2f pos = body.getPosition(); surface.save().setFillStyle(KnownColor.GREEN_YELLOW) .fillShape(new ShapeBuilder().drawCircle(pos.getX(), pos.getY(), 5).build()).restore(); drawBody(body); } // renderTime = System.currentTimeMillis() - beforeRender; // update data model // long beforeLogic = System.currentTimeMillis(); for (int i = 0; i < 5; i++) { world.step(); } // logicTime = System.currentTimeMillis() - beforeLogic; if (needsReset) { world.clear(); initDemo(); needsReset = false; // frameAverage = target; // yield = 10000f; } } /** * Demo customisable GUI render * * @param g * The graphics context to use for rendering here */ // protected void renderGUI(Graphics2D g) { // g.setColor(Color.black); // g.drawString("R - Restart Demo",15,430); // } // /** // * Draw a specific contact point determined from the simulation // * // * @param g The graphics context on which to draw // * @param contact The contact to draw // */ // protected void drawContact(Graphics2D g, Contact contact) { // int x = (int) contact.getPosition().getX(); // int y = (int) contact.getPosition().getY(); // if (contacts) { // g.setColor(Color.blue); // g.fillOval(x-3,y-3,6,6); // } // // if (normals) { // int dx = (int) (contact.getNormal().getX() * 10); // int dy = (int) (contact.getNormal().getY() * 10); // g.setColor(Color.darkGray); // g.drawLine(x,y,x+dx,y+dy); // } // } // /** * Draw a body * @param body The body to be drawn */ protected void drawBody(Body body) { if (body.getShape() instanceof Box) { drawBoxBody(body, (Box) body.getShape()); } // if (body.getShape() instanceof Circle) { // drawCircleBody(g,body,(Circle) body.getShape()); // } // if (body.getShape() instanceof Line) { // drawLineBody(g,body,(Line) body.getShape()); // } // if (body.getShape() instanceof Polygon) { // drawPolygonBody(g,body,(Polygon) body.getShape()); } // } // /** // * Draw a polygon into the demo // * // * @param g The graphics to draw the poly onto // * @param body The body describing the poly's position // * @param poly The poly to be drawn // */ // protected void drawPolygonBody(Graphics2D g, Body body, Polygon poly) { // g.setColor(Color.black); // // ROVector2f[] verts = poly.getVertices(body.getPosition(), // body.getRotation()); // for ( int i = 0, j = verts.length-1; i < verts.length; j = i, i++ ) { // g.drawLine( // (int) (0.5f + verts[i].getX()), // (int) (0.5f + verts[i].getY()), // (int) (0.5f + verts[j].getX()), // (int) (0.5f + verts[j].getY())); // } // } // // /** // * Draw a line into the demo // * // * @param g The graphics to draw the line onto // * @param body The body describing the line's position // * @param line The line to be drawn // */ // protected void drawLineBody(Graphics2D g, Body body, Line line) { // g.setColor(Color.black); // // // // float x = body.getPosition().getX(); // // float y = body.getPosition().getY(); // // float dx = line.getDX(); // // float dy = line.getDY(); // // // // g.drawLine((int) x,(int) y,(int) (x+dx),(int) (y+dy)); // Vector2f[] verts = line.getVertices(body.getPosition(), // body.getRotation()); // g.drawLine( // (int) verts[0].getX(), // (int) verts[0].getY(), // (int) verts[1].getX(), // (int) verts[1].getY()); // } // // /** // * Draw a circle in the world // * // * @param g The graphics contact on which to draw // * @param body The body to be drawn // * @param circle The shape to be drawn // */ // protected void drawCircleBody(Graphics2D g, Body body, Circle circle) { // g.setColor(Color.black); // float x = body.getPosition().getX(); // float y = body.getPosition().getY(); // float r = circle.getRadius(); // float rot = body.getRotation(); // float xo = (float) (Math.cos(rot) * r); // float yo = (float) (Math.sin(rot) * r); // // g.drawOval((int) (x-r),(int) (y-r),(int) (r*2),(int) (r*2)); // g.drawLine((int) x,(int) y,(int) (x+xo),(int) (y+yo)); // } // /** * Draw a box in the world * * @param body * The body to be drawn * @param box * The shape to be drawn */ protected void drawBoxBody(Body body, Box box) { Vector2f[] pts = box.getPoints(body.getPosition(), body.getRotation()); Vector2f v1 = pts[0]; Vector2f v2 = pts[1]; Vector2f v3 = pts[2]; Vector2f v4 = pts[3]; // g.setColor(Color.black); // g.drawLine((int) v1.x, (int) v1.y, (int) v2.x, (int) v2.y); // g.drawLine((int) v2.x, (int) v2.y, (int) v3.x, (int) v3.y); // g.drawLine((int) v3.x, (int) v3.y, (int) v4.x, (int) v4.y); // g.drawLine((int) v4.x, (int) v4.y, (int) v1.x, (int) v1.y); surface.save().setFillStyle(KnownColor.DARK_BLUE) .strokeShape(new ShapeBuilder().drawLineSegment(v1.x, v1.y, v2.x, v2.y) .drawLineSegment(v2.x, v2.y, v3.x, v3.y).drawLineSegment(v3.x, v3.y, v4.x, v4.y) .drawLineSegment(v4.x, v4.y, v1.x, v1.y).build()) .fillShape(new ShapeBuilder().drawCircle(v1.x, v1.y, 2).drawCircle(v2.x, v2.y, 2) .drawCircle(v3.x, v3.y, 2).drawCircle(v4.x, v4.y, 2).build()) .restore(); } // /** // * Draw a joint // * // * @param g The graphics contact on which to draw // * @param j The joint to be drawn // */ // public void drawJoint(Graphics2D g, Joint j) { // if (j instanceof FixedJoint) { // FixedJoint joint = (FixedJoint) j; // // g.setColor(Color.red); // float x1 = joint.getBody1().getPosition().getX(); // float x2 = joint.getBody2().getPosition().getX(); // float y1 = joint.getBody1().getPosition().getY(); // float y2 = joint.getBody2().getPosition().getY(); // // g.drawLine((int) x1,(int) y1,(int) x2,(int) y2); // } // if(j instanceof SlideJoint){ // SlideJoint joint = (SlideJoint) j; // // Body b1 = joint.getBody1(); // Body b2 = joint.getBody2(); // // Matrix2f R1 = new Matrix2f(b1.getRotation()); // Matrix2f R2 = new Matrix2f(b2.getRotation()); // // ROVector2f x1 = b1.getPosition(); // Vector2f p1 = MathUtil.mul(R1,joint.getAnchor1()); // p1.add(x1); // // ROVector2f x2 = b2.getPosition(); // Vector2f p2 = MathUtil.mul(R2,joint.getAnchor2()); // p2.add(x2); // // Vector2f im = new Vector2f(p2); // im.sub(p1); // im.normalise(); // // // // g.setColor(Color.red); // g.drawLine((int)p1.x,(int)p1.y,(int)(p1.x+im.x*joint.getMinDistance()),(int)(p1.y+im.y*joint.getMinDistance())); // g.setColor(Color.blue); // g.drawLine((int)(p1.x+im.x*joint.getMinDistance()),(int)(p1.y+im.y*joint.getMinDistance()),(int)(p1.x+im.x*joint.getMaxDistance()),(int)(p1.y+im.y*joint.getMaxDistance())); // } // if(j instanceof AngleJoint){ // AngleJoint angleJoint = (AngleJoint)j; // Body b1 = angleJoint.getBody1(); // Body b2 = angleJoint.getBody2(); // float RA = j.getBody1().getRotation() + angleJoint.getRotateA(); // float RB = j.getBody1().getRotation() + angleJoint.getRotateB(); // // Vector2f VA = new Vector2f((float) Math.cos(RA), (float) Math.sin(RA)); // Vector2f VB = new Vector2f((float) Math.cos(RB), (float) Math.sin(RB)); // // Matrix2f R1 = new Matrix2f(b1.getRotation()); // Matrix2f R2 = new Matrix2f(b2.getRotation()); // // ROVector2f x1 = b1.getPosition(); // Vector2f p1 = MathUtil.mul(R1,angleJoint.getAnchor1()); // p1.add(x1); // // ROVector2f x2 = b2.getPosition(); // Vector2f p2 = MathUtil.mul(R2,angleJoint.getAnchor2()); // p2.add(x2); // // g.setColor(Color.red); // g.drawLine((int)p1.x,(int)p1.y,(int)(p1.x+VA.x*20),(int)(p1.y+VA.y*20)); // g.drawLine((int)p1.x,(int)p1.y,(int)(p1.x+VB.x*20),(int)(p1.y+VB.y*20)); // } // if (j instanceof BasicJoint) { // BasicJoint joint = (BasicJoint) j; // // Body b1 = joint.getBody1(); // Body b2 = joint.getBody2(); // // Matrix2f R1 = new Matrix2f(b1.getRotation()); // Matrix2f R2 = new Matrix2f(b2.getRotation()); // // ROVector2f x1 = b1.getPosition(); // Vector2f p1 = MathUtil.mul(R1,joint.getLocalAnchor1()); // p1.add(x1); // // ROVector2f x2 = b2.getPosition(); // Vector2f p2 = MathUtil.mul(R2,joint.getLocalAnchor2()); // p2.add(x2); // // g.setColor(Color.red); // g.drawLine((int) x1.getX(), (int) x1.getY(), (int) p1.x, (int) p1.y); // g.drawLine((int) p1.x, (int) p1.y, (int) x2.getX(), (int) x2.getY()); // g.drawLine((int) x2.getX(), (int) x2.getY(), (int) p2.x, (int) p2.y); // g.drawLine((int) p2.x, (int) p2.y, (int) x1.getX(), (int) x1.getY()); // } // if(j instanceof DistanceJoint){ // DistanceJoint joint = (DistanceJoint) j; // // Body b1 = joint.getBody1(); // Body b2 = joint.getBody2(); // // Matrix2f R1 = new Matrix2f(b1.getRotation()); // Matrix2f R2 = new Matrix2f(b2.getRotation()); // // ROVector2f x1 = b1.getPosition(); // Vector2f p1 = MathUtil.mul(R1,joint.getAnchor1()); // p1.add(x1); // // ROVector2f x2 = b2.getPosition(); // Vector2f p2 = MathUtil.mul(R2,joint.getAnchor2()); // p2.add(x2); // // g.setColor(Color.red); // g.drawLine((int) p1.getX(), (int) p1.getY(), (int) p2.x, (int) p2.y); // } // if (j instanceof SpringJoint) { // SpringJoint joint = (SpringJoint) j; // // Body b1 = joint.getBody1(); // Body b2 = joint.getBody2(); // // Matrix2f R1 = new Matrix2f(b1.getRotation()); // Matrix2f R2 = new Matrix2f(b2.getRotation()); // // ROVector2f x1 = b1.getPosition(); // Vector2f p1 = MathUtil.mul(R1,joint.getLocalAnchor1()); // p1.add(x1); // // ROVector2f x2 = b2.getPosition(); // Vector2f p2 = MathUtil.mul(R2,joint.getLocalAnchor2()); // p2.add(x2); // // g.setColor(Color.red); // g.drawLine((int) x1.getX(), (int) x1.getY(), (int) p1.x, (int) p1.y); // g.drawLine((int) p1.x, (int) p1.y, (int) p2.getX(), (int) p2.getY()); // g.drawLine((int) p2.getX(), (int) p2.getY(), (int) x2.getX(), (int) // x2.getY()); // } // } // // /** // * Draw the whole simulation // * // * @param g The graphics context on which to draw // */ // protected void draw(Graphics2D g) { // BodyList bodies = world.getBodies(); // // for (int i=0;i<bodies.size();i++) { // Body body = bodies.get(i); // // drawBody(g, body); // } // // JointList joints = world.getJoints(); // // for (int i=0;i<joints.size();i++) { // Joint joint = joints.get(i); // // drawJoint(g, joint); // } // // ArbiterList arbs = world.getArbiters(); // // for (int i=0;i<arbs.size();i++) { // Arbiter arb = arbs.get(i); // // Contact[] contacts = arb.getContacts(); // int numContacts = arb.getNumContacts(); // // for (int j=0;j<numContacts;j++) { // drawContact(g, contacts[j]); // } // } // } /** * Initialise the demo - clear the world */ public final void initDemo() { world.clear(); world.setGravity(0, 10); System.out.println("Initialising:" + getTitle()); init(world); } /** * Should be implemented by the demo, add the bodies/joints to the world. * * @param world * The world in which the simulation is going to run */ protected abstract void init(World world); }