diff options
| author | Georgios Andreadis <g.andreadis@student.tudelft.nl> | 2017-01-24 12:06:09 +0100 |
|---|---|---|
| committer | Georgios Andreadis <g.andreadis@student.tudelft.nl> | 2017-01-24 12:06:09 +0100 |
| commit | c96e6ffafb62bde1e08987b1fdf3c0786487f6ec (patch) | |
| tree | 37eaf4cf199ca77dc131b4212c526b707adf2e30 /src/scripts/views | |
Initial commit
Diffstat (limited to 'src/scripts/views')
| -rw-r--r-- | src/scripts/views/layers/dcobject.ts | 252 | ||||
| -rw-r--r-- | src/scripts/views/layers/dcprogressbar.ts | 99 | ||||
| -rw-r--r-- | src/scripts/views/layers/gray.ts | 145 | ||||
| -rw-r--r-- | src/scripts/views/layers/grid.ts | 59 | ||||
| -rw-r--r-- | src/scripts/views/layers/hover.ts | 129 | ||||
| -rw-r--r-- | src/scripts/views/layers/layer.ts | 8 | ||||
| -rw-r--r-- | src/scripts/views/layers/room.ts | 177 | ||||
| -rw-r--r-- | src/scripts/views/layers/roomtext.ts | 68 | ||||
| -rw-r--r-- | src/scripts/views/layers/wall.ts | 62 | ||||
| -rw-r--r-- | src/scripts/views/mapview.ts | 373 |
10 files changed, 1372 insertions, 0 deletions
diff --git a/src/scripts/views/layers/dcobject.ts b/src/scripts/views/layers/dcobject.ts new file mode 100644 index 00000000..6cec1f7e --- /dev/null +++ b/src/scripts/views/layers/dcobject.ts @@ -0,0 +1,252 @@ +import {Colors} from "../../colors"; +import {Util, IntensityLevel} from "../../util"; +import {MapView} from "../mapview"; +import {DCProgressBar} from "./dcprogressbar"; +import {Layer} from "./layer"; +import {CELL_SIZE} from "../../controllers/mapcontroller"; + + +export class DCObjectLayer implements Layer { + public static ITEM_MARGIN = CELL_SIZE / 7.0; + public static ITEM_PADDING = CELL_SIZE / 10.0; + public static STROKE_WIDTH = CELL_SIZE / 20.0; + public static PROGRESS_BAR_DISTANCE = CELL_SIZE / 17.0; + public static CONTENT_SIZE = CELL_SIZE - DCObjectLayer.ITEM_MARGIN * 2 - DCObjectLayer.ITEM_PADDING * 3; + + public container: createjs.Container; + public detailedMode: boolean; + public coloringMode: boolean; + public intensityLevels: { [key: number]: IntensityLevel; } = {}; + + private mapView: MapView; + private preload: createjs.LoadQueue; + private rackSpaceBitmap: createjs.Bitmap; + private rackEnergyBitmap: createjs.Bitmap; + private psuBitmap: createjs.Bitmap; + private coolingItemBitmap: createjs.Bitmap; + + // This associative lookup object keeps all DC display objects with as property name the index of the global map + // array that they are located in. + private dcObjectMap: { [key: number]: any; }; + + + public static drawHoverRack(position: IGridPosition): createjs.Container { + let result = new createjs.Container(); + + DCObjectLayer.drawItemRectangle( + position, Colors.RACK_BACKGROUND, Colors.RACK_BORDER, result + ); + DCProgressBar.drawItemProgressRectangle( + position, Colors.RACK_SPACE_BAR_BACKGROUND, result, 0, 1 + ); + DCProgressBar.drawItemProgressRectangle( + position, Colors.RACK_ENERGY_BAR_BACKGROUND, result, 1, 1 + ); + + return result; + } + + public static drawHoverPSU(position: IGridPosition): createjs.Container { + let result = new createjs.Container(); + + DCObjectLayer.drawItemRectangle( + position, Colors.PSU_BACKGROUND, Colors.PSU_BORDER, result + ); + + return result; + } + + public static drawHoverCoolingItem(position: IGridPosition): createjs.Container { + let result = new createjs.Container(); + + DCObjectLayer.drawItemRectangle( + position, Colors.COOLING_ITEM_BACKGROUND, Colors.COOLING_ITEM_BORDER, result + ); + + return result; + } + + /** + * Draws an object rectangle in a given grid cell, with margin around its border. + * + * @param position The coordinates of the grid cell in which it should be located + * @param color The background color of the item + * @param borderColor The border color + * @param container The container to which it should be drawn + * @returns {createjs.Shape} The drawn shape + */ + private static drawItemRectangle(position: IGridPosition, color: string, borderColor: string, + container: createjs.Container): createjs.Shape { + let shape = new createjs.Shape(); + shape.graphics.beginStroke(borderColor); + shape.graphics.setStrokeStyle(DCObjectLayer.STROKE_WIDTH); + shape.graphics.beginFill(color); + shape.graphics.drawRect( + position.x * CELL_SIZE + DCObjectLayer.ITEM_MARGIN, + position.y * CELL_SIZE + DCObjectLayer.ITEM_MARGIN, + CELL_SIZE - DCObjectLayer.ITEM_MARGIN * 2, + CELL_SIZE - DCObjectLayer.ITEM_MARGIN * 2 + ); + container.addChild(shape); + return shape; + } + + /** + * Draws an bitmap in item format. + * + * @param position The coordinates of the grid cell in which it should be located + * @param container The container to which it should be drawn + * @param originBitmap The bitmap that should be drawn + * @returns {createjs.Bitmap} The drawn bitmap + */ + private static drawItemIcon(position: IGridPosition, container: createjs.Container, + originBitmap: createjs.Bitmap): createjs.Bitmap { + let bitmap = originBitmap.clone(); + container.addChild(bitmap); + bitmap.x = position.x * CELL_SIZE + DCObjectLayer.ITEM_MARGIN + DCObjectLayer.ITEM_PADDING * 1.5; + bitmap.y = position.y * CELL_SIZE + DCObjectLayer.ITEM_MARGIN + DCObjectLayer.ITEM_PADDING * 1.5; + return bitmap; + } + + constructor(mapView: MapView) { + this.mapView = mapView; + this.container = new createjs.Container(); + + this.detailedMode = true; + this.coloringMode = false; + + this.preload = new createjs.LoadQueue(); + this.preload.addEventListener("complete", () => { + this.rackSpaceBitmap = new createjs.Bitmap(<HTMLImageElement>this.preload.getResult("rack-space")); + this.rackEnergyBitmap = new createjs.Bitmap(<HTMLImageElement>this.preload.getResult("rack-energy")); + this.psuBitmap = new createjs.Bitmap(<HTMLImageElement>this.preload.getResult("psu")); + this.coolingItemBitmap = new createjs.Bitmap(<HTMLImageElement>this.preload.getResult("coolingitem")); + + // Scale the images + this.rackSpaceBitmap.scaleX = DCProgressBar.PROGRESS_BAR_WIDTH / this.rackSpaceBitmap.image.width; + this.rackSpaceBitmap.scaleY = DCProgressBar.PROGRESS_BAR_WIDTH / this.rackSpaceBitmap.image.height; + + this.rackEnergyBitmap.scaleX = DCProgressBar.PROGRESS_BAR_WIDTH / this.rackEnergyBitmap.image.width; + this.rackEnergyBitmap.scaleY = DCProgressBar.PROGRESS_BAR_WIDTH / this.rackEnergyBitmap.image.height; + + this.psuBitmap.scaleX = DCObjectLayer.CONTENT_SIZE / this.psuBitmap.image.width; + this.psuBitmap.scaleY = DCObjectLayer.CONTENT_SIZE / this.psuBitmap.image.height; + + this.coolingItemBitmap.scaleX = DCObjectLayer.CONTENT_SIZE / this.coolingItemBitmap.image.width; + this.coolingItemBitmap.scaleY = DCObjectLayer.CONTENT_SIZE / this.coolingItemBitmap.image.height; + + + this.populateObjectList(); + this.draw(); + + this.mapView.updateScene = true; + }); + + this.preload.loadFile({id: "rack-space", src: 'img/app/rack-space.png'}); + this.preload.loadFile({id: "rack-energy", src: 'img/app/rack-energy.png'}); + this.preload.loadFile({id: "psu", src: 'img/app/psu.png'}); + this.preload.loadFile({id: "coolingitem", src: 'img/app/coolingitem.png'}); + } + + /** + * Generates a list of DC objects with their associated display objects. + */ + public populateObjectList(): void { + this.dcObjectMap = {}; + + this.mapView.currentDatacenter.rooms.forEach((room: IRoom) => { + room.tiles.forEach((tile: ITile) => { + if (tile.object !== undefined) { + let index = tile.position.y * MapView.MAP_SIZE + tile.position.x; + + switch (tile.objectType) { + case "RACK": + this.dcObjectMap[index] = { + spaceBar: new DCProgressBar(this.container, + Colors.RACK_SPACE_BAR_BACKGROUND, Colors.RACK_SPACE_BAR_FILL, + this.rackSpaceBitmap, tile.position, 0, + Util.getFillRatio((<IRack>tile.object).machines)), + energyBar: new DCProgressBar(this.container, + Colors.RACK_ENERGY_BAR_BACKGROUND, Colors.RACK_ENERGY_BAR_FILL, + this.rackEnergyBitmap, tile.position, 1, + Util.getFillRatio((<IRack>tile.object).machines)), + itemRect: createjs.Shape, + tile: tile, model: tile.object, position: tile.position, type: tile.objectType + }; + + break; + case "COOLING_ITEM": + this.dcObjectMap[index] = { + itemRect: createjs.Shape, batteryIcon: createjs.Bitmap, + tile: tile, model: tile.object, position: tile.position, type: tile.objectType + }; + break; + case "PSU": + this.dcObjectMap[index] = { + itemRect: createjs.Shape, freezeIcon: createjs.Bitmap, + tile: tile, model: tile.object, position: tile.position, type: tile.objectType + }; + break; + } + } + }); + }); + } + + public draw(): void { + let currentObject; + + this.container.removeAllChildren(); + + this.container.cursor = "pointer"; + + for (let property in this.dcObjectMap) { + if (this.dcObjectMap.hasOwnProperty(property)) { + currentObject = this.dcObjectMap[property]; + + switch (currentObject.type) { + case "RACK": + let color = Colors.RACK_BACKGROUND; + + if (this.coloringMode && currentObject.tile.roomId === + this.mapView.mapController.roomModeController.currentRoom.id) { + color = Util.convertIntensityToColor(this.intensityLevels[currentObject.model.id]); + } + + currentObject.itemRect = DCObjectLayer.drawItemRectangle( + currentObject.position, color, Colors.RACK_BORDER, this.container + ); + + if (this.detailedMode) { + currentObject.spaceBar.fillRatio = Util.getFillRatio(currentObject.model.machines); + currentObject.energyBar.fillRatio = Util.getEnergyRatio(currentObject.model); + + currentObject.spaceBar.draw(); + currentObject.energyBar.draw(); + } + break; + case "COOLING_ITEM": + currentObject.itemRect = DCObjectLayer.drawItemRectangle( + currentObject.position, Colors.COOLING_ITEM_BACKGROUND, Colors.COOLING_ITEM_BORDER, + this.container + ); + + currentObject.freezeIcon = DCObjectLayer.drawItemIcon(currentObject.position, this.container, + this.coolingItemBitmap); + break; + case "PSU": + currentObject.itemRect = DCObjectLayer.drawItemRectangle( + currentObject.position, Colors.PSU_BACKGROUND, Colors.PSU_BORDER, + this.container + ); + + currentObject.batteryIcon = DCObjectLayer.drawItemIcon(currentObject.position, this.container, + this.psuBitmap); + break; + } + } + } + + this.mapView.updateScene = true; + } +}
\ No newline at end of file diff --git a/src/scripts/views/layers/dcprogressbar.ts b/src/scripts/views/layers/dcprogressbar.ts new file mode 100644 index 00000000..d0ec4397 --- /dev/null +++ b/src/scripts/views/layers/dcprogressbar.ts @@ -0,0 +1,99 @@ +import {DCObjectLayer} from "./dcobject"; +import {CELL_SIZE} from "../../controllers/mapcontroller"; + + +export class DCProgressBar { + public static PROGRESS_BAR_WIDTH = CELL_SIZE / 7.0; + + public container: createjs.Container; + public fillRatio: number; + + private backgroundRect: createjs.Shape; + private backgroundColor: string; + private fillRect: createjs.Shape; + private fillColor: string; + private bitmap: createjs.Bitmap; + private position: IGridPosition; + private distanceFromBottom: number; + + + /** + * Draws a progress rectangle with rounded ends. + * + * @param position The coordinates of the grid cell in which it should be located + * @param color The background color of the item + * @param container The container to which it should be drawn + * @param distanceFromBottom The index of its vertical position, counted from the bottom (0 is the lowest position) + * @param fractionFilled The fraction of the available horizontal space that the progress bar should take up + * @returns {createjs.Shape} The drawn shape + */ + public static drawItemProgressRectangle(position: IGridPosition, color: string, + container: createjs.Container, distanceFromBottom: number, + fractionFilled: number): createjs.Shape { + let shape = new createjs.Shape(); + shape.graphics.beginFill(color); + let x = position.x * CELL_SIZE + DCObjectLayer.ITEM_MARGIN + DCObjectLayer.ITEM_PADDING; + let y = (position.y + 1) * CELL_SIZE - DCObjectLayer.ITEM_MARGIN - DCObjectLayer.ITEM_PADDING - + DCProgressBar.PROGRESS_BAR_WIDTH - distanceFromBottom * + (DCProgressBar.PROGRESS_BAR_WIDTH + DCObjectLayer.PROGRESS_BAR_DISTANCE); + let width = (CELL_SIZE - (DCObjectLayer.ITEM_MARGIN + DCObjectLayer.ITEM_PADDING) * 2) * fractionFilled; + let height; + let radius; + + if (width < DCProgressBar.PROGRESS_BAR_WIDTH) { + height = width; + radius = width / 2; + y += (DCProgressBar.PROGRESS_BAR_WIDTH - height) / 2; + } else { + height = DCProgressBar.PROGRESS_BAR_WIDTH; + radius = DCProgressBar.PROGRESS_BAR_WIDTH / 2; + } + + shape.graphics.drawRoundRect( + x, y, width, height, radius + ); + container.addChild(shape); + return shape; + } + + /** + * Draws an bitmap in progressbar format. + * + * @param position The coordinates of the grid cell in which it should be located + * @param container The container to which it should be drawn + * @param originBitmap The bitmap that should be drawn + * @param distanceFromBottom The index of its vertical position, counted from the bottom (0 is the lowest position) + * @returns {createjs.Bitmap} The drawn bitmap + */ + public static drawProgressbarIcon(position: IGridPosition, container: createjs.Container, originBitmap: createjs.Bitmap, + distanceFromBottom: number): createjs.Bitmap { + let bitmap = originBitmap.clone(); + container.addChild(bitmap); + bitmap.x = (position.x + 0.5) * CELL_SIZE - DCProgressBar.PROGRESS_BAR_WIDTH * 0.5; + bitmap.y = (position.y + 1) * CELL_SIZE - DCObjectLayer.ITEM_MARGIN - DCObjectLayer.ITEM_PADDING - + DCProgressBar.PROGRESS_BAR_WIDTH - distanceFromBottom * + (DCProgressBar.PROGRESS_BAR_WIDTH + DCObjectLayer.PROGRESS_BAR_DISTANCE); + return bitmap; + } + + constructor(container: createjs.Container, backgroundColor: string, + fillColor: string, bitmap: createjs.Bitmap, position: IGridPosition, + indexFromBottom: number, fillRatio: number) { + this.container = container; + this.backgroundColor = backgroundColor; + this.fillColor = fillColor; + this.bitmap = bitmap; + this.position = position; + this.distanceFromBottom = indexFromBottom; + this.fillRatio = fillRatio; + } + + public draw() { + this.backgroundRect = DCProgressBar.drawItemProgressRectangle(this.position, this.backgroundColor, + this.container, this.distanceFromBottom, 1); + this.fillRect = DCProgressBar.drawItemProgressRectangle(this.position, this.fillColor, this.container, + this.distanceFromBottom, this.fillRatio); + + DCProgressBar.drawProgressbarIcon(this.position, this.container, this.bitmap, this.distanceFromBottom); + } +}
\ No newline at end of file diff --git a/src/scripts/views/layers/gray.ts b/src/scripts/views/layers/gray.ts new file mode 100644 index 00000000..ed3c9429 --- /dev/null +++ b/src/scripts/views/layers/gray.ts @@ -0,0 +1,145 @@ +import {MapView} from "../mapview"; +import {Colors} from "../../colors"; +import {Util} from "../../util"; +import {Layer} from "./layer"; + + +/** + * Class responsible for graying out non-active UI elements. + */ +export class GrayLayer implements Layer { + public container: createjs.Container; + public currentRoom: IRoom; + public currentObjectTile: ITile; + + private mapView: MapView; + private grayRoomShape: createjs.Shape; + + + constructor(mapView: MapView) { + this.mapView = mapView; + this.container = new createjs.Container(); + } + + /** + * Draws grayed out areas around a currently selected room. + * + * @param redraw Whether this is a redraw, or an initial draw action + */ + public draw(redraw?: boolean): void { + if (this.currentRoom === undefined) { + return; + } + + this.container.removeAllChildren(); + + let roomBounds = Util.calculateRoomBounds(this.currentRoom); + + let shape = new createjs.Shape(); + shape.graphics.beginFill(Colors.GRAYED_OUT_AREA); + shape.cursor = "pointer"; + + this.drawLargeRects(shape, roomBounds); + this.drawFineGrainedRects(shape, roomBounds); + + this.container.addChild(shape); + if (redraw === true) { + shape.alpha = 1; + } else { + shape.alpha = 0; + this.mapView.animate(shape, {alpha: 1}); + } + + if (this.grayRoomShape !== undefined && !this.grayRoomShape.visible) { + this.grayRoomShape = undefined; + this.drawRackLevel(redraw); + } + + this.mapView.updateScene = true; + } + + private drawLargeRects(shape: createjs.Shape, roomBounds: IBounds): void { + if (roomBounds.min[0] > 0) { + MapView.drawRectangleToShape({x: 0, y: 0}, shape, roomBounds.min[0], MapView.MAP_SIZE); + } + if (roomBounds.min[1] > 0) { + MapView.drawRectangleToShape({x: roomBounds.min[0], y: 0}, shape, roomBounds.max[0] - roomBounds.min[0], + roomBounds.min[1]); + } + if (roomBounds.max[0] < MapView.MAP_SIZE - 1) { + MapView.drawRectangleToShape({x: roomBounds.max[0], y: 0}, shape, MapView.MAP_SIZE - roomBounds.max[0], + MapView.MAP_SIZE); + } + if (roomBounds.max[1] < MapView.MAP_SIZE - 1) { + MapView.drawRectangleToShape({x: roomBounds.min[0], y: roomBounds.max[1]}, shape, + roomBounds.max[0] - roomBounds.min[0], MapView.MAP_SIZE - roomBounds.max[1]); + } + } + + private drawFineGrainedRects(shape: createjs.Shape, roomBounds: IBounds): void { + for (let x = roomBounds.min[0]; x < roomBounds.max[0]; x++) { + for (let y = roomBounds.min[1]; y < roomBounds.max[1]; y++) { + if (!Util.tileListContainsPosition(this.currentRoom.tiles, {x: x, y: y})) { + MapView.drawRectangleToShape({x: x, y: y}, shape); + } + } + } + } + + public drawRackLevel(redraw?: boolean): void { + if (this.currentObjectTile === undefined) { + return; + } + + this.grayRoomShape = new createjs.Shape(); + this.grayRoomShape.graphics.beginFill(Colors.GRAYED_OUT_AREA); + this.grayRoomShape.cursor = "pointer"; + this.grayRoomShape.alpha = 0; + + this.currentRoom.tiles.forEach((tile: ITile) => { + if (this.currentObjectTile.position.x !== tile.position.x || + this.currentObjectTile.position.y !== tile.position.y) { + MapView.drawRectangleToShape({x: tile.position.x, y: tile.position.y}, this.grayRoomShape); + } + }); + + this.container.addChild(this.grayRoomShape); + if (redraw === true) { + this.grayRoomShape.alpha = 1; + } else { + this.grayRoomShape.alpha = 0; + this.mapView.animate(this.grayRoomShape, {alpha: 1}); + } + } + + public hideRackLevel(): void { + if (this.currentObjectTile === undefined) { + return; + } + + this.mapView.animate(this.grayRoomShape, { + alpha: 0, visible: false + }); + } + + /** + * Clears the container. + */ + public clear(): void { + this.mapView.animate(this.container, {alpha: 0}, () => { + this.container.removeAllChildren(); + this.container.alpha = 1; + }); + this.grayRoomShape = undefined; + this.currentRoom = undefined; + } + + /** + * Checks whether there is already an active room with grayed out areas around it. + * + * @returns {boolean} Whether the room is grayed out + */ + public isGrayedOut(): boolean { + return this.currentRoom !== undefined; + } +}
\ No newline at end of file diff --git a/src/scripts/views/layers/grid.ts b/src/scripts/views/layers/grid.ts new file mode 100644 index 00000000..9a52b2af --- /dev/null +++ b/src/scripts/views/layers/grid.ts @@ -0,0 +1,59 @@ +import {Layer} from "./layer"; +import {MapView} from "../mapview"; +import {Colors} from "../../colors"; +import {CELL_SIZE} from "../../controllers/mapcontroller"; + + +/** + * Class responsible for rendering the grid. + */ +export class GridLayer implements Layer { + public container: createjs.Container; + public gridPixelSize: number; + + private mapView: MapView; + private gridLineWidth: number; + + + constructor(mapView: MapView) { + this.mapView = mapView; + this.container = new createjs.Container(); + + this.gridLineWidth = 0.5; + this.gridPixelSize = MapView.MAP_SIZE * CELL_SIZE; + + this.draw(); + } + + /** + * Draws the entire grid (later to be navigated around with offsets). + */ + public draw(): void { + this.container.removeAllChildren(); + + let currentCellX = 0; + let currentCellY = 0; + + while (currentCellX <= MapView.MAP_SIZE) { + MapView.drawLine( + currentCellX * CELL_SIZE, 0, + currentCellX * CELL_SIZE, MapView.MAP_SIZE * CELL_SIZE, + this.gridLineWidth, Colors.GRID_COLOR, this.container); + + currentCellX++; + } + + while (currentCellY <= MapView.MAP_SIZE) { + MapView.drawLine( + 0, currentCellY * CELL_SIZE, + MapView.MAP_SIZE * CELL_SIZE, currentCellY * CELL_SIZE, + this.gridLineWidth, Colors.GRID_COLOR, this.container); + + currentCellY++; + } + } + + public setVisibility(value: boolean): void { + this.container.visible = value; + } +}
\ No newline at end of file diff --git a/src/scripts/views/layers/hover.ts b/src/scripts/views/layers/hover.ts new file mode 100644 index 00000000..b9f5509c --- /dev/null +++ b/src/scripts/views/layers/hover.ts @@ -0,0 +1,129 @@ +import {Layer} from "./layer"; +import {MapView} from "../mapview"; +import {Colors} from "../../colors"; +import {DCObjectLayer} from "./dcobject"; +import {CELL_SIZE} from "../../controllers/mapcontroller"; + + +/** + * Class responsible for rendering the hover layer. + */ +export class HoverLayer implements Layer { + public container: createjs.Container; + public hoverTilePosition: IGridPosition; + + private mapView: MapView; + private hoverTile: createjs.Shape; + private hoverRack: createjs.Container; + private hoverPSU: createjs.Container; + private hoverCoolingItem: createjs.Container; + + + constructor(mapView: MapView) { + this.mapView = mapView; + this.container = new createjs.Container(); + + this.initialDraw(); + } + + /** + * Draws the hover tile to the container at its current location and with its current color. + */ + public draw(): void { + let color; + + if (this.mapView.roomLayer.checkHoverTileValidity(this.hoverTilePosition)) { + color = Colors.ROOM_HOVER_VALID; + } else { + color = Colors.ROOM_HOVER_INVALID; + } + + this.hoverTile.graphics.clear().beginFill(color) + .drawRect(this.hoverTilePosition.x * CELL_SIZE, this.hoverTilePosition.y * CELL_SIZE, + CELL_SIZE, CELL_SIZE) + .endFill(); + if (this.hoverRack.visible) { + this.hoverRack.x = this.hoverTilePosition.x * CELL_SIZE; + this.hoverRack.y = this.hoverTilePosition.y * CELL_SIZE; + } else if (this.hoverPSU.visible) { + this.hoverPSU.x = this.hoverTilePosition.x * CELL_SIZE; + this.hoverPSU.y = this.hoverTilePosition.y * CELL_SIZE; + } else if (this.hoverCoolingItem.visible) { + this.hoverCoolingItem.x = this.hoverTilePosition.x * CELL_SIZE; + this.hoverCoolingItem.y = this.hoverTilePosition.y * CELL_SIZE; + } + } + + /** + * Performs the initial drawing action. + */ + public initialDraw(): void { + this.container.removeAllChildren(); + + this.hoverTile = new createjs.Shape(); + + this.hoverTilePosition = {x: 0, y: 0}; + + this.hoverTile = MapView.drawRectangle(this.hoverTilePosition, Colors.ROOM_HOVER_VALID, this.container); + this.hoverTile.visible = false; + + this.hoverRack = DCObjectLayer.drawHoverRack(this.hoverTilePosition); + this.hoverPSU = DCObjectLayer.drawHoverPSU(this.hoverTilePosition); + this.hoverCoolingItem = DCObjectLayer.drawHoverCoolingItem(this.hoverTilePosition); + + this.container.addChild(this.hoverRack); + this.container.addChild(this.hoverPSU); + this.container.addChild(this.hoverCoolingItem); + + this.hoverRack.visible = false; + this.hoverPSU.visible = false; + this.hoverCoolingItem.visible = false; + } + + /** + * Sets the hover tile visibility to true/false. + * + * @param value The visibility value + */ + public setHoverTileVisibility(value: boolean): void { + this.hoverTile.visible = value; + this.mapView.updateScene = true; + } + + /** + * Sets the hover item visibility to true/false. + * + * @param value The visibility value + * @param type The type of the object to be shown + */ + public setHoverItemVisibility(value: boolean, type?: string): void { + if (value === true) { + this.hoverTile.visible = true; + + this.setHoverItemVisibilities(type); + } else { + this.hoverTile.visible = false; + this.hoverRack.visible = false; + this.hoverPSU.visible = false; + this.hoverCoolingItem.visible = false; + } + + this.mapView.updateScene = true; + } + + private setHoverItemVisibilities(type: string): void { + if (type === "RACK") { + this.hoverRack.visible = true; + this.hoverPSU.visible = false; + this.hoverCoolingItem.visible = false; + } else if (type === "PSU") { + this.hoverRack.visible = false; + this.hoverPSU.visible = true; + this.hoverCoolingItem.visible = false; + } else if (type === "COOLING_ITEM") { + this.hoverRack.visible = false; + this.hoverPSU.visible = false; + this.hoverCoolingItem.visible = true; + } + } +}
\ No newline at end of file diff --git a/src/scripts/views/layers/layer.ts b/src/scripts/views/layers/layer.ts new file mode 100644 index 00000000..5e5295ac --- /dev/null +++ b/src/scripts/views/layers/layer.ts @@ -0,0 +1,8 @@ +/** + * Interface for a subview, representing a layer of the map view. + */ +export interface Layer { + container: createjs.Container; + + draw(): void; +} diff --git a/src/scripts/views/layers/room.ts b/src/scripts/views/layers/room.ts new file mode 100644 index 00000000..0e31fee0 --- /dev/null +++ b/src/scripts/views/layers/room.ts @@ -0,0 +1,177 @@ +import {InteractionLevel} from "../../controllers/mapcontroller"; +import {Util, IntensityLevel} from "../../util"; +import {Colors} from "../../colors"; +import {MapView} from "../mapview"; +import {Layer} from "./layer"; + + +/** + * Class responsible for rendering the rooms. + */ +export class RoomLayer implements Layer { + public container: createjs.Container; + public coloringMode: boolean; + public selectedTiles: ITile[]; + public selectedTileObjects: TilePositionObject[]; + public intensityLevels: { [key: number]: IntensityLevel; } = {}; + + private mapView: MapView; + private allRoomTileObjects: TilePositionObject[]; + private validNextTilePositions: IGridPosition[]; + + + constructor(mapView: MapView) { + this.mapView = mapView; + this.container = new createjs.Container(); + + this.allRoomTileObjects = []; + this.selectedTiles = []; + this.validNextTilePositions = []; + this.selectedTileObjects = []; + this.coloringMode = false; + + this.draw(); + } + + /** + * Draws all rooms to the canvas. + */ + public draw() { + this.container.removeAllChildren(); + + this.mapView.currentDatacenter.rooms.forEach((room: IRoom) => { + let color = Colors.ROOM_DEFAULT; + + if (this.coloringMode && room.roomType === "SERVER" && this.intensityLevels[room.id] !== undefined) { + color = Util.convertIntensityToColor(this.intensityLevels[room.id]); + } + + room.tiles.forEach((tile: ITile) => { + this.allRoomTileObjects.push({ + position: tile.position, + tileObject: MapView.drawRectangle(tile.position, color, this.container) + }); + }); + }); + } + + /** + * Adds a newly selected tile to the list of selected tiles. + * + * If the tile was already selected beforehand, it is removed. + * + * @param tile The tile to be added + */ + public addSelectedTile(tile: ITile): void { + this.selectedTiles.push(tile); + + let tileObject = MapView.drawRectangle(tile.position, Colors.ROOM_SELECTED, this.container); + this.selectedTileObjects.push({ + position: {x: tile.position.x, y: tile.position.y}, + tileObject: tileObject + }); + + this.validNextTilePositions = Util.deriveValidNextTilePositions( + this.mapView.currentDatacenter.rooms, this.selectedTiles); + + this.mapView.updateScene = true; + } + + /** + * Removes a selected tile (upon being clicked on again). + * + * @param position The position at which a selected tile should be removed + * @param objectIndex The index of the tile in the selectedTileObjects array + */ + public removeSelectedTile(position: IGridPosition, objectIndex: number): void { + let index = Util.tileListPositionIndexOf(this.selectedTiles, position); + + // Check whether the given position doesn't belong to an already removed tile + if (index === -1) { + return; + } + + this.selectedTiles.splice(index, 1); + + this.container.removeChild(this.selectedTileObjects[objectIndex].tileObject); + this.selectedTileObjects.splice(objectIndex, 1); + + this.validNextTilePositions = Util.deriveValidNextTilePositions( + this.mapView.currentDatacenter.rooms, this.selectedTiles); + + this.mapView.updateScene = true; + } + + /** + * Checks whether a hovered tile is in a valid location. + * + * @param position The tile location to be checked + * @returns {boolean} Whether it is a valid location + */ + public checkHoverTileValidity(position: IGridPosition): boolean { + if (this.mapView.mapController.interactionLevel === InteractionLevel.BUILDING) { + if (this.selectedTiles.length === 0) { + return !Util.checkRoomCollision(this.mapView.currentDatacenter.rooms, position); + } + return Util.positionListContainsPosition(this.validNextTilePositions, position); + } else if (this.mapView.mapController.interactionLevel === InteractionLevel.ROOM) { + let valid = false; + this.mapView.mapController.roomModeController.currentRoom.tiles.forEach((element: ITile) => { + if (position.x === element.position.x && position.y === element.position.y && + element.object === undefined) { + valid = true; + } + }); + return valid; + } + } + + /** + * Cancels room tile selection by removing all selected tiles from the scene. + */ + public cancelRoomConstruction(): void { + if (this.selectedTiles.length === 0) { + return; + } + + this.selectedTileObjects.forEach((tileObject: TilePositionObject) => { + this.container.removeChild(tileObject.tileObject); + }); + + this.resetTileLists(); + + this.mapView.updateScene = true; + } + + /** + * Finalizes the selected room tiles into a standard room. + */ + public finalizeRoom(room: IRoom): void { + if (this.selectedTiles.length === 0) { + return; + } + + this.mapView.currentDatacenter.rooms.push(room); + + this.resetTileLists(); + + // Trigger a redraw + this.draw(); + this.mapView.wallLayer.generateWalls(); + this.mapView.wallLayer.draw(); + + this.mapView.updateScene = true; + } + + private resetTileLists(): void { + this.selectedTiles = []; + this.validNextTilePositions = []; + this.selectedTileObjects = []; + } + + public setClickable(value: boolean): void { + this.allRoomTileObjects.forEach((tileObj: TilePositionObject) => { + tileObj.tileObject.cursor = value ? "pointer" : "default"; + }); + } +}
\ No newline at end of file diff --git a/src/scripts/views/layers/roomtext.ts b/src/scripts/views/layers/roomtext.ts new file mode 100644 index 00000000..65ea0735 --- /dev/null +++ b/src/scripts/views/layers/roomtext.ts @@ -0,0 +1,68 @@ +import {MapView} from "../mapview"; +import {Colors} from "../../colors"; +import {Util} from "../../util"; +import {Layer} from "./layer"; +import {CELL_SIZE} from "../../controllers/mapcontroller"; + + +export class RoomTextLayer implements Layer { + private static TEXT_PADDING = 4; + + public container: createjs.Container; + + private mapView: MapView; + + + constructor(mapView: MapView) { + this.mapView = mapView; + this.container = new createjs.Container(); + + this.draw(); + } + + public draw(): void { + this.container.removeAllChildren(); + + this.mapView.currentDatacenter.rooms.forEach((room: IRoom) => { + if (room.name !== "" && room.roomType !== "") { + this.renderTextOverlay(room); + } + }); + } + + public setVisibility(value: boolean): void { + this.mapView.animate(this.container, {alpha: value === true ? 1 : 0}); + } + + /** + * Draws a name and type overlay over the given room. + */ + private renderTextOverlay(room: IRoom): void { + if (room.name === null || room.tiles.length === 0) { + return; + } + + let textPos = Util.calculateRoomNamePosition(room); + + let bottomY = this.renderText(room.name, "12px Arial", textPos, + textPos.topLeft.y * CELL_SIZE + RoomTextLayer.TEXT_PADDING); + this.renderText("Type: " + Util.toSentenceCase(room.roomType), "10px Arial", textPos, bottomY + 5); + } + + private renderText(text: string, font: string, textPos: IRoomNamePos, startY: number): number { + let name = new createjs.Text(text, font, Colors.ROOM_NAME_COLOR); + + if (name.getMeasuredWidth() > textPos.length * CELL_SIZE - RoomTextLayer.TEXT_PADDING * 2) { + name.scaleX = name.scaleY = (textPos.length * CELL_SIZE - RoomTextLayer.TEXT_PADDING * 2) / + name.getMeasuredWidth(); + } + + // Position the text to the top left of the selected tile + name.x = textPos.topLeft.x * CELL_SIZE + RoomTextLayer.TEXT_PADDING; + name.y = startY; + + this.container.addChild(name); + + return name.y + name.getMeasuredHeight() * name.scaleY; + } +} diff --git a/src/scripts/views/layers/wall.ts b/src/scripts/views/layers/wall.ts new file mode 100644 index 00000000..06ba4675 --- /dev/null +++ b/src/scripts/views/layers/wall.ts @@ -0,0 +1,62 @@ +import {Colors} from "../../colors"; +import {MapView} from "../mapview"; +import {Util} from "../../util"; +import {Layer} from "./layer"; +import {CELL_SIZE} from "../../controllers/mapcontroller"; + + +/** + * Class responsible for rendering the walls. + */ +export class WallLayer implements Layer { + public container: createjs.Container; + + private mapView: MapView; + private walls: IRoomWall[]; + private wallLineWidth: number; + + + constructor(mapView: MapView) { + this.mapView = mapView; + this.container = new createjs.Container(); + this.wallLineWidth = CELL_SIZE / 20.0; + + this.generateWalls(); + this.draw(); + } + + /** + * Calls the Util.deriveWallLocations function to generate the wall locations. + */ + public generateWalls(): void { + this.walls = Util.deriveWallLocations(this.mapView.currentDatacenter.rooms); + } + + /** + * Draws all walls to the canvas. + */ + public draw(): void { + this.container.removeAllChildren(); + + // Draw walls + this.walls.forEach((element: IRoomWall) => { + if (element.horizontal) { + MapView.drawLine( + CELL_SIZE * element.startPos[0] - this.wallLineWidth / 2.0, + CELL_SIZE * element.startPos[1], + CELL_SIZE * (element.startPos[0] + element.length) + this.wallLineWidth / 2.0, + CELL_SIZE * element.startPos[1], + this.wallLineWidth, Colors.WALL_COLOR, this.container + ); + } else { + MapView.drawLine( + CELL_SIZE * element.startPos[0], + CELL_SIZE * element.startPos[1] - this.wallLineWidth / 2.0, + CELL_SIZE * element.startPos[0], + CELL_SIZE * (element.startPos[1] + element.length) + this.wallLineWidth / 2.0, + this.wallLineWidth, Colors.WALL_COLOR, this.container + ); + } + }); + } +}
\ No newline at end of file diff --git a/src/scripts/views/mapview.ts b/src/scripts/views/mapview.ts new file mode 100644 index 00000000..ae7fd5cb --- /dev/null +++ b/src/scripts/views/mapview.ts @@ -0,0 +1,373 @@ +///<reference path="../../../typings/globals/createjs-lib/index.d.ts" /> +///<reference path="../../../typings/globals/easeljs/index.d.ts" /> +///<reference path="../../../typings/globals/tweenjs/index.d.ts" /> +///<reference path="../../../typings/globals/preloadjs/index.d.ts" /> +///<reference path="../definitions.ts" /> +///<reference path="../controllers/mapcontroller.ts" /> +import * as $ from "jquery"; +import {Util} from "../util"; +import {MapController, CELL_SIZE} from "../controllers/mapcontroller"; +import {GridLayer} from "./layers/grid"; +import {RoomLayer} from "./layers/room"; +import {HoverLayer} from "./layers/hover"; +import {WallLayer} from "./layers/wall"; +import {DCObjectLayer} from "./layers/dcobject"; +import {GrayLayer} from "./layers/gray"; +import {RoomTextLayer} from "./layers/roomtext"; + + +/** + * Class responsible for rendering the map, by delegating the rendering tasks to appropriate instances. + */ +export class MapView { + public static MAP_SIZE = 100; + public static CELL_SIZE_METERS = 0.5; + public static MIN_ZOOM = 0.5; + public static DEFAULT_ZOOM = 2; + public static MAX_ZOOM = 6; + public static GAP_CORRECTION_DELTA = 0.2; + public static ANIMATION_LENGTH = 250; + + // Models + public simulation: ISimulation; + public currentDatacenter: IDatacenter; + + // Controllers + public mapController: MapController; + + // Canvas objects + public stage: createjs.Stage; + public mapContainer: createjs.Container; + + // Flag indicating whether the scene should be redrawn + public updateScene: boolean; + public animating: boolean; + + // Subviews + public gridLayer: GridLayer; + public roomLayer: RoomLayer; + public dcObjectLayer: DCObjectLayer; + public roomTextLayer: RoomTextLayer; + public hoverLayer: HoverLayer; + public wallLayer: WallLayer; + public grayLayer: GrayLayer; + + // Dynamic canvas attributes + public canvasWidth: number; + public canvasHeight: number; + + + /** + * Draws a line from (x1, y1) to (x2, y2). + * + * @param x1 The x coord. of start point + * @param y1 The y coord. of start point + * @param x2 The x coord. of end point + * @param y2 The y coord. of end point + * @param lineWidth The width of the line to be drawn + * @param color The color to be used + * @param container The container to be drawn to + */ + public static drawLine(x1: number, y1: number, x2: number, y2: number, + lineWidth: number, color: string, container: createjs.Container): createjs.Shape { + let line = new createjs.Shape(); + line.graphics.setStrokeStyle(lineWidth).beginStroke(color); + line.graphics.moveTo(x1, y1); + line.graphics.lineTo(x2, y2); + container.addChild(line); + return line; + } + + /** + * Draws a tile at the given location with the given color. + * + * @param position The grid coordinates of the tile + * @param color The color with which the rectangle should be drawn + * @param container The container to be drawn to + * @param sizeX Optional parameter specifying the width of the tile to be drawn (in grid units) + * @param sizeY Optional parameter specifying the height of the tile to be drawn (in grid units) + */ + public static drawRectangle(position: IGridPosition, color: string, container: createjs.Container, + sizeX?: number, sizeY?: number): createjs.Shape { + let tile = new createjs.Shape(); + tile.graphics.setStrokeStyle(0); + tile.graphics.beginFill(color); + tile.graphics.drawRect( + position.x * CELL_SIZE - MapView.GAP_CORRECTION_DELTA, + position.y * CELL_SIZE - MapView.GAP_CORRECTION_DELTA, + CELL_SIZE * (sizeX === undefined ? 1 : sizeX) + MapView.GAP_CORRECTION_DELTA * 2, + CELL_SIZE * (sizeY === undefined ? 1 : sizeY) + MapView.GAP_CORRECTION_DELTA * 2 + ); + container.addChild(tile); + return tile; + } + + /** + * Draws a tile at the given location with the given color, and add it to the given shape object. + * + * The fill color must be set beforehand, in order to not set it repeatedly and produce unwanted transparent overlap + * artifacts. + * + * @param position The grid coordinates of the tile + * @param shape The shape to be drawn to + * @param sizeX Optional parameter specifying the width of the tile to be drawn (in grid units) + * @param sizeY Optional parameter specifying the height of the tile to be drawn (in grid units) + */ + public static drawRectangleToShape(position: IGridPosition, shape: createjs.Shape, + sizeX?: number, sizeY?: number) { + shape.graphics.drawRect( + position.x * CELL_SIZE - MapView.GAP_CORRECTION_DELTA, + position.y * CELL_SIZE - MapView.GAP_CORRECTION_DELTA, + CELL_SIZE * (sizeX === undefined ? 1 : sizeX) + MapView.GAP_CORRECTION_DELTA * 2, + CELL_SIZE * (sizeY === undefined ? 1 : sizeY) + MapView.GAP_CORRECTION_DELTA * 2 + ); + } + + constructor(simulation: ISimulation, stage: createjs.Stage) { + this.simulation = simulation; + let path = this.simulation.paths[this.simulation.paths.length - 1]; + this.currentDatacenter = path.sections[path.sections.length - 1].datacenter; + + this.stage = stage; + + console.log("THE DATA", simulation); + + let canvas = $("#main-canvas"); + this.canvasWidth = canvas.width(); + this.canvasHeight = canvas.height(); + + this.mapContainer = new createjs.Container(); + + this.initializeLayers(); + + this.drawMap(); + this.updateScene = true; + this.animating = false; + + this.mapController = new MapController(this); + + // Zoom DC to fit, if rooms are present + if (this.currentDatacenter.rooms.length > 0) { + this.zoomOutOnDC(); + } + + // Checks at every rendering tick whether the scene has changed, and updates accordingly + createjs.Ticker.addEventListener("tick", (event: createjs.TickerEvent) => { + if (this.updateScene || this.animating) { + if (this.mapController.isInHoverMode()) { + this.hoverLayer.draw(); + } + + this.updateScene = false; + this.stage.update(event); + } + }); + } + + private initializeLayers(): void { + this.gridLayer = new GridLayer(this); + this.roomLayer = new RoomLayer(this); + this.dcObjectLayer = new DCObjectLayer(this); + this.roomTextLayer = new RoomTextLayer(this); + this.hoverLayer = new HoverLayer(this); + this.wallLayer = new WallLayer(this); + this.grayLayer = new GrayLayer(this); + } + + /** + * Triggers a redraw and re-population action on all layers. + */ + public redrawMap(): void { + this.gridLayer.draw(); + this.roomLayer.draw(); + this.dcObjectLayer.populateObjectList(); + this.dcObjectLayer.draw(); + this.roomTextLayer.draw(); + this.hoverLayer.initialDraw(); + this.wallLayer.generateWalls(); + this.wallLayer.draw(); + this.grayLayer.draw(true); + this.updateScene = true; + } + + /** + * Zooms in on a given position with a given amount. + * + * @param position The position that should appear centered after the zoom action + * @param amount The amount of zooming that should be performed + */ + public zoom(position: number[], amount: number): void { + const newZoom = this.mapContainer.scaleX + 0.01 * amount; + + // Check whether zooming too far in / out + if (newZoom > MapView.MAX_ZOOM || + newZoom < MapView.MIN_ZOOM) { + return; + } + + // Calculate position difference if zoomed, in order to later compensate for this + // unwanted movement + let oldPosition = [ + position[0] - this.mapContainer.x, position[1] - this.mapContainer.y + ]; + let newPosition = [ + (oldPosition[0] / this.mapContainer.scaleX) * newZoom, + (oldPosition[1] / this.mapContainer.scaleX) * newZoom + ]; + let positionDelta = [ + newPosition[0] - oldPosition[0], newPosition[1] - oldPosition[1] + ]; + + // Apply the transformation operation to keep the selected position static + let newX = this.mapContainer.x - positionDelta[0]; + let newY = this.mapContainer.y - positionDelta[1]; + + let finalPos = this.mapController.checkCanvasMovement(newX, newY, newZoom); + + if (!this.animating) { + this.animate(this.mapContainer, { + scaleX: newZoom, scaleY: newZoom, + x: finalPos.x, y: finalPos.y + }); + } + } + + /** + * Adjusts the viewing scale to fully display a selected room and center it in view. + * + * @param room The room to be centered + * @param redraw Optional argument specifying whether this is a scene redraw + */ + public zoomInOnRoom(room: IRoom, redraw?: boolean): void { + this.zoomInOnRooms([room]); + + if (redraw === undefined || redraw === false) { + if (!this.grayLayer.isGrayedOut()) { + this.grayLayer.currentRoom = room; + this.grayLayer.draw(); + } + } + + this.updateScene = true; + } + + /** + * Zooms out to global building view. + */ + public zoomOutOnDC(): void { + this.grayLayer.clear(); + + if (this.currentDatacenter.rooms.length > 0) { + this.zoomInOnRooms(this.currentDatacenter.rooms); + } + + this.updateScene = true; + } + + /** + * Fits a given list of rooms to view, by scaling the viewport appropriately and moving the mapContainer. + * + * @param rooms The array of rooms to be viewed + */ + private zoomInOnRooms(rooms: IRoom[]): void { + let bounds = Util.calculateRoomListBounds(rooms); + let newScale = this.calculateNewScale(bounds); + + // Coordinates of the center of the room, relative to the global origin of the map + let roomCenterCoords = [ + bounds.center[0] * CELL_SIZE * newScale, + bounds.center[1] * CELL_SIZE * newScale + ]; + // Coordinates of the center of the stage (the visible part of the canvas), relative to the global map origin + let stageCenterCoords = [ + -this.mapContainer.x + this.canvasWidth / 2, + -this.mapContainer.y + this.canvasHeight / 2 + ]; + + let newX = this.mapContainer.x - roomCenterCoords[0] + stageCenterCoords[0]; + let newY = this.mapContainer.y - roomCenterCoords[1] + stageCenterCoords[1]; + + let newPosition = this.mapController.checkCanvasMovement(newX, newY, newScale); + + this.animate(this.mapContainer, { + scaleX: newScale, scaleY: newScale, + x: newPosition.x, y: newPosition.y + }); + } + + private calculateNewScale(bounds: IBounds): number { + const viewPadding = 30; + const sideMenuWidth = 350; + + let width = bounds.max[0] - bounds.min[0]; + let height = bounds.max[1] - bounds.min[1]; + + let scaleX = (this.canvasWidth - 2 * sideMenuWidth) / (width * CELL_SIZE + 2 * viewPadding); + let scaleY = this.canvasHeight / (height * CELL_SIZE + 2 * viewPadding); + + let newScale = Math.min(scaleX, scaleY); + + if (this.mapContainer.scaleX > MapView.MAX_ZOOM) { + newScale = MapView.MAX_ZOOM; + } else if (this.mapContainer.scaleX < MapView.MIN_ZOOM) { + newScale = MapView.MIN_ZOOM; + } + + return newScale; + } + + /** + * Draws all tiles contained in the MapModel. + */ + private drawMap(): void { + // Create and draw the container for the entire map + let gridPixelSize = CELL_SIZE * MapView.MAP_SIZE; + + // Add a white background to the entire container + let background = new createjs.Shape(); + background.graphics.beginFill("#fff"); + background.graphics.drawRect(0, 0, + gridPixelSize, gridPixelSize); + this.mapContainer.addChild(background); + + this.stage.addChild(this.mapContainer); + + // Set the map container to a default offset and zoom state (overridden if rooms are present) + this.mapContainer.x = -50; + this.mapContainer.y = -50; + this.mapContainer.scaleX = this.mapContainer.scaleY = MapView.DEFAULT_ZOOM; + + this.addLayerContainers(); + } + + private addLayerContainers(): void { + this.mapContainer.addChild(this.gridLayer.container); + this.mapContainer.addChild(this.roomLayer.container); + this.mapContainer.addChild(this.dcObjectLayer.container); + this.mapContainer.addChild(this.roomTextLayer.container); + this.mapContainer.addChild(this.hoverLayer.container); + this.mapContainer.addChild(this.wallLayer.container); + this.mapContainer.addChild(this.grayLayer.container); + } + + /** + * Wrapper function for TweenJS animate functionality. + * + * @param target What to animate + * @param properties Properties to be passed on to TweenJS + * @param callback To be called when animation ready + */ + public animate(target: any, properties: any, callback?: () => any): void { + this.animating = true; + createjs.Tween.get(target) + .to(properties, MapView.ANIMATION_LENGTH, createjs.Ease.getPowInOut(4)) + .call(() => { + this.animating = false; + this.updateScene = true; + + if (callback !== undefined) { + callback(); + } + }); + } +}
\ No newline at end of file |
