All right people, I've got a slight performance bottle neck.
Basically I have a graph that consists of a screen div ("screen") and a chart div ("chart"), when this graph finishes rendering it checks to see what scale it needs to set on the chart in order to have the chart fit inside the screen. The problem is that whatever scale I come up with needs to be an exponent of 1.2. In other words you need to be able to get to the number of the new scale by taking 1.2 to the power of some number.
This function is what calculates the scale I need.
fitScale = function (width, height)
{
var scale = 1,
gWidth = graph.element.offsetWidth,
gHeight = graph.element.offsetHeight;
while (gWidth > width * scale && gHeight > height * scale)
scale *= 1.2;
while (gWidth < width * scale || gHeight < height * scale)
scale /= 1.2;
return 900 / scale;
}
The problem is that this sucks...
What it's doing is getting the chart size (width, height) and the screen size (gWidth, gHeight) and looping through a new scale until it hits the right number.
First it makes the scale bigger until at least one dimension of the chart times the scale is bigger than one dimension of the screen.
Than it loops back to make sure that both the dimensions of chart * scale are at least a little bit smaller than the screen.
I'de like to perform this action with just one math calculation. Maybe by calculating a snug fit and then by rounding down, but I can't figure out how to round down to an exponent of 1.2
-fix-
Here's the resulting working version...
fitScale = function (width, height)
{
var wScale = graph.element.offsetWidth / width,
hScale = graph.element.offsetHeight / height,
snugg = wScale < hScale ? wScale : hScale,
exp = Math.log(snugg) / Math.log(1 / 1.2),
scale = Math.pow(1 / 1.2, Math.ceil(exp));
return 900 / scale;
}
My math skills are rusty, so go easy if I wander off the Path of Truth here.
Basically you want to know what power y of 1.2 is equal to some given number x. While the log function would appear not to be helpful since it tells you what power of e will equal your number x, with some mad logarithm skillz you can in fact use that to get to where you want to go:
var y = Math.log(x) / Math.log(1.2);
Odds are pretty good that y won't be a whole number which is I think what you want, so if you just go ahead and Math.floor(y), you should be all set.
Related
I'm trying to implement a feature where you can drag on your screen to change the Position of an Object.
Right now the object is moving similarly to the change of the mouseX.
What I want is the further down on the screen you drag your mouse, the slower the object moves.
I'm pretty bad at maths so i dont really now how to achieve that in a good way.
Right now I'm doing it like that
factor = Math.abs(e.deltaY)/4;
this.newX = this.currentX + (e.deltaX / factor);
currentX is the start X value of the object.
No idea if it is clear what i want to achieve or if i provided all information, but any help is appreciated!
Your arithmetics always makes step equal to 4.
Consider using some function like exponent. For example,
factor = Exp(- k * Abs(deltaX))
If you want to provide factor 1 at close distances and factor halves per every 100 pixels (I've got arbitrary reasonable values), then
0.5 = exp( - k * 100)
ln(0.5) = -k * 100
k = - ln(0.5) / 100 ~= 0.007
note that I mean multiplicative factor:
factor = Exp(- 0.007 * Abs(deltaX))
this.newX = this.currentX + (e.deltaX * factor);
Now speed will be 1 for small distances, 0.5 for 100, 0.25 for 200, 0.125 for 300 and so on.
If you want another dependence, it is possible to find appropriate function.
This question already has answers here:
Converting 3D position to 2d screen position [r69!]
(4 answers)
Closed 7 years ago.
I am trying to figure out how to get the 2D screen coordinates for a 3D point.
I am using three.js to generate some snowflakes that fall slowly down the screen. I originally wrote the script just as a 2d canvas animation and added mouse interaction so you could blow the snow around with mouse movement. It worked well, but when switching to webgl the snowflakes were now represented as 3D points and getting the distance of the mouse to each particle makes particles further from the center of the screen behave in an undesired way because of the perspective.
http://www.simplemathguild.com/snowtest.html
What you need to do is to transform your worldPos vec3 by the viewProjection matrix and then perform the perspective divide. This brings it to NDC space where (-1,-1,x) = bottom left of your screen and (+1,+1,x) = upper right of your screen. Adjust this by your screen width and screen height and you have the coordinate in screen space.
In code, this is what it looks like:
worldToScreen: function(viewProjectionMatrix, screenWidth, screenHeight){
var m = viewProjectionMatrix;
var w = m[3] * this[0] + m[7] * this[1] + m[11] * this[2] + m[15]; // required for perspective divide
this.transformByMat4(viewProjectionMatrix);
if (w!==0){ // do perspective divide and NDC -> screen conversion
var invW = 1.0/w;
this[0] = (this[0]*invW + 1) / 2 * screenWidth;
this[1] = (1-this[1]*invW) / 2 * screenHeight; // screen space Y goes from top to bottom
this[2] *= invW;
}
return this;
}
Btw this is what essentially what the GPU is doing under the hood to render stuff, minus the NDC to screen coordinate conversion.
Check the THREE Vector3 methods, very likely it is implemented there, or just use the code snippet above.
If I have an image that has 100 x 100 size, then I resize it to 200 x 200, how can I calculate how much it was scaled?
In this example the scale amount would be 2.0. How can I get that number from the known variables (original size, new size) ?
Simply divide the new height by the original height and divide the new width by the original width...
double xfactor = (double)NewImage.Height / (double)OldImage.Height;
double yfactor = (double)NewImage.Width / (double)OldImage.Width;
Basics
I am working on a small tool that is supposed help with some geometric calculations for print-related products.
Overview
I have two inputs (w and h) where the user is supposed to enter a width and a height for a box. This box is supposed to be a representation of the users measurements as a small CSS-based box.
The problem is that I cannot just take the measurements and apply them as pixels, or even pixels * 10, or anything, as width/height for the display box, because my space is limited.
The box can have a maximum measurement of 69 x 69.
What I want to achieve is that to apply the longer entered measurement to its according axis, then calculate the other axis in proportion to this.
My approach
I am not a maths person at all. But I did my best and I put together a function that will accomplish the above:
updateRectBox: function(w, h){
// define maximum width and height for box
var max_x=69;
var max_y=69;
var factor,x,y;
factor=w/h;
if(w==h){
// if we have a 1:1 ratio, we want the box to fill `69px` on both axis
x=max_x;
y=max_y;
} else {
if(w>h){
// if width is larger than height, we calculate the box height using the factor
x=max_x;
y=(factor>1 ? max_y/factor : max_y*factor);
} else {
// if height is larger than width, we calculate the box width using the factor
x=(factor>1 ? max_x/factor : max_x*factor);
y=max_y;
}
}
// using this to set the box element's properties
jQuery('#rect').css({
'width': (x)+'px',
'height': (y)+'px'
});
}
This function works well, but:
Question
I know this can be done more beautifully, with less code. But due to my lack of math skills, I just cannot think of anything more compact than what I wrote.
I've created a working fiddle to make it easier for you to test your optimizations.
Your function accomplishes exactly what it needs to. There are ways which are arguably more elegant to write, however.
The basic idea is that you have a box with dimensions (w × h) and you want a box which is a scaled version of this one to fit in a (69 × 69) box.
To fit in a (69 × 69) box, your (w × h) box must be less than 69 wide, and less than 69 tall. Suppose you scale by the quantity s. Then your new box has dimension (s * w × s * h). Using the above constraint, we know that:
s * w <= 69 and that s * h <= 69. Rewrite these, solving for s, and you get:
s <= 69 / w and s <= 69 / h. Both must hold true, so you can rewrite this as:
s <= min( 69 / w, 69 / h). In addition, you want s to be as large as possible (so the box completely fills the region) so s = min( 69 / w, 69 / h).
Your code accomplishes the same, but through if-statements. You can rewrite it considerably terser by doing:
updateRectBox: function(width, height) {
// define maximum width and height for box
var max_width = 69;
var max_height = 69;
var scale = Math.min( max_width / width, max_height / height );
var x = scale * width;
var y = scale * height;
// using this to set the box element's properties
jQuery('#rect').css({
'width': x+'px',
'height': y+'px'
});
}
Changing the variable names helps make it slightly more readable (w and h presumably do mean width and height, but making this explicit is helpful).
All this said, it's unlikely that there will be noticeable performance differences between this and your original. The code is extremely fast, since it does very little. That said, I made a jsperf which shows that using Math.min is about 1.7 times faster on my browser.
So I'm working on a particle emitter with javascript and canvas.
And I want to be able to set what direction the particles are emitting based on an angle.
This can be done with this function:
y = Math.tan(45 * Math.PI/180);
Which returns 1 if the angle is 45. etc.
But I don't exacly know how I should implement this since pixels are calculated a little different. Think -1 as removing one pixel each step and 1 as adding one pixel.
If the angle is 45, Y is 1 and X is 1 which is correct.
But to get a pixel traveling at 315 degrees Y is -1 and X should be 1.
And at 225 degrees Y should be -1 (but is 1) and X should be -1.
How should the function look like if it should work like this?
Here is an image of how im thinking:
(The emitter is in the origin.)
Actually it's simple,
angle = (angle * Math.PI/180) % 360;
tangent = Math.tan(angle);
Since you do not know where is x;
section_x_positive = (angle<90||angle>270?1:-1);
section_y_positive = (angle>0&&angle<180?1:-1);
x = abs(tangent) * section_x_positive;
y = abs(tangent) * section_y_positive;
It sounds to me like your problem is that you're thinking about direction, which is a vector quantity, as if it were a scalar.
You need to remember that a 2D vector is represented as two components:
You can work in terms of unit vectors, so the magnitude r = 1.
So if you have a direction angle, which should be measured in radians, increasing in the counterclockwise direction, and starting at the x = 0 horizontal axis, you'll end up with two components of the unit vector that points in the direction you want.