Created
January 29, 2015 23:58
-
-
Save kordero/d243522cc4745c2624a0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var names = { | |
options: { | |
unsupported_warning: false | |
}, | |
MAX_TEXT_HEIGHT: 30, | |
MARGIN: [3, 3], | |
PADDING: [16, 8],//[x, y] | |
NUM_PLACE_NAMES: 25, | |
init: function() { | |
// Compares two arrays | |
// Returns true if equal | |
// false otherwise | |
Array.prototype.equals = function (array) { | |
if (!array) { | |
return false; | |
} | |
if (this.length != array.length) { | |
return false; | |
} | |
for (var i = 0; i < this.length; i++) { | |
if (this[i] != array[i]) { | |
return false; | |
} | |
} | |
return true; | |
} | |
// Randomly resorts array | |
Array.prototype.shuffle = function () { | |
var j, tmp; | |
for (var i = 0; i < this.length; i++) { | |
// Random Integer between 0 and array length - 1 | |
j = Math.round(Math.random() * (this.length - 1)); | |
// If i and j are equal no point swaping | |
if (i != j) { | |
tmp = this[j]; | |
this[j] = this[i]; | |
this[i] = tmp; | |
} | |
} | |
} | |
names.displayNewPlaceNames(); | |
}, | |
// Uses ajax to retrieve new Place Names | |
displayNewPlaceNames: function () { | |
var url = 'https://metaverse.highfidelity.io/api/v1/new_place_names'; | |
$.getJSON(url, function (data) { | |
var names = String(data['data']['new_place_names']).split(','); | |
names.shuffle(); | |
if (names.length > names.NUM_PLACE_NAMES) { | |
names.slice(0, names.NUM_PLACE_NAMES); | |
} | |
names.renderCloud(names); | |
}); | |
setTimeout(names.displayNewPlaceNames, 10000); | |
}, | |
// Creates and draws Place Name Cloud | |
renderCloud: function (names) { | |
// Get Canvas and generate 2d context | |
var canvas = document.getElementById('placeNameCloud'); | |
var context = canvas.getContext('2d'); | |
// Clear the canvas | |
context.clearRect(0, 0, canvas.width, canvas.height); | |
// Set center of PlaceName to be center of text | |
context.textAlign = 'center'; | |
context.textBaseline = 'middle'; | |
// Want to place first PlaceName in center | |
// Can't use 'i' incase first Place Name has profanity | |
var firstPlaceName = true; | |
var placeNames = new Array(); | |
//Array of angles for the Place Name starting points | |
var j = 0; | |
//The following angles form the shape of the cloud | |
//Plus Minus 30deg and 22.5deg at 0deg and 180deg | |
var angles = [ | |
Math.PI / 6, | |
5 * Math.PI / 6, | |
7 * Math.PI / 6, | |
11 * Math.PI / 6, | |
Math.PI / 9, | |
8 * Math.PI / 9, | |
10 * Math.PI / 9, | |
17 * Math.PI / 9, | |
]; | |
for (var i = 0; i < names.length; i++) { | |
// Check for profanity, if found skip | |
if (hasProfanity(names[i])) { | |
continue; | |
} | |
if (firstPlaceName) { | |
// First PlaceName goes at center of canvas with largest size | |
placeNames.push(new PlaceName( | |
canvas, context, names[i], | |
[Math.round(canvas.width/2), Math.round(canvas.height/2)], | |
names.MAX_TEXT_HEIGHT | |
)); | |
firstPlaceName = false; | |
} else { | |
// Set text height in stages based on random number | |
var tmp = i / names.length; | |
var textHeight; | |
if (tmp < 0.2) { | |
textHeight = Math.round(names.MAX_TEXT_HEIGHT * 0.8); | |
} else if (tmp < 0.4) { | |
textHeight = Math.round(names.MAX_TEXT_HEIGHT * 0.6); | |
} else { | |
textHeight = Math.round(names.MAX_TEXT_HEIGHT * 0.4); | |
} | |
// Position PlaceName 800px from center at random angle | |
placeNames.push(new PlaceName( | |
canvas, context, names[i], | |
[ | |
Math.round(canvas.width/2 + 800*Math.cos(angles[j])), | |
Math.round(canvas.height/2 + 800*Math.sin(angles[j])) | |
], | |
textHeight | |
)); | |
j++; | |
while (j >= angles.length) { | |
j -= angles.length; | |
} | |
// Move PlaceName towards center | |
placeNames[placeNames.length - 1].gravitate(placeNames); | |
} | |
// Draw PlaceName | |
placeNames[placeNames.length - 1].render(); | |
} | |
}, | |
isCompatible: function() { | |
return true; | |
} | |
}; | |
// PlaceName "class" constructor | |
function PlaceName (canvas, context, name, center, textHeight) { | |
// Initialise class variables | |
this.canvas = canvas; | |
this.context = context; | |
this.name = name; | |
this.center = center; | |
this.textHeight = textHeight; | |
// To check textWidth the context height needs to be set | |
// This seems to take a bit of processing time | |
// Thus don't do it if it's not necessary | |
if (this.context.font.split(' ')[0] != textHeight + 'px') { | |
this.context.font = textHeight + 'px proxima-nova, sans-serif'; | |
} | |
this.textWidth = this.context.measureText(this.name).width; | |
} | |
// Returns the angle in radians from the center of PlaceName to center of canvas | |
PlaceName.prototype.getAngleToCenter = function () { | |
var x = this.center[0] - this.canvas.width / 2; | |
var y = this.center[1] - this.canvas.height / 2; | |
return Math.atan2(y, x); | |
} | |
// Returns coordinates of top left corner and bottom right corner (Canvas origin is top left) | |
// as array [topLeftX, topLeftY, BottomRightX, BottomRightY] | |
PlaceName.prototype.getBoundingBox = function () { | |
var box = [ | |
Math.floor(this.center[0] - (this.textWidth / 2 + names.PADDING[0] + names.MARGIN[0])), | |
Math.floor(this.center[1] - (this.textHeight / 2 + names.PADDING[1] + names.MARGIN[1])), | |
Math.ceil(this.center[0] + (this.textWidth / 2 + names.PADDING[0] + names.MARGIN[0])), | |
Math.ceil(this.center[1] + (this.textHeight / 2 + names.PADDING[1] + names.MARGIN[1])) | |
]; | |
return box; | |
} | |
// Checks this PlaceName against array of PlaceNames | |
// Returns true if collision is detected | |
// false otherwise | |
PlaceName.prototype.hasCollision = function (placeName) { | |
// Find distance in the y between center of this and center of supplied placename | |
var yDist = Math.abs(this.center[1] - placeName.center[1]); | |
// Subtract the minimum distance in the y to disallow collision | |
yDist -= (this.textHeight + placeName.textHeight) / 2 + (names.PADDING[1] + names.MARGIN[1]) * 2; | |
// If positive or zero then no collision | |
// If negative need to check x | |
if (yDist >= 0) { | |
return false; | |
} | |
// Repeat for x | |
var xDist = Math.abs(this.center[0] - placeName.center[0]); | |
xDist -= (this.textWidth + placeName.textWidth) / 2 + (names.PADDING[0] + names.MARGIN[0]) * 2; | |
// If negative collision detected | |
if (xDist < 0) { | |
return true; | |
} | |
// Otherwise no collision | |
return false; | |
} | |
// Attempts to move PlaceName dist in axis direction (0=x, 1=y) | |
// Fails if the move would cause collision | |
PlaceName.prototype.slide = function (placeNames, axis, dist) { | |
// No point wasting processor time if dist is zero | |
if (dist == 0) { | |
return false; | |
} | |
// Add dist to center's coordinate | |
this.center[axis] += dist; | |
for (var i = 0; i < placeNames.length; i++) { | |
// Don't check for collision with self | |
if (this == placeNames[i]) { | |
continue; | |
} | |
// If collision results, revert change and return false | |
if (this.hasCollision(placeNames[i])) { | |
this.center[axis] -= dist; | |
return false; | |
} | |
} | |
// No collision | |
return true; | |
} | |
// Moves PlaceName towards center | |
PlaceName.prototype.gravitate = function (placeNames) { | |
var dist = 32; | |
var theta, dx, dy; | |
var prevCenters = new Array(); | |
var stillMoving, bouncing = false; | |
// Had an issue in which the PlaceName was | |
// bouncing between two or three positions | |
do { | |
// Record of previous positions | |
prevCenters.push(this.center.slice()); | |
// Get angle to center | |
theta = this.getAngleToCenter(); | |
// Break into components | |
dx = Math.round(-dist*Math.cos(theta)); | |
dy = Math.round(-dist*Math.sin(theta)); | |
// Move PlaceName | |
this.slide(placeNames, 1, dy); | |
this.slide(placeNames, 0, dx); | |
// Check if PlaceName actually moved | |
stillMoving = !this.center.equals(prevCenters[prevCenters.length - 1]); | |
// Check if revisiting old positions (bouncing) | |
for (var i = prevCenters.length - 2; i >= 0; i--) { | |
if (this.center.equals(prevCenters[i])) { | |
bouncing = true; | |
break; | |
} | |
} | |
// If no movement occured and dist is greater than one | |
if (!stillMoving && dist > 1) { | |
// Attempt a smaller movement next time | |
dist /= 2; | |
// Prevent do-while loop ending | |
stillMoving = true; | |
bouncing = false; | |
} | |
} while (stillMoving && !bouncing); | |
} | |
// Removes PlaceName from canvas | |
PlaceName.prototype.erase = function () { | |
var box = this.getBoundingBox(); | |
if (box) { | |
this.context.clearRect(box[0], box[1], box[2]-box[0], box[3]-box[1]); | |
} | |
} | |
// Draws PlaceName on canvas | |
PlaceName.prototype.render = function () { | |
// Radius of corners | |
var radius = 3; | |
var box = this.getBoundingBox(); | |
if (!box) { | |
return; | |
} | |
// Remove names.MARGIN from bounding box, only want names.PADDING | |
box[0] += names.MARGIN[0]; | |
box[1] += names.MARGIN[1]; | |
box[2] -= names.MARGIN[0]; | |
box[3] -= names.MARGIN[1]; | |
// Check if PlaceName is outside of canvas | |
// Don't want to display if PlaceName is cut off | |
if (box[0] < 0 || box[1] < 0 || box[2] > this.canvas.width || box[3] > this.canvas.height) { | |
return; | |
} | |
// Find transparency based on text height | |
var alpha = this.textHeight / names.MAX_TEXT_HEIGHT; | |
// Set colour and transparency | |
this.context.fillStyle = "rgba(96, 148, 197, " + alpha + ")"; | |
this.context.strokeStyle = "rgba(96, 148, 197, " + alpha + ")"; | |
// Draw the text | |
this.context.fillText(this.name, this.center[0], this.center[1]); | |
// Draw the box | |
this.context.beginPath(); | |
this.context.moveTo(box[0] + radius, box[1]); | |
this.context.lineTo(box[2] - radius, box[1]); | |
this.context.arcTo(box[2], box[1], box[2], box[1] + radius, radius); | |
this.context.lineTo(box[2], box[3] - radius); | |
this.context.arcTo(box[2], box[3], box[2] - radius, box[3], radius); | |
this.context.lineTo(box[0] + radius, box[3]); | |
this.context.arcTo(box[0], box[3], box[0], box[3] - radius, radius); | |
this.context.lineTo(box[0], box[1] + radius); | |
this.context.arcTo(box[0], box[1], box[0] + radius, box[1], radius); | |
this.context.stroke(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment