Ignore:
Timestamp:
2010-09-15T18:54:18+02:00 (14 years ago)
Author:
stoecker
Message:

remove tabs

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  
    1717
    1818class engine {
    19         public engine() {
    20                 rpm = 0.0;
    21         }
     19    public engine() {
     20        rpm = 0.0;
     21    }
    2222
    23         public void start() {
    24                 rpm = 0.3;
    25                 speed = 0.0;
    26                 n = 0;
     23    public void start() {
     24        rpm = 0.3;
     25        speed = 0.0;
     26        n = 0;
    2727
    28                 if (output != null)
    29                         stop();
     28        if (output != null)
     29            stop();
    3030
    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);
     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);
    3535
    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         }
     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    }
    4949
    50         public void stop() {
    51                 rpm = 0.0;
    52                 n = 0;
     50    public void stop() {
     51        rpm = 0.0;
     52        n = 0;
    5353
    54                 if (output == null)
    55                         return;
     54        if (output == null)
     55            return;
    5656
    57                 tick.cancel();
    58                 tick.purge();
     57        tick.cancel();
     58        tick.purge();
    5959
    60                 output.stop();
    61                 output.flush();
    62                 output.close();
    63                 output = null;
    64         }
     60        output.stop();
     61        output.flush();
     62        output.close();
     63        output = null;
     64    }
    6565
    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;
     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;
    7272
    73                 if (accel > 0.05)
    74                         accel = 0.05;
    75                 else if (accel < -0.05)
    76                         accel = -0.05;
    77                 rpm += accel;
     73        if (accel > 0.05)
     74            accel = 0.05;
     75        else if (accel < -0.05)
     76            accel = -0.05;
     77        rpm += accel;
    7878
    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         }
     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    }
    9292
    93         public boolean is_on() {
    94                 return output != null;
    95         }
     93    public boolean is_on() {
     94        return output != null;
     95    }
    9696
    97         protected double speed;
    98         protected double rpm;
    99         protected int n;
     97    protected double speed;
     98    protected double rpm;
     99    protected int n;
    100100
    101         protected SourceDataLine output = null;
    102         protected long frames_written;
    103         protected Timer tick = new Timer();
     101    protected SourceDataLine output = null;
     102    protected long frames_written;
     103    protected Timer tick = new Timer();
    104104
    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;
     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;
    109109
    110         protected class audio_task extends TimerTask {
    111                 public void run() {
    112                         if (output == null)
    113                                 return;
     110    protected class audio_task extends TimerTask {
     111        public void run() {
     112            if (output == null)
     113                return;
    114114
    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                         }
     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            }
    123123
    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);
     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);
    131131
    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                         }
     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            }
    147147
    148                         frames_written +=
    149                                 output.write(buffer, 0, bufferlen) / 2;
     148            frames_written +=
     149                output.write(buffer, 0, bufferlen) / 2;
    150150
    151                         reschedule(frames_current);
    152                 }
    153         }
     151            reschedule(frames_current);
     152        }
     153    }
    154154
    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         }
     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    }
    165165}
  • applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge/FakeMapView.java

    r21477 r23190  
    2626
    2727class fake_map_view extends MapView {
    28         public ProjectionBounds view_bounds;
    29         public MapView parent;
     28    public ProjectionBounds view_bounds;
     29    public MapView parent;
    3030
    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;
     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;
    3737
    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;
     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;
    4242
    43                 ProjectionBounds parent_bounds = parent.getProjectionBounds();
    44                 max_east_west =
    45                         parent_bounds.max.east() - parent_bounds.min.east();
    46         }
     43        ProjectionBounds parent_bounds = parent.getProjectionBounds();
     44        max_east_west =
     45            parent_bounds.max.east() - parent_bounds.min.east();
     46    }
    4747
    48         public void setProjectionBounds(ProjectionBounds bounds) {
    49                 view_bounds = bounds;
     48    public void setProjectionBounds(ProjectionBounds bounds) {
     49        view_bounds = bounds;
    5050
    51                 if (bounds.max.east() - bounds.min.east() > max_east_west) {
    52                         max_east_west = bounds.max.east() - bounds.min.east();
     51        if (bounds.max.east() - bounds.min.east() > max_east_west) {
     52            max_east_west = bounds.max.east() - bounds.min.east();
    5353
    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())));
     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())));
    7070
    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                 }
     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        }
    7979
    80                 Point vmin = getPoint(bounds.min);
    81                 Point vmax = getPoint(bounds.max);
    82                 int w = vmax.x + 1;
    83                 int h = vmin.y + 1;
     80        Point vmin = getPoint(bounds.min);
     81        Point vmax = getPoint(bounds.max);
     82        int w = vmax.x + 1;
     83        int h = vmin.y + 1;
    8484
    85                 if (w <= ground_width && h <= ground_height) {
    86                         graphics.setClip(0, 0, w, h);
    87                         return;
    88                 }
     85        if (w <= ground_width && h <= ground_height) {
     86            graphics.setClip(0, 0, w, h);
     87            return;
     88        }
    8989
    90                 if (w > ground_width)
    91                         ground_width = w;
    92                 if (h > ground_height)
    93                         ground_height = h;
     90        if (w > ground_width)
     91            ground_width = w;
     92        if (h > ground_height)
     93            ground_height = h;
    9494
    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         }
     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    }
    101101
    102         public ProjectionBounds getProjectionBounds() {
    103                 return view_bounds;
    104         }
     102    public ProjectionBounds getProjectionBounds() {
     103        return view_bounds;
     104    }
    105105
    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;
     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;
    111111
    112                 return new Point((int) x, (int) y);
    113         }
     112        return new Point((int) x, (int) y);
     113    }
    114114
    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         }
     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    }
    120120
    121         public boolean isVisible(int x, int y) {
    122                 return true;
    123         }
     121    public boolean isVisible(int x, int y) {
     122        return true;
     123    }
    124124
    125         public Graphics getGraphics() {
    126                 return graphics;
    127         }
     125    public Graphics getGraphics() {
     126        return graphics;
     127    }
    128128
    129         public void repaint() {
    130         }
     129    public void repaint() {
     130    }
    131131}
  • applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge/GameWindow.java

    r19990 r23190  
    4242
    4343public class GameWindow extends JFrame implements ActionListener {
    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;
     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;
    701701}
  • applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge/WMSRacer.java

    r21761 r23190  
    2020
    2121public class WMSRacer extends Plugin implements LayerChangeListener {
    22         public WMSRacer(PluginInformation info) {
    23                 super(info);
    24                 driveAction.updateEnabledState();
     22    public WMSRacer(PluginInformation info) {
     23        super(info);
     24        driveAction.updateEnabledState();
    2525
    26                 JMenu toolsMenu = Main.main.menu.toolsMenu;
    27                 toolsMenu.addSeparator();
    28                 toolsMenu.add(new JMenuItem(driveAction));
    29         }
     26        JMenu toolsMenu = Main.main.menu.toolsMenu;
     27        toolsMenu.addSeparator();
     28        toolsMenu.add(new JMenuItem(driveAction));
     29    }
    3030
    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;
     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;
    3838
    39                 public DriveAction() {
    40                         super("Go driving", "wmsracer",
    41                                         "Drive a race car on this layer",
    42                                         null, true);
    43                         setEnabled(false);
    44                 }
     39        public DriveAction() {
     40            super("Go driving", "wmsracer",
     41                    "Drive a race car on this layer",
     42                    null, true);
     43            setEnabled(false);
     44        }
    4545
    46                 public void actionPerformed(ActionEvent ev) {
    47                         if (groundLayer == null ||
    48                                         !groundLayer.isBackgroundLayer())
    49                                 return;
     46        public void actionPerformed(ActionEvent ev) {
     47            if (groundLayer == null ||
     48                    !groundLayer.isBackgroundLayer())
     49                return;
    5050
    51                         new GameWindow(groundLayer);
    52                 }
     51            new GameWindow(groundLayer);
     52        }
    5353
    54                 public void updateEnabledState() {
    55                         if (frame == null) {
    56                                 groundLayer = null;
    57                                 setEnabled(false);
    58                                 return;
    59                         }
     54        public void updateEnabledState() {
     55            if (frame == null) {
     56                groundLayer = null;
     57                setEnabled(false);
     58                return;
     59            }
    6060
    61                         if (currentLayer != null &&
    62                                         currentLayer.isBackgroundLayer()) {
    63                                 groundLayer = currentLayer;
    64                                 setEnabled(true);
    65                                 return;
    66                         }
     61            if (currentLayer != null &&
     62                    currentLayer.isBackgroundLayer()) {
     63                groundLayer = currentLayer;
     64                setEnabled(true);
     65                return;
     66            }
    6767
    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                                 }
     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                }
    8585
    86                         groundLayer = null;
    87                         setEnabled(false);
    88                 }
    89         }
     86            groundLayer = null;
     87            setEnabled(false);
     88        }
     89    }
    9090
    91         protected DriveAction driveAction = new DriveAction();
     91    protected DriveAction driveAction = new DriveAction();
    9292
    93         public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
    94                 if (oldFrame != null)
    95                         oldFrame.mapView.removeLayerChangeListener(this);
     93    public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
     94        if (oldFrame != null)
     95            oldFrame.mapView.removeLayerChangeListener(this);
    9696
    97                 driveAction.frame = newFrame;
    98                 driveAction.updateEnabledState();
     97        driveAction.frame = newFrame;
     98        driveAction.updateEnabledState();
    9999
    100                 if (newFrame != null)
    101                         newFrame.mapView.addLayerChangeListener(this);
    102         }
     100        if (newFrame != null)
     101            newFrame.mapView.addLayerChangeListener(this);
     102    }
    103103
    104         /* LayerChangeListener methods */
    105         public void activeLayerChange(Layer oldLayer, Layer newLayer) {
    106                 driveAction.currentLayer = newLayer;
    107                 driveAction.updateEnabledState();
    108         }
     104    /* LayerChangeListener methods */
     105    public void activeLayerChange(Layer oldLayer, Layer newLayer) {
     106        driveAction.currentLayer = newLayer;
     107        driveAction.updateEnabledState();
     108    }
    109109
    110         public void layerAdded(Layer newLayer) {
    111                 driveAction.updateEnabledState();
    112         }
     110    public void layerAdded(Layer newLayer) {
     111        driveAction.updateEnabledState();
     112    }
    113113
    114         public void layerRemoved(Layer oldLayer) {
    115                 driveAction.updateEnabledState();
    116         }
     114    public void layerRemoved(Layer oldLayer) {
     115        driveAction.updateEnabledState();
     116    }
    117117}
Note: See TracChangeset for help on using the changeset viewer.