Function to convert numbers over 1000 to 1k etc. AS3 - actionscript-3

G'day,
The function is coded, but it's on the stage frame. I'm looking to get it converted into a more dynamic function so I can just call it on all my textfields.
Here's the code:
function numtolet():void
{
output.text = String(int(earner * 100) / 100);
if (earner >= 1000 && earner < 1000000)
{
output.text = String(int((earner/1000) * 100) / 100 + "k");
}
else if (earner >=1000000 && earner < 1000000000)
{
output.text = String(int((earner/ 1000000) * 100 ) / 100 + " M");
}
}
I'm looking to turn the 'output.text' portion into a variable that changes based on the text field calling the function and 'earner' to the variable the textfield reads.
Cheers,
-Aidan.

You'd better write your function as proper function that can return a String value to assign to a text property or use elsewhere. Also, you should use a pattern that is easily extendable to bigger prefixes, should you need them. Say, I have found a game with a W prefix being used, which is one beyond the common "yotta" prefix, and there was a set of subsequent prefixes as well. So, this is how you should devise such a function:
function numtolet(x:Number):String {
const prefixes:Vector.<String> = Vector.<String>(["","k","m","g","t"]);
// add more to taste. Empty prefix is used if the number is less than 1000
var y:Number=x;
var i:int=1;
// provided x>0, if not, store a minus somewhere and attach later
while((y>=1000) && (i<prefixes.length)) {
y=y/1000;
i++;
}
// there, you have just divided X by 1000 a couple of times and selected the prefix
var s:String = y.toFixed(2)+prefixes[i-1];
// if there was a minus, add it here: s="-"+s;
return s;
}
Then you just call it like this:
output.text=numtolet(earner);

You can do this using the CHANGE event:
output.addEventListener(Event.CHANGE, numtolet);
function numtolet(e:Event):void
{
output.text = String(int(earner * 100) / 100);
if (earner >= 1000 && earner < 1000000)
{
output.text = String(int((earner/1000) * 100) / 100 + "k");
}
else if (earner >=1000000 && earner < 1000000000)
{
output.text = String(int((earner/ 1000000) * 100 ) / 100 + " M");
}
}
This will make the function run every time the text is changed by a user, but you'd probably want to add a few conditional (if)'s to the function, or use a variable to keep track of the curent number. When the number converts to 1k, how does it know what do to at 1000k?
Feel free to ask if you need help on this.

When you use the CHANGE event like Neguido said, and add listeners to different text fields, you can use e.target.text = to change the text in the calling text field.
To target a different variable for each text field is more difficult, because you cant pass extra arguments into the event handlers, and you cant add your own variables/properties to textFields. You could stick each textField into a parent MovieClip and then create variables in there like MovieClip1.earner = 0 and retrieve the values with e.target.parent.earner. You could also write a dynamic extension to the TextField class where you add custom variables. Alternatively you could use a switch statement in your event handler to use different variables for different callers.

Here's a quick function I wrote up for something else. It can easily be adapted to larger numbers by adding another if statement and adding 000 to the number. It also doesn't include the output display, but that can very easily be added as well. Hope it helps!
Call the function via numToLet(earner).
function numToLet(x) {
if (x > 1000000000000000000) {
x = x / 1000000000000000000
x = Number(x.toFixed(2));
return x + "Quin";
}
if (x > 1000000000000000) {
x = x / 1000000000000000
x = Number(x.toFixed(2));
return x + "Quad";
}
if (x > 1000000000000) {
x = x / 1000000000000
x = Number(x.toFixed(2));
return x + "Tril";
}
if (x > 1000000000) {
x = x / 1000000000
x = Number(x.toFixed(2));
return x + "Bil";
}
if (x > 1000000) {
x = x / 1000000
x = Number(x.toFixed(2));
return x + "Mil";
}
if (x < 1000000) {
x = Number(x.toFixed(2));
return x;
}
}

Related

Phaser 3 - set min and max zoom levels

