| 1 |
/* |
|---|
| 2 |
* vim:et sts=4 sw=4 cindent: |
|---|
| 3 |
* $Id$ |
|---|
| 4 |
*/ |
|---|
| 5 |
|
|---|
| 6 |
package com.modestmaps.core |
|---|
| 7 |
{ |
|---|
| 8 |
import com.modestmaps.Map; |
|---|
| 9 |
import com.modestmaps.geo.Location; |
|---|
| 10 |
import com.modestmaps.mapproviders.IMapProvider; |
|---|
| 11 |
import com.stamen.twisted.*; |
|---|
| 12 |
|
|---|
| 13 |
import flash.display.DisplayObject; |
|---|
| 14 |
import flash.display.Sprite; |
|---|
| 15 |
import flash.events.Event; |
|---|
| 16 |
import flash.events.MouseEvent; |
|---|
| 17 |
import flash.geom.Point; |
|---|
| 18 |
import flash.geom.Rectangle; |
|---|
| 19 |
import flash.utils.Dictionary; |
|---|
| 20 |
|
|---|
| 21 |
public class TileGrid extends Sprite |
|---|
| 22 |
{ |
|---|
| 23 |
// Real maps use 256. |
|---|
| 24 |
public static const TILE_WIDTH:Number = 256; |
|---|
| 25 |
public static const TILE_HEIGHT:Number = 256; |
|---|
| 26 |
|
|---|
| 27 |
protected var _map:Map; |
|---|
| 28 |
|
|---|
| 29 |
protected var _width:Number; |
|---|
| 30 |
protected var _height:Number; |
|---|
| 31 |
protected var _draggable:Boolean; |
|---|
| 32 |
|
|---|
| 33 |
// Row and column counts are kept up-to-date. |
|---|
| 34 |
protected var _rows:int; |
|---|
| 35 |
protected var _columns:int; |
|---|
| 36 |
protected var _tiles:/*Tile*/Array; |
|---|
| 37 |
|
|---|
| 38 |
// overlay markers |
|---|
| 39 |
protected var markers:MarkerSet; |
|---|
| 40 |
|
|---|
| 41 |
// Markers overlapping the currently-included set of tiles, hash of booleans |
|---|
| 42 |
protected var _overlappingMarkers:Dictionary; |
|---|
| 43 |
|
|---|
| 44 |
// Allow (true) or prevent (false) tiles to paint themselves. |
|---|
| 45 |
protected var _paintingAllowed:Boolean; |
|---|
| 46 |
|
|---|
| 47 |
// Starting point for the very first tile |
|---|
| 48 |
protected var _initTilePoint:Point; |
|---|
| 49 |
protected var _initTileCoord:Coordinate; |
|---|
| 50 |
|
|---|
| 51 |
// the currently-native zoom level |
|---|
| 52 |
public var zoomLevel:int; |
|---|
| 53 |
|
|---|
| 54 |
// some limits on scrolling distance, initially set to none |
|---|
| 55 |
protected var topLeftOutLimit:Coordinate; |
|---|
| 56 |
protected var bottomRightInLimit:Coordinate; |
|---|
| 57 |
|
|---|
| 58 |
protected var _startingWellPosition:Point; |
|---|
| 59 |
|
|---|
| 60 |
// Tiles attach to the well. |
|---|
| 61 |
protected var _well:Sprite; |
|---|
| 62 |
|
|---|
| 63 |
// Mask clip to hide outside edges of tiles. |
|---|
| 64 |
protected var _mask:Sprite; |
|---|
| 65 |
|
|---|
| 66 |
// Active when the well is being dragged on the stage. |
|---|
| 67 |
protected var _wellDragTask:DelayedCall; |
|---|
| 68 |
|
|---|
| 69 |
// Defines a ring of extra, masked-out tiles around |
|---|
| 70 |
// the edges of the well, acting as a pre-fetching cache. |
|---|
| 71 |
// High tileBuffer may hurt performance. |
|---|
| 72 |
protected var _tileBuffer:int = 0; |
|---|
| 73 |
|
|---|
| 74 |
// Who do we get our Map graphics from? |
|---|
| 75 |
protected var _mapProvider:IMapProvider; |
|---|
| 76 |
|
|---|
| 77 |
protected var _drawWell:Boolean = true; |
|---|
| 78 |
protected var _drawGridArea:Boolean = true; |
|---|
| 79 |
|
|---|
| 80 |
public function TileGrid(width:Number, height:Number, draggable:Boolean, provider:IMapProvider, map:Map) |
|---|
| 81 |
{ |
|---|
| 82 |
if (!Reactor.running()) |
|---|
| 83 |
throw new Error('com.modestmaps.core.TileGrid.init(): com.stamen.Twisted.Reactor really ought to be running at this point. Seriously.'); |
|---|
| 84 |
|
|---|
| 85 |
_map = map; |
|---|
| 86 |
_width = width; |
|---|
| 87 |
_height = height; |
|---|
| 88 |
_draggable = draggable; |
|---|
| 89 |
_mapProvider = provider; |
|---|
| 90 |
|
|---|
| 91 |
cacheAsBitmap = true; |
|---|
| 92 |
|
|---|
| 93 |
buildWell(); |
|---|
| 94 |
buildMask(); |
|---|
| 95 |
allowPainting(true); |
|---|
| 96 |
redraw(); |
|---|
| 97 |
|
|---|
| 98 |
_overlappingMarkers = new Dictionary(true); |
|---|
| 99 |
markers = new MarkerSet(this); |
|---|
| 100 |
|
|---|
| 101 |
setInitialTile(new Coordinate(0,0,1), new Point(-TILE_WIDTH, -TILE_HEIGHT)); |
|---|
| 102 |
initializeTiles(); |
|---|
| 103 |
} |
|---|
| 104 |
|
|---|
| 105 |
/** |
|---|
| 106 |
* Set initTileCoord and initTilePoint for use by initializeTiles(). |
|---|
| 107 |
*/ |
|---|
| 108 |
public function setInitialTile(coord:Coordinate, point:Point):void |
|---|
| 109 |
{ |
|---|
| 110 |
_initTileCoord = coord; |
|---|
| 111 |
_initTilePoint = point; |
|---|
| 112 |
} |
|---|
| 113 |
|
|---|
| 114 |
/** |
|---|
| 115 |
* Reset tile grid with a new initial tile, and expire old tiles in the background. |
|---|
| 116 |
*/ |
|---|
| 117 |
public function resetTiles(coord:Coordinate, point:Point):void |
|---|
| 118 |
{ |
|---|
| 119 |
if (!_tiles) |
|---|
| 120 |
{ |
|---|
| 121 |
setInitialTile(coord, point); |
|---|
| 122 |
return; |
|---|
| 123 |
} |
|---|
| 124 |
|
|---|
| 125 |
try { |
|---|
| 126 |
var initTile:Tile; |
|---|
| 127 |
var condemnedTiles:/*Tile*/Array = activeTiles(); |
|---|
| 128 |
|
|---|
| 129 |
for (var i:int = 0; i < condemnedTiles.length; i++) |
|---|
| 130 |
{ |
|---|
| 131 |
condemnedTiles[i].expire(); |
|---|
| 132 |
} |
|---|
| 133 |
|
|---|
| 134 |
Reactor.callLater(condemnationDelay(), destroyTiles, condemnedTiles); |
|---|
| 135 |
|
|---|
| 136 |
zoomLevel = coord.zoom; |
|---|
| 137 |
initTile = createTile(this, coord, point.x, point.y); |
|---|
| 138 |
|
|---|
| 139 |
centerWell(true); |
|---|
| 140 |
|
|---|
| 141 |
_rows = 1; |
|---|
| 142 |
_columns = 1; |
|---|
| 143 |
|
|---|
| 144 |
allocateTiles(); |
|---|
| 145 |
} |
|---|
| 146 |
catch(e:Error) { |
|---|
| 147 |
trace(e.getStackTrace()); |
|---|
| 148 |
} |
|---|
| 149 |
|
|---|
| 150 |
} |
|---|
| 151 |
|
|---|
| 152 |
/** |
|---|
| 153 |
* Create the first tiles, based on initTileCoord and initTilePoint. |
|---|
| 154 |
*/ |
|---|
| 155 |
protected function initializeTiles():void |
|---|
| 156 |
{ |
|---|
| 157 |
var initTile:Tile; |
|---|
| 158 |
|
|---|
| 159 |
if (!_initTileCoord) { |
|---|
| 160 |
trace("no _initTileCoord"); |
|---|
| 161 |
return; |
|---|
| 162 |
} |
|---|
| 163 |
|
|---|
| 164 |
// impose some limits |
|---|
| 165 |
zoomLevel = _initTileCoord.zoom; |
|---|
| 166 |
topLeftOutLimit = _mapProvider.outerLimits()[0]; |
|---|
| 167 |
bottomRightInLimit = _mapProvider.outerLimits()[1]; |
|---|
| 168 |
|
|---|
| 169 |
_tiles = []; |
|---|
| 170 |
initTile = createTile(this, _initTileCoord, _initTilePoint.x, _initTilePoint.y); |
|---|
| 171 |
|
|---|
| 172 |
centerWell(false); |
|---|
| 173 |
|
|---|
| 174 |
_rows = 1; |
|---|
| 175 |
_columns = 1; |
|---|
| 176 |
|
|---|
| 177 |
// buffer must not be negative! |
|---|
| 178 |
_tileBuffer = Math.max(0, _tileBuffer); |
|---|
| 179 |
|
|---|
| 180 |
allocateTiles(); |
|---|
| 181 |
|
|---|
| 182 |
// let 'em know we're coming |
|---|
| 183 |
markers.indexAtZoom(zoomLevel); |
|---|
| 184 |
|
|---|
| 185 |
updateMarkers(); |
|---|
| 186 |
} |
|---|
| 187 |
|
|---|
| 188 |
public function putMarker(id:String, coord:Coordinate, location:Location):Marker |
|---|
| 189 |
{ |
|---|
| 190 |
var marker:Marker = new Marker(id, coord, location); |
|---|
| 191 |
markers.put(marker); |
|---|
| 192 |
|
|---|
| 193 |
updateMarkers(); |
|---|
| 194 |
return marker; |
|---|
| 195 |
} |
|---|
| 196 |
|
|---|
| 197 |
public function removeMarker(id:String):void |
|---|
| 198 |
{ |
|---|
| 199 |
var marker:Marker = markers.getMarker(id); |
|---|
| 200 |
if (marker) |
|---|
| 201 |
markers.remove(marker); |
|---|
| 202 |
} |
|---|
| 203 |
|
|---|
| 204 |
/** |
|---|
| 205 |
* Create the well clip, assign event handlers. |
|---|
| 206 |
*/ |
|---|
| 207 |
protected function buildWell():void |
|---|
| 208 |
{ |
|---|
| 209 |
_well = new Sprite(); |
|---|
| 210 |
_well.name = 'well'; |
|---|
| 211 |
|
|---|
| 212 |
if (_draggable) |
|---|
| 213 |
{ |
|---|
| 214 |
_well.mouseChildren = false; |
|---|
| 215 |
_well.addEventListener(MouseEvent.MOUSE_DOWN, startWellDrag); |
|---|
| 216 |
_well.addEventListener(MouseEvent.MOUSE_UP, stopWellDrag); |
|---|
| 217 |
_well.doubleClickEnabled = true; |
|---|
| 218 |
} |
|---|
| 219 |
|
|---|
| 220 |
addChild(_well); |
|---|
| 221 |
centerWell(false); |
|---|
| 222 |
} |
|---|
| 223 |
|
|---|
| 224 |
/** |
|---|
| 225 |
* Create the mask clip. |
|---|
| 226 |
*/ |
|---|
| 227 |
protected function buildMask():void |
|---|
| 228 |
{ |
|---|
| 229 |
_mask = new Sprite(); |
|---|
| 230 |
_mask.name = 'mask'; |
|---|
| 231 |
// as3 masks need to be child, so add the mask to the grid not the well |
|---|
| 232 |
// because well children are all tiles |
|---|
| 233 |
addChild(_mask); |
|---|
| 234 |
this.mask = _mask; |
|---|
| 235 |
} |
|---|
| 236 |
|
|---|
| 237 |
public function setDoubleClickEnabled(enabled:Boolean):void |
|---|
| 238 |
{ |
|---|
| 239 |
if (enabled) { |
|---|
| 240 |
_well.addEventListener(MouseEvent.DOUBLE_CLICK, onWellDoubleClick); |
|---|
| 241 |
} |
|---|
| 242 |
else if (_well.hasEventListener(MouseEvent.DOUBLE_CLICK)) { |
|---|
| 243 |
_well.removeEventListener(MouseEvent.DOUBLE_CLICK, onWellDoubleClick); |
|---|
| 244 |
} |
|---|
| 245 |
} |
|---|
| 246 |
|
|---|
| 247 |
public function getMapProvider():IMapProvider |
|---|
| 248 |
{ |
|---|
| 249 |
return _mapProvider; |
|---|
| 250 |
} |
|---|
| 251 |
|
|---|
| 252 |
public function setMapProvider(mapProvider:IMapProvider):void |
|---|
| 253 |
{ |
|---|
| 254 |
var previousGeometry:String = _mapProvider.geometry(); |
|---|
| 255 |
|
|---|
| 256 |
_mapProvider = mapProvider; |
|---|
| 257 |
topLeftOutLimit = _mapProvider.outerLimits()[0]; |
|---|
| 258 |
bottomRightInLimit = _mapProvider.outerLimits()[1]; |
|---|
| 259 |
|
|---|
| 260 |
if (_mapProvider.geometry() != previousGeometry) |
|---|
| 261 |
{ |
|---|
| 262 |
markers.initializeIndex(); |
|---|
| 263 |
markers.indexAtZoom(zoomLevel); |
|---|
| 264 |
updateMarkers(); |
|---|
| 265 |
} |
|---|
| 266 |
} |
|---|
| 267 |
|
|---|
| 268 |
|
|---|
| 269 |
/** |
|---|
| 270 |
* Create a new tile, add it to _tiles array, and return it. |
|---|
| 271 |
*/ |
|---|
| 272 |
protected function createTile(grid:TileGrid, coord:Coordinate, x:Number, y:Number):Tile |
|---|
| 273 |
{ |
|---|
| 274 |
var tile:Tile = new Tile(grid, coord, x, y); |
|---|
| 275 |
tile.name = 'tile' + _tiles.length; |
|---|
| 276 |
_well.addChild(tile); |
|---|
| 277 |
|
|---|
| 278 |
tile.redraw(); |
|---|
| 279 |
_tiles.push(tile); |
|---|
| 280 |
|
|---|
| 281 |
return tile; |
|---|
| 282 |
} |
|---|
| 283 |
|
|---|
| 284 |
/** |
|---|
| 285 |
* Remove an old tile from the _tiles array, then destroy it. |
|---|
| 286 |
*/ |
|---|
| 287 |
protected function destroyTile(tile:Tile):void |
|---|
| 288 |
{ |
|---|
| 289 |
_tiles.splice(tileIndex(tile), 1); |
|---|
| 290 |
tile.cancelDraw(); |
|---|
| 291 |
_well.removeChild(tile); |
|---|
| 292 |
} |
|---|
| 293 |
|
|---|
| 294 |
/* |
|---|
| 295 |
* Slowly mete out destruction to a list of tiles. |
|---|
| 296 |
*/ |
|---|
| 297 |
protected function destroyTiles(tiles:/*Tile*/Array):void |
|---|
| 298 |
{ |
|---|
| 299 |
if (tiles.length) |
|---|
| 300 |
{ |
|---|
| 301 |
destroyTile(Tile(tiles.shift())); |
|---|
| 302 |
Reactor.callLater(0, destroyTiles, tiles); |
|---|
| 303 |
} |
|---|
| 304 |
} |
|---|
| 305 |
|
|---|
| 306 |
/* |
|---|
| 307 |
* Reposition tiles and schedule a recursive call for the next frame. |
|---|
| 308 |
*/ |
|---|
| 309 |
protected function onWellDrag(previousPosition:Point):void |
|---|
| 310 |
{ |
|---|
| 311 |
if(positionTiles()) |
|---|
| 312 |
updateMarkers(); |
|---|
| 313 |
|
|---|
| 314 |
if(previousPosition.x != _well.x || previousPosition.y != _well.y) |
|---|
| 315 |
_map.onPanned(new Point(_well.x - _startingWellPosition.x, _well.y - _startingWellPosition.y)); |
|---|
| 316 |
|
|---|
| 317 |
_wellDragTask = Reactor.callNextFrame(onWellDrag, new Point(_well.x, _well.y)); |
|---|
| 318 |
} |
|---|
| 319 |
|
|---|
| 320 |
/* |
|---|
| 321 |
* Return the point position of a tile with the given coordinate in the |
|---|
| 322 |
* context of the given movie clip. |
|---|
| 323 |
* |
|---|
| 324 |
* Respect infinite rows or columns, to bind movement on one (or no) axis. |
|---|
| 325 |
*/ |
|---|
| 326 |
public function coordinatePoint(coord:Coordinate, context:DisplayObject, fearBigNumbers:Boolean=false):Point |
|---|
| 327 |
{ |
|---|
| 328 |
// pick a reference tile, an arbitrary choice |
|---|
| 329 |
// but known to exist regardless of grid size. |
|---|
| 330 |
var tile:Tile = activeTiles()[0]; |
|---|
| 331 |
|
|---|
| 332 |
// get the position of the reference tile. |
|---|
| 333 |
var point:Point = new Point(tile.x, tile.y); |
|---|
| 334 |
|
|---|
| 335 |
// make sure coord is using the same zoom level |
|---|
| 336 |
coord = coord.zoomTo(tile.coord.zoom); |
|---|
| 337 |
|
|---|
| 338 |
// store the infinite |
|---|
| 339 |
var force:Point = new Point(0, 0); |
|---|
| 340 |
|
|---|
| 341 |
if(coord.column == Number.POSITIVE_INFINITY || coord.column == Number.NEGATIVE_INFINITY) { |
|---|
| 342 |
force.x = coord.column; |
|---|
| 343 |
} else { |
|---|
| 344 |
point.x += TILE_WIDTH * (coord.column - tile.coord.column); |
|---|
| 345 |
} |
|---|
| 346 |
|
|---|
| 347 |
if(coord.row == Number.POSITIVE_INFINITY || coord.row == Number.NEGATIVE_INFINITY) { |
|---|
| 348 |
force.y = coord.row; |
|---|
| 349 |
} else { |
|---|
| 350 |
point.y += TILE_HEIGHT * (coord.row - tile.coord.row); |
|---|
| 351 |
} |
|---|
| 352 |
|
|---|
| 353 |
if(fearBigNumbers) { |
|---|
| 354 |
if(point.x < -1e6) { |
|---|
| 355 |
force.x = Number.NEGATIVE_INFINITY; |
|---|
| 356 |
} |
|---|
| 357 |
if(point.x > 1e6) { |
|---|
| 358 |
force.x = Number.POSITIVE_INFINITY; |
|---|
| 359 |
} |
|---|
| 360 |
if(point.y < -1e6) { |
|---|
| 361 |
force.y = Number.NEGATIVE_INFINITY; |
|---|
| 362 |
} |
|---|
| 363 |
if(point.y > 1e6) { |
|---|
| 364 |
force.y = Number.POSITIVE_INFINITY; |
|---|
| 365 |
} |
|---|
| 366 |
} |
|---|
| 367 |
|
|---|
| 368 |
point = _well.localToGlobal(point); |
|---|
| 369 |
point = context.globalToLocal(point); |
|---|
| 370 |
|
|---|
| 371 |
if(force.x) { |
|---|
| 372 |
point.x = force.x; |
|---|
| 373 |
} |
|---|
| 374 |
if(force.y) { |
|---|
| 375 |
point.y = force.y; |
|---|
| 376 |
} |
|---|
| 377 |
return point; |
|---|
| 378 |
} |
|---|
| 379 |
|
|---|
| 380 |
public function pointCoordinate(point:Point, context:DisplayObject=null):Coordinate |
|---|
| 381 |
{ |
|---|
| 382 |
var tile:Tile; |
|---|
| 383 |
var tileCoord:Coordinate; |
|---|
| 384 |
var pointCoord:Coordinate; |
|---|
| 385 |
|
|---|
| 386 |
if (null == context) context = this; |
|---|
| 387 |
// point is assumed to be in tile grid local coordinates |
|---|
| 388 |
point = context.localToGlobal(point); |
|---|
| 389 |
point = _well.globalToLocal(point); |
|---|
| 390 |
|
|---|
| 391 |
// an arbitrary reference tile, zoomed to the maximum |
|---|
| 392 |
tile = activeTiles()[0]; |
|---|
| 393 |
tileCoord = tile.coord.zoomTo(Coordinate.MAX_ZOOM); |
|---|
| 394 |
|
|---|
| 395 |
// distance in tile widths from reference tile to point |
|---|
| 396 |
var xTiles:Number = (point.x - tile.x) / TILE_WIDTH; |
|---|
| 397 |
var yTiles:Number = (point.y - tile.y) / TILE_HEIGHT; |
|---|
| 398 |
|
|---|
| 399 |
// distance in rows & columns at maximum zoom |
|---|
| 400 |
var xDistance:Number = xTiles * Math.pow(2, (Coordinate.MAX_ZOOM - tile.coord.zoom)); |
|---|
| 401 |
var yDistance:Number = yTiles * Math.pow(2, (Coordinate.MAX_ZOOM - tile.coord.zoom)); |
|---|
| 402 |
|
|---|
| 403 |
// new point coordinate reflecting that distance |
|---|
| 404 |
pointCoord = new Coordinate(Math.round(tileCoord.row + yDistance), |
|---|
| 405 |
Math.round(tileCoord.column + xDistance), |
|---|
| 406 |
tileCoord.zoom); |
|---|
| 407 |
|
|---|
| 408 |
return pointCoord.zoomTo(tile.coord.zoom); |
|---|
| 409 |
} |
|---|
| 410 |
|
|---|
| 411 |
public function topLeftCoordinate():Coordinate |
|---|
| 412 |
{ |
|---|
| 413 |
var point:Point = new Point(0, 0); |
|---|
| 414 |
return pointCoordinate(point); |
|---|
| 415 |
} |
|---|
| 416 |
|
|---|
| 417 |
public function centerCoordinate():Coordinate |
|---|
| 418 |
{ |
|---|
| 419 |
var point:Point = new Point(_width/2, _height/2); |
|---|
| 420 |
return pointCoordinate(point); |
|---|
| 421 |
} |
|---|
| 422 |
|
|---|
| 423 |
public function bottomRightCoordinate():Coordinate |
|---|
| 424 |
{ |
|---|
| 425 |
var point:Point = new Point(_width, _height); |
|---|
| 426 |
return pointCoordinate(point); |
|---|
| 427 |
} |
|---|
| 428 |
|
|---|
| 429 |
/* |
|---|
| 430 |
* Start dragging the well with the mouse. |
|---|
| 431 |
* Calls onWellDrag(). |
|---|
| 432 |
*/ |
|---|
| 433 |
protected function getWellBounds(fearBigNumbers:Boolean):Bounds |
|---|
| 434 |
{ |
|---|
| 435 |
var min:Point, max:Point; |
|---|
| 436 |
|
|---|
| 437 |
// "min" = furthest well position left & up, |
|---|
| 438 |
// use the location of the bottom-right limit |
|---|
| 439 |
min = coordinatePoint(bottomRightInLimit, this, fearBigNumbers); |
|---|
| 440 |
min.x = _well.x - min.x + _width; |
|---|
| 441 |
min.y = _well.y - min.y + _height; |
|---|
| 442 |
|
|---|
| 443 |
// "max" = furthest well position right & down, |
|---|
| 444 |
// use the location of the top-left limit |
|---|
| 445 |
max = coordinatePoint(topLeftOutLimit, this, fearBigNumbers); |
|---|
| 446 |
max.x = _well.x - max.x; |
|---|
| 447 |
max.y = _well.y - max.y; |
|---|
| 448 |
|
|---|
| 449 |
// weird negative edge conditions, limit all movement on an axis |
|---|
| 450 |
if(min.x > max.x) |
|---|
| 451 |
min.x = max.x = _well.x; |
|---|
| 452 |
|
|---|
| 453 |
if(min.y > max.y) |
|---|
| 454 |
min.y = max.y = _well.y; |
|---|
| 455 |
|
|---|
| 456 |
return new Bounds(min, max); |
|---|
| 457 |
} |
|---|
| 458 |
|
|---|
| 459 |
private function onWellDoubleClick(event:MouseEvent):void |
|---|
| 460 |
{ |
|---|
| 461 |
var p:Point = new Point(event.localX, event.localY); |
|---|
| 462 |
var l:Location = _map.pointLocation(p,_well); |
|---|
| 463 |
_map.panTo(l); |
|---|
| 464 |
} |
|---|
| 465 |
|
|---|
| 466 |
/* |
|---|
| 467 |
* Start dragging the well with the mouse. |
|---|
| 468 |
* Calls onWellDrag(). |
|---|
| 469 |
*/ |
|---|
| 470 |
public function startWellDrag(event:MouseEvent):void |
|---|
| 471 |
{ |
|---|
| 472 |
stage.addEventListener(MouseEvent.MOUSE_UP, stopWellDrag); |
|---|
| 473 |
stage.addEventListener(Event.MOUSE_LEAVE, stopWellDrag); |
|---|
| 474 |
|
|---|
| 475 |
var bounds:Bounds = getWellBounds(true); |
|---|
| 476 |
|
|---|
| 477 |
// startDrag seems to hate the infinities, |
|---|
| 478 |
// so we'll fudge it with some implausibly large numbers. |
|---|
| 479 |
|
|---|
| 480 |
var xMin:Number = (bounds.min.x == Number.POSITIVE_INFINITY) |
|---|
| 481 |
? 100000 |
|---|
| 482 |
: ((bounds.min.x == Number.NEGATIVE_INFINITY) |
|---|
| 483 |
? -100000 |
|---|
| 484 |
: bounds.min.x); |
|---|
| 485 |
|
|---|
| 486 |
var yMin:Number = (bounds.min.y == Number.POSITIVE_INFINITY) |
|---|
| 487 |
? 100000 |
|---|
| 488 |
: ((bounds.min.y == Number.NEGATIVE_INFINITY) |
|---|
| 489 |
? -100000 |
|---|
| 490 |
: bounds.min.y); |
|---|
| 491 |
|
|---|
| 492 |
var xMax:Number = (bounds.max.x == Number.POSITIVE_INFINITY) |
|---|
| 493 |
? 100000 |
|---|
| 494 |
: ((bounds.max.x == Number.NEGATIVE_INFINITY) |
|---|
| 495 |
? -100000 |
|---|
| 496 |
: bounds.max.x); |
|---|
| 497 |
|
|---|
| 498 |
var yMax:Number = (bounds.max.y == Number.POSITIVE_INFINITY) |
|---|
| 499 |
? 100000 |
|---|
| 500 |
: ((bounds.max.y == Number.NEGATIVE_INFINITY) |
|---|
| 501 |
? -100000 |
|---|
| 502 |
: bounds.max.y); |
|---|
| 503 |
|
|---|
| 504 |
_startingWellPosition = new Point(_well.x, _well.y); |
|---|
| 505 |
|
|---|
| 506 |
_map.onStartPan(); |
|---|
| 507 |
var rect:Rectangle = new Rectangle(xMin, yMin, xMax - xMin, yMax - yMin); |
|---|
| 508 |
_well.startDrag(false, rect); |
|---|
| 509 |
onWellDrag(_startingWellPosition.clone()); |
|---|
| 510 |
} |
|---|
| 511 |
|
|---|
| 512 |
/* |
|---|
| 513 |
* Stop dragging the well with the mouse. |
|---|
| 514 |
* Halts _wellDragTask. |
|---|
| 515 |
*/ |
|---|
| 516 |
public function stopWellDrag(event:Event):void |
|---|
| 517 |
{ |
|---|
| 518 |
stage.removeEventListener(MouseEvent.MOUSE_UP, stopWellDrag); |
|---|
| 519 |
stage.removeEventListener(Event.MOUSE_LEAVE, stopWellDrag); |
|---|
| 520 |
|
|---|
| 521 |
if (_wellDragTask) { |
|---|
| 522 |
_wellDragTask.call(); // issue final onPan, notify markers, etc. |
|---|
| 523 |
_wellDragTask.cancel(); // but cancel the follow-on call |
|---|
| 524 |
} |
|---|
| 525 |
_map.onStopPan(); |
|---|
| 526 |
_well.stopDrag(); |
|---|
| 527 |
|
|---|
| 528 |
if(positionTiles()) |
|---|
| 529 |
updateMarkers(); |
|---|
| 530 |
|
|---|
| 531 |
centerWell(true); |
|---|
| 532 |
} |
|---|
| 533 |
|
|---|
| 534 |
public function zoomBy(amount:Number, redraw:Boolean):void |
|---|
| 535 |
{ |
|---|
| 536 |
if(!_tiles) |
|---|
| 537 |
return; |
|---|
| 538 |
|
|---|
| 539 |
var roundScale:Number = Math.round(_well.scaleX * 10000.0) / 10000.0; |
|---|
| 540 |
if(amount > 0 && zoomLevel >= bottomRightInLimit.zoom && roundScale) |
|---|
| 541 |
return; |
|---|
| 542 |
|
|---|
| 543 |
if(amount < 0 && zoomLevel <= topLeftOutLimit.zoom && roundScale) |
|---|
| 544 |
return; |
|---|
| 545 |
|
|---|
| 546 |
_well.scaleX *= Math.pow(2, amount); |
|---|
| 547 |
_well.scaleY *= Math.pow(2, amount); |
|---|
| 548 |
|
|---|
| 549 |
boundWell(); |
|---|
| 550 |
|
|---|
| 551 |
if(redraw) { |
|---|
| 552 |
normalizeWell(); |
|---|
| 553 |
allocateTiles(); |
|---|
| 554 |
//trace('New well scale: '+_well.scaleX.toString()); |
|---|
| 555 |
} |
|---|
| 556 |
} |
|---|
| 557 |
|
|---|
| 558 |
public function resizeTo(bottomRight:Point):void |
|---|
| 559 |
{ |
|---|
| 560 |
_width = bottomRight.x; |
|---|
| 561 |
_height = bottomRight.y; |
|---|
| 562 |
|
|---|
| 563 |
redraw(); |
|---|
| 564 |
|
|---|
| 565 |
if(!_tiles) |
|---|
| 566 |
return; |
|---|
| 567 |
|
|---|
| 568 |
centerWell(false); |
|---|
| 569 |
allocateTiles(); |
|---|
| 570 |
} |
|---|
| 571 |
|
|---|
| 572 |
public function panRight(pixels:Number):void |
|---|
| 573 |
{ |
|---|
| 574 |
if(!_tiles) |
|---|
| 575 |
return; |
|---|
| 576 |
|
|---|
| 577 |
_well.x -= pixels; |
|---|
| 578 |
|
|---|
| 579 |
if(positionTiles()) |
|---|
| 580 |
updateMarkers(); |
|---|
| 581 |
|
|---|
| 582 |
centerWell(true); |
|---|
| 583 |
} |
|---|
| 584 |
|
|---|
| 585 |
public function panLeft(pixels:Number):void |
|---|
| 586 |
{ |
|---|
| 587 |
if(!_tiles) |
|---|
| 588 |
return; |
|---|
| 589 |
|
|---|
| 590 |
_well.x += pixels; |
|---|
| 591 |
|
|---|
| 592 |
if(positionTiles()) |
|---|
| 593 |
updateMarkers(); |
|---|
| 594 |
|
|---|
| 595 |
centerWell(true); |
|---|
| 596 |
} |
|---|
| 597 |
|
|---|
| 598 |
public function panUp(pixels:Number):void |
|---|
| 599 |
{ |
|---|
| 600 |
if(!_tiles) |
|---|
| 601 |
return; |
|---|
| 602 |
|
|---|
| 603 |
_well.y += pixels; |
|---|
| 604 |
|
|---|
| 605 |
if(positionTiles()) |
|---|
| 606 |
updateMarkers(); |
|---|
| 607 |
|
|---|
| 608 |
centerWell(true); |
|---|
| 609 |
} |
|---|
| 610 |
|
|---|
| 611 |
public function panDown(pixels:Number):void |
|---|
| 612 |
{ |
|---|
| 613 |
if(!_tiles) |
|---|
| 614 |
return; |
|---|
| 615 |
|
|---|
| 616 |
_well.y -= pixels; |
|---|
| 617 |
|
|---|
| 618 |
if(positionTiles()) |
|---|
| 619 |
updateMarkers(); |
|---|
| 620 |
|
|---|
| 621 |
centerWell(true); |
|---|
| 622 |
} |
|---|
| 623 |
|
|---|
| 624 |
/** |
|---|
| 625 |
* Get the subset of still-active tiles. |
|---|
| 626 |
*/ |
|---|
| 627 |
protected function activeTiles():/*Tile*/Array |
|---|
| 628 |
{ |
|---|
| 629 |
var matches:Array = new Array(); |
|---|
| 630 |
if (_tiles) { |
|---|
| 631 |
matches = _tiles.filter(function(item:Tile, index:int, list:Array):Boolean { return item.isActive();} ); |
|---|
| 632 |
if (matches.length == 0) { |
|---|
| 633 |
trace("no matches for active tiles... DOOM!"); |
|---|
| 634 |
} |
|---|
| 635 |
} |
|---|
| 636 |
return matches; |
|---|
| 637 |
} |
|---|
| 638 |
|
|---|
| 639 |
/** |
|---|
| 640 |
* Find the given tile in the tiles array. |
|---|
| 641 |
*/ |
|---|
| 642 |
protected function tileIndex(tile:Tile):Number |
|---|
| 643 |
{ |
|---|
| 644 |
return _tiles.indexOf(tile); |
|---|
| 645 |
} |
|---|
| 646 |
|
|---|
| 647 |
/** |
|---|
| 648 |
* Determine the number of tiles needed to cover the current grid, |
|---|
| 649 |
* and add rows and columns if necessary. Finally, position new tiles. |
|---|
| 650 |
*/ |
|---|
| 651 |
protected function allocateTiles():void |
|---|
| 652 |
{ |
|---|
| 653 |
if(!_tiles) |
|---|
| 654 |
return; |
|---|
| 655 |
|
|---|
| 656 |
// internal pixel dimensions of well, compensating for scale |
|---|
| 657 |
var wellWidth:Number = _well.scaleX * _width; |
|---|
| 658 |
var wellHeight:Number = _well.scaleY * _height; |
|---|
| 659 |
|
|---|
| 660 |
var targetCols:Number = Math.ceil(wellWidth / TILE_WIDTH) + 1 + 2 * _tileBuffer; |
|---|
| 661 |
var targetRows:Number = Math.ceil(wellHeight / TILE_HEIGHT) + 1 + 2 * _tileBuffer; |
|---|
| 662 |
|
|---|
| 663 |
// grid can't drop below 1 x 1 |
|---|
| 664 |
targetCols = Math.max(1, targetCols); |
|---|
| 665 |
targetRows = Math.max(1, targetRows); |
|---|
| 666 |
|
|---|
| 667 |
// change column count to match target |
|---|
| 668 |
while(_columns != targetCols) { |
|---|
| 669 |
if(_columns < targetCols) { |
|---|
| 670 |
pushTileColumn(); |
|---|
| 671 |
} else if(_columns > targetCols) { |
|---|
| 672 |
popTileColumn(); |
|---|
| 673 |
} |
|---|
| 674 |
} |
|---|
| 675 |
|
|---|
| 676 |
// change row count to match target |
|---|
| 677 |
while(_rows != targetRows) { |
|---|
| 678 |
if(_rows < targetRows) { |
|---|
| 679 |
pushTileRow(); |
|---|
| 680 |
} else if(_rows > targetRows) { |
|---|
| 681 |
popTileRow(); |
|---|
| 682 |
} |
|---|
| 683 |
} |
|---|
| 684 |
|
|---|
| 685 |
if(positionTiles()) |
|---|
| 686 |
updateMarkers(); |
|---|
| 687 |
|
|---|
| 688 |
//trace("allocateTiles(): " + _tiles.length); |
|---|
| 689 |
} |
|---|
| 690 |
|
|---|
| 691 |
/** |
|---|
| 692 |
* Adjust position of the well, so it does not stray outside the provider boundaries. |
|---|
| 693 |
*/ |
|---|
| 694 |
protected function boundWell():void |
|---|
| 695 |
{ |
|---|
| 696 |
var bounds:Bounds = getWellBounds(true); |
|---|
| 697 |
|
|---|
| 698 |
_well.x = Math.min(bounds.max.x, Math.max(bounds.min.x, _well.x)); |
|---|
| 699 |
_well.y = Math.min(bounds.max.y, Math.max(bounds.min.y, _well.y)); |
|---|
| 700 |
} |
|---|
| 701 |
|
|---|
| 702 |
/** |
|---|
| 703 |
* Adjust position of the well, so it stays in the center. |
|---|
| 704 |
* Optionally, compensate tile positions to prevent |
|---|
| 705 |
* visual discontinuity. |
|---|
| 706 |
*/ |
|---|
| 707 |
protected function centerWell(adjustTiles:Boolean):void |
|---|
| 708 |
{ |
|---|
| 709 |
var center:Point = new Point(_width/2, _height/2); |
|---|
| 710 |
|
|---|
| 711 |
var xAdjustment:Number = _well.x - center.x; |
|---|
| 712 |
var yAdjustment:Number = _well.y - center.y; |
|---|
| 713 |
|
|---|
| 714 |
_well.x -= xAdjustment; |
|---|
| 715 |
_well.y -= yAdjustment; |
|---|
| 716 |
|
|---|
| 717 |
if(!_tiles) |
|---|
| 718 |
return; |
|---|
| 719 |
|
|---|
| 720 |
if(adjustTiles) { |
|---|
| 721 |
for (var i:int = 0; i < _tiles.length; i += 1) { |
|---|
| 722 |
_tiles[i].x += xAdjustment / _well.scaleX; |
|---|
| 723 |
_tiles[i].y += yAdjustment / _well.scaleX; |
|---|
| 724 |
} |
|---|
| 725 |
} |
|---|
| 726 |
} |
|---|
| 727 |
|
|---|
| 728 |
/** |
|---|
| 729 |
* Adjust position and scale of the well, so it stays |
|---|
| 730 |
* in the center and within reason. Compensate tile |
|---|
| 731 |
* zoom and positions to prevent visual discontinuity. |
|---|
| 732 |
*/ |
|---|
| 733 |
protected function normalizeWell():void |
|---|
| 734 |
{ |
|---|
| 735 |
//trace("normalizing well"); |
|---|
| 736 |
if(!_tiles) { |
|---|
| 737 |
return; |
|---|
| 738 |
} |
|---|
| 739 |
|
|---|
| 740 |
var zoomAdjust:Number, scaleAdjust:Number; |
|---|
| 741 |
var active:/*Tile*/Array; |
|---|
| 742 |
|
|---|
| 743 |
// just in case? |
|---|
| 744 |
centerWell(true); |
|---|
| 745 |
|
|---|
| 746 |
//trace("well scale: " + _well.scaleX + " " + _well.scaleY); |
|---|
| 747 |
if(Math.abs(_well.scaleX - 1.0) < 0.01) { |
|---|
| 748 |
active = activeTiles(); |
|---|
| 749 |
|
|---|
| 750 |
// set to 100% if within 99% - 101% |
|---|
| 751 |
//trace("scaling well to 100% from " + _well.scaleX*100 + "%"); |
|---|
| 752 |
_well.scaleX = _well.scaleY = 1.0; |
|---|
| 753 |
|
|---|
| 754 |
active.sort(compareTileRowColumn); |
|---|
| 755 |
|
|---|
| 756 |
// lock the tiles back to round-pixel positions |
|---|
| 757 |
active[0].x = Math.floor(active[0].x); |
|---|
| 758 |
active[0].y = Math.floor(active[0].y); |
|---|
| 759 |
|
|---|
| 760 |
for(var i:int = 1; i < active.length; i += 1) { |
|---|
| 761 |
active[i].x = active[0].x + (active[i].coord.column - active[0].coord.column) * TILE_WIDTH; |
|---|
| 762 |
active[i].y = active[0].y + (active[i].coord.row - active[0].coord.row) * TILE_HEIGHT; |
|---|
| 763 |
|
|---|
| 764 |
//trace(active[i].toString()+' at '+active[i].x+', '+active[i].y+' vs. '+active[0].toString()); |
|---|
| 765 |
} |
|---|
| 766 |
|
|---|
| 767 |
} else if(_well.scaleX <= 0.6 || _well.scaleX >= 1.65) { |
|---|
| 768 |
// split or merge tiles if outside of 60% - 165% |
|---|
| 769 |
|
|---|
| 770 |
// zoom adjust: base-2 logarithm of the scale |
|---|
| 771 |
// see http://mathworld.wolfram.com/Logarithm.html (15) |
|---|
| 772 |
zoomAdjust = Math.round(Math.log(_well.scaleX) / Math.log(2)); |
|---|
| 773 |
scaleAdjust = Math.pow(2, zoomAdjust); |
|---|
| 774 |
|
|---|
| 775 |
//trace('This is where we scale the whole well by '+zoomAdjust+' zoom levels: '+(100 / scaleAdjust)+'%'); |
|---|
| 776 |
|
|---|
| 777 |
var n:int; |
|---|
| 778 |
for (n = 0; n < zoomAdjust; n += 1) |
|---|
| 779 |
{ |
|---|
| 780 |
splitTiles(); |
|---|
| 781 |
zoomLevel += 1; |
|---|
| 782 |
} |
|---|
| 783 |
|
|---|
| 784 |
for (n = 0; n > zoomAdjust; n -= 1) |
|---|
| 785 |
{ |
|---|
| 786 |
mergeTiles(); |
|---|
| 787 |
zoomLevel -= 1; |
|---|
| 788 |
} |
|---|
| 789 |
|
|---|
| 790 |
_well.scaleX = _well.scaleX / scaleAdjust; |
|---|
| 791 |
_well.scaleY = _well.scaleY / scaleAdjust; |
|---|
| 792 |
|
|---|
| 793 |
for (var j:int = 0; j < _tiles.length; j += 1) { |
|---|
| 794 |
_tiles[j].x = _tiles[j].x * scaleAdjust; |
|---|
| 795 |
_tiles[j].y = _tiles[j].y * scaleAdjust; |
|---|
| 796 |
_tiles[j].scaleX = _tiles[j].scaleX * scaleAdjust; |
|---|
| 797 |
_tiles[j].scaleY = _tiles[j].scaleY * scaleAdjust; |
|---|
| 798 |
} |
|---|
| 799 |
|
|---|
| 800 |
//trace('Scaled to '+zoomLevel+', '+(_well.scaleX*100.0)+'%'); |
|---|
| 801 |
markers.indexAtZoom(zoomLevel); |
|---|
| 802 |
} |
|---|
| 803 |
} |
|---|
| 804 |
|
|---|
| 805 |
/** |
|---|
| 806 |
* How many milliseconds before condemned tiles are destroyed? |
|---|
| 807 |
*/ |
|---|
| 808 |
protected function condemnationDelay():Number |
|---|
| 809 |
{ |
|---|
| 810 |
// half a second for each tile, plus five seconds overhead |
|---|
| 811 |
return (5 + .5 * _rows * _columns) * 1000; |
|---|
| 812 |
} |
|---|
| 813 |
|
|---|
| 814 |
/** |
|---|
| 815 |
* Do a 1-to-4 tile split: pick a reference tile and use it |
|---|
| 816 |
* as a position for four new tiles at a higher zoom level. |
|---|
| 817 |
* Expire all existing tiles, and trust that allocateTiles() and |
|---|
| 818 |
* positionTiles() will take care of filling the remaining space. |
|---|
| 819 |
*/ |
|---|
| 820 |
protected function splitTiles():void |
|---|
| 821 |
{ |
|---|
| 822 |
//trace("splitting tiles"); |
|---|
| 823 |
var condemnedTiles:/*Tile*/Array = []; |
|---|
| 824 |
var referenceTile:Tile, newTile:Tile; |
|---|
| 825 |
var xOffset:Number, yOffset:Number; |
|---|
| 826 |
|
|---|
| 827 |
for(var i:int = _tiles.length - 1; i >= 0; i -= 1) { |
|---|
| 828 |
if(_tiles[i].isActive()) { |
|---|
| 829 |
// remove old tile |
|---|
| 830 |
_tiles[i].expire(); |
|---|
| 831 |
condemnedTiles.push(_tiles[i]); |
|---|
| 832 |
|
|---|
| 833 |
// save for later (you only need one) |
|---|
| 834 |
referenceTile = _tiles[i]; |
|---|
| 835 |
} |
|---|
| 836 |
} |
|---|
| 837 |
|
|---|
| 838 |
Reactor.callLater(condemnationDelay(), destroyTiles, condemnedTiles); |
|---|
| 839 |
|
|---|
| 840 |
// this should never happen |
|---|
| 841 |
if(!referenceTile) { |
|---|
| 842 |
trace("TileGrid problem - no reference tile"); |
|---|
| 843 |
return; |
|---|
| 844 |
} |
|---|
| 845 |
|
|---|
| 846 |
// this should never happen either |
|---|
| 847 |
if(!referenceTile.coord) { |
|---|
| 848 |
trace("TileGrid problem - no coord in reference tile"); |
|---|
| 849 |
return; |
|---|
| 850 |
} |
|---|
| 851 |
|
|---|
| 852 |
for(var q:Number = 0; q < 4; q += 1) { |
|---|
| 853 |
// two-bit value into two one-bit values |
|---|
| 854 |
xOffset = q & 1; |
|---|
| 855 |
yOffset = (q >> 1) & 1; |
|---|
| 856 |
|
|---|
| 857 |
newTile = createTile(referenceTile.grid, referenceTile.coord, referenceTile.x, referenceTile.y); |
|---|
| 858 |
newTile.coord = newTile.coord.zoomBy(1); |
|---|
| 859 |
|
|---|
| 860 |
if(xOffset) |
|---|
| 861 |
newTile.coord = newTile.coord.right(); |
|---|
| 862 |
|
|---|
| 863 |
if(yOffset) |
|---|
| 864 |
newTile.coord = newTile.coord.down(); |
|---|
| 865 |
|
|---|
| 866 |
newTile.x = referenceTile.x + (xOffset * TILE_WIDTH / 2); |
|---|
| 867 |
newTile.y = referenceTile.y + (yOffset * TILE_HEIGHT / 2); |
|---|
| 868 |
|
|---|
| 869 |
newTile.scaleX = newTile.scaleY = referenceTile.scaleX / 2; |
|---|
| 870 |
newTile.redraw(); |
|---|
| 871 |
} |
|---|
| 872 |
|
|---|
| 873 |
// The remaining tiles get taken care of later |
|---|
| 874 |
_rows = 2; |
|---|
| 875 |
_columns = 2; |
|---|
| 876 |
} |
|---|
| 877 |
|
|---|
| 878 |
/** |
|---|
| 879 |
* Do a 4-to-1 tile merge: pick a reference tile and use it |
|---|
| 880 |
* as a position for the upper-left-hand corder of one new tile |
|---|
| 881 |
* at a higher zoom level. Expire all existing tiles, and trust |
|---|
| 882 |
* that allocateTiles() and positionTiles() will take care of |
|---|
| 883 |
* filling the remaining space. |
|---|
| 884 |
*/ |
|---|
| 885 |
protected function mergeTiles():void |
|---|
| 886 |
{ |
|---|
| 887 |
//trace("merging tiles"); |
|---|
| 888 |
var condemnedTiles:/*Tile*/Array = []; |
|---|
| 889 |
var referenceTile:Tile, newTile:Tile; |
|---|
| 890 |
|
|---|
| 891 |
_tiles.sort(compareTileRowColumn); |
|---|
| 892 |
|
|---|
| 893 |
for(var i:int = _tiles.length - 1; i >= 0; i -= 1) { |
|---|
| 894 |
if(_tiles[i].isActive()) { |
|---|
| 895 |
// remove old tile |
|---|
| 896 |
_tiles[i].expire(); |
|---|
| 897 |
condemnedTiles.push(_tiles[i]); |
|---|
| 898 |
|
|---|
| 899 |
if(_tiles[i].coord.zoomBy(-1).isEdge()) { |
|---|
| 900 |
// save for later (you only need one) |
|---|
| 901 |
referenceTile = _tiles[i]; |
|---|
| 902 |
} |
|---|
| 903 |
} |
|---|
| 904 |
} |
|---|
| 905 |
|
|---|
| 906 |
Reactor.callLater(condemnationDelay(), destroyTiles, condemnedTiles); |
|---|
| 907 |
|
|---|
| 908 |
// this should never happen |
|---|
| 909 |
if(!referenceTile) { |
|---|
| 910 |
throw new Error("no reference tile in mergeTiles()"); |
|---|
| 911 |
} |
|---|
| 912 |
|
|---|
| 913 |
// this should never happen either |
|---|
| 914 |
if(!referenceTile.coord) { |
|---|
| 915 |
throw new Error("no reference tile coord in mergeTiles()"); |
|---|
| 916 |
} |
|---|
| 917 |
|
|---|
| 918 |
// we are only interested in tiles that are edges for this zoom |
|---|
| 919 |
newTile = createTile(referenceTile.grid, referenceTile.coord, referenceTile.x, referenceTile.y); |
|---|
| 920 |
newTile.coord = newTile.coord.zoomBy(-1); |
|---|
| 921 |
|
|---|
| 922 |
newTile.scaleX = newTile.scaleY = referenceTile.scaleX * 2; |
|---|
| 923 |
newTile.redraw(); |
|---|
| 924 |
|
|---|
| 925 |
// The remaining tiles get taken care of later |
|---|
| 926 |
_rows = 1; |
|---|
| 927 |
_columns = 1; |
|---|
| 928 |
} |
|---|
| 929 |
|
|---|
| 930 |
/** |
|---|
| 931 |
* Determine if any tiles have wandered too far to the right, left, |
|---|
| 932 |
* top, or bottom, and shunt them to the opposite side if needed. |
|---|
| 933 |
* Return true if any tiles have been repositioned. |
|---|
| 934 |
*/ |
|---|
| 935 |
protected function positionTiles():Boolean |
|---|
| 936 |
{ |
|---|
| 937 |
if(!_tiles) |
|---|
| 938 |
return false; |
|---|
| 939 |
|
|---|
| 940 |
var tile:Tile; |
|---|
| 941 |
var point:Point; |
|---|
| 942 |
var active:/*Tile*/Array = activeTiles(); |
|---|
| 943 |
|
|---|
| 944 |
// if any tile is moved... |
|---|
| 945 |
var touched:Boolean = false; |
|---|
| 946 |
|
|---|
| 947 |
point = new Point(0, 0); |
|---|
| 948 |
point = this.localToGlobal(point); |
|---|
| 949 |
point = _well.globalToLocal(point); // all tiles are attached to well |
|---|
| 950 |
|
|---|
| 951 |
var xMin:Number = point.x - (1 + _tileBuffer) * TILE_WIDTH; |
|---|
| 952 |
var yMin:Number = point.y - (1 + _tileBuffer) * TILE_HEIGHT; |
|---|
| 953 |
|
|---|
| 954 |
point = new Point(_width, _height); |
|---|
| 955 |
point = this.localToGlobal(point); |
|---|
| 956 |
point = _well.globalToLocal(point); // all tiles are attached to well |
|---|
| 957 |
|
|---|
| 958 |
var xMax:Number = point.x + (0 + _tileBuffer) * TILE_WIDTH; |
|---|
| 959 |
var yMax:Number = point.y + (0 + _tileBuffer) * TILE_HEIGHT; |
|---|
| 960 |
|
|---|
| 961 |
for(var i:int = 0; i < active.length; i += 1) { |
|---|
| 962 |
|
|---|
| 963 |
tile = active[i]; |
|---|
| 964 |
|
|---|
| 965 |
// only interested in moving active tiles |
|---|
| 966 |
if(!tile.isActive()) |
|---|
| 967 |
break; // shouldn't happen, TODO: perhaps a case for throwing an Error? |
|---|
| 968 |
|
|---|
| 969 |
if(tile.y < yMin) { |
|---|
| 970 |
// too far up |
|---|
| 971 |
tile.panDown(_rows); |
|---|
| 972 |
tile.y += _rows * TILE_HEIGHT; |
|---|
| 973 |
touched = true; |
|---|
| 974 |
|
|---|
| 975 |
} else if(tile.y > yMax) { |
|---|
| 976 |
// too far down |
|---|
| 977 |
if((tile.y - _rows * TILE_HEIGHT) > yMin) { |
|---|
| 978 |
// moving up wouldn't put us too far |
|---|
| 979 |
tile.panUp(_rows); |
|---|
| 980 |
tile.y -= _rows * TILE_HEIGHT; |
|---|
| 981 |
touched = true; |
|---|
| 982 |
} |
|---|
| 983 |
} |
|---|
| 984 |
|
|---|
| 985 |
if(tile.x < xMin) { |
|---|
| 986 |
// too far left |
|---|
| 987 |
tile.panRight(_columns); |
|---|
| 988 |
tile.x += _columns * TILE_WIDTH; |
|---|
| 989 |
touched = true; |
|---|
| 990 |
|
|---|
| 991 |
} else if(tile.x > xMax) { |
|---|
| 992 |
// too far right |
|---|
| 993 |
if((tile.x - _columns * TILE_WIDTH) > xMin) { |
|---|
| 994 |
// moving left wouldn't put us too far |
|---|
| 995 |
tile.panLeft(_columns); |
|---|
| 996 |
tile.x -= _columns * TILE_WIDTH; |
|---|
| 997 |
touched = true; |
|---|
| 998 |
} |
|---|
| 999 |
} |
|---|
| 1000 |
|
|---|