I need an algorithm which will determine the maximum scale increase that is possible for an image.
The rules (size limitation of a BitmapData object for the Flash Player, but this is not a Flash question):
both the width and length of the image must be less than 8,191 pixels
the maximum number of pixels in the image can not exceed 16,777,215
therefore, as mentioned in the documentation:
if a BitmapData object is 8,191 pixels
wide, it can only be 2,048 pixels
high.
In my code, I first determine if any of these rules are broken and throw and error if they are. this lets me know that any image that is loaded and does not throw an error has scalability.
The image I'm using is 2514 width x 1029 height. This image will not throw an error since both width and height are less than 8,191 and it's pixel count, or width multiplied by height, is less than 16,777,215.
I identify as being right-brained and don't have much confidence in my math skills, but the following is what I've come up with to determine the maximum allowed scale for the image.
private static const MAX_BITMAP_MEASUREMENT:uint = 8191;
private static const MAX_BITMAP_PIXELS:uint = 16777215;
var imageWidth:uint = 2514;
var imageHeight:uint = 1029;
var roughScaleUp:Number = 1.0 / Math.max(imageWidth, imageHeight) * MAX_BITMAP_MEASUREMENT;
var scaleBack:Number = Math.max(Math.min(imageWidth, imageHeight) * roughScaleUp - MAX_BITMAP_PIXELS / MAX_BITMAP_MEASUREMENT, 0);
var maxScale:Number = 1.0 / (Math.max(imageWidth, imageHeight) + scaleBack) * MAX_BITMAP_MEASUREMENT;
This code outputs the maximum scale for my image as 2.145144435977516, but I tested it and there is still a lot of pixel space remaining, so it should be able to be scaled up more and I'm pretty sure my code is terribly wrong.
Any math wizards on here care to help out a lowly art school graduate? I'm fully ready to accept that there is probably a much more simplistic solution to this problem and I'm prepared for the lashings.
You have to multiply both the width and the height by a constant, and the scaled result of the multiplication should be less than 16777215.
So
a^2 * w * h == 16,777,215
Which yields, for your values of w and h
a = 2.5466520486244177 [= Sqrt(16,777,215 / (w*h)) ]
So , for your values for the new w and h, you get:
NewW = a * w = 6402.283250241786
NewH = a * h = 2620.5049580345258
... just round them down :)
Well, here is an ugly solution. I couldn't get the rounding quite right, so I brute forced over the 4 possibilities to get the optimal value. The code should be straightforward enough to understand:
from math import *
def opt_image(w, h):
aspect = w / h
if aspect >= 1:
v = min(sqrt(1677215 / aspect), 8191)
a, b = floor(aspect * v), floor(v)
area, nw, nh = max([ ( (a+x)*(b+y), (a+x), (b+y) ) for x in range(2) for y in range(2) if (a+x)*(b+y) < 1677215 ])
return nw, nh
a, b = opt_image(w, h)
return b, a
For your example with width 2514, height 1029; I got:
(1831.0, 916.0)
Related
I have a resizable parent element containing an ever-changing number of video elements. Each video should maintain a width/height aspect ratio of 4/3. As the total size of the parent is stretched and changed, each video element should resize itself so as to maximize the total area of video space, or to minimize the unused (non-video) area within the parent.
I feel like this should be relatively easy yet here I am hours later... what am I forgetting from childhood geometry?
There are no constraints on css elements I can use - I've tried with grids and flexbox layouts, all to little-to-no avail.
Your task is to place n rectangles with fixed C=W/H = 4/3 ratio on the rectangular container with given Width and Height
Let scaled rectangle height is h (unknown yet), width is w = C * h
Every row of grid contains nr small rectangles
nr = Floor(Width / (C * h)) // rounding down
Every column contains nc rows
nc = Floor(Height / h)
Write inequality
n <= nc * nr
n <= Floor(Width / (C * h)) * Floor(Height / h)
and solve it for unknown h (find maximal possible h value)
For real values of parameters h might be found getting initial approximate value:
h0 = Ceil(Sqrt(Width * Height / (n * C))) //rounding up
and decrementing h value until inequality becomes true
I'm new to GLSL and learning from the tutorial here.
(It's using ShaderToy)
https://gamedevelopment.tutsplus.com/tutorials/a-beginners-guide-to-coding-graphics-shaders--cms-23313
My question is why you can set x coordinates to 0-1 by dividing the fragCoord's x coordinates by the iResolution(screensize).
It might be just a math question, but I'm confused what exactly the "iResolution.x" indicates or what kind of calculation is made here. (Is it a vector division? )
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 xy = fragCoord.xy; //We obtain our coordinates for the current pixel
xy.x = xy.x / iResolution.x; //We divide the coordinates by the screen size
xy.y = xy.y / iResolution.y;
// Now x is 0 for the leftmost pixel, and 1 for the rightmost pixel
vec4 solidRed = vec4(0,0.0,0.0,1.0); //This is actually black right now
if(xy.x > 0.5){
solidRed.r = 1.0; //Set its red component to 1.0
}
fragColor = solidRed;
}
The other answers are correct. fragCoord is the pixel currently being drawn, iResolution is the size of the screen so
xy.x = xy.x / iResolution.x; //We divide the coordinates by the screen size
xy.y = xy.y / iResolution.y
Gives normalized values where xy.x goes from 0 to 1 across and xy.y goes from 0 to 1 up the screen which seems to be exactly what the comments say
It's important to note though that iResolution and fragCoord are user variables. In this case I'm guessing you're getting this GLSL from Shadertoy. Those variables are not part of WebGL or GLSL, they are defined by Shadertoy and so their values and meaning are defined by shadertoy.
Note that if you are new to GLSL and WebGL you might want to consider some webgl tutorials. Also see this answer about shadertoy
iResolution.x is the width of your screen in pixels. Dividing the pixel x location by the total width transforms the location into a fraction of the screen width. So, if your screen is 1000 pixels wide, and your current position is x=500, xy.x = xy.x / iResolution.x; will convert xy.x to 0.500.
I read an article about normalized device coordinates (on the german DGL wiki) and the following example is provided:
"Let's consider that we had a Viewport with dimensions 1024 pixel(width) and 768 pixel height. A point P with absolute, not normalized, coordinates P(350/210) would be in normalized coordinates P(-0,32/-0,59).These coordinates can now be projected on a Viewport (800x600) just by multiplying the normalized device coordinates (similar to vector scaling) with the size of the viewport. In this case the result would be P(273/164).
Somehow I can't understand how one can get to the result provided (I mean 273/164 and -0,32/-0,59 ...could somebody explain to me how to calculate the coordinates?
P.S. : This is the article - https://wiki.delphigl.com/index.php/Normalisierte_Ger%C3%A4tekoordinate
Thank you!
That article is definitely lacking description. I can get you part of the way there; maybe someone with more math can help finish.
According to this answer, the formula to convert non-normalized coords to normalized coords is:
(where Cx/y = Coordinate X/Y; Sx/y = Screen X/Y; and Nx/y = Normalized X/Y).
Plugging the example's numbers in:
Nx = (350/1024) * 2 - 1 = -0.31640625
Ny = 1 - (210/768) * 2 = 0.453125
...or (-.36, 0.45).
Reversing this to get the new coords:
Cx = (1 + -0.31640625) / 2 * 800 = 273.4375
Cy = (1 - 0.453125) / 2 * 600 = 164.0625
Note that the Y value doesn't match. This is probably because my calculation doesn't account for the aspect ratio, and it should be since these screens have a .75 aspect ratio, while NDC's is 1. This SO answer may help too.
I am looking at this example and one part of this does not make sense.
The code creates an array of values like this:
for (i = 0; i < 84; i++) {
data.push(i * 10 / 84);
}
It then uses this array to get both the x and y values for the graph where d is an element of the array:
sine
.x(function (d, i) { return xScale(d); })
.y(function (d, i) { return yScale(Math.sin(d - time)); });
Is 84 just an arbitrary number for the available width remaining for the graph or is there any particular reason of where this came from?
I think it is a number of points per circle... compromise between accuracy and speed. I usually use 36 for small circles and 90 for big. ... and few thousand for huge ones ... so the idea is to use as low count as possible while the circle still looks like circle (in max zoom) and not like polygon.
You can also compute this algebraically ...
da=2.0*M_PI/n
e=r-(r*cos(0.5*da))
where n is the number of line segments per circumference and e is the max distance from desired circle shape. if you set it to desired error in pixels (and radius r is in pixels) then:
n=M_PI/acos((r-e)/r)
Hopefully I did not make any mistake while deriving the equations directly in SO editor. So if you want really precise circle set e=0.4 [pixels] and you should be fine
[edit1] sin wave
The for loop creates list wit these properties:
d(i) = < 0.0 , 10.0 )
i = { 0,1,2,...83 }
Then the sinwave is rendered:
x(i) = xscale * d(i)
y(i) = yscale * sin(d(i)-time)
Which gives you:
x(i) = < 0.0 , xscale )
y(i) = < -yscale , +yscale )
So the sinwave renders 10/(2*PI)= ~ 1.59 periods. The half overlap is cut off by the view. So in theory you could use 6.28/84 -> 7/84 instead of 10/84 but it is maybe just safety value to handle different aspect ratio seettings of the rendering (I do not code in that platform so this is just speculation on my side) But as I said in the comments the sinwave is scaled so the PI period x size is equal to PI*circle_radius so the 84 most likely comes from the circle (my original answer).
This is possibly just a magic number, that is, completely arbitrary. In fact, as you said, the first thing I thought was that it is related to the width of the graph.
Here is a fiddle: https://jsfiddle.net/1nboube9/1/
You can tweak the number and see what happens. It seems to me that any number above 44 does the trick.
for (i = 0; i < 44; i++) {
data.push(i * 10 / 84);
}
But, of course, the path is not the same if you change the denominator as well:
for (i = 0; i < 44; i++) {
data.push(i * 10 / 44);
}
This creates a very different path. And, so, I tried this:
for (i = 0; i < someNumber; i++) {
data.push(i);
}
And it creates a very unpleasant path. So, I believe that this is what happened: the designer first created data.push(i * 10 / 84); to make the path more circular, and then changed the loop accordingly. Maybe I'm completely wrong, but that's my bet.
How can one calculate the camera distance from an object in 3D space (an image in this case) such that the image is at its original pixel width.
Am I right in assuming that this is possible given the aspect ratio of the camera, fov, and the original width/height of the image in pixels?
(In case it is relevant, I am using THREE.js in this particular instance).
Thanks to anyone who can help or lead me in the right direction!
Thanks everyone for all the input!
After doing some digging and then working out how this all fits into the exact problem I was trying to solve with THREE.js, this was the answer I came up with in JavaScript as the target Z distance for displaying things at their original scale:
var vFOV = this.camera.fov * (Math.PI / 180), // convert VERTICAL fov to radians
var targetZ = window.innerHeight / (2 * Math.tan(vFOV / 2) );
I was trying to figure out which one to mark as the answer but I kind of combined all of them into this solution.
Trigonometrically:
A line segment of length l at a right angle to the view plane and at a distance of n perpendicular to it will subtend arctan(l/n) degrees on the camera. You can arrive at that result by simple trigonometry.
Hence if your field of view in direction of the line is q, amounting to p pixels, you'll end up occupying p*arctan(l/n)/q pixels.
So, using y as the output number of pixels:
y = p*arctan(l/n)/q
y*q/p = arctan(l/n)
l/tan(y*q/p) = n
Linear algebra:
In a camera with a field-of-view of 90 degrees and a viewport of 2w pixels wide, the projection into screen space is equivalent to:
x' = w - w*x/z
When perpendicular, the length of a line on screen is the difference between two such xs so by normal associativity and commutivity rules:
l' = w - w*l/z
Hence:
w - l' = w*l/z
z = (w - l') / (w*l)
If your field of view is actually q degrees rather than 90 then you can use the cotangent to scale appropriately.
In your original question you said that you're using css3D. I suggest that you do the following:
Set up an orthographic camera with fov = 1..179 degrees, where left = screenWidth / 2, right = screenWidth / - 2, top = screenHeight / 2, bottom = screenHeight / - 2. Near and far planes do not affect CSS3D rendering as far as I can tell from experience.
camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);
camera.fov = 75;
now you need to calculate the distance between the camera and object in such way that when the object is projected using the camera with settings above, the object has 1:1 coordinate correspondence on screen. This can be done in following way:
var camscale = Math.tan(( camera.fov / 2 ) / 180 * Math.PI);
var camfix = screenHeight / 2 / camscale;
place your div to position: x, y, z
set the camera's position to 0, 0, z + camfix
This should give you 1:1 coordinate correspondence with rendered result and your pixel values in css / div styles. Remember that the origin is in center and the object's position is the center of the object so you need to do adjustments in order to achieve coordinate specs from top-left corner for example
object.x = ( screenWidth - objectWidth ) / 2 + positionLeft
object.y = ( screenHeight - objectHeight ) / 2 + positionTop
object.z = 0
I hope this helps, I was struggling with same thing (exact control of the css3d scene) but managed to figure out that the Orthographic camera + viewport size adjusted distance from object did the trick. Don't alter the camera rotation or its x and y coordinates, just fiddle with the z and you're safe.