I'm using Phaser to create an online comic. One functionality I want to have is the option to zoom into images, for the sake of legibility on small screens.
I'm using the following on a container holding the image.
container.scale = 1;
this.input.on('wheel', function (pointer, gameObjects, deltaX, deltaY, deltaZ) {
var x = deltaY * 0.002;
container.scale += x;
console.log(container.scale);
});
So far so good, the image zooms.
I want to set a minimum zoom level of 1 and a maximum zoom level of 1.5.
I thought this modification to the code would do it:
container.scale = 1;
this.input.on('wheel', function (pointer, gameObjects, deltaX, deltaY, deltaZ) {
var x = deltaY * 0.002;
function between(x, min, max) {
return x >= min && x <= max;
}
if (between(x, 1, 1.5)) {
container.scale += x;
console.log(x, container.scale);
}
});
But the code won't fire at all. I've tried variations and gotten nowhere - can anyone help with this?
The WheelEvent.deltaY read-only property is a double representing the vertical scroll amount in the WheelEvent.deltaMode unit.
You're comparing the set amount the wheel is actually spinning versus that range, which is why it'll never fire. On my end your x value will be either 0.24 (down-spin) or -0.24 (up-spin) depending on the wheel spin direction.
This is closer to what you might want to be achieving:
if((x < 0 && container.scale + x >= 1) || (x > 0 && container.scale + x <= 1.5)) {
container.scale += x;
}

Floating point inaccuracy with Vue.js during content rendering [duplicate]

This question already has answers here:
How to deal with floating point number precision in JavaScript?
(47 answers)
Closed 8 years ago.
I have a large amount of numeric values y in javascript. I want to group them by rounding them down to the nearest multiple of x and convert the result to a string.
How do I get around the annoying floating point precision?
For example:
0.2 + 0.4 = 0.6000000000000001
Two things I have tried:
>>> y = 1.23456789
>>> x = 0.2
>>> parseInt(Math.round(Math.floor(y/x))) * x;
1.2000000000000002
and:
>>> y = 1.23456789
>>> x = 0.2
>>> y - (y % x)
1.2000000000000002
From this post: How to deal with floating point number precision in JavaScript?
You have a few options:
Use a special datatype for decimals, like decimal.js
Format your result to some fixed number of significant digits, like this:
(Math.floor(y/x) * x).toFixed(2)
Convert all your numbers to integers
You could do something like this:
> +(Math.floor(y/x)*x).toFixed(15);
1.2
Edit: It would be better to use big.js.
big.js
A small, fast, easy-to-use library for arbitrary-precision decimal arithmetic.
>> bigX = new Big(x)
>> bigY = new Big(y)
>> bigY.div(bigX).round().times(bigX).toNumber() // => 1.2
> var x = 0.1
> var y = 0.2
> var cf = 10
> x * y
0.020000000000000004
> (x * cf) * (y * cf) / (cf * cf)
0.02
Quick solution:
var _cf = (function() {
function _shift(x) {
var parts = x.toString().split('.');
return (parts.length < 2) ? 1 : Math.pow(10, parts[1].length);
}
return function() {
return Array.prototype.reduce.call(arguments, function (prev, next) { return prev === undefined || next === undefined ? undefined : Math.max(prev, _shift (next)); }, -Infinity);
};
})();
Math.a = function () {
var f = _cf.apply(null, arguments); if(f === undefined) return undefined;
function cb(x, y, i, o) { return x + f * y; }
return Array.prototype.reduce.call(arguments, cb, 0) / f;
};
Math.s = function (l,r) { var f = _cf(l,r); return (l * f - r * f) / f; };
Math.m = function () {
var f = _cf.apply(null, arguments);
function cb(x, y, i, o) { return (x*f) * (y*f) / (f * f); }
return Array.prototype.reduce.call(arguments, cb, 1);
};
Math.d = function (l,r) { var f = _cf(l,r); return (l * f) / (r * f); };
> Math.m(0.1, 0.2)
0.02
You can check the full explanation here.
Check out this link.. It helped me a lot.
http://www.w3schools.com/jsref/jsref_toprecision.asp
The toPrecision(no_of_digits_required) function returns a string so don't forget to use the parseFloat() function to convert to decimal point of required precision.
Tackling this task, I'd first find the number of decimal places in x, then round y accordingly. I'd use:
y.toFixed(x.toString().split(".")[1].length);
It should convert x to a string, split it over the decimal point, find the length of the right part, and then y.toFixed(length) should round y based on that length.

as3 move towards function jitters on Y axis

