package activeTable; import render.*; public class ActiveTableDisplay extends RenderApplet implements ActiveTableInterface { public static boolean debug = false; // DATA VehicleInfo vi[]; public Geometry table, vehicle[]; Material white, silver, black, red, shade; double Y = -.1; final static double MAXSPEED = .1355; public boolean connect() { // MAKE SURE TO DECLARE A REASONABLE NUMBER OF VEHICLES N = Math.max(0, Math.min(10, N));; this.N = N; // CREATE ALL THE VEHICLE INTERFACES vi = new VehicleInfo[N]; for (int i = 0 ; i < N ; i++) vi[i] = new VehicleInfo(); // INITIALIZE MATERIALS white = (new Material()).setColor(.5,.5,.5, 0,0,0,20, .6,.6,.6); silver = (new Material()).setColor(.3,.3,.3, 1,1,1,20, .4,.4,.4); black = (new Material()).setColor(.5,.5,.5, 0,0,0,20); red = (new Material()).setColor(0,0,0, 0,0,0,20, 1,0,0); shade = (new Material()).setColor(0,0,0).setTransparency(.7); push(); translate(0,Y,0); transform(world); pop(); // INITIALIZE THE TABLE TOP push(); table = world.add().cube(); Material tableColor = new Material(); tableColor.setColor(.3,.3,.3, 0,0,0,1, .5,.5,.5); table.setMaterial(tableColor); translate(0,-.01,0); scale(.5,.01,.5); transform(table); pop(); // INITIALIZE THE TABLE BASE push(); Geometry base = world.add().cube(); base.setMaterial(black); translate(0,-.504,0); scale(.504,.5,.504); transform(base); pop(); // CREATE ALL THE VEHICLES vehicle = new Geometry[N]; for (int i = 0 ; i < N ; i++) createVehicle(vehicle[i] = world.add()); return true; } // INITIALIZATION (CALLED ONCE) public void initialize() { // LIGHTS, CAMERA, ... setBgColor(.4,.4,.5); // SKY BLUE BACKGROUND setFOV(.1); addLight(1,1,1, .7,.7,1); addLight(-1,0,-1, .5,.4,.3); addLight(0,-1,-.5, .2,.2,.2); renderer.headsUp(true); // MAKE SURE AT LEAST ONE VEHICLE HAS BEEN DECLARED if (vi == null) connect(); // INITIALIZE VEHICLE POSITIONS/DIRECTIONS for (int i = 0 ; i < N ; i++) { push(); translate(vi[i].x, 0, -vi[i].y); transform(vehicle[i]); pop(); push(); rotateY(-vi[i].theta); transform(vehicle[i].child[0]); pop(); } } double dt = 0, oldTime = 0; public void animate(double time) { dt = time - oldTime; oldTime = time; for (int j = 0 ; j < N ; j++) vehicle[j].child[0].child[0].setMaterial(isAtGoal(j) ? black : red); } int puck; public void setPuck(int i) { puck = i; } public int getPuck() { return puck; } int N; public void setMaxVehicles(int n) { N = n; } public int getMaxVehicles() { return N; } public VehicleInfo getVehicleInfo(int i) { return vi[i]; } public boolean isAtGoal(int i) { Matrix MV = vehicle[i].getMatrix(); double dx = vi[i].x - MV.get(0,3); double dy = vi[i].y + MV.get(2,3); double distanceToTarget = Math.sqrt(dx*dx + dy*dy); return distanceToTarget <= .02 && Math.abs(getTheta(i, vi[i]) - vi[i].theta) <= .02; } public double getTheta(int i, VehicleInfo vi) { Matrix P = vehicle[i].child[0].getMatrix(); double theta = Math.atan2(P.get(0,2),P.get(0,0)); if (theta > vi.theta + Math.PI) theta -= 2*Math.PI; if (theta < vi.theta - Math.PI) theta += 2*Math.PI; return theta; } double speed = 0; public void setVehicleInfo(int i, VehicleInfo vi) { Matrix MP = vehicle[i].child[0].getMatrix(); double theta = getTheta(i, vi); Matrix MV = vehicle[i].getMatrix(); double dx = vi.x - MV.get(0,3); double dy = vi.y + MV.get(2,3); double distanceToTarget = Math.sqrt(dx*dx + dy*dy); double forwardTheta = Math.atan2(dy,dx); if (forwardTheta > theta + Math.PI) forwardTheta -= 2*Math.PI; if (forwardTheta < theta - Math.PI) forwardTheta += 2*Math.PI; double reverseTheta = forwardTheta + Math.PI; if (reverseTheta > theta + Math.PI) reverseTheta -= 2*Math.PI; if (reverseTheta < theta - Math.PI) reverseTheta += 2*Math.PI; // ALREADY AT TARGET? ROTATE INTO PROPER ORIENTATION if (distanceToTarget < .01) { MP.rotateY(limit(vi.theta - theta,dt)); return; } // ALREADY AIMED AT TARGET? MOVE TOWARD TARGET double angleDiff = Math.abs(forwardTheta - theta); if (angleDiff < .01) { if (! isBlocked(i)) { speed = .5 * speed + .5 * MAXSPEED; double travel = MAXSPEED * dt; double s = Math.sqrt(dx*dx + dy*dy); MV.translate(travel * dx/s, 0, travel * -dy/s); } else speed = .5 * speed; return; } // NOT YET AIMED AT TARGET? ROTATE TO FACE TARGET MP.rotateY(limit(forwardTheta - theta,dt)); } boolean isBlocked(int i) { Matrix MV = vehicle[i].getMatrix(); Matrix MP = vehicle[i].child[0].getMatrix(); double x = MV.get(0,3) + MAXSPEED * dt * MP.get(0,0); double z = MV.get(2,3) + MAXSPEED * dt * MP.get(2,0); double dd = DIAMETER * DIAMETER; for (int j = 0 ; j < N ; j++) if (j != i) { MV = vehicle[j].getMatrix(); double dx = MV.get(0,3) - x; double dz = MV.get(2,3) - z; if (dx * dx + dz * dz < dd) return true; } return false; } double limit(double t, double bound) { return Math.max(-bound, Math.min(bound, t)); } // CREATE A SINGLE VEHICLE // ALL DIMENSIONS ARE IN METERS public final static double DIAMETER = .0835; // OUTER BODY DIAMETER public final static double WHEELBASE = .0600; // DISTANCE BETWEEN LEFT/RIGHT WHEELS public final static double WHEEL_DIAMETER = .0330; // DIAMETER OF ONE WHEEL public final static double Y_TOP = .0410; // Y AT TOP OF VEHICLE BODY public final static double Y_BOTTOM = .0050; // Y AT BOTTOM OF VEHICLE BODY void createVehicle(Geometry vehicle) { double radius = DIAMETER / 2; double height = Y_TOP - Y_BOTTOM; double wheel_radius = WHEEL_DIAMETER / 2; // THE FIRST CHILD OBJECT HAS THE VEHICLE'S PARTS Geometry parts = vehicle.add(); // MAKE THE (FAKE) GROUND SHADOW Geometry shadow = vehicle.add().disk(20); shadow.setMaterial(shade); push(); translate( -.07*radius,.002, -.07*radius); scale(1.07*radius, .001, 1.07*radius); rotateX(-Math.PI/2); transform(shadow); pop(); //----------------- MAKE ALL THE PARTS OF THE VEHICLE // THE FRONT-POINTING ARROW ON TOP Geometry arrow = parts.add().disk(3); arrow.setMaterial(black); push(); translate(-WHEELBASE/9, 1.1 * Y_TOP, 0); scale(WHEELBASE/2, 1, WHEELBASE/4); rotateX(-Math.PI/2); transform(arrow); pop(); // THE WHITE CYLINDRICAL BODY Geometry body = parts.add().cylinder(20); body.setMaterial(white); push(); translate(0, Y_BOTTOM + height/2, 0); scale(radius, height/2, radius); rotateX(Math.PI/2); transform(body); pop(); // THE TWO WHEELS WITH THEIR TIRES for (int lr = 0 ; lr <= 1 ; lr++) { push(); translate(0, wheel_radius, lr==0 ? WHEELBASE/2 : -WHEELBASE/2); scale(wheel_radius, wheel_radius, wheel_radius); if (isDetailedModel) { Geometry wheel = parts.add().pill(20, .3,.7); wheel.setMaterial(silver); push(); scale(.9,.9,.22); transform(wheel); pop(); } Geometry tire = parts.add().setMaterial(black); push(); if (isDetailedModel) { scale(1,1,.11); transform(tire.pill(20,.2,.8)); } else { scale(1,1,.22); transform(tire.cylinder(20)); } pop(); pop(); } } boolean isDetailedModel = false; }