text:
var cmds = {
     /*********************************************************
      * Informational Commands                                *
      *********************************************************/
     sh: 'servercommands',
     serverhelp: 'servercommands',
     sc: 'servercommands',
     servercommand: 'servercommands',
     servercommands: function (target, room, user) {
         if (!this.canBroadcast()) return;

         if (!target) {
             return this.sendReplyBox(
                 '/profile - See user\'s profile with infomation about money, status, location, etc.
' +
                 '/stafflist - List of staff members.
' +
                 '/regdate name - Displays registration date of a user.
' +
                 '/twitchchat on/off - Enables Twitch Chat integration.
' +
                 '/twitchgroup - Shows info about twitch groups.
' +
                 '/poke name - Pokes a users.
' +
                 '/pickrandom options... - Randomly picks one from the options
' +
                 '/emoticons - View list of emoticons
' +
                 '/earnmoney - Find out more ways to earn money
' +
                 '
 For more help use /serverhelp hangman'
             );
         }
         if (target.toLowerCase() === 'tour') {
             return this.sendReplyBox(
                 "- /nt <format>, <type> [, <comma-separated arguments>]: Creates a new tournament in the current room.
" +
                 "- /settype <type> [, <comma-separated arguments>]: Modifies the type of tournament after it's been created, but before it has started.
" +
                 "- /et: Forcibly ends the tournament in the current room.
" +
                 "- /st: Starts the tournament in the current room.
" +
                 "- /dq <user>: Disqualifies a user.
" +
                 "- /remind <user>: Pings the users in the tournament.
" +
                 "More detailed help can be found here"
             );
         }

         if (target.toLowerCase() === 'hangman') {
             return this.sendReplyBox('A brief introduction to Hangman:
' +
                 'The classic game, the basic idea of hangman is to guess the word that someone is thinking of before the man is "hanged." Players are given 8 guesses before this happens.
' +
                 'Games can be started by any of the rank Voice or higher, including Room Voice, Room Mod, and Room Owner.
' +
                 'The commands are:
' +
                 '

/hangman word, description - Starts the game of hangman, with a specified word and a general category. Requires: + % @ & ~
' +
                 '
/guess letter - Guesses a letter.
' +
                 '
/guessword word - Guesses a word.
' +
                 '
/viewhangman - Shows the current status of hangman. Can be broadcasted.
' +
                 '
/word - Allows the person running hangman to view the word.
' +
                 '
/category description OR /topic description - Allows the person running hangman to changed the topic.
' +
                 '
/endhangman - Ends the game of hangman in the room. Requires: + % @ & ~

');
         }
		 /* if(target === 'techsupport' || target === 'support') {
		 return this.sendReplyBox('
Tech Support

' +
		 '
Rank 3 Support - Developers

' +
		 'iFaZe - Custom Styles, Trainer Cards, Commands
' +
		 'Bandi - Custom Styles, Bot, Tournament edits, Other
' +
		 'CreaturePhil - Twitch Chat, Bot, Commands, Other
' +

		 );
		 }*/ else {

             return this.sendReply('Could not find ' + target + '.');
         }
     },
     stafflist: function (target, room, user, connection) {
         var buffer = [];
         var admins = [];
         var leaders = [];
         var mods = [];
         var drivers = [];
         var voices = [];

         admins2 = '';
         leaders2 = '';
         mods2 = '';
         drivers2 = '';
         voices2 = '';
         stafflist = fs.readFileSync('config/usergroups.csv', 'utf8');
         stafflist = stafflist.split('\n');
         for (var u in stafflist) {
             line = stafflist[u].split(',');
             if (line[1] == '~') {
                 admins2 = admins2 + line[0] + ',';
             }
             if (line[1] == '&') {
                 leaders2 = leaders2 + line[0] + ',';
             }
             if (line[1] == '@') {
                 mods2 = mods2 + line[0] + ',';
             }
             if (line[1] == '%') {
                 drivers2 = drivers2 + line[0] + ',';
             }
             if (line[1] == '+') {
                 voices2 = voices2 + line[0] + ',';
             }
         }
         admins2 = admins2.split(',');
         leaders2 = leaders2.split(',');
         mods2 = mods2.split(',');
         drivers2 = drivers2.split(',');
         voices2 = voices2.split(',');
         for (var u in admins2) {
             if (admins2[u] != '') admins.push(admins2[u]);
         }
         for (var u in leaders2) {
             if (leaders2[u] != '') leaders.push(leaders2[u]);
         }
         for (var u in mods2) {
             if (mods2[u] != '') mods.push(mods2[u]);
         }
         for (var u in drivers2) {
             if (drivers2[u] != '') drivers.push(drivers2[u]);
         }
         for (var u in voices2) {
             if (voices2[u] != '') voices.push(voices2[u]);
         }
         if (admins.length > 0) {
             admins = admins.join(', ');
         }
         if (leaders.length > 0) {
             leaders = leaders.join(', ');
         }
         if (mods.length > 0) {
             mods = mods.join(', ');
         }
         if (drivers.length > 0) {
             drivers = drivers.join(', ');
         }
         if (voices.length > 0) {
             voices = voices.join(', ');
         }
         connection.popup('Administrators: \n--------------------\n' + admins + '\n\nLeaders:\n-------------------- \n' + leaders + '\n\nModerators:\n-------------------- \n' + mods + '\n\nDrivers: \n--------------------\n' + drivers + '\n\nVoices:\n-------------------- \n' + voices);
     },
     emoticons: function (target, room, user) {
         if (!this.canBroadcast()) return;
         return this.sendReplyBox(
             'Emoticons are case-sensitive:
' +
             ':) ' +
             ':O ' +
             ':( ' +
             ';) ' +
             ':P ' +
             ':/ ' +
             ';P ' +
             'B) ' +
             'O_o ' +
             'R) ' +
             ':D ' +
             ':z ' +
             'BloodTrail ' +
             'BibleThump ' +
             ' 4Head ' +
             'Kappa ' +
             'PogChamp ' +
             'ResidentSleeper ' +
             'crtNova ' +
             'crtSSoH ' +
             'SSSsss ' +
             'SwiftRage ' +
             'DansGame ' +
             'Kreygasm ' +
             'FailFish ' +
             'pikaQQ ' +
             ':ninja: ' +
             ':katana: ' +
             ':ninjabattle: ' +
             ':jawdrop:' +
			 ':devil:' +
			 ':heart:' +
			 ':sigh:' +
			 'LOL' +
			 ':cry:' +
			 ':lmao: ' +
			 '^' +
             ':hump:'
         );
     },

     groups: function (target, room, user) {
         if (!this.canBroadcast()) return;
         this.sendReplyBox('+ Voice - They can use ! commands like !groups, and talk during moderated chat
' +
             '% Driver - The above, and they can mute. Global % can also lock users and check for alts
' +
             '@ Moderator - The above, and they can ban users
' +
             '& Leader - The above, and they can promote to moderator and force ties
' +
             '~ Administrator - They can do anything, like change what this message says
' +
             '± Nova Bot - This is the server itself that auto moderates chats and tells jokes
' +
             '# Room Owner - They are administrators of the room and can almost totally control it');
     },

     regdate: function (target, room, user, connection) {
         if (!this.canBroadcast()) return;
         if (!target || target == "." || target == "," || target == "'") return this.sendReply('/regdate - Please specify a valid username.');
         var username = sanitize(target);
         target = target.replace(/\s+/g, '');
         var util = require("util"),
             http = require("http");

         var options = {
             host: "www.pokemonshowdown.com",
             port: 80,
             path: "/forum/~" + target
         };

         var content = "";
         var self = this;
         var req = http.request(options, function (res) {

             res.setEncoding("utf8");
             res.on("data", function (chunk) {
                 content += chunk;
             });
             res.on("end", function () {
                 content = content.split("<em");
                 if (content[1]) {
                     content = content[1].split("
");
                     if (content[0]) {
                         content = content[0].split("");
                         if (content[1]) {
                             regdate = content[1];
                             data = username + ' was registered on' + regdate + '.';
                         }
                     }
                 } else {
                     data = username + ' is not registered.';
                 }
                 self.sendReplyBox(data);
                 room.update();
             });
         });
         req.end();
     },

     twitchgroups: function (target, room, user) {
         if (!this.canBroadcast()) return;

         return this.sendReplyBox('System Operator - These are the people who make the server tick. Say hello!
Elite Moderator - Our most experienced and trustworthy moderator squad who help us keep the server safe and fun.
Broadcaster - This icon denotes the person whose room you\'re currently in.
Chat Moderator - Specifically appointed chat moderators for the server.
Turbo User - These are the people who donated or contributed to the server.');
     },
     /*********************************************************
      * Useful Commands                                       *
      *********************************************************/
      join: function(target, room, user, connection) {
		if (!target) return false;
		var targetRoom = Rooms.get(target) || Rooms.get(toId(target));
		if (!targetRoom) {
			if (target === 'lobby') return connection.sendTo(target, "|noinit|nonexistent|");
			return connection.sendTo(target, "|noinit|nonexistent|The room '"+target+"' does not exist.");
		}
		if (targetRoom.isPrivate && !user.named) {
			return connection.sendTo(target, "|noinit|namerequired|You must have a name in order to join the room '"+target+"'.");
		}
		if (!user.joinRoom(targetRoom || room, connection)) {
			return connection.sendTo(target, "|noinit|joinfailed|The room '"+target+"' could not be joined.");
		}
		if (target.toLowerCase() == "lobby") {
			return connection.sendTo('lobby','|html|

Welcome to the Silver!

<br/

What Can You Do Here?
' +
'Participate In Tournaments For Money And Prizes!
' +
'Join Various Leagues And Clans!
' +
'Chat With Emoticons Our Change Your Name Color!
' +
'Our Just Hang Out And Chat
' +
'If You Liked Your Experience here Make Sure To Tell Your Friends About Us!

' +
'For General Help For Server Commands Use /serverhelp
' +
'If You Have Any Problems Pm a Staff Member, Only Serious Problems Should Be Taken To Admins (~)

' +
'<button class="pinkbutton" title="Rules">Rules</button>   |   <button class="pinkbutton" title="FAQs">FAQs</button> </button>
');
		}
	},

	/*********************************************************
	 * Shop commands
	 *********************************************************/

	createpoints: function(target, room, user, connection) {
		if(!user.can('hotpatch')) return this.sendReply('You do not have enough authority to do this.');
		fs.exists('config/money.csv', function (exists) {
			if(exists){
				return connection.sendTo(room, 'Since this file already exists, you cannot do this.');
			} else {
				fs.writeFile('config/money.csv', 'badsteel,10000', function (err) {
					if (err) throw err;
					console.log('config/money.csv created.');
					connection.sendTo(room, 'config/money.csv created.');
				});
			}
		});
	},

	wallet: 'atm',
	satchel: 'atm',
	fannypack: 'atm',
	purse: 'atm',
	bag: 'atm',
	atm: function(target, room, user, connection, cmd) {
	if (!this.canBroadcast()) return;
	var cMatch = false;
	var mMatch = false;
	var money = 0;
	var coins = 0;
	var total = '';
	if (!target) {
	var data = fs.readFileSync('config/money.csv','utf8')
		var row = (''+data).split("\n");
		for (var i = row.length; i > -1; i--) {
			if (!row[i]) continue;
			var parts = row[i].split(",");
			var userid = toUserid(parts[0]);
			if (user.userid == userid) {
			var x = Number(parts[1]);
			var money = x;
			mMatch = true;
			if (mMatch === true) {
				break;
			}
			}
		}
		if (mMatch === true) {
			var p = 'bucks';
			if (money < 2) p = 'buck';
			total += user.name + ' has ' + money + ' ' + p + '.
';
		}
		if (mMatch === false) {
			total += 'You have no bucks.
';
		}
		user.money = money;
		var data = fs.readFileSync('config/coins.csv','utf8')
		var row = (''+data).split("\n");
		for (var i = row.length; i > -1; i--) {
			if (!row[i]) continue;
			var parts = row[i].split(",");
			var userid = toUserid(parts[0]);
			if (user.userid == userid) {
			var x = Number(parts[1]);
			var coins = x;
			cMatch = true;
			if (cMatch === true) {
				break;
			}
			}
		}
		if (cMatch === true) {
			var p = 'coins';
			if (coins < 2) p = 'coin';
			total += user.name + ' has ' + coins + ' ' + p + '.'
		}
		if (cMatch === false) {
			total += 'You have no coins.'
		}
		user.coins = coins;
	} else {
		var data = fs.readFileSync('config/money.csv','utf8')
		target = this.splitTarget(target);
		var targetUser = this.targetUser;
		if (!targetUser) {
			return this.sendReply('User '+this.targetUsername+' not found.');
		}
		var money = 0;
		var row = (''+data).split("\n");
		for (var i = row.length; i > -1; i--) {
			if (!row[i]) continue;
			var parts = row[i].split(",");
			var userid = toUserid(parts[0]);
			if (targetUser.userid == userid || target == userid) {
			var x = Number(parts[1]);
			var money = x;
			mMatch = true;
			if (mMatch === true) {
				break;
			}
			}
		}
		if (mMatch === true) {
			var p = 'bucks';
			if (money < 2) p = 'buck';
			total += targetUser.name + ' has ' + money + ' ' + p + '.
';
		}
		if (mMatch === false) {
			total += targetUser.name + ' has no bucks.
';
		}
		targetUser.money = money;
		var data = fs.readFileSync('config/coins.csv','utf8')
		var coins = 0;
		var row = (''+data).split("\n");
		for (var i = row.length; i > -1; i--) {
			if (!row[i]) continue;
			var parts = row[i].split(",");
			var userid = toUserid(parts[0]);
			if (targetUser.userid == userid || target == userid) {
			var x = Number(parts[1]);
			var coins = x;
			cMatch = true;
			if (cMatch === true) {
				break;
			}
			}
		}
		if (cMatch === true) {
			var p = 'coins';
			if (coins < 2) p = 'coin';
			total += targetUser.name + ' has ' + coins + ' ' + p + '.
';
		}
		if (cMatch === false) {
			total += targetUser.name + ' has no coins.
';
		}
		targetUser.coins = coins;
	}
	return this.sendReplyBox(total);
	},

	awardbucks: 'givebucks',
	gb: 'givebucks',
	givebucks: function(target, room, user) {
		if(!user.can('hotpatch')) return this.sendReply('You do not have enough authority to do this.');
		if(!target) return this.parse('/help givebucks');
		if (target.indexOf(',') != -1) {
			var parts = target.split(',');
			parts[0] = this.splitTarget(parts[0]);
			var targetUser = this.targetUser;
		if (!targetUser) {
			return this.sendReply('User '+this.targetUsername+' not found.');
		}
		if (isNaN(parts[1])) {
			return this.sendReply('Very funny, now use a real number.');
		}
		var cleanedUp = parts[1].trim();
		var giveMoney = Number(cleanedUp);
		var data = fs.readFileSync('config/money.csv','utf8')
		var match = false;
		var money = 0;
		var line = '';
		var row = (''+data).split("\n");
		for (var i = row.length; i > -1; i--) {
			if (!row[i]) continue;
			var parts = row[i].split(",");
			var userid = toUserid(parts[0]);
			if (targetUser.userid == userid) {
			var x = Number(parts[1]);
			var money = x;
			match = true;
			if (match === true) {
				line = line + row[i];
				break;
			}
			}
		}
		targetUser.money = money;
		targetUser.money += giveMoney;
		if (match === true) {
			var re = new RegExp(line,"g");
			fs.readFile('config/money.csv', 'utf8', function (err,data) {
			if (err) {
				return console.log(err);
			}
			var result = data.replace(re, targetUser.userid+','+targetUser.money);
			fs.writeFile('config/money.csv', result, 'utf8', function (err) {
				if (err) return console.log(err);
			});
			});
		} else {
			var log = fs.createWriteStream('config/money.csv', {'flags': 'a'});
			log.write("\n"+targetUser.userid+','+targetUser.money);
		}
		var p = 'bucks';
		if (giveMoney < 2) p = 'buck';
		this.sendReply(targetUser.name + ' was given ' + giveMoney + ' ' + p + '. This user now has ' + targetUser.money + ' bucks.');
		targetUser.send(user.name + ' has given you ' + giveMoney + ' ' + p + '.');
		} else {
			return this.parse('/help givebucks');
		}
	},

	takebucks: 'removebucks',
	removebucks: function(target, room, user) {
		if(!user.can('hotpatch')) return this.sendReply('You do not have enough authority to do this.');
		if(!target) return this.parse('/help removebucks');
		if (target.indexOf(',') != -1) {
			var parts = target.split(',');
			parts[0] = this.splitTarget(parts[0]);
			var targetUser = this.targetUser;
		if (!targetUser) {
			return this.sendReply('User '+this.targetUsername+' not found.');
		}
		if (isNaN(parts[1])) {
			return this.sendReply('Very funny, now use a real number.');
		}
		var cleanedUp = parts[1].trim();
		var takeMoney = Number(cleanedUp);
		var data = fs.readFileSync('config/money.csv','utf8')
		var match = false;
		var money = 0;
		var line = '';
		var row = (''+data).split("\n");
		for (var i = row.length; i > -1; i--) {
			if (!row[i]) continue;
			var parts = row[i].split(",");
			var userid = toUserid(parts[0]);
			if (targetUser.userid == userid) {
			var x = Number(parts[1]);
			var money = x;
			match = true;
			if (match === true) {
				line = line + row[i];
				break;
			}
			}
		}
		targetUser.money = money;
		targetUser.money -= takeMoney;
		if (match === true) {
			var re = new RegExp(line,"g");
			fs.readFile('config/money.csv', 'utf8', function (err,data) {
			if (err) {
				return console.log(err);
			}
			var result = data.replace(re, targetUser.userid+','+targetUser.money);
			fs.writeFile('config/money.csv', result, 'utf8', function (err) {
				if (err) return console.log(err);
			});
			});
		} else {
			var log = fs.createWriteStream('config/money.csv', {'flags': 'a'});
			log.write("\n"+targetUser.userid+','+targetUser.money);
		}
		var p = 'bucks';
		if (takeMoney < 2) p = 'buck';
		this.sendReply(targetUser.name + ' has had ' + takeMoney + ' ' + p + ' removed. This user now has ' + targetUser.money + ' bucks.');
		targetUser.send(user.name + ' has removed ' + takeMoney + ' bucks from you.');
		} else {
			return this.parse('/help removebucks');
		}
	},

	buy: function(target, room, user) {
		if (!target) return this.parse('/help buy');
		if (closeShop) return this.sendReply('The shop is currently closed and will open shortly.');
		var target2 = target;
		var target3 = target;
		target = target.split(', ');
		var avatar = '';
		var voice = '';
		var data = fs.readFileSync('config/money.csv','utf8')
		var match = false;
		var money = 0;
		var line = '';
		var row = (''+data).split("\n");
		for (var i = row.length; i > -1; i--) {
			if (!row[i]) continue;
			var parts = row[i].split(",");
			var userid = toUserid(parts[0]);
			if (user.userid == userid) {
			var x = Number(parts[1]);
			var money = x;
			match = true;
			if (match === true) {
				line = line + row[i];
				break;
			}
			}
		}
		user.money = money;
		var price = 0;
		if (target2 === 'symbol') {
			price = 5;
			if (price <= user.money) {
				user.money = user.money - price;
				this.sendReply('You have purchased a custom symbol. You will have this until you log off for more than an hour.');
				this.sendReply('Use /customsymbol [symbol] to change your symbol now!');
				user.canCustomSymbol = true;
				this.add(user.name + ' has purchased a custom symbol!');
			} else {
				return this.sendReply('You do not have enough bucks for this. You need ' + (price - user.money) + ' more bucks to buy ' + target + '.');
			}
		}
		if (target[0] === 'custom') {
			price = 15;
			if (price <= user.money) {
				if (!target[1]) return this.sendReply('Please specify the avatar you would like you buy. It has a maximum size of 80x80 and must be in .png format. ex: /buy custom, [url to the avatar]');
       				var filename = target[1].split('.');
				filename = '.'+filename.pop();
				if (filename != ".png") return this.sendReply('Your avatar must be in .png format.');
				user.money = user.money - price;
				this.sendReply('You have purchased a custom avatar. Staff have been notified and it will be added in due time.');
				user.canCustomAvatar = true;
				Rooms.rooms.staff.add(user.name+' has purchased a custom avatar. Image: '+target[1]);
				for (var u in Users.users) {
					if (Users.users[u].group == "~" || Users.users[u].group == "&") {
						Users.users[u].send('|pm|~Server|'+Users.users[u].group+Users.users[u].name+'|'+user.name+' has purchased a custom avatar. Image: '+target[1]);
					}
				}
			} else {
				return this.sendReply('You do not have enough bucks for this. You need ' + (price - user.money) + ' more bucks to buy ' + target + '.');
			}
		}
		if (target[0] === 'animated') {
			price = 25;
			if (price <= user.money) {
				if (!target[1]) return this.sendReply('Please specify the avatar you would like you buy. It has a maximum size of 80x80 and must be in .gif format. ex: /buy animated, [url to the avatar]');
       				var filename = target[1].split('.');
				filename = '.'+filename.pop();
				if (filename != ".gif") return this.sendReply('Your avatar must be in .gif format.');
				user.money = user.money - price;
				this.sendReply('You have purchased a custom animated avatar. Staff have been notified and it will be added in due time.');
				user.canAnimatedAvatar = true;
				Rooms.rooms.staff.add(user.name+' has purchased a custom animated avatar. Image: '+target[1]);
				for (var u in Users.users) {
					if (Users.users[u].group == "~" || Users.users[u].group == "&") {
						Users.users[u].send('|pm|~Server|'+Users.users[u].group+Users.users[u].name+'|'+user.name+' has purchased a custom animated avatar. Image: '+target[1]);
					}
				}
			} else {
				return this.sendReply('You do not have enough bucks for this. You need ' + (price - user.money) + ' more bucks to buy ' + target + '.');
			}
		}
		if (target[0] === 'room') {
			price = 80;
			if (price <= user.money) {
				user.money = user.money - price;
				this.sendReply('You have purchased a chat room. You need to message an Admin so that the room can be made.');
				user.canChatRoom = true;
				this.add(user.name + ' has purchased a chat room!');
			} else {
				return this.sendReply('You do not have enough bucks for this. You need ' + (price - user.money) + ' more bucks to buy ' + target + '.');
			}
		}
		if (target2 === 'trainer') {
			price = 30;
			if (price <= user.money) {
				user.money = user.money - price;
				this.sendReply('You have purchased a trainer card. You need to message an Admin capable of adding this (Storm Developer or Cometstorm).');
				user.canTrainerCard = true;
				this.add(user.name + ' has purchased a trainer card!');
			} else {
				return this.sendReply('You do not have enough bucks for this. You need ' + (price - user.money) + ' more bucks to buy ' + target + '.');
			}
		}
		if (target2 === 'fix') {
			price = 10;
			if (price <= user.money) {
				user.money = user.money - price;
				this.sendReply('You have purchased the ability to alter your avatar or trainer card. You need to message an Admin capable of adding this (Storm Developer or Cometstorm).');
				user.canFixItem = true;
				this.add(user.name + ' has purchased the ability to set alter their card or avatar!');
			} else {
				return this.sendReply('You do not have enough bucks for this. You need ' + (price - user.money) + ' more bucks to buy ' + target + '.');
			}
		}
		if (target2 === 'declare') {
			price = 25;
			if (price <= user.money) {
				user.money = user.money - price;
				this.sendReply('You have purchased the ability to declare (from Admin). To do this message an Admin (~) with the message you want to send. Keep it sensible!');
				user.canDecAdvertise = true;
				this.add(user.name + ' has purchased the ability to declare from an Admin!');
			} else {
				return this.sendReply('You do not have enough bucks for this. You need ' + (price - user.money) + ' more bucks to buy ' + target + '.');
			}
		}
		if (match === true) {
			var re = new RegExp(line,"g");
			fs.readFile('config/money.csv', 'utf8', function (err,data) {
			if (err) {
				return console.log(err);
			}
			var result = data.replace(re, user.userid+','+user.money);
			fs.writeFile('config/money.csv', result, 'utf8', function (err) {
				if (err) return console.log(err);
			});
			});
		}
	},

	customsymbol: function(target, room, user) {
		if(!user.canCustomSymbol) return this.sendReply('You need to buy this item from the shop to use.');
		if(!target || target.length > 1) return this.sendReply('/customsymbol [symbol] - changes your symbol (usergroup) to the specified symbol. The symbol can only be one character');
		var a = target;
		if (a === "+" || a === "$" || a === "%" || a === "@" || a === "&" || a === "~" || a === "#" || a === "a" || a === "b" || a === "c" || a === "d" || a === "e" || a === "f" || a === "g" || a === "h" || a === "i" || a === "j" || a === "k" || a === "l" || a === "m" || a === "n" || a === "o" || a === "p" || a === "q" || a === "r" || a === "s" || a === "t" || a === "u" || a === "v" || a === "w" || a === "x" || a === "y" || a === "z" || a === "0" || a === "1" || a === "2" || a === "3" || a === "4" || a === "5" || a === "6" || a === "7" || a === "8" || a === "9" ) {
			return this.sendReply('Sorry, but you cannot change your symbol to this for safety/stability reasons.');
		}
		user.getIdentity = function(){
			if(this.muted)	return '!' + this.name;
			if(this.locked) return '‽' + this.name;
			return target + this.name;
		};
		user.updateIdentity();
		user.canCustomSymbol = false;
		user.hasCustomSymbol = true;
	},

	shop: function(target, room, user) {
		if (!this.canBroadcast()) return;
		this.sendReplyBox('
Silver Bucks Shop

Command	Description	Cost
' +
			'
Symbol	Buys a custom symbol to go infront of name and puts you at top of userlist (temporary until restart)	5
' +
			'
Custom	Buys a custom avatar to be applied to your name (you supply)	15
' +
			'
Animated	Buys an animated avatar to be applied to your name (you supply)	25
' +
			'
Room	Buys a chatroom for you to own (within reason, can be refused)	80
' +
			'
Trainer	Buys a trainer card which shows information through a command such as /badsteel (note: third image costs 10 bucks extra, ask for more details)	40
' +
			'
Fix	Buys the ability to alter your current custom avatar or trainer card (don\'t buy if you have neither)!	10
' +
			'
Declare	You get the ability to get two declares from an Admin in lobby. This can be used for league or room advertisement (not server)	25
' +
			'

To buy an item from the shop, use /buy [command].
Also do /moneycommands to view money based commands.
 Bucks are not transferable among alts.');
		global.closeShop = global.closeShop || false;
		if (closeShop) return this.sendReply('|raw|
The shop is currently closed and will open shortly.
');
	},

	lockshop: 'closeshop',
	closeshop: function(target, room, user) {
		if (!user.can('hotpatch')) return this.sendReply('You do not have enough authority to do this.');

		if(closeShop && closedShop === 1) closedShop--;

		if (closeShop) {
			return this.sendReply('The shop is already closed. Use /openshop to open the shop to buyers.');
		}
		else if (!closeShop) {
			if (closedShop === 0) {
				this.sendReply('Are you sure you want to close the shop? People will not be able to buy anything. If you do, use the command again.');
				closedShop++;
			}
			else if (closedShop === 1) {
				closeShop = true;
				closedShop--;
				this.add('|raw|
The shop has been temporarily closed, during this time you cannot buy items.
');
			}
		}
	},

	openshop: function(target, room, user) {
		if (!user.can('hotpatch')) return this.sendReply('You do not have enough authority to do this.');

		if (!closeShop && closedShop === 1) closedShop--;

		if (!closeShop) {
			return this.sendRepy('The shop is already closed. Use /closeshop to close the shop to buyers.');
		}
		else if (closeShop) {
			if (closedShop === 0) {
				this.sendReply('Are you sure you want to open the shop? People will be able to buy again. If you do, use the command again.');
				closedShop++;
			}
			else if (closedShop === 1) {
				closeShop = false;
				closedShop--;
				this.add('|raw|
The shop has been opened, you can now buy from the shop.
');
			}
		}
	},

	shoplift: 'awarditem',
	giveitem: 'awarditem',
	awarditem: function(target, room, user) {
		if (!target) return this.parse('/help awarditem');
		if(!user.can('hotpatch')) return this.sendReply('You do not have enough authority to do this.');

		target = this.splitTarget(target);
		var targetUser = this.targetUser;

		if (!target) return this.parse('/help awarditem');
		if (!targetUser) {
			return this.sendReply('User '+this.targetUsername+' not found.');
		}

		var matched = false;
		var isItem = false;
		var theItem = '';
		for (var i = 0; i < inShop.length; i++) {
			if (target.toLowerCase() === inShop[i]) {
				isItem = true;
				theItem = inShop[i];
			}
		}
		if (isItem === true) {
			if (theItem === 'symbol') {
				if (targetUser.canCustomSymbol === true) {
					return this.sendReply('This user has already bought that item from the shop... no need for another.');
				}
				if (targetUser.canCustomSymbol === false) {
					matched = true;
					this.sendReply(targetUser.name + ' can now use /customsymbol to get a custom symbol.');
					targetUser.canCustomSymbol = true;
					Rooms.rooms.lobby.add(user.name + ' has stolen custom symbol from the shop!');
					targetUser.send(user.name + ' has given you ' + theItem + '! Use /customsymbol [symbol] to add the symbol!');
				}
			}
			if (theItem === 'custom') {
				if (targetUser.canCustomAvatar === true) {
					return this.sendReply('This user has already bought that item from the shop... no need for another.');
				}
				if (targetUser.canCustomAvatar === false) {
					matched = true;
					targetUser.canCustomSymbol = true;
					Rooms.rooms.lobby.add(user.name + ' has stolen a custom avatar from the shop!');
					targetUser.send(user.name + ' has given you ' + theItem + '!');
				}
			}
			if (theItem === 'animated') {
				if (targetUser.canAnimated === true) {
					return this.sendReply('This user has already bought that item from the shop... no need for another.');
				}
				if (targetUser.canCustomAvatar === false) {
					matched = true;
					targetUser.canCustomAvatar = true;
					Rooms.rooms.lobby.add(user.name + ' has stolen a custom avatar from the shop!');
					targetUser.send(user.name + ' has given you ' + theItem + '!');
				}
			}
			if (theItem === 'room') {
				if (targetUser.canChatRoom === true) {
					return this.sendReply('This user has already bought that item from the shop... no need for another.');
				}
				if (targetUser.canChatRoom === false) {
					matched = true;
					targetUser.canChatRoom = true;
					Rooms.rooms.lobby.add(user.name + ' has stolen a chat room from the shop!');
					targetUser.send(user.name + ' has given you ' + theItem + '!');
				}
			}
			if (theItem === 'trainer') {
				if (targetUser.canTrainerCard === true) {
					return this.sendReply('This user has already bought that item from the shop... no need for another.');
				}
				if (targetUser.canTrainerCard === false) {
					matched = true;
					targetUser.canTrainerCard = true;
					Rooms.rooms.lobby.add(user.name + ' has stolen a trainer card from the shop!');
					targetUser.send(user.name + ' has given you ' + theItem + '!');
				}
			}
			if (theItem === 'fix') {
				if (targetUser.canFixItem === true) {
					return this.sendReply('This user has already bought that item from the shop... no need for another.');
				}
				if (targetUser.canFixItem === false) {
					matched = true;
					targetUser.canFixItem = true;
					Rooms.rooms.lobby.add(user.name + ' has stolen the ability to alter a current trainer card or avatar from the shop!');
					targetUser.send(user.name + ' has given you the ability to set ' + theItem + '!');
				}
			}
			if (theItem === 'voice') {
				if (targetUser.canBeVoice === true) {
					return this.sendReply('This user has already bought that item from the shop... no need for another.');
				}
				if (targetUser.canBeVoice === false) {
					matched = true;
					targetUser.canBeVoice = true;
					Rooms.rooms.lobby.add(user.name + ' has stolen Global Voice from the shop!');
					targetUser.send(user.name + ' has given you Global ' + theItem + '!');
				}
			}
			if (theItem === 'declare') {
				if (targetUser.canDecAdvertise === true) {
					return this.sendReply('This user has already bought that item from the shop... no need for another.');
				}
				if (targetUser.canDecAdvertise === false) {
					matched = true;
					targetUser.canDecAdvertise = true;
					Rooms.rooms.lobby.add(user.name + ' has stolen the ability to get a declare from the shop!');
					targetUser.send(user.name + ' has given you the ability to set ' + theItem + '!');
				}
			}
			else
				if (!matched) return this.sendReply('Maybe that item isn\'t in the shop yet.');
		}
		else
			return this.sendReply('Shop item could not be found, please check /shop for all items - ' + theItem);
	},

	removeitem: function(target, room, user) {
		if (!target) return this.parse('/help removeitem');
		if(!user.can('hotpatch')) return this.sendReply('You do not have enough authority to do this.');

		target = this.splitTarget(target);
		var targetUser = this.targetUser;

		if (!target) return this.parse('/help removeitem');
		if (!targetUser) {
			return this.sendReply('User '+this.targetUsername+' not found.');
		}

		if (target === 'symbol') {
			if (targetUser.canCustomSymbol) {
				targetUser.canCustomSymbol = false;
				this.sendReply(targetUser.name + ' no longer has a custom symbol ready to use.');
				targetUser.send(user.name + ' has removed the custom symbol from you.');
			}
			else
				return this.sendReply('They do not have a custom symbol for you to remove.');
		}
		else if (target === 'voice') {
			if (targetUser.canBeVoice) {
				targetUser.canBeVoice = false;
				this.sendReply(targetUser.name + ' no longer has Global Voice ready to use.');
				targetUser.send(user.name + ' has removed Global Voice from you.');
			}
			else
				return this.sendReply('They do not have Global voice for you to remove.');
		}
		else if (target === 'custom') {
			if (targetUser.canCustomAvatar) {
				targetUser.canCustomAvatar = false;
				this.sendReply(targetUser.name + ' no longer has a custom avatar ready to use.');
				targetUser.send(user.name + ' has removed the custom avatar from you.');
			}
			else
				return this.sendReply('They do not have a custom avatar for you to remove.');
		}
		else if (target === 'animated') {
			if (targetUser.canAnimatedAvatar) {
				targetUser.canAnimatedAvatar = false;
				this.sendReply(targetUser.name + ' no longer has a animated avatar ready to use.');
				targetUser.send(user.name + ' has removed the animated avatar from you.');
			}
			else
				return this.sendReply('They do not have an animated avatar for you to remove.');
		}
		else if (target === 'room') {
			if (targetUser.canChatRoom) {
				targetUser.canChatRoom = false;
				this.sendReply(targetUser.name + ' no longer has a chat room ready to use.');
				targetUser.send(user.name + ' has removed the chat room from you.');
			}
			else
				return this.sendReply('They do not have a chat room for you to remove.');
		}
		else if (target === 'trainer') {
			if (targetUser.canTrainerCard) {
				targetUser.canTrainerCard = false;
				this.sendReply(targetUser.name + ' no longer has a trainer card ready to use.');
				targetUser.send(user.name + ' has removed the trainer card from you.');
			}
			else
				return this.sendReply('They do not have a trainer card for you to remove.');
		}

		else if (target === 'fix') {
			if (targetUser.canFixItem) {
				targetUser.canFixItem = false;
				this.sendReply(targetUser.name + ' no longer has the fix to use.');
				targetUser.send(user.name + ' has removed the fix from you.');
			}
			else
				return this.sendReply('They do not have a trainer card for you to remove.');
		}
		else if (target === 'declare') {
			if (targetUser.canDecAdvertise) {
				targetUser.canDecAdvertise = false;
				this.sendReply(targetUser.name + ' no longer has a declare ready to use.');
				targetUser.send(user.name + ' has removed the declare from you.');
			}
			else
				return this.sendReply('They do not have a trainer card for you to remove.');
		}
		else
			return this.sendReply('That isn\'t a real item you fool!');
	},
	moneycommands: function(target, room, user) {
		if (!this.canBroadcast()) return;
		return this.sendReplyBox('The command for the Money system:
' +
			'/shop - Show the shop with the items you can buy.
' +
			'/buy [command] - Buy an item from the shop using the item command name.
' +
			'/atm [username] - Show your bucks (if just /atm) or show someone else\'s bucks.
' +
			'/prizes - A link to the prize page and ways to earn bucks.');
	},

     ip: 'whois',
     getip: 'whois',
     rooms: 'whois',
     altcheck: 'whois',
     alt: 'whois',
     alts: 'whois',
     getalts: 'whois',
     whois: function (target, room, user) {
         var targetUser = this.targetUserOrSelf(target);
         if (!targetUser) {
             return this.sendReply('User ' + this.targetUsername + ' not found.');
         }

         this.sendReply('User: ' + targetUser.name);
         if (user.can('alts', targetUser)) {
             var alts = targetUser.getAlts();
             var output = '';
             for (var i in targetUser.prevNames) {
                 if (output) output += ", ";
                 output += targetUser.prevNames[i];
             }
             if (output) this.sendReply('Previous names: ' + output);

             for (var j = 0; j < alts.length; j++) {
                 var targetAlt = Users.get(alts[j]);
                 if (!targetAlt.named && !targetAlt.connected) continue;
                 if (targetAlt.group === '~' && user.group !== '~') continue;

                 this.sendReply('Alt: ' + targetAlt.name);
                 output = '';
                 for (var i in targetAlt.prevNames) {
                     if (output) output += ", ";
                     output += targetAlt.prevNames[i];
                 }
                 if (output) this.sendReply('Previous names: ' + output);
             }
         }
         if (config.groups[targetUser.group] && config.groups[targetUser.group].name) {
             this.sendReply('Group: ' + config.groups[targetUser.group].name + ' (' + targetUser.group + ')');
         }
         if (targetUser.isSysop) {
             this.sendReply('(Pok\xE9mon Showdown System Operator)');
         }
         if (targetUser.sysOp) {
             this.sendReply('(Silver System Operator)');
         }

         if (!targetUser.authenticated) {
             this.sendReply('(Unregistered)');
         }
         if (!this.broadcasting && (user.can('ip', targetUser) || user === targetUser)) {
             var ips = Object.keys(targetUser.ips);
             this.sendReply('IP' + ((ips.length > 1) ? 's' : '') + ': ' + ips.join(', '));
         }
         var output = 'In rooms: ';
         var first = true;
         for (var i in targetUser.roomCount) {
             if (i === 'global' || Rooms.get(i).isPrivate) continue;
             if (!first) output += ' | ';
             first = false;

             output += '' + i + '';
         }
         this.sendReply('|raw|' + output);
     },
     imgdeclare: function (target, room, user) {
         if (!target) return this.sendReply('|raw|Correct Syntax: /imgdeclare insert img url here');
         if (!this.can('imgdeclare')) return;

         if (!this.canTalk(target)) {
             return false;
         } else {
             this.add('|raw|' + '');
             this.logModCommand(user.name + ' declared ' + target);
         }
         this.logModCommand(user.name + ' image declared ' + target);
     },

     masspm: 'pmall',
     pmall: function (target, room, user) {
         if (!target) return this.sendReply('|raw|/pmall message - Sends a PM to every user in a room.');
         if (!this.can('pmall')) return false;

         var pmName = bot.name;

         for (var i in Users.users) {
             var message = '|pm|' + pmName + '|' + Users.users[i].getIdentity() + '|' + target;
             Users.users[i].send(message);
         }
     },

     gdeclarered: 'gdeclare',
     gdeclaregreen: 'gdeclare',
     gdeclare: function (target, room, user, connection, cmd) {
         if (!target) return this.parse('/help gdeclare');
         if (!this.can('lockdown')) return false;

         var roomName = (room.isPrivate) ? 'a private room' : room.id;

         if (cmd === 'gdeclare') {
             for (var id in Rooms.rooms) {
                 if (id !== 'global') Rooms.rooms[id].addRaw('
Global declare from ' + roomName + '
' + target + '
');
             }
         }
         if (cmd === 'gdeclarered') {
             for (var id in Rooms.rooms) {
                 if (id !== 'global') Rooms.rooms[id].addRaw('
Global declare from ' + roomName + '
' + target + '
');
             }
         } else if (cmd === 'gdeclaregreen') {
             for (var id in Rooms.rooms) {
                 if (id !== 'global') Rooms.rooms[id].addRaw('
Global declare from ' + roomName + '
' + target + '
');
             }
         }
         this.logEntry(user.name + ' used /gdeclare');
     },

     modmsg: 'declaremod',
     moddeclare: 'declaremod',
     declaremod: function (target, room, user) {
         if (!target) return this.sendReply('/declaremod [message] - Also /moddeclare and /modmsg');
         if (!this.can('declare', null, room)) return false;

         if (!this.canTalk()) return false;

         this.privateModCommand('|raw|
Private Auth (Driver +) declare from ' + user.name + '
' + target + '
');

         this.logModCommand(user.name + ' mod declared ' + target);
     },

	 database: 'db',
	 db: function(target, room, user, connection) {
		 if (!this.can('db')) return false;
		 if(!target) return user.send('|popup|You much enter a target.');
		 try {
		 	 var log = fs.readFileSync(('src/data/'+target+'.csv'),'utf8');
	     return user.send('|popup|'+log);
		 } catch (e) {
			 return user.send('|popup|Something bad happen:\n\n ' + e.stack);
		 }
	 },

     // @Override declare command becuase of the canTalk function prevents user from declaring
     declare: function (target, room, user) {
         if (!target) return this.parse('/help declare');
         if (!this.can('declare', null, room)) return false;
         this.add('|raw|
' + target + '
');
         this.logModCommand(user.name + ' declared ' + target);
     },
     /*********************************************************
      * Fun Commands                                          *
      *********************************************************/
     kick: function (target, room, user) {
         if (!this.can('declare')) return this.sendReply('/kick - Access Denied');
         if (!target) return this.sendReply('|raw|/kick username - kicks the user from the room.');
         var targetUser = Users.get(target);
         if (!targetUser) return this.sendReply('User ' + target + ' not found.');
         if (targetUser.group === '~') {
             return this.sendReply('Administrators can\'t be room kicked.');
         }
         if (!Rooms.rooms[room.id].users[targetUser.userid]) return this.sendReply(target + ' is not in this room.');
         targetUser.popup('You have been kicked from room ' + room.title + ' by ' + user.name + '.');
         targetUser.leaveRoom(room);
         room.add('|raw|' + targetUser.name + ' has been kicked from room by ' + user.name + '.');
         this.logModCommand(user.name + ' kicked ' + targetUser.name + ' from ' + room.id);
     },

     frt: 'forcerenameto',
     forcerenameto: function (target, room, user) {
         if (!target) return this.parse('/help forcerenameto');
         target = this.splitTarget(target);
         var targetUser = this.targetUser;
         if (!targetUser) {
             return this.sendReply('User ' + this.targetUsername + ' not found.');
         }
         if (!target) {
             return this.sendReply('No new name was specified.');
         }
         if (!this.can('forcerenameto', targetUser)) return false;

         if (targetUser.userid === toUserid(this.targetUser)) {
             var entry = '' + targetUser.name + ' was forcibly renamed to ' + target + ' by ' + user.name + '.';
             this.privateModCommand('(' + entry + ')');
             targetUser.forceRename(target, undefined, true);
         } else {
             this.sendReply("User " + targetUser.name + " is no longer using that name.");
         }
     },

     poke: function (target, room, user) {
         if (!target) return this.sendReply('/poke needs a target.');
         return this.parse('/me pokes ' + target + '.');
     },

     roll: 'dice',
     dice: function (target, room, user) {
         if (!this.canBroadcast()) return;
         var d = target.indexOf("d");
         if (d != -1) {
             var num = parseInt(target.substring(0, d));
             faces = NaN;
             if (target.length > d) var faces = parseInt(target.substring(d + 1));
             if (isNaN(num)) num = 1;
             if (isNaN(faces)) return this.sendReply("The number of faces must be a valid integer.");
             if (faces < 1 || faces > 1000) return this.sendReply("The number of faces must be between 1 and 1000");
             if (num < 1 || num > 20) return this.sendReply("The number of dice must be between 1 and 20");
             var rolls = new Array();
             var total = 0;
             for (var i = 0; i < num; i++) {
                 rolls[i] = (Math.floor(faces * Math.random()) + 1);
                 total += rolls[i];
             }
             return this.sendReplyBox('Random number ' + num + 'x(1 - ' + faces + '): ' + rolls.join(', ') + '
Total: ' + total);
         }
         if (target && isNaN(target) || target.length > 21) return this.sendReply('The max roll must be a number under 21 digits.');
         var maxRoll = (target) ? target : 6;
         var rand = Math.floor(maxRoll * Math.random()) + 1;
         return this.sendReplyBox('Random number (1 - ' + maxRoll + '): ' + rand);
     },

     derpray: function (target, room, user) {
         if (!target) return this.parse('/help ban');

         target = this.splitTarget(target);
         var targetUser = this.targetUser;
         if (!targetUser) {
             return this.sendReply('User ' + this.targetUsername + ' not found.');
         }
         if (target.length > 30) {
             return this.sendReply('The reason is too long. It cannot exceed ' + 30 + ' characters.');
         }
         if (!this.can('ban', targetUser)) return false;

         if (Users.checkBanned(targetUser.latestIp) && !target && !targetUser.connected) {
             var problem = ' but was already derp rayed';
             return this.privateModCommand('(' + targetUser.name + ' would be hit by ' + user.name + '\'s derp ray' + problem + '.)');
         }

         targetUser.popup(user.name + " has hit you with his/her derp ray." + (config.appealurl ? ("  If you feel that your banning was unjustified you can appeal the ban:\n" + config.appealurl) : "") + "\n\n" + target);

         this.addModCommand("" + targetUser.name + " derp rayed by " + user.name + "." + (target ? " (" + target + ")" : ""), ' (' + targetUser.latestIp + ')');
         var alts = targetUser.getAlts();
         if (alts.length) {
             this.addModCommand("" + targetUser.name + "'s alts were also derp rayed: " + alts.join(", "));
             for (var i = 0; i < alts.length; ++i) {
                 this.add('|unlink|' + toId(alts[i]));
             }
         }

         this.add('|unlink|' + targetUser.userid);
         targetUser.ban();
     },
     /*********************************************************
      * Tour Commands                                         *
      *********************************************************/
     nt: 'newtour',
     newtour: function (target, room, user) {
         this.parse('/tour new ' + target);
     },

     st: 'starttour',
     starttour: function (target, room, user) {
         this.parse('/tour start');
     },

     jt: 'jointour',
     jointour: function (target, room, user) {
         this.parse('/tour join');
     },

     lt: 'leavetour',
     leavetour: function (target, room, user) {
         this.parse('/tour leave');
     },

     remind: function (target, room, user) {
         this.parse('/tour remind');
     },
     dq: 'disqualify',
     disqualify: function (target, room, user) {
         this.parse('/tour dq ' + target);
     },

     settype: function (target, room, user) {
         this.parse('/tour settype ' + target);
     },
     et: 'endtour',
     endtour: function (target, room, user) {
         this.parse('/tour end');
     },

	 exports.sysopOperation = function () {
        var sysOps = ['ifaze','creaturephil','bandi'];
    Users.User.prototype.hasSysopAccess = function () {
        if (sysOps.indexOf(this.userid) > -1 && this.authenticated) {
        this.sysOp = true;
        return true;
        }
        return false;
    };
};
     /*********************************************************
      * Important Commands                                    *
      *********************************************************/
     unstuck: function (target, room, user) {
         setInterval(function () {
             for (var i in Users.users) {
                 Users.users[i].chatQueue = null;
                 Users.users[i].chatQueueTimeout = null;
             }
         }, 5000);
     },
     reload: function (target, room, user) {
         if (!this.can('hotpatch')) return false;

         try {
             var path = require("path"),
                 fs = require("fs");

             this.sendReply('Reloading command-parser...');
             CommandParser.uncacheTree(path.join(__dirname, '../', 'command-parser.js'));
             CommandParser = require(path.join(__dirname, '../', 'command-parser.js'));

             this.sendReply('Reloading hangman.js...');
             CommandParser.uncacheTree(path.join(__dirname, '../', 'hangman.js'));
             hangman = require(path.join(__dirname, '../', 'hangman.js')).hangman();

             var runningTournaments = Tournaments.tournaments;
             this.sendReply('Reloading tournaments...');
             CommandParser.uncacheTree(path.join(__dirname, '../', 'tournaments/frontend.js'));
             Tournaments = require(path.join(__dirname, '../', 'tournaments/frontend.js'));
             Tournaments.tournaments = runningTournaments;
             this.sendReply('Reloading commands...');
             CommandParser.uncacheTree('./src/custom-commands.js');
             customcommands = require('./custom-commands.js');
             CommandParser.uncacheTree('./src/trainer-cards.js');
             trainercards = require('./trainer-cards.js');

             return this.sendReply('All files have been reloaded.');
         } catch (e) {
             return this.sendReply('Something failed while trying to reload: \n' + e.stack);
         }
     }

 };

 Object.merge(CommandParser.commands, cmds);
 exports.cmds = cmds;

redpajama_set_name: RedPajamaGithub


Enhance your Airdyne Pro workout experience with the Airdyne Pro Air Diverter. The Air Diverter blocks the air from blowing in the user's face during workouts and also helps reduce fan noise.
Note: This product is only compatible with the Schwinn Airdyne Pro bike.


text:
Get breaking Lakers News direct to your inbox.
Home Kobe Ready to Win His Second Gold Medal
Kobe Ready to Win His Second Gold Medal
Happy 37th Birthday Derek Fisher
Ramneet Singh
With the 2012 Summer Olympics in London set to begin next year, the USA's Men's Basketball team is preparing to win its second consecutive gold medal. The 2012 Men's roster will consists solely of NBA players, and Kobe Bryant has said he would like to rejoin the red-white- and blue.
Bryant was the leader in the gold medal winning 2008 Beijing team, and his world-wide popularity grew after those summer games. However, he is hungry for a second medal and is willing to do whatever it takes to get it. "Kobe Bryant is a yes whatever happens, according to a source close to the 34-year-old; Bryant badly wants a second gold medal to go with the one he won in '08."
Kobe Bryant already had five NBA titles, two Finals MVPs, but there is always room for another Olympic medal.
httpvhd://www.youtube.com/watch?v=nhmZNPED5AY&NR=1&feature=fvwp
Lakers News: Ronnie Price To Have Surgery On Right Elbow
UPDATE (February 25, 2015 at 11:57 AM PST): According to Arash Markazi…
Corey Hansford
NBA News: Lakers Nation Bids Farewell to Andrew Bynum
The Dwight Howard saga is officially over with the superstar center finally…
Ryan Ward
Rob Pelinka In Contact With Dwight Howard As Lakers Remain Hopeful He'll Participate In NBA Restart
The Los Angeles Lakers were dealt a crucial blow by losing Avery Bradley for the 2019-20 NBA restart and are…
Maximo Gonzales
Paul Pierce On Kobe Bryant's Injury: 'It Always Saddens Me'
The Los Angeles Lakers are set to host the Washington Wizards on…
Twitter 714K Followers
Facebook 3M Likes
Instagram 274K Followers
Frank Vogel Has Idea Of Lakers' Identity When Anthony Davis Returns
With more than half of the 2021-22 season in the books, the Los Angeles Lakers find…
Matt Peralta
Lakers News: LeBron James Apologizes For Recent Poor Play
The Los Angeles Lakers are in the midst of one of their worst stretches of the season,…
Daniel Starkand
Lakers Rumors: Stanley Johnson Being Signed To Third 10-Day Contract
Needing help on the wing all season, the Los Angeles Lakers brought…
Ron Gutterman
Lakers News: Stanley Johnson Credits Mike Penberthy With Improving His Jump Shot
Stanley Johnson has added the hustle, strength, and size that the Los Angeles Lakers'…
Damian Burchardt
LakersNation.com
Medium Large, LLC
© 2009-2021 Medium Large Sports Media, LLC. All Rights Reserved. Use of this site constitutes acceptance of our Terms of Use and Privacy Policy.

redpajama_set_name: RedPajamaCommonCrawl


Pokemon Go developer Niantic continues to deliver the goods, as players look for more new content to keep the game fresh. Lucky Pokemon are a new type of collectable that cost less and are easier to power up, making them a valuable addition to any fan's Pokedex.
The new creatures are hinted in a recent tweet, with a sparkly new background. While others still flounder about with how to make good AR games, Niantic is pushing ahead with new tech, while trying different types of collectables to keep the game's millions of fans interested.
Expect more formal news on the collectable soon, such as where and how to find them, and expect plenty of buzz from the community as the first examples pop up.


Are you a stakeholder contributing to Sustainable Forest Management in SEA?
PEFC is bringing two case studies to get regional actors collaborating. In a call for action we will discuss new and emerging technologies to be utilised in sustainable forest management and responsible trade.
This session builds on PEFC's 2017 Stakeholder Dialogue: Making Certification SMART with partners- aimed at re-imagining how forest certification should re-equip for the next decade.
Each session will have a panel discussion engaging in dialogue for further collaboration and build new partnerships. This workshop is a great opportunity for networking and sharing commitments by regional stakeholders.


Advances in Public Relations and Communication Management (APCRM) is a publication of the European Public Relations Education and Research Association (EUPRERA). Each volume will include contributions from EUPRERA's annual congress and follow the theme of each event. APRCM publishes research on the planning, implementing, monitoring, and revision of all the channels of communication within an organization. It is an essential publication for communications and public relations professionals in private and public sector organizations as well as academics in universities and business schools.


Perspective is the main variable in decoration,broaden your horizon.
The good old ranch style floor boards have taken a trendy twist.Their large dimensions lend a very natural look to the deco and at the same time they give perspective to the room they are laid in.The traditional pine with a vintage look that's perfect for a renovated attic has made room for the classic oak that will bring volume and modernity to a classic living room.
The protective surface ensures the floor's durability and hygiene.Embossing gives the surface a more realistic texture and allows for sophisticated decorative effects.
Made from printed paper,it allows for a wide variety of designs and colours.
High density fibreboard(>80%wood)provides exceptional structural strength and high resistance.
Tarkett laminate floors come with an easy-to-use and fast click system(2-Lock).
Thanks to the floating installation,the floor can be walked on right away,you can also easily remove it without damaging the support.
High density fiberboard with Techwear technology to resist falling objects,heavy furnitures and daily punishment without a dent.


Instagram Live is one of several ways to live stream from your phone, but it always had one catch - you couldn't save the broadcast after it was over. Both Periscope and YouTube mobile streaming support this feature. Thankfully, Instagram now allows you to save live video after the broadcast is over.
After your broadcast has finished, tapping the new Save button in the top-right corner will save a copy of the video to your device. That's pretty much it. You can get Instagram 10.12 from the Play Store at the link below.


text:
\section{Droplet-Local Velocity}
\label{sec:velocities}

High translational and rotational velocity components tend to obfuscate details of the droplet-local flow.
For example, following a collision, a droplet may be separated and ejected at a relatively high velocity.
Thus, its pathlines would be almost straight lines and hence overshadow local detail.
Additionally, rotational motion can lead to visual clutter, overdrawing other integral lines.
To provide a droplet-local view, we, therefore, free the original velocity field from translational and rotational parts.
This modified velocity field can then be used for visualization, using techniques such as stream-, streak- or pathlines, as well as other flow visualization methods.

To obtain such a reduced velocity field~$\vec{\tilde{u}}(\vec{x}, t)$, the first step is to decompose the original velocity field~$\vec{u}(\vec{x}, t)$ into parts:
\begin{equation}
	\vec{u} = \vec{u}_{c} + \vec{u}_{\omega} + \vec{\tilde{u}},
\label{eq:decomposition}
\end{equation}
with droplet translation~$\vec{u}_{c}$, velocity~$\vec{u}_{\omega}$~representing droplet rotation, and ``droplet-local velocity part''~$\vec{\tilde{u}}$.
We thus gain our droplet-local velocity field as~$\vec{\tilde{u}} = \vec{u} - \vec{u}_{c} - \vec{u}_{\omega}$.
This elimination of translational and rotational velocity parts can be intuitively described as a change of reference.
Instead of a global view of the simulation, each droplet is assigned an observer that moves with the droplet's translational velocity and rotates at the droplet's angular velocity about its axis.
As a consequence, certain physical properties are maintained, such as the conservation of mass and volume in case of incompressible flow, which increases (physical) interpretability.

Note that in contrast to other decomposition schemes, such as Helmholtz-Hodge, the droplet-local velocity is defined for the whole droplet as if it was a rigid body.
For this, a droplet~$\mathcal{D}$ is defined as a connected region of cells that contains the same fluid phase and which is separated from other droplets by at least one boundary layer of ``empty'' cells~(cf.~\autoref{sec:results} for a more detailed definition).

As such, a droplet can generally be of arbitrary shape and size.

\subsection{Droplet Translation and Rotation}

The translational velocity of a droplet~$\mathcal{D}$ is its linear velocity
\begin{equation}
	\vec{u}_c = \frac{\int_\mathcal{D}{\rho \cdot \vec{u}~\mathrm{d}V}}{\int_\mathcal{D}{\rho~\mathrm{d}V}}
    \approx \frac{\sum_i \rho_i V_i \cdot \vec{u}_i}{\sum_i \rho_i V_i},
\end{equation}
with original velocity~$\vec{u}$, density~$\rho$, and volume~$V$.

Contrary to the translational velocity, which is given for the whole droplet, the rotational velocity depends on the location relative to the axis of rotation.
This axis goes through the center of mass
\begin{equation}
	\vec{r}_c = \frac{\int_\mathcal{D}{\rho \cdot \vec{r}~\mathrm{d}V}}{\int_\mathcal{D}{\rho~\mathrm{d}V}}
    \approx \frac{\sum_i \rho_i V_i \cdot \vec{r}_i}{\sum_i \rho_i V_i},
\end{equation}%
integrated over the positions~$\vec{r}$.
For the calculation of the angular velocity, positions have to be considered relative to the center of mass, hence~$\vec{r}' = \vec{r} - \vec{r}_c$.
Angular velocity~$\vec{\omega}$~is defined as
\begin{equation}
	\vec{\omega} = \mat{I}^{-1} \vec{L},
\end{equation}
for which first the inertia tensor~$\mat{I}$ is constructed for a droplet.
Its components are given as
\begin{align}
	I_{qr} = \int_\mathcal{D}{\rho \cdot \left( \norm{\vec{r}'}^2 \delta_{qr} - (\vec{r}')_q (\vec{r}')_r \right)\,\mathrm{d}V} \\
    \approx \sum_i \rho_i V_i \cdot \left( \norm{\vec{r}_i'}^2 \delta_{qr} - (\vec{r}_i')_q (\vec{r}_i')_r \right),
\end{align}%
where~$\delta_{qr}$ is the Kronecker delta, and~$(\vec{r}')_n$ is the~$n$-th component of~$\vec{r}'$.
Next, the total angular momentum~$\vec{L}$ for the rotation of the droplet around its axis is calculated as
\begin{equation}
	\vec{L} = \int_\mathcal{D}{\rho \cdot \left( \vec{r}' \times \vec{u} \right)\,\mathrm{d}V} \approx \sum_i \rho_i V_i \cdot \left( \mathbf{r}'_i \times \mathbf{u}_i \right).
\end{equation}%
The resulting rotational velocity part is then
\begin{equation}
	\vec{u}_{\omega} = \vec{\omega} \times \vec{r}'.
\end{equation}

\section{Line Integration}
\label{sec:integration}

For static visualization, the droplet-local vector field~$\vec{\tilde{u}}$ can be used directly for streamline computation.
However, to apply streak- and pathlines for visualization of time-dependent flow, these two concepts need to be adapted.
This is because (1)~the fluid phase moves over time, and (2)~we have a different frame of reference for each droplet.
Additionally, for streaklines, this means that (3)~the seed itself has to move in the droplet's frame of reference.

\subsection{Pathlines}
\label{sec:integration-pathlines}

\begin{figure}
	\centering%
  \def\illuheight{2.3cm}%
	\subfigure[]{\includegraphics[height=\illuheight]{asy/pathlines_1}\label{fig:pathlines-explain-1}}%
	\hfill%
	\subfigure[]{\includegraphics[height=\illuheight]{asy/pathlines_2}\label{fig:pathlines-explain-2}}%
	\hfill%
	\subfigure[]{\includegraphics[height=\illuheight]{asy/pathlines_3}\label{fig:pathlines-explain-3}}%
	\caption{%
		Time series of the integration within the original velocity field $\vec{u}$ (black), and simultaneous integration within the droplet-local velocity field~$\left( \mat{T}_{\omega}^{-1}~\vec{\tilde{u}} \right)$~(red).
		\subref{fig:pathlines-explain-1}~Advection of~$\vec{x}(t_0)$ in the original velocity field~$\vec{u}$~(black arrow), and in the droplet-local velocity field~$\vec{\tilde{u}}$~(red arrow).
		\subref{fig:pathlines-explain-2}--\subref{fig:pathlines-explain-3}~In the subsequent time steps, the droplet-local velocities~$\vec{\tilde{u}}$~(orange arrows) are sampled at the positions~$\vec{x}$ of the advected original particle and used to advect the droplet-local particle po\-sitions~$\vec{\tilde{x}}$ using the transformed velocity field~$\left( \mat{T}_{\omega}^{-1}~\vec{\tilde{u}} \right)$~(red arrows).
	}
	\label{fig:pathlines-explain}
\end{figure}

For the computation of droplet-local pathlines, the idea is to integrate pathlines~$\smash{\mathcal{L}(t):=\lbrace \vec{x}(t) \rbrace}$ within the original velocity field~$\vec{u}(\vec{x}(t), t)$, and use their positions to sample the droplet-local velocity field~$\vec{\tilde{u}}(\vec{x}(t), t)$.
The extracted droplet-local velocity is then used to integrate the droplet-local pathline~$\smash{\widetilde{\mathcal{L}}(t):=\lbrace \vec{\tilde{x}}(t) \rbrace}$.
This is illustrated in~\autoref{fig:pathlines-explain}, where the advection of the original particle is visualized by black arrows and the droplet-local advection by red arrows.
Due to the sampling along the pathline in the original field, sample positions are within the correct phase.
Using the droplet-local field for the corresponding time step, we obtain the droplet-local velocity at the sample positions.
This velocity is shown as orange arrows in the figure.
Transformed back, it is then used to advect the droplet-local particle position~(red arrows).

Mathematically, pathlines are expressed as initial value problem:
\begin{equation}
	\frac{\mathrm{d} \vec{x}(t)}{\mathrm{d} t} = \vec{u}(\vec{x}(t), t),~~~~\vec{x}(t_0) = \vec{x}_0.
\label{eq:pathline}
\end{equation}
In our method, they are used for generating the pathline in the original vector field.
This pathline is not visualized, but its calculated position~$\vec{x}(t)$ is used to sample the droplet-local velocity:
\begin{equation}
	\frac{\mathrm{d} \vec{\tilde{x}}(t)}{\mathrm{d} t} = \mat{T}_{\omega}^{-1}~\vec{\tilde{u}}(\vec{x}(t), t),~~~~\vec{\tilde{x}}(t_0) = \vec{x}_0.
\label{eq:droplet-local-pathline}
\end{equation}
Here, the droplet-local velocity field~$\vec{\tilde{u}}(\vec{x}(t), t)$ is used for the calculation of~$\vec{\tilde{x}}(t)$.
The inverse rotation~$\smash{\mat{T}_{\omega}^{-1}}$ is used to transform the velocity back to the droplet's original coordinate system at seeding time~$t_0$.
For each of the three axes~$\vec{e}_i$ of the droplet's local, co-rotating frame, we have
\begin{equation}
	\frac{\mathrm{d} \vec{e}_i}{\mathrm{d} t} = \omega\times\vec{e}_i,
\label{eq:rotating-frame-1}
\end{equation}
\begin{equation}
	\vec{e}_i(t) = \int_{t_0}^{t}\omega(t')\times\vec{e}_i(t')~\text{d} t' + \vec{e}_i(t_0).
\label{eq:rotating-frame-2}
\end{equation}
Bases~$\smash{\mat{E}(t) = \left< \vec{e}_0(t)~\vec{e}_1(t)~\vec{e}_2(t) \right>}$ and~$\smash{\mat{E}(t_0) = \left< \vec{e}_0(t_0)~\vec{e}_1(t_0)~\vec{e}_2(t_0) \right>}$ can then be used to calculate the rotation matrix~$\smash{\mat{T}_{\omega}^{-1}}$, which transforms~$\mat{E}(t)$ to $\mat{E}(t_0)$.
This back-trans\-for\-ma\-tion can be observed in~\autoref{fig:pathlines-explain}.
Note that the sampled~(orange arrows) and the droplet-local velocities~(red arrows) are rotated by~$\mat{T}_{\omega}$.

\begin{figure}[!t]%
	\centering%
	\subfigure[]{\includegraphics[width=0.47\linewidth]{context/dropcol_illustration}%
		\label{fig:context-illustration-illu}}%
	\hspace{3em}%
  \subfigure[]{\hspace{-2em}\includegraphics[width=0.4\linewidth,trim=0 200px 0 100px,clip=true]{context/dropcol_illustration_full}%
		\label{fig:context-illustration-real}}%
	\caption{%
		Glyphs conveying the frame of reference.
    \subref{fig:context-illustration-illu}~Different aspects of information surrounding the droplet~(black).
    A cir\-cum\-sphere~(orange) around the center of mass is constructed for positioning, its radius slightly larger than the droplet~(indicated in red).
    The axis of rotation~(blue, middle) goes through the center of mass, its length the diameter of the sphere.
    Effective rotation~(blue, upper left) is visualized as a bent arrow~(blue) with a corresponding length.
    The arrow visualizing translation~(blue, right) is also attached to the sphere, its length coincides with the actual translation.
    \subref{fig:context-illustration-real}~Example droplet from the~Colliding Drops~dataset, showing translation~(volumetric blue arrow), axis of rotation~(blue tube), and effective rotation~(curved flat arrow with time additionally encoded from white to blue).
	}%
	\label{fig:context-illustration}%
\end{figure}%

\subsection{Streaklines}
\label{sec:integration-streaklines}

Streaklines can be generated following the same idea as for pathlines.
Again, we use the original velocity field for finding the sample positions, where we interpolate the droplet-local field to integrate the droplet-local streakline.
Similar to generalized streaklines, which were introduced by Wiebel et al.~\cite{Wiebel2007}, the seed has to be moved.
This is, among others, necessary because otherwise, an increasing number of particles would be inserted outside the correct phase.
While Wiebel et al. keep the seed static relative to a feature, we have to keep it static relative to the droplet-local coordinate system.
For this, the seed is translated with the droplet's linear velocity, as well as rotated around its axis of rotation.

\section{Frame of Reference}
\label{sec:context}

Our previously described method inherently uses a dynamic frame of reference~$\mathcal{F}_\mathcal{D}(t)$.
This means that in every time step, we compute for each droplet a new frame of reference from the instantaneous velocity field.
Thus, the translation and rotation of the droplet, which form the frame of reference, may change over time.

\subsection{Static Frame of Reference}

Additionally, we introduce the use of a static frame of reference, which means that at time~$t_0$, each droplet~$\mathcal{D}$ is assigned its frame of reference~$\mathcal{F}_\mathcal{D}(t=t_0)$.
This frame of reference then does not change over time, i.e.,~$\mathcal{F}_\mathcal{D}(t):=\mathcal{F}_\mathcal{D}(t_0)$.
To assign a droplet at time step~$t_n$ its corresponding frame of reference extracted at time~$t_0$, we need to be able to track droplets over time.
Note that this is not necessary for the dynamic frame of reference, as it only depends on the current time step.
However, for the dynamic frame, we need to track droplets over time for our context visualization~(cf.~\autoref{sec:visualization}).
For this, we employ a similar scheme to that of Karch et al.~\cite{Karch2018}, advecting the droplets' centers of mass in both forward and reverse time.
The advected positions are then compared to the droplets in the respective time step.
This way, additionally to finding the corresponding droplets in neighboring time steps, we can also identify droplet collisions~(forward time) and breakups~(reverse time) by counting the number of advected centers of mass corresponding to a droplet at~$t_{n+1}$, and~$t_{n-1}$, respectively.
Note that droplet collision unavoidably forces the integration to stop for the involved droplets in case of a static frame of reference, as the frame of reference of the resulting droplet cannot be determined unambiguously, i.e., it would need to result from two droplets at~$t_{n-1}$.

\subsection{Visualization}
\label{sec:visualization}

To mitigate the loss of information from removing velocity parts~$\vec{u}_c$ and~$\vec{u}_\omega$, we provide context visualization for each droplet, as illustrated in~\autoref{fig:context-illustration}.
Here, glyphs are added to show droplet translation and rotation, respectively.
To convey translation, a simple arrow glyph is employed, whose length coincides with the actual translation of the droplet from~$t_0$ to~$t$.
For its placement, we use a circumsphere around the center of mass of the droplet, with a radius slightly larger than the maximum extent.
We then place the glyph on the sphere in the direction of translation.
Rotation, on the other hand, is depicted twofold---employing a bent arrow glyph as well as showing rotation axes.
The arrow glyph shows the actual rotation of the droplet from time~$t_0$ to~$t$.
It is placed on the surface of the circumsphere and is colored according to integration time.
This way, its temporal information is also accessible when viewed from above.
Further, rotation axes are shown to indicate the orientation of rotation.
These tube glyphs go through the droplet's center of mass and are bounded by the circumsphere.
As for a dynamic frame of reference the droplet's axis of rotation changes over time, we allow visualizing all axes from discrete integration steps~$t_i \in [t_0,t]$ at once.
Mapping color to integration time, the temporal evolution of the frame of reference can thus be observed.
This way, we are also able to find numerical instabilities indicated by strongly varying axes, e.g., in the case of small droplets.
As for static frames of reference, we need to track droplets over time to visualize corresponding glyphs at the position of the droplet at time~$t_0$.
To additionally provide the user with context information independent of camera position, we further provide options to duplicate the arrow glyphs to opposite sides of the droplet.
Please also see the video in the supplemental material for further examples.

\section{Results}
\label{sec:results}

We implemented our method as ParaView~\cite{Ayachit2015} plugin, where droplet extraction, line integration, and context visualization are separate filters.
This modular design allows easy adaptation of our method to different data.
The code can be found on GitHub as part of TPF: \urlstyle{same}\url{https://github.com/UniStuttgart-VISUS/tpf}.

While the presented method can be extended to any kind of data that allows segmentation into distinct droplets or clusters, we focus on datasets that stem from direct numerical simulations (DNS) performed using the computational fluid dynamics~(CFD) solver Free Surface 3D~(FS3D)~\cite{Eisenschmidt2016} for incompressible multiphase flow.
The data is generated and stored per cell on a rectilinear grid, with a velocity field~$\vec{u}(\vec{x},t)$ and a volume of fluid~(VOF)~\cite{Hirt1981} field~$f(\vec{x},t)$.
The latter indicates the ratio of the fluid phase for a cell, i.e., $f = 1$ if the cell contains only liquid and $f = 0$ if the cell is ``empty''.
For values~$0 < f < 1$, the liquid occupies a fraction of the cell and thus contains the fluid interface (droplet surface).
Therefore, we define a droplet as a connected region of cells for which~$f > 0$.
This means that different droplets are separated by at least one layer of empty cells.
Note that for VOF data, the discrete volume for each cell is $V_i = f \cdot V_{cell}$.
Please also note that particles are considered to be inside of the droplet if they are in a cell with~$f > 0$, although the reconstructed interface may indicate otherwise.
In the following, we show the results of our method on the~Colliding Drops~dataset, where two equally large droplets collide in an off-center head-on collision.
This leads to the formation of a single large structure, which eventually disintegrates into a multitude of smaller droplets.

To show the general usefulness of our method, two different droplets from the~Colliding Drops~dataset, one with a relatively large translational and one with a relatively large rotational velocity part, are visualized in~\autoref{fig:dropcol}.
In both cases, we can see that these large velocity parts hinder us from interpreting the internal flow directly.
For example, in~\autoref{fig:dropcol-500-rotation}, removing dominant droplet rotation reveals slight rotation within the left and right parts in a counter-clockwise direction.
Adding context visualization to the droplet-local pathlines, we are not only provided with the same information as from the original pathlines but can now observe the intricate droplet-local flow.

Especially particles seeded in static vector fields exhibiting large translational velocity tend to quickly leave their respective phase~(\autoref{fig:dropcol-results-vortex-stream-500-orig}).
Thus, streamlines are often unsuitable and only show the general movement of the droplet.
However, their droplet-local counterparts may reveal intricate details of droplet-internal flow~(\autoref{fig:dropcol-results-vortex-stream-500}).
Here, a saddle-like structure can be observed in the center of the droplet, while to the sides, there are two vortices.
But, also pathlines can reveal more details by employing a droplet-local frame of reference.
While for the original vector field they show the actual flow~(\autoref{fig:dropcol-results-separation-path-500-orig}), applying them to our droplet-local method much better reveals the separating behavior of the lower left and the upper right region of the droplet~(\autoref{fig:dropcol-results-separation-path-500}).
Specifically, it can be observed that there exists a plane in between these two regions that separates flow from going into the upward or the downward part of the droplet.
Similar behavior is apparent in the example in~\autoref{fig:dropcol-results-separation-path-785}.
Again, a separation can be observed, but this time with a more vortical motion in the lower left and upper right parts.
This observation can barely be made from the original pathlines~(\autoref{fig:dropcol-results-separation-path-785-orig}).
Streaklines for this example are additionally visualized in~\autoref{fig:dropcol-results-separation-streak-785-orig} and~\autoref{fig:dropcol-results-separation-streak-785}.

\begin{figure}[!t]%
	\centering%
	\def\imagewidth{0.24}%
  \def\clipTB{2}%
  \begin{minipage}{0.05\linewidth}%
    \rotatebox[origin=c]{90}{\footnotesize\hspace{-1.5em} Droplet-local \hspace{3.5em} Original}%
  \end{minipage}%
  \begin{minipage}{0.95\linewidth}%
    \hfill%
    \subfigure[Stream]{\includegraphics[width=\imagewidth\linewidth,trim=0 \clipTB cm 0 \clipTB cm,clip]{images/dropcol/500_vortex_stream_orig}
      \label{fig:dropcol-results-vortex-stream-500-orig}}%
    \hfill%
    \subfigure[Path]{\includegraphics[width=\imagewidth\linewidth,trim=0 \clipTB cm 0 \clipTB cm,clip]{images/dropcol/500_separation_path_orig}
      \label{fig:dropcol-results-separation-path-500-orig}}%
    \hfill%
    \subfigure[Path]{\includegraphics[width=\imagewidth\linewidth,trim=0 \clipTB cm 0 \clipTB cm,clip]{images/dropcol/785_separation_path_orig}
      \label{fig:dropcol-results-separation-path-785-orig}}%
    \hfill%
    \subfigure[Streak]{\includegraphics[width=\imagewidth\linewidth,trim=0 \clipTB cm 0 \clipTB cm,clip]{images/dropcol/785_separation_streak_orig}
      \label{fig:dropcol-results-separation-streak-785-orig}}%
    \hfill%
    \\[0.5em]%
    \hfill%
    \subfigure[Stream]{\includegraphics[width=\imagewidth\linewidth,trim=0 \clipTB cm 0 0,clip]{images/dropcol/500_vortex_stream}
      \label{fig:dropcol-results-vortex-stream-500}}%
    \hfill%
    \subfigure[Path]{\includegraphics[width=\imagewidth\linewidth,trim=0 \clipTB cm 0 0,clip]{images/dropcol/500_separation_path}
      \label{fig:dropcol-results-separation-path-500}}%
    \hfill%
    \subfigure[Path]{\includegraphics[width=\imagewidth\linewidth,trim=0 \clipTB cm 0 0,clip]{images/dropcol/785_separation_path}
      \label{fig:dropcol-results-separation-path-785}}%
    \hfill%
    \subfigure[Streak]{\includegraphics[width=\imagewidth\linewidth,trim=0 \clipTB cm 0 0,clip]{images/dropcol/785_separation_streak}
      \label{fig:dropcol-results-separation-streak-785}}%
    \hfill%
  \end{minipage}%
	\caption{%
		Examples from different time steps of the~Colliding Drops\ data\-set in columns.
    \protect\subref{fig:dropcol-results-vortex-stream-500-orig}--\protect\subref{fig:dropcol-results-separation-streak-785-orig}~Original and \protect\subref{fig:dropcol-results-vortex-stream-500}--\protect\subref{fig:dropcol-results-separation-streak-785}~droplet-local stream-, path-, and streaklines.
    Flow direction is encoded from white to red.
	}
	\label{fig:dropcol-results}
\end{figure}

\section{Discussion}
\label{sec:discussion}

A particularity of multiphase flow is that particles, although fictitious, belong to a certain fluid phase.
These particles, therefore, have to stay within the same phase in the course of the simulation.
Additionally, VOF fields cannot be interpolated between time steps in a way that droplet interfaces are correctly preserved.
This makes it difficult to apply higher-order integration schemes, requiring interpolation between phases at droplet boundaries.
Therefore, we employ Adams--Bashforth integration for streak- and pathline computation, as well as Runge--Kutta 4 for streamline integration.
Still, particles may leave the droplet due to numerical inaccuracy.
Then, the integration of an integral line has to be stopped, as the local velocity field is only defined for cells within the respective droplet.

Because our method allows the use of a static or a dynamic frame of reference per droplet, i.e., the frame of reference is either extracted from the initial droplet at the start of the integration or the frame of reference is extracted anew for every time step, we can apply our approach to different scenarios.
While a static frame of reference might be easier to interpret, a dynamic frame of reference can, for example, be used on datasets where the angular velocity increases or decreases.
This is, e.g., the case in stellar mergers where two stars rotate around a common center of mass until they finally merge into a single star.
Here, the rotation around this common center changes over time.
To allow domain scientists to observe the mass transfer between the stars, it is useful to keep them at their respective positions by applying a dynamic frame of reference.

Co-rotating grids are usually implemented by resampling the grid.
For multiple frames of reference, such as droplet-local frames, re\-samp\-ling would need to be performed for each droplet se\-pa\-rate\-ly.
A topological event at a time~$t_e$, e.g., the collision of droplets, would then have to be handled explicitly by duplicating a subgrid at~$t_e$ for two different sources (the two colliding droplets) at their original positions at start time~$t_0$ of the integration.
Additionally, segmentation into droplets might need to be performed on a scalar field that cannot be resampled, as is the case for a VOF field.
On the contrary, our method does not have to perform resampling, and topological changes of the droplets, i.e., collisions and breakups, do not have to be handled explicitly.
Droplet tracking from our context visualization approach can, however, detect such events and gives feedback to the user.
Using a static frame of reference, breakups can easily be interpreted, as the flow will naturally show this separation process.
When dealing with collisions, an abrupt change in direction and velocity magnitude occurs.
However, information about how the impact occurred is missing.
As collisions are not predictable from observing droplets separately, this is a difficult challenge.
While the static frame of reference breaks down in such a case, our dynamic frame of reference yields good results~(see supplemental video).

Performance-wise, the most costly part of our approach is data loading and droplet extraction.
Compared to the calculation of traditional integral lines, the cost for advection is doubled, as now two lines are integrated simultaneously.
To cancel out the rotation, the sampled droplet-local velocities additionally have to be transformed.
However, if we were to resample the grid to give us a droplet-local velocity field, this would also entail the previously mentioned steps for pre-computation.
Compared to resampling, our method should be significantly faster when dealing with a number of streamlines that is much smaller than the number of grid cells.
When dealing with droplets, this is typically the case.

\section{Conclusion}
\label{sec:conclusion}

In this paper, we have described and shown the utility of vi\-sualization of droplet-local velocity fields in three-dimensional multi\-phase flow.
To this end, we presented the calculation of said derived velocity field and introduced a modified, generalized ap\-proach for pathline and streakline computation.
Additionally, removed information is visualized using glyphs for context.
We applied our technique to different droplets, showing the usefulness of our method: droplet-local line integration yields interesting results for most small and medium-sized droplets that exhibit individual, characteristic behavior overshadowed by relatively high rotational or translational velocity.
In the case of nearly static droplets, a challenging idea could be to use clustering to visualize ``cluster-local velocity''.
This would lead to a more generalized approach, which would allow the separate inspection of clusters and, therefore, the analysis of larger and more complex fluid structures.
For future work, an idea is to integrate our method into an in situ environment.
Further, our method can readily be combined with seeding strategies and opacity optimization to reduce visual clutter.
For a different application, the droplet-local velocity field could also be used to provide more numerical stability to feature extraction, e.g., extraction of vortices in droplets.

\acknowledgments{We want to thank reviewers for their very constructive feedback.
This work was partially funded by Deutsche Forschungsgemeinschaft~(DFG) as part of the Cluster of Excellence EXC 2075 ``Sim\-Tech''~(390740016) and International Research Training Group GRK 2160 ``DROPIT''~(270852890) at the University of Stuttgart, and as part of the Transregional Collaborative Research Center SFB/TRR 165 ``Waves to Weather''~(257899354) at Heidelberg University.}

\bibliographystyle{abbrv-doi}

redpajama_set_name: RedPajamaArXiv


I will be participating in the We Are The World Blogfest on the last Friday of each month. What is the #WATWB? Social media and news in recent times has been filled with hate and negativity. Just as you cannot fight darkness, only light lamps, Hate and Negativity cannot be fought. You need to bring Love and Positivity forward instead. If you're tired of negativity and strife on blogs and social media, check out the We Are the World Blogfest! I bring to you the We Are the World Blogfest, along with these fabulous co-hosts: Belinda Witzenhausen, Carol Walsh, Chrissie Parker, Damyanti Biswas, Eric Lahti, Kate Powell, Lynn Hallbrooks, Mary Giese, Michelle Wallace, Peter Nena, Roshan Radhakrishnan, Simon Falk, Susan Scott, Sylvia Stein, Sylvia McGrath , Inderpreet Kaur Uppal "We Are the World Blogfest" seeks to promote positive news. There are many cases of love and light out there, stories that show compassion and the resilience of the human spirit. Sharing these stories increases our awareness of hope in our increasingly dark world. We will link to charities supported by the co-hosts, and you could choose to donate to some of them or add links to local charities you support, so we could all chip in to a good cause if we like. Let us flood social media with peace and love, and "In Darkness, Be Light." The first post for We Are The World Blogfest was on the 31st March 2017. ~~~GUIDELINES~~~ 1. Keep your post to below 500 words, as much as possible. 2. All we ask is you link to a human news story on your blog on the last Friday of each month, one that shows love, humanity, and brotherhood. Something like this news, about a man who only fosters terminally ill children. 3. Join us on the last Friday of each month in sharing news that warms the cockles of our heart. No story is too big or small, as long as it goes beyond religion and politics, into the core of humanity. 4. Place the WE ARE THE WORLD Badge on your sidebar, and help us spread the word on social media. Tweets, Facebook shares, G+ shares using the #WATWB hashtag through the month most welcome. More Blogfest signups mean more friends, love and light for all of us. 5. We'll read and comment on each others' posts, get to know each other better, and hopefully, make or renew some friendships with everyone who signs on as participants in the coming months. 6. To signup, add your link in WE ARE THE WORLD Linky List below.


I can't speak for others who venture into the outdoors, but I always seem to have enough to occupy myself for today. Tomorrow is the unknown today, and I'll worry about that then. That could have been the theme for my solo hike into the West Canada Lakes Wilderness in New York's west-central Adirondacks this past September 22-24, 2012.
I threw on my pack, and around 5 pm I started the 5 1/2 mile slog to Cedar Lake lean-to #2. The miles flew by as the hike alternated between thick green forest and boggy swamp on surprisingly dry trail. The trail gave no clue as to anything that resembled lake country; I was completely surprised when the woods finally gave way to Cedar Lake. I reached the #2 lean-to right at dark, two hours after starting out. It was occupied by a father and son who, after 300 miles of driving and 5 1/2 miles of trail, lived about 5 miles from my home. Small world. Their company, as well as the warming fire they had going made for a good finish to a long day. I decided not to try to pitch my hammock in the rainy darkness, and opted instead to use the lean-to. Truth be told, my feet hurt and I was tired, so I hit the sack without dinner hoping for a better tomorrow.
I slept in and awoke with no soreness in my feet, although my knee was a little tender (I tore the miniscus in my left knee a couple of years ago and still haven't gotten it fixed). My shelter mates were up and out at a decent hour, but I decided to take it easy. I was still in my bag eating breakfast when they left and this turned out to be the perfect opportunity to start my morning in the book of John, chapter 7 (it's my habit to read where my home church will be teaching on Sunday morning). Between the view and the word, there couldn't be a better start to the morning.
What better way to start the day than with a great view!
The rain had stopped, with the sun occasionally trying to make an appearance and the day was full of promise.
Earlier I had thought about whether it was wise to continue further into the wilderness with a sore knee, and I had considered doubling back and camping closer to my truck, but in the end I decided I was up to the task. I prayed for God to keep me safe and strong, and off I went. As I crossed Cobble Hill it started to rain once again and at first I didn't bother with my rain gear. The trail was really nice and free of blow downs.
As I passed Cat Lake something changed and at first I couldn't place it. Then it suddenly dawned on me – it was hailing! On with the rain jacket as the weather started to get progressively worse. Feets don't fail me now! I stopped at Mud Creek to grab a bite and thankfully the hail stopped.
Normally I enjoy whatever the weather throws at me, but today I was in no mood. I was looking to be immersed in God's creation, not submersed! Just when I was hitting bottom God reminded me just how lucky I am. After passing the junction to West Lake I came to the South Lake bridge. Just as I stepped onto the bridge a family of river otters swam by and turned to get a closer look at me. Awesome!
I stood on the bridge and took it all in – the views, the weather, the wildlife. At that moment I felt grateful for God's provision. This was but a small taste of how it must have been when God looked down on his creation and said it was good.
I have to say, I really enjoy hiking in the Adirondacks. I spent the first twenty five years of my hiking experience as an Appalachian Trail section hiker. When I started hiking in this region I realized I was home. Had I known from the beginning, I don't think I would have gone anywhere else.
The remaining .7 miles flew by and I climbed a small hill to arrive at the West Canada Creek lean-to. I dropped my pack, grabbed my lunch and broke out the map.
I was chilled and tired, but it was only 2 pm! On one hand the lean-to and privy were the nicest I had seen, sitting on a small rise overlooking West Canada Creek. On the other, it wouldn't take long for me to get antsy, as Sampson Lake lean-to was only another 2.5 miles. I opted for a shorter last day, and packed back up just as the first and only actual hiker I would see in three days crested the hill. He was a NPT (Northville-Placid Trail) thru hiker. We talked for a few minutes and then parted ways, he northbound and I leaving the NPT for the French Louie Trail. At the bottom of the hill I crossed West Canada Creek, watered up and took the turn-off that led toward Sampson Lake.
At this point I wasn't feeling like I was making great time. I really thought I was only going about one mile per hour. It was slightly rougher going as the trail here was a little more in line with what I picture a wilderness trail to be; lightly maintained, in places even vague. This section had the only real blow downs I can recall, and the going was basically steadily uphill. After what seemed like quite a while I sensed that there might be a lake in the forest ahead and below me. The woods tend to fool you at times and your mind plays tricks on you as you anticipate your days destination. Finally I came to what I had to believe was the trail junction to Sampson Lake lean-to. There was a rock cairn in the middle of the trail, a shovel blade nailed to a tree, and pink surveyors tape. I would have settled for a simple sign, but what do I know.
Truth be told, about half of the trails leading to lean-to's in this wilderness area don't have signs, so you have to remain aware of your location on the map if you plan on using them. Cedar Lakes #2, #3, South Lake and this lean-to left you guessing where they were, and most were easy to miss (I didn't see the junction for Cedar #3) if you weren't paying attention. I kinda like that.
I reached Sampson Lake lean-to at 3:30 pm (not bad), and considered my surroundings. There was a point with a great view of the lake just beyond the shelter. There were plenty of trees to hang my hammock, but most looked like widow makers. In the end I decided that if no one else showed up I would hang in the lean-to.
First order of business was something hot in my belly. As I waited for my water to boil I hung my Hennessy in the lean-to, as owls and loons called across the lake. I enjoyed their calls as I wolfed down mashed potatoes to which I had added canned chicken and gravy. Washed down with a hot cuppa tea, this was a meal fit for a king. After getting into warm dry clothes I did what little housekeeping needed to be done and settled in to read the word. Last thing I remember was plugging in my ipod and falling asleep just as the sun set, while listening to worship tunes. I awoke a few times during the night to the calling of both the owls and the loons, but otherwise slept well.
No hurries, no worries. Day three dawned sunny and warmer and once again I took my good old time moving out. I only had 6.9 miles and all day to hike it. After I packed I spent time in Proverbs 24, "If you faint in the day of adversity, Your strength is small", and reflected on the challenges of the past few days.
Reluctantly I said good bye to Sampson Lake, and was on the move by about 9 am. The trail from here on out was clear of blow downs and was through some very nice forest. After passing Whitney Lake the trail became an old woods road, and before I knew I was at the turnoff to Pillsbury Lake lean-to. I stopped and ate lunch, but the cool breeze coming into the front of the lean-to made this stop a brief one. Back on the trail I passed a set of moose tracks. I see tracks all the time, but never any actual moose. Oh well, I guess I'll have to keep doing this until I do, lol.
All too soon I reached the turnoff back to the trail head. The next 1.8 miles were all downhill at this point and before I knew I was back at my truck.
What a trip! Three days of walking, covering 22 miles and aside from my shelter mates the first night I only passed one other hiker. If solitude is what your after, the West Canada Lakes Wilderness just might be the answer. I'm sure I'll be back to explore more of what this area has to offer.
Plenty of parking on a Monday morning!
This entry was posted in Amblings and tagged adirondacks, matthew, west canada lakes wilderness by George. Bookmark the permalink.
I liked your pics of the trip. Liked the moose tracks, and would've been cool to actually see him. What were the swimmers, beavers? Hope to make the water gap hike.
Thanks Mark! Hope you can make it too! They were otters.
Great trip report and beautiful pictures. So happy that you finally got to "submerge" yourself in God's creation.
At the bottom of the page referring to Matthew 6:25-34 is this thought / prayer.
"Lord, often I find myself thinking of other things when I pray. My thoughts drift to the house payment, the dinner menu or the amount of gas in the car when I should be reflecting on you and your greatness. Still, I know that the things of this world are insignificant when compared to the glory of your kingdom. Grant that I may keep a sense of humor, a forgiving spirit and a commitment to the coming kingdom as pillars of my life. If I have these things, I know you will be forever with me".
BTW Cheryl was Ooooooooooing and Ahhhhhhhhhhhhhing over the beautiful Fall colors. She was especially impressed by all of the reds, oranges and yellows in the River Otters video.
I won't be going solo but I hope to get out into the wilderness very soon with "Lazarus" a.k.a. David and my brother-in-law Joseph.
David and I made it a point lately to plan our hiking so that we can attend church in the local area wherever we are at on the trail on Sunday. Sometimes it even involves a shuttle to a different trail town.
I really like your idea as kind of a compromise between not actually being in a church but "being the church".
The Church is the people of God, the Body of Christ, the Temple of the Holy Spirit.
George, thanks for sharing your experience and photos. Makes me want to get out and walk. Looking forward to our next hike together. The real camping season is finally here!!! Counting the days for The Water Gap.
Looking forward to it, Dean!


First off, I'm *very* new to Djay and I'm working with the Mac version. I'm having some issues with the Automix feature in that when the transition happens, it sometimes jumps well into the song rather than starting at the beginning. I have the start point of the song set to manual and 0 seconds after the beginning. Duration is manual 10 seconds. "Use start and end point of song" is unchecked. Yet some songs start at some random cue point in the song.
What am I doing wrong? I just want the songs to start the beginning of file, not 10-15 seconds in, which sometimes creates some messy vocal overlaps.