As the title suggests, I've written a move towards function that works perfectly fine, except that it jitters on the Y axis.
The problem
It happens when the function is operating on the Y axis and the difference between the starting value and the target value is less than that between the starting value and the value to return - meaning that the value has gone past its target. as this point, it's supposed to set the returning value to the target value, but for the most part it's not. Except when it's operating on the x axis, in which case it works fine. It's really strange.
The code
here's the code I'm using for the function:
public static function LookAt(thisX:Number, thisY:Number, targetX:Number, targetY:Number, speed:Number = 0, startRot:Number = 0):Number
{
// Get the distances between the two parsed points
var xDif:Number = targetX - thisX;
var yDif:Number = targetY - thisY;
// Use a tangent formula to get the rotation to return in radians, then convert to degrees
var rot:Number = Math.atan2(xDif, yDif) * 180/Math.PI * -1 - 180;
// If a speed has been parsed
if (speed != 0)
{
// Ensure the parsed starting rotation is between -180 and 180
while (startRot > 180)
{startRot -= 360;}
while (startRot < -180)
{startRot += 360;}
// If the rotation previously calculated is less than the parsed starting rotation,
// return the starting rotation minus the speed. Otherwise, return the starting rotation
// plus the speed
return (rot > startRot) ? startRot + speed : startRot - speed;
}
else
{
return rot;
}
}
public static function PointAround (axisPos:Number, angle:Number, speed:Number, axis:String = "x"):Number
{
// Convert the parsed angle into radians
var fixedRot = angle * Math.PI / 180;
// Return the parsed position plus speed multiplied by the sine of the angle in radians for the x axis,
// or the cosine of the angle in radians for the y axis
return (axis == "x") ? axisPos + speed * Math.sin(fixedRot) : axisPos + speed * Math.cos(fixedRot) * -1;
}
public static function PointTowards(thisX:Number, thisY:Number, targetX:Number, targetY:Number, speed:Number, axis:String = "x"):Number
{
// Use the LookAt function to calculate a rotation for later use in this function
var workingAngle:Number = ExtraMath.LookAt (thisX, thisY, targetX, targetY);
var toReturn;
var thisVar:Number = (axis == "x") ? thisX : thisY;
var targetVar:Number = (axis == "x") ? thisX : thisY;
toReturn = ExtraMath.PointAround (thisVar, workingAngle, speed);
// BUGGY LINE
toReturn = (thisVar >= targetVar && toReturn <= targetVar
|| thisVar <= targetVar && toReturn >= targetVar)
? targetVar
: toReturn;
return toReturn;
}
and here's the code I'm using to test it:
public var c:Sprite;
public function TestZone()
{
// constructor code
stage.addEventListener(Event.ENTER_FRAME, Update);
}
private function Update (e:Event):void
{
c.x = ExtraMath.PointTowards(c.x, c.y, stage.mouseX, stage.mouseY, 5);
c.y = ExtraMath.PointTowards(c.x, c.y, stage.mouseX, stage.mouseY, 5, "y");
}
things I've tried already
turning the line into regular a regular if statement with curly brackets and all, and tracing the variables thisY, targetY and toReturn after it has been operated on. the really annoying thing is that it turns out it sometimes actually returns the right number, but then proceeds to bug out again
Using an absolute value instead of stage.mouseY in testing. bug occurs as usual
Performing the function on the Y axis before the X axis. no difference
Changing the condition for setting the variables thisVar and targetVar to (axis != x) and switching the if/else values. do difference
A few things may be causing the problem:
[LookAt]
return (rot > startRot) ? startRot + speed : startRot - speed;
This may cause overshooting. If rot is only very slightly different from startRot, you are still adding (or subtracting) a full speed increment. If the absolute distance between rot and startRot is less than speed, it should return rot regardless.
This reduces angular jittering.
[PointAround]
return (axis == "x") ? axisPos + speed * Math.sin(fixedRot) : axisPos + speed * Math.cos(fixedRot) * -1;
Watch out for operator precedence. You are expecting this line to be parsed as
(axis == "x") ?
(axisPos + speed * Math.sin(fixedRot)) :
(axisPos + speed * Math.cos(fixedRot) * -1);
But that may not be the case. The line may instead be interpreted as
(
(axis == "x") ?
(axisPos + speed * Math.sin(fixedRot)) :
axisPos
)
+ speed * Math.cos(fixedRot) * -1;
You can either memorize all precedence rules and make sure you never mistake them, or put parenthesis around to ensure it's doing the right thing. In this case you can simplify the expression to
axisPos + speed * (axis == "x" ? Math.sin(fixedRot) ? -Math.cos(fixedRot))
var thisVar:Number = (axis == "x") ? thisX : thisY;
var targetVar:Number = (axis == "x") ? thisX : thisY;
So thisVar and targetVar always have the same value? I don't understand what was supposed to happen here, and nobody seems to reassign those variables later.
c.x = ExtraMath.PointTowards(c.x, c.y, stage.mouseX, stage.mouseY, 5);
c.y = ExtraMath.PointTowards(c.x, c.y, stage.mouseX, stage.mouseY, 5, "y");
You are changing the position separately in each axis. It may work, but it's harder and error prone. For example
c.x = ...PointTowards(c.x ...);
c.y = ...PointTowards(c.x ...);
You are changing the value of c.x between the calls, so the first call to PointTowards see a different point from the second call. That may be the reason why the jittering only happens on the y axis. I suggest making a function that deals with box axis at once, or at the very least storing the old values of c.x and c.y:
var oldX:Number = c.x;
var oldY:Number = c.y;
c.x = ExtraMath.PointTowards(oldX, oldY, stage.mouseX, stage.mouseY, 5);
c.y = ExtraMath.PointTowards(oldX, oldY, stage.mouseX, stage.mouseY, 5, "y");
I fixed it! I changed the function to return a point object instead of a number and now it works perfectly

