Changeset 23190 in osm for applications/editors/josm/plugins/wms-turbo-challenge2
- Timestamp:
- 2010-09-15T18:54:18+02:00 (14 years ago)
- Location:
- applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge/EngineSound.java
r19990 r23190 17 17 18 18 class engine { 19 20 21 19 public engine() { 20 rpm = 0.0; 21 } 22 22 23 24 25 26 23 public void start() { 24 rpm = 0.3; 25 speed = 0.0; 26 n = 0; 27 27 28 29 28 if (output != null) 29 stop(); 30 30 31 32 33 34 31 AudioFormat output_format = 32 new AudioFormat(S_RATE, 16, 1, true, true); 33 DataLine.Info info = 34 new DataLine.Info(SourceDataLine.class, output_format); 35 35 36 37 38 39 40 41 42 43 44 45 46 47 48 36 /* Get the data line, open it and initialise the device */ 37 try { 38 output = (SourceDataLine) AudioSystem.getLine(info); 39 output.open(output_format); 40 output.start(); 41 frames_written = 0; 42 reschedule(0); 43 } catch (Exception e) { 44 output = null; 45 System.out.println("Audio not available: " + 46 e.getClass().getSimpleName()); 47 } 48 } 49 49 50 51 52 50 public void stop() { 51 rpm = 0.0; 52 n = 0; 53 53 54 55 54 if (output == null) 55 return; 56 56 57 58 57 tick.cancel(); 58 tick.purge(); 59 59 60 61 62 63 64 60 output.stop(); 61 output.flush(); 62 output.close(); 63 output = null; 64 } 65 65 66 67 68 69 70 71 66 public void set_speed(double speed) { 67 /* This engine is equipped with an automatic gear box that 68 * switches gears when the RPM becomes too high or too low. */ 69 double new_speed = Math.abs(speed); 70 double accel = new_speed - this.speed; 71 this.speed = new_speed; 72 72 73 74 75 76 77 73 if (accel > 0.05) 74 accel = 0.05; 75 else if (accel < -0.05) 76 accel = -0.05; 77 rpm += accel; 78 78 79 80 81 82 83 84 85 86 87 88 89 90 91 79 if (accel > 0.0 && rpm > 1.0 + n * 0.2 && speed > 0.0) { 80 rpm = 0.3 + n * 0.2; 81 n ++; 82 } else if (accel < 0.0 && rpm < 0.3) { 83 if (n > 0) { 84 rpm = 0.7 + n * 0.1; 85 n --; 86 } else 87 rpm = 0.2; 88 } 89 if (speed < 2.0) 90 n = 0; 91 } 92 92 93 94 95 93 public boolean is_on() { 94 return output != null; 95 } 96 96 97 98 99 97 protected double speed; 98 protected double rpm; 99 protected int n; 100 100 101 102 103 101 protected SourceDataLine output = null; 102 protected long frames_written; 103 protected Timer tick = new Timer(); 104 104 105 106 107 108 105 /* Audio parameters. */ 106 protected static final int S_RATE = 44100; 107 protected static final int MIN_BUFFER = 4096; 108 protected static final double volume = 0.3; 109 109 110 111 112 113 110 protected class audio_task extends TimerTask { 111 public void run() { 112 if (output == null) 113 return; 114 114 115 116 117 118 119 120 121 122 115 /* If more than a two buffers left to play, 116 * reschedule and try to wake up closer to the 117 * end of already written data. */ 118 long frames_current = output.getLongFramePosition(); 119 if (frames_current < frames_written - MIN_BUFFER * 2) { 120 reschedule(frames_current); 121 return; 122 } 123 123 124 125 126 127 128 129 130 124 /* Build a new buffer */ 125 /* double freq = 20 * Math.pow(1.3, rpm * 5.0); */ 126 double freq = (rpm - 0.1) * 160.0; 127 int wavelen = (int) (S_RATE / freq); 128 int bufferlen = MIN_BUFFER - (MIN_BUFFER % wavelen) + 129 wavelen; 130 int value = (int) (0x7fff * volume); 131 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 132 bufferlen *= 2; 133 byte[] buffer = new byte[bufferlen]; 134 for (int b = 0; b < bufferlen; ) { 135 int j; 136 for (j = wavelen / 2; j > 0; j --) { 137 buffer[b ++] = (byte) (value >> 8); 138 buffer[b ++] = (byte) (value & 0xff); 139 } 140 value = 0x10000 - value; 141 for (j = wavelen - wavelen / 2; j > 0; j --) { 142 buffer[b ++] = (byte) (value >> 8); 143 buffer[b ++] = (byte) (value & 0xff); 144 } 145 value = 0x10000 - value; 146 } 147 147 148 149 148 frames_written += 149 output.write(buffer, 0, bufferlen) / 2; 150 150 151 152 153 151 reschedule(frames_current); 152 } 153 } 154 154 155 156 157 158 159 160 161 162 163 164 155 protected void reschedule(long frames) { 156 /* Send a new buffer as close to the end of the 157 * currently playing buffer as possible (aim at 158 * about half into the last frame). */ 159 long delay = (frames_written - frames - MIN_BUFFER / 2) * 160 1000 / S_RATE; 161 if (delay < 0) 162 delay = 0; 163 tick.schedule(new audio_task(), delay); 164 } 165 165 } -
applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge/FakeMapView.java
r21477 r23190 26 26 27 27 class fake_map_view extends MapView { 28 29 28 public ProjectionBounds view_bounds; 29 public MapView parent; 30 30 31 32 33 34 35 36 31 public Graphics2D graphics; 32 public BufferedImage ground_image; 33 public int ground_width = -1; 34 public int ground_height = -1; 35 public double scale; 36 public double max_east_west; 37 37 38 39 40 41 38 public fake_map_view(MapView parent, double scale) { 39 super(null); //TODO MapView constructor contains registering listeners and other code, that probably shouldn't be called in fake map view 40 this.parent = parent; 41 this.scale = scale; 42 42 43 44 45 46 43 ProjectionBounds parent_bounds = parent.getProjectionBounds(); 44 max_east_west = 45 parent_bounds.max.east() - parent_bounds.min.east(); 46 } 47 47 48 49 48 public void setProjectionBounds(ProjectionBounds bounds) { 49 view_bounds = bounds; 50 50 51 52 51 if (bounds.max.east() - bounds.min.east() > max_east_west) { 52 max_east_west = bounds.max.east() - bounds.min.east(); 53 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 54 /* We need to set the parent MapView's bounds (i.e. 55 * zoom level) to the same as ours max possible 56 * bounds to avoid WMSLayer thinking we're zoomed 57 * out more than we are or it'll pop up an annoying 58 * "requested area is too large" popup. 59 */ 60 EastNorth parent_center = parent.getCenter(); 61 parent.zoomTo(new ProjectionBounds( 62 new EastNorth( 63 parent_center.east() - 64 max_east_west / 2, 65 parent_center.north()), 66 new EastNorth( 67 parent_center.east() + 68 max_east_west / 2, 69 parent_center.north()))); 70 70 71 72 73 74 75 76 77 78 71 /* Request again because NavigatableContent adds 72 * a border just to be sure. 73 */ 74 ProjectionBounds new_bounds = 75 parent.getProjectionBounds(); 76 max_east_west = 77 new_bounds.max.east() - new_bounds.min.east(); 78 } 79 79 80 81 82 83 80 Point vmin = getPoint(bounds.min); 81 Point vmax = getPoint(bounds.max); 82 int w = vmax.x + 1; 83 int h = vmin.y + 1; 84 84 85 86 87 88 85 if (w <= ground_width && h <= ground_height) { 86 graphics.setClip(0, 0, w, h); 87 return; 88 } 89 89 90 91 92 93 90 if (w > ground_width) 91 ground_width = w; 92 if (h > ground_height) 93 ground_height = h; 94 94 95 96 97 98 99 100 95 ground_image = new BufferedImage(ground_width, 96 ground_height, 97 BufferedImage.TYPE_INT_RGB); 98 graphics = ground_image.createGraphics(); 99 graphics.setClip(0, 0, w, h); 100 } 101 101 102 103 104 102 public ProjectionBounds getProjectionBounds() { 103 return view_bounds; 104 } 105 105 106 107 108 109 110 106 public Point getPoint(EastNorth p) { 107 double x = p.east() - view_bounds.min.east(); 108 double y = view_bounds.max.north() - p.north(); 109 x /= this.scale; 110 y /= this.scale; 111 111 112 113 112 return new Point((int) x, (int) y); 113 } 114 114 115 116 117 118 119 115 public EastNorth getEastNorth(int x, int y) { 116 return new EastNorth( 117 view_bounds.min.east() + x * this.scale, 118 view_bounds.min.north() - y * this.scale); 119 } 120 120 121 122 123 121 public boolean isVisible(int x, int y) { 122 return true; 123 } 124 124 125 126 127 125 public Graphics getGraphics() { 126 return graphics; 127 } 128 128 129 130 129 public void repaint() { 130 } 131 131 } -
applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge/GameWindow.java
r19990 r23190 42 42 43 43 public class GameWindow extends JFrame implements ActionListener { 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 44 public GameWindow(Layer ground) { 45 setTitle("The Ultimate WMS Super-speed Turbo Challenge II"); 46 setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 47 setUndecorated(true); 48 setSize(s.getScreenSize().width, s.getScreenSize().height); 49 setLocationRelativeTo(null); 50 setResizable(false); 51 52 while (s.getScreenSize().width < width * scale || 53 s.getScreenSize().height < height * scale) 54 scale --; 55 add(panel); 56 57 setVisible(true); 58 59 /* TODO: "Intro" screen perhaps with "Hall of Fame" */ 60 61 screen_image = new BufferedImage(width, height, 62 BufferedImage.TYPE_INT_RGB); 63 screen = screen_image.getGraphics(); 64 65 this.ground = ground; 66 ground_view = new fake_map_view(Main.map.mapView, 0.0000001); 67 68 /* Retrieve start position */ 69 EastNorth start = ground_view.parent.getCenter(); 70 lat = start.north(); 71 lon = start.east(); 72 73 addKeyListener(new TAdapter()); 74 75 timer = new Timer(80, this); 76 timer.start(); 77 78 car_gps = new gps(); 79 car_gps.start(); 80 81 car_engine = new engine(); 82 car_engine.start(); 83 84 for (int i = 0; i < maxsprites; i ++) 85 sprites[i] = new sprite_pos(); 86 87 generate_sky(); 88 } 89 90 protected engine car_engine; 91 92 protected gps car_gps; 93 protected class gps extends Timer implements ActionListener { 94 public gps() { 95 super(1000, null); 96 addActionListener(this); 97 98 trackSegs = new ArrayList<Collection<WayPoint>>(); 99 } 100 101 protected Collection<WayPoint> segment; 102 protected Collection<Collection<WayPoint>> trackSegs; 103 104 public void actionPerformed(ActionEvent e) { 105 /* We should count the satellites here, see if we 106 * have a fix and add any distortions. */ 107 108 segment.add(new WayPoint(Main.proj.eastNorth2latlon( 109 new EastNorth(lon, lat)))); 110 } 111 112 public void start() { 113 super.start(); 114 115 /* Start recording */ 116 segment = new ArrayList<WayPoint>(); 117 trackSegs.add(segment); 118 actionPerformed(null); 119 } 120 121 public void save_trace() { 122 int len = 0; 123 for (Collection<WayPoint> seg : trackSegs) 124 len += seg.size(); 125 126 /* Don't save traces shorter than 5s */ 127 if (len <= 5) 128 return; 129 130 GpxData data = new GpxData(); 131 data.tracks.add(new ImmutableGpxTrack(trackSegs, 132 new HashMap<String, Object>())); 133 134 ground_view.parent.addLayer( 135 new GpxLayer(data, "Car GPS trace")); 136 } 137 } 138 139 /* These are EastNorth, not actual LatLon */ 140 protected double lat, lon; 141 /* Camera's altitude above surface (same units as lat/lon above) */ 142 protected double ele = 0.000003; 143 /* Cut off at ~75px from bottom of the screen */ 144 protected double horizon = 0.63; 145 /* Car's distance from the camera lens */ 146 protected double cardist = ele * 3; 147 148 /* Pixels per pixel, the bigger the more oldschool :-) */ 149 protected int scale = 5; 150 151 protected BufferedImage screen_image; 152 protected Graphics screen; 153 protected int width = 320; 154 protected int height = 200; 155 protected int centre = width / 2; 156 157 double maxdist = ele / (horizon - 0.6); 158 double realwidth = maxdist * width / height; 159 double pixelperlat = 1.0 * width / realwidth; 160 double sratio = 0.85; 161 protected int sw = (int) (2 * Math.PI * maxdist * pixelperlat * sratio); 162 163 /* TODO: figure out how to load these dynamically after splash 164 * screen is shown */ 165 protected static final ImageIcon car[] = new ImageIcon[] { 166 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 167 WMSRacer.class.getResource( 168 "/images/car0-l.png"))), 169 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 170 WMSRacer.class.getResource( 171 "/images/car0.png"))), 172 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 173 WMSRacer.class.getResource( 174 "/images/car0-r.png"))), 175 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 176 WMSRacer.class.getResource( 177 "/images/car1-l.png"))), 178 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 179 WMSRacer.class.getResource( 180 "/images/car1.png"))), 181 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 182 WMSRacer.class.getResource( 183 "/images/car1-r.png"))), 184 }; 185 protected static final ImageIcon bg[] = new ImageIcon[] { 186 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 187 WMSRacer.class.getResource( 188 "/images/bg0.png"))), 189 }; 190 protected static final ImageIcon skyline[] = new ImageIcon[] { 191 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 192 WMSRacer.class.getResource( 193 "/images/horizon.png"))), 194 }; 195 protected static final ImageIcon cactus[] = new ImageIcon[] { 196 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 197 WMSRacer.class.getResource( 198 "/images/cactus0.png"))), 199 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 200 WMSRacer.class.getResource( 201 "/images/cactus1.png"))), 202 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 203 WMSRacer.class.getResource( 204 "/images/cactus2.png"))), 205 }; 206 protected static final ImageIcon cloud[] = new ImageIcon[] { 207 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 208 WMSRacer.class.getResource( 209 "/images/cloud0.png"))), 210 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 211 WMSRacer.class.getResource( 212 "/images/cloud1.png"))), 213 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 214 WMSRacer.class.getResource( 215 "/images/cloud2.png"))), 216 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 217 WMSRacer.class.getResource( 218 "/images/cloud3.png"))), 219 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 220 WMSRacer.class.getResource( 221 "/images/cloud4.png"))), 222 }; 223 protected static final ImageIcon aircraft[] = new ImageIcon[] { 224 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 225 WMSRacer.class.getResource( 226 "/images/aircraft0.png"))), 227 }; 228 protected static final ImageIcon loading = new ImageIcon( 229 Toolkit.getDefaultToolkit().createImage( 230 WMSRacer.class.getResource( 231 "/images/loading.png"))); 232 protected static Toolkit s = Toolkit.getDefaultToolkit(); 233 protected int current_bg = 0; 234 protected int current_car = 0; 235 protected boolean cacti_on = true; 236 protected List<EastNorth> cacti = new ArrayList<EastNorth>(); 237 protected List<EastNorth> todelete = new ArrayList<EastNorth>(); 238 protected int splashframe = -1; 239 protected EastNorth splashcactus; 240 241 protected Layer ground; 242 protected double heading = 0.0; 243 protected double wheelangle = 0.0; 244 protected double speed = 0.0; 245 protected boolean key_down[] = new boolean[] { 246 false, false, false, false, }; 247 248 protected void move() { 249 /* Left */ 250 /* (At high speeds make more gentle turns) */ 251 if (key_down[0]) 252 wheelangle -= 0.1 / (1.0 + Math.abs(speed)); 253 /* Right */ 254 if (key_down[1]) 255 wheelangle += 0.1 / (1.0 + Math.abs(speed)); 256 if (wheelangle > 0.3) 257 wheelangle = 0.3; /* Radians */ 258 if (wheelangle < -0.3) 259 wheelangle = -0.3; 260 261 wheelangle *= 0.7; 262 263 /* Up */ 264 if (key_down[2]) 265 speed += speed >= 0.0 ? 1.0 / (2.0 + speed) : 0.5; 266 /* Down */ 267 if (key_down[3]) { 268 if (speed >= 0.5) /* Brake (TODO: sound) */ 269 speed -= 0.5; 270 else if (speed >= 0.01) /* Brake (TODO: sound) */ 271 speed = 0.0; 272 else /* Reverse */ 273 speed -= 0.5 / (4.0 - speed); 274 } 275 276 speed *= 0.97; 277 car_engine.set_speed(speed); 278 279 if (speed > -0.1 && speed < 0.1) 280 speed = 0; 281 282 heading += wheelangle * speed; 283 284 boolean chop = false; 285 double newlat = lat + Math.cos(heading) * speed * ele * 0.2; 286 double newlon = lon + Math.sin(heading) * speed * ele * 0.2; 287 for (EastNorth pos : cacti) { 288 double alat = Math.abs(pos.north() - newlat); 289 double alon = Math.abs(pos.east() - newlon); 290 if (alat + alon < ele * 1.0) { 291 if (Math.abs(speed) < 2.0) { 292 if (speed > 0.0) 293 speed = -0.5; 294 else 295 speed = 0.3; 296 newlat = lat; 297 newlon = lon; 298 break; 299 } 300 301 chop = true; 302 splashframe = 0; 303 splashcactus = pos; 304 todelete.add(pos); 305 } 306 } 307 308 lat = newlat; 309 lon = newlon; 310 311 /* Seed a new cactus if we're moving. 312 * TODO: hook into data layers and avoid putting the cactus on 313 * the road! 314 */ 315 if (cacti_on && Math.random() * 30.0 < speed) { 316 double left_x = maxdist * (width - centre) / height; 317 double right_x = maxdist * (0 - centre) / height; 318 double x = left_x + Math.random() * (right_x - left_x); 319 double clat = lat + (maxdist - cardist) * 320 Math.cos(heading) - x * Math.sin(heading); 321 double clon = lon + (maxdist - cardist) * 322 Math.sin(heading) + x * Math.cos(heading); 323 324 cacti.add(new EastNorth(clon, clat)); 325 chop = true; 326 } 327 328 /* Chop down any cactus far enough that it can't 329 * be seen. ``If a cactus falls in a forest and 330 * there is nobody around did it make a sound?'' 331 */ 332 if (chop) { 333 for (EastNorth pos : cacti) { 334 double alat = Math.abs(pos.north() - lat); 335 double alon = Math.abs(pos.east() - lon); 336 if (alat + alon > 2 * maxdist) 337 todelete.add(pos); 338 } 339 cacti.removeAll(todelete); 340 todelete = new ArrayList<EastNorth>(); 341 } 342 } 343 344 int frame; 345 boolean downloading = false; 346 protected void screen_repaint() { 347 /* Draw background first */ 348 sky_paint(); 349 350 /* On top of it project the floor */ 351 ground_paint(); 352 353 /* Messages */ 354 frame ++; 355 if ((frame & 8) == 0 && downloading) 356 screen.drawImage(loading.getImage(), centre - 357 loading.getIconWidth() / 2, 50, this); 358 359 /* Sprites */ 360 sprites_paint(); 361 } 362 363 static double max3(double x[]) { 364 return x[0] > x[1] ? x[2] > x[0] ? x[2] : x[0] : 365 (x[2] > x[1] ? x[2] : x[1]); 366 } 367 static double min3(double x[]) { 368 return x[0] < x[1] ? x[2] < x[0] ? x[2] : x[0] : 369 (x[2] < x[1] ? x[2] : x[1]); 370 } 371 372 protected void ground_paint() { 373 double sin = Math.sin(heading); 374 double cos = Math.cos(heading); 375 376 /* First calculate the bounding box for the visible area. 377 * The area will be (nearly) a triangle, so calculate the 378 * EastNorth for the three corners and make a bounding box. 379 */ 380 double left_x = maxdist * (width - centre) / height; 381 double right_x = maxdist * (0 - centre) / height; 382 double e_lat[] = new double[] { 383 lat + (maxdist - cardist) * cos - left_x * sin, 384 lat + (maxdist - cardist) * cos - right_x * sin, 385 lat - cardist * cos, }; 386 double e_lon[] = new double[] { 387 lon + (maxdist - cardist) * sin + left_x * cos, 388 lon + (maxdist - cardist) * sin + right_x * cos, 389 lon - cardist * sin, }; 390 ground_view.setProjectionBounds(new ProjectionBounds( 391 new EastNorth(min3(e_lon), min3(e_lat)), 392 new EastNorth(max3(e_lon), max3(e_lat)))); 393 394 /* If the layer is a WMS layer, check if any tiles are 395 * missing */ 396 if (ground instanceof wmsplugin.WMSLayer) { 397 wmsplugin.WMSLayer wms = (wmsplugin.WMSLayer) ground; 398 downloading = wms.hasAutoDownload() && ( 399 null == wms.findImage(new EastNorth( 400 e_lon[0], e_lat[0])) || 401 null == wms.findImage(new EastNorth( 402 e_lon[0], e_lat[0])) || 403 null == wms.findImage(new EastNorth( 404 e_lon[0], e_lat[0]))); 405 } 406 407 /* Request the image from ground layer */ 408 ground.paint(ground_view.graphics, ground_view, null); 409 410 for (int y = (int) (height * horizon + 0.1); y < height; y ++) { 411 /* Assume a 60 deg vertical Field of View when 412 * calculating the distance at given pixel. */ 413 double dist = ele / (1.0 * y / height - 0.6); 414 double lat_off = lat + (dist - cardist) * cos; 415 double lon_off = lon + (dist - cardist) * sin; 416 417 for (int x = 0; x < width; x ++) { 418 double p_x = dist * (x - centre) / height; 419 420 EastNorth en = new EastNorth( 421 lon_off + p_x * cos, 422 lat_off - p_x * sin); 423 424 Point pt = ground_view.getPoint(en); 425 426 int rgb = ground_view.ground_image.getRGB( 427 pt.x, pt.y); 428 screen_image.setRGB(x, y, rgb); 429 } 430 } 431 } 432 433 protected BufferedImage sky_image; 434 protected Graphics sky; 435 public void generate_sky() { 436 sky_image = new BufferedImage(sw, 70, 437 BufferedImage.TYPE_INT_ARGB); 438 sky = sky_image.getGraphics(); 439 440 int n = (int) (Math.random() * sw * 0.03); 441 for (int i = 0; i < n; i ++) { 442 int t = (int) (Math.random() * 5.0); 443 int x = (int) (Math.random() * 444 (sw - cloud[t].getIconWidth())); 445 int y = (int) ((1 - Math.random() * Math.random()) * 446 (70 - cloud[t].getIconHeight())); 447 sky.drawImage(cloud[t].getImage(), x, y, this); 448 } 449 450 if (Math.random() < 0.5) { 451 int t = 0; 452 int x = (int) (300 + Math.random() * (sw - 500 - 453 aircraft[t].getIconWidth())); 454 sky.drawImage(aircraft[t].getImage(), x, 0, this); 455 } 456 } 457 458 public void sky_paint() { 459 /* for x -> 0, lim sin(x) / x = 1 */ 460 int hx = (int) (-heading * maxdist * pixelperlat); 461 int hw = skyline[current_bg].getIconWidth(); 462 hx = ((hx % hw) - hw) % hw; 463 464 int sx = (int) (-heading * maxdist * pixelperlat * sratio); 465 sx = ((sx % sw) - sw) % sw; 466 467 screen.drawImage(bg[current_bg].getImage(), 0, 0, this); 468 screen.drawImage(sky_image, sx, 50, this); 469 if (sw + sx < width) 470 screen.drawImage(sky_image, sx + sw, 50, this); 471 screen.drawImage(skyline[current_bg].getImage(), hx, 66, this); 472 if (hw + hx < width) 473 screen.drawImage(skyline[current_bg].getImage(), 474 hx + hw, 66, this); 475 } 476 477 protected class sprite_pos implements Comparable { 478 double dist; 479 480 int x, y, sx, sy; 481 Image sprite; 482 483 public sprite_pos() { 484 } 485 486 public int compareTo(Object x) { 487 sprite_pos other = (sprite_pos) x; 488 return (int) ((other.dist - this.dist) * 1000000.0); 489 } 490 } 491 492 /* sizes decides how many zoom levels the sprites have. We 493 * could do just normal scalling according to distance but 494 * that's not what old games did, they had prescaled sprites 495 * for the different distances and you could see the feature 496 * grow discretely as you approached it. */ 497 protected final static int sizes = 8; 498 499 protected final static int maxsprites = 32; 500 protected sprite_pos sprites[] = new sprite_pos[maxsprites]; 501 502 protected void sprites_paint() { 503 /* The vehicle */ 504 int orientation = (wheelangle > -0.02 ? wheelangle < 0.02 ? 505 1 : 2 : 0) + current_car * 3; 506 sprites[0].sprite = car[orientation].getImage(); 507 sprites[0].dist = cardist; 508 sprites[0].sx = car[orientation].getIconWidth(); 509 sprites[0].x = centre - sprites[0].sx / 2; 510 sprites[0].sy = car[orientation].getIconHeight(); 511 sprites[0].y = height - sprites[0].sy - 10; /* TODO */ 512 513 /* The cacti */ 514 double sin = Math.sin(-heading); 515 double cos = Math.cos(-heading); 516 int i = 1; 517 518 for (EastNorth ll : cacti) { 519 double clat = ll.north() - lat; 520 double clon = ll.east() - lon; 521 double dist = (clat * cos - clon * sin) + cardist; 522 double p_x = clat * sin + clon * cos; 523 524 if (dist * 8 <= cardist || dist > maxdist) 525 continue; 526 527 int x = (int) (p_x * height / dist + centre); 528 int y = (int) ((ele / dist + 0.6) * height); 529 530 if (i >= maxsprites) 531 break; 532 if (x < -10 || x > width + 10) 533 continue; 534 535 int type = (((int) (ll.north() * 10000000.0) & 31) % 3); 536 int sx = cactus[type].getIconWidth(); 537 int sy = cactus[type].getIconHeight(); 538 539 sprite_pos pos = sprites[i ++]; 540 pos.dist = dist; 541 pos.sprite = cactus[type].getImage(); 542 pos.sx = (int) (sx * cardist * 0.7 / dist); 543 pos.sy = (int) (sy * cardist * 0.7 / dist); 544 pos.x = x - pos.sx / 2; 545 pos.y = y - pos.sy; 546 } 547 548 Arrays.sort(sprites, 0, i); 549 for (sprite_pos sprite : sprites) 550 if (i --> 0) 551 screen.drawImage(sprite.sprite, 552 sprite.x, sprite.y, 553 sprite.sx, sprite.sy, this); 554 else 555 break; 556 557 if (splashframe >= 0) { 558 splashframe ++; 559 if (splashframe >= 8) 560 splashframe = -1; 561 562 int type = (((int) (splashcactus.north() * 563 10000000.0) & 31) % 3); 564 int sx = cactus[type].getIconWidth(); 565 int sy = cactus[type].getIconHeight(); 566 Image image = cactus[type].getImage(); 567 568 for (i = 0; i < 50; i ++) { 569 int x = (int) (Math.random() * sx); 570 int y = (int) (Math.random() * sy); 571 int w = (int) (Math.random() * 20); 572 int h = (int) (Math.random() * 20); 573 int nx = centre + splashframe * (x - sx / 2); 574 int ny = height - splashframe * (sy - y); 575 int nw = w + splashframe; 576 int nh = h + splashframe; 577 578 screen.drawImage(image, 579 nx, ny, nx + nw, ny + nh, 580 x, y, x + w, y + h, this); 581 } 582 } 583 } 584 585 public boolean no_super_repaint = false; 586 protected class GamePanel extends JPanel { 587 public GamePanel() { 588 setBackground(Color.BLACK); 589 setDoubleBuffered(true); 590 } 591 592 public void paint(Graphics g) { 593 int w = (int) getSize().getWidth(); 594 int h = (int) getSize().getHeight(); 595 596 if (no_super_repaint) 597 no_super_repaint = false; 598 else 599 super.paint(g); 600 601 g.drawImage(screen_image, (w - width * scale) / 2, 602 (h - height * scale) / 2, 603 width * scale, height * scale, this); 604 605 Toolkit.getDefaultToolkit().sync(); 606 } 607 } 608 JPanel panel = new GamePanel(); 609 610 protected void quit() { 611 timer.stop(); 612 613 car_engine.stop(); 614 615 car_gps.stop(); 616 car_gps.save_trace(); 617 618 setVisible(false); 619 panel = null; 620 screen_image = null; 621 screen = null; 622 dispose(); 623 } 624 625 /* 626 * Supposedly a thread drawing frames and sleeping in a loop is 627 * better than for animating than swing Timers. For the moment 628 * I'll use a timer because I don't want to deal with all the 629 * potential threading issues. 630 */ 631 protected Timer timer; 632 public void actionPerformed(ActionEvent e) { 633 move(); 634 screen_repaint(); 635 636 no_super_repaint = true; 637 panel.repaint(); 638 } 639 640 protected class TAdapter extends KeyAdapter { 641 public void keyPressed(KeyEvent e) { 642 int key = e.getKeyCode(); 643 644 if (key == KeyEvent.VK_LEFT && !key_down[0]) { 645 wheelangle -= 0.02; 646 key_down[0] = true; 647 } 648 649 if (key == KeyEvent.VK_RIGHT && !key_down[1]) { 650 wheelangle += 0.02; 651 key_down[1] = true; 652 } 653 654 if (key == KeyEvent.VK_UP) 655 key_down[2] = true; 656 657 if (key == KeyEvent.VK_DOWN) 658 key_down[3] = true; 659 660 if (key == KeyEvent.VK_ESCAPE) 661 quit(); 662 663 /* Toggle sound */ 664 if (key == KeyEvent.VK_S) { 665 if (car_engine.is_on()) 666 car_engine.stop(); 667 else 668 car_engine.start(); 669 } 670 671 /* Toggle cacti */ 672 if (key == KeyEvent.VK_C) { 673 cacti_on = !cacti_on; 674 if (!cacti_on) 675 cacti = new ArrayList<EastNorth>(); 676 } 677 678 /* Switch vehicle */ 679 if (key == KeyEvent.VK_V) 680 if (current_car ++>= 1) 681 current_car = 0; 682 } 683 684 public void keyReleased(KeyEvent e) { 685 int key = e.getKeyCode(); 686 687 if (key == KeyEvent.VK_LEFT) 688 key_down[0] = false; 689 690 if (key == KeyEvent.VK_RIGHT) 691 key_down[1] = false; 692 693 if (key == KeyEvent.VK_UP) 694 key_down[2] = false; 695 696 if (key == KeyEvent.VK_DOWN) 697 key_down[3] = false; 698 } 699 } 700 protected fake_map_view ground_view; 701 701 } -
applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge/WMSRacer.java
r21761 r23190 20 20 21 21 public class WMSRacer extends Plugin implements LayerChangeListener { 22 23 24 22 public WMSRacer(PluginInformation info) { 23 super(info); 24 driveAction.updateEnabledState(); 25 25 26 27 28 29 26 JMenu toolsMenu = Main.main.menu.toolsMenu; 27 toolsMenu.addSeparator(); 28 toolsMenu.add(new JMenuItem(driveAction)); 29 } 30 30 31 32 33 34 35 36 37 31 /* Rather than add an action or main menu entry we should add 32 * an entry in the new layer's context menus in layerAdded 33 * but there doesn't seem to be any way to do that :( */ 34 protected class DriveAction extends JosmAction { 35 public MapFrame frame = null; 36 public Layer currentLayer = null; 37 protected Layer groundLayer = null; 38 38 39 40 41 42 43 44 39 public DriveAction() { 40 super("Go driving", "wmsracer", 41 "Drive a race car on this layer", 42 null, true); 43 setEnabled(false); 44 } 45 45 46 47 48 49 46 public void actionPerformed(ActionEvent ev) { 47 if (groundLayer == null || 48 !groundLayer.isBackgroundLayer()) 49 return; 50 50 51 52 51 new GameWindow(groundLayer); 52 } 53 53 54 55 56 57 58 59 54 public void updateEnabledState() { 55 if (frame == null) { 56 groundLayer = null; 57 setEnabled(false); 58 return; 59 } 60 60 61 62 63 64 65 66 61 if (currentLayer != null && 62 currentLayer.isBackgroundLayer()) { 63 groundLayer = currentLayer; 64 setEnabled(true); 65 return; 66 } 67 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 68 /* TODO: should only iterate through visible layers? 69 * or only wms layers? or perhaps we should allow 70 * driving on data/gpx layers too, or the full layer 71 * stack (by calling mapView.paint() instead of 72 * layer.paint()? Nah. 73 * (Note that for GPX or Data layers we could do 74 * some clever rendering directly on our perspectivic 75 * pseudo-3d surface by defining a strange projection 76 * like that or rendering in "stripes" at different 77 * horizontal scanlines (lines equidistant from 78 * camera eye)) */ 79 for (Layer l : frame.mapView.getAllLayers()) 80 if (l.isBackgroundLayer()) { 81 groundLayer = l; 82 setEnabled(true); 83 return; 84 } 85 85 86 87 88 89 86 groundLayer = null; 87 setEnabled(false); 88 } 89 } 90 90 91 91 protected DriveAction driveAction = new DriveAction(); 92 92 93 94 95 93 public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) { 94 if (oldFrame != null) 95 oldFrame.mapView.removeLayerChangeListener(this); 96 96 97 98 97 driveAction.frame = newFrame; 98 driveAction.updateEnabledState(); 99 99 100 101 102 100 if (newFrame != null) 101 newFrame.mapView.addLayerChangeListener(this); 102 } 103 103 104 105 106 107 108 104 /* LayerChangeListener methods */ 105 public void activeLayerChange(Layer oldLayer, Layer newLayer) { 106 driveAction.currentLayer = newLayer; 107 driveAction.updateEnabledState(); 108 } 109 109 110 111 112 110 public void layerAdded(Layer newLayer) { 111 driveAction.updateEnabledState(); 112 } 113 113 114 115 116 114 public void layerRemoved(Layer oldLayer) { 115 driveAction.updateEnabledState(); 116 } 117 117 }
Note:
See TracChangeset
for help on using the changeset viewer.