An alternative to bitmapdata with less memory usage?

Im using a very big BitmapData as a pathing map for my platformer game, however I only use pixels for 4 particular values, instead of, well 4294967295.
Would converting this Bitmapdata as 2 2D Vectors of Boolean save me some memory ?
And if it does, what about performance, would it be faster or slower to do something like:
MapGetPixel(x:int, y:int):int
{
return MapBoolFirst[x][y] + MapBoolSecond[x][y]*2;
}
instead of the bitmapdata class getPixel32(x:int, y:int):uint ?
In short im looking for a way to reduce the size and/or optimize my 4 colors bitmapdata.
Edit :
Using my boolean method apparently consumes 2 times more memory than the bitmapdata one.
I guess a boolean takes more than one bit in memory, else that would be too easy. So im thinking about bitshifting ints and thus have an int store the value for several pixels, but im not sure about this…
Edit 2 :
Using int bitshifts I can manage the data of 16 pixels into a single int, this trick should work to save some memory, even if it'll probably hit performance a bit.
Bitshifting will be the most memory-optimized way of handling it. Performance wise, that shouldn't be too big of an issue unless you need to poll a lot of asks each frame. The issue with AS is that booleans are 4bits :(
As I see it you can handle it in different cases:
1) Create a lower res texture for the hit detections, usually it is okay to shrink it 4 times (256x256 --> 64x64)
2) Use some kind of technique of saving that data into some kind of storage (bool is easiest, but if that is too big, then you need to find another solution for it)
3) Do the integer-solution (I haven't worked with bit-shifting before, so I thought it would be a fun challenge, here's the result of that)
And that solution is way smaller than the one used for boolean, and also way harder to understand :/
public class Foobar extends MovieClip {
const MAX_X:int = 32;
const MAX_Y:int = 16;
var _itemPixels:Vector.<int> = new Vector.<int>(Math.ceil(MAX_X * MAX_Y / 32));
public function Foobar() {
var pre:Number = System.totalMemory;
init();
trace("size=" + _itemPixels.length);
for (var i = 0; i < MAX_Y; ++i) {
for (var j = 0; j < MAX_X; ++j) {
trace("item=" + (i*MAX_X+j) + "=" + isWalkablePixel(j, i));
}
}
trace("memory preInit=" + pre);
trace("memory postInit=" + System.totalMemory);
}
public function init() {
var MAX_SIZE:int = MAX_X * MAX_Y;
var id:int = 0;
var val:int = 0;
var b:Number = 0;
for(var y=0; y < MAX_Y; ++y) {
for (var x = 0; x < MAX_X; ++x) {
b = Math.round(Math.random()); //lookup the pixel from some kind of texture or however you expose the items
if (b == 1) {
id = Math.floor((y * MAX_X + x) / 32);
val = _itemPixels[id];
var it:uint = (y * MAX_X + x) % 32;
b = b << it;
val |= b;
_itemPixels[id] = val;
}
}
}
}
public function isWalkablePixel(x, y):Boolean {
var val:int = _itemPixels[Math.floor((y * MAX_X + x) / 32)];
var it:uint = 1 << (y * MAX_X + x) % 32;
return (val & it) != 0;
}
}
One simple improvement is to use a ByteArray instead of BitmapData. That means each "pixel" only takes up 1 byte instead of 4. This is still a bit wasteful since you're only needing 2 bits per pixel and not 8, but it's a lot less than using BitmapData. It also gives you some "room to grow" without having to change anything significant later if you need to store more than 4 values per pixel.
ByteArray.readByte()/ByteArray.writeByte() works with integers, so it's really convenient to use. Of course, only the low 8 bits of the integer is written when calling writeByte().
You set ByteArray.position to the point (0-based index) where you want the next read or write to start from.
To sum up: Think of the ByteArray as a one dimensional Array of integers valued 0-255.
Here are the results, I was using an imported 8 bit colored .png by the way, not sure if it changes anything when he gets converted into a
BitmapData.
Memory usage :
BitmapData : 100%
Double Boolean vectors : 200%
Int Bitshifting : 12%
So int bitshifting win hands down, it works pretty much the same way as hexadecimal color components, however in that case I store 16 components (pixel values in 2 bits) not the 4 ARGB:
var pixels:int = -1;// in binary full of 1
for (var i:int = 0; i < 16; i++)
trace("pixel " + (i + 1) +" value : " + (pixels >> i * 2 & 3));
outputs as expected :
"pixel i value : 3"

Trouble creating a spectrogram

I know it was asked a thousand times before, but I still can't find a solution.
Searching SO, I indeed found the algorithm for it, but lacking the mathematical knowledge required to truly understand it, I am helplessly lost!
To start with the beginning, my goal is to compute an entire spectrogram and save it to an image in order to use it for a visualizer.
I tried using Sound.computeSpectrum, but this requires to play the sound and wait for it to end, I want to compute the spectrogram in a way shorter time than that will require to listen all the song. And I have 2 hours long mp3s.
What I am doing now is to read the bytes from a Sound object, the separate into two Vectors(.); Then using a timer, at each 100 ms I call a function (step1) where I have the implementation of the algorithm, as follows:
for each vector (each for a channel) I apply the hann function to the elements;
for each vector I nullify the imaginary part (I have a secondary vector for that)
for each vector I apply FFT
for each vector I find the magnitude for the first N / 2 elements
for each vector I convert squared magnitude to dB scale
end.
But I get only negative values, and only 30 percent of the results might be useful (in the way that the rest are identical)
I will post the code for only one channel to get rid off the "for each vector" part.
private var N:Number = 512;
private function step1() : void
{
var xReLeft:Vector.<Number> = new Vector.<Number>(N);
var xImLeft:Vector.<Number> = new Vector.<Number>(N);
var leftA:Vector.<Number> = new Vector.<Number>(N);
// getting sample range
leftA = this.channels.left.slice(step * N, step * (N) + (N));
if (leftA.length < N)
{
stepper.removeEventListener(TimerEvent.TIMER, getFreq100ms);
return;
}
else if (leftA.length == 0)
{
stepper.removeEventListener(TimerEvent.TIMER, getFreq100ms);
return;
}
var i:int;
// hann window function init
m_win = new Vector.<Number>(N);
for ( var i:int = 0; i < N; i++ )
m_win[i] = (4.0 / N) * 0.5 * (1 - Math.cos(2 * Math.PI * i / N));
// applying hann window function
for ( i = 0; i < N; i++ )
{
xReLeft[i] = m_win[i]*leftA[i];
//xReRight[i] = m_win[i]*rightA[i];
}
// nullify the imaginary part
for ( i = 0; i < N; i++ )
{
xImLeft[i] = 0.0;
//xImRight[i] = 0.0;
}
var magnitutel:Vector.<Number> = new Vector.<Number>(N);
fftl.run( xReLeft, xImLeft );
current = xReLeft;
currf = xImLeft;
for ( i = 0; i < N / 2; i++ )
{
var re:Number = xReLeft[i];
var im:Number = xImLeft[i];
magnitutel[i] = Math.sqrt(re * re + im * im);
}
const SCALE:Number = 20 / Math.LN10;
var l:uint = this.total.length;
for ( i = 0; i < N / 2; i++ )
{
magnitutel[i] = SCALE * Math.log( magnitutel[i] + Number.MIN_VALUE );
}
var bufferl:Vector.<Number> = new Vector.<Number>();
for (i = 0; i < N / 2 ; i++)
{
bufferl[i] = magnitutel[i];
}
var complete:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
complete[0] = bufferl;
this.total[step] = complete;
this.step++;
}
This function is executed in the event dispatched by the timer (stepper).
Obviously I do something wrong, as I said I have only negative values and further more values range between 1 and 7000 (at least).
I want to thank you in advance for any help.
With respect,
Paul
Negative dB values are OK. Just add a constant (representing your volume control) until the number of points you want to color become positive. The remaining values that stay negative are usually just displayed or colored as black in a spectrogram. No matter how negative (as they might just be the FFT's numerical noise, which can be a huge negative dB number or even NaN or -Inf for log(0)).