Convert Field of View value, to CSS3D perspective value - javascript

If I would like to have a camera, with 60° fov, then how to calculate the CSS3D perspective value?
perspective: ?;
perspective-origin: center center;
I've found a description about, how to calculate the projection matrix from perspective value, but I still don't really understand it: http://www.w3.org/TR/css-transforms-1/#perspective-matrix-computation
So if I have a given Field of View, and I know the element's offsetWidth/offsetHeight, then how should I calculate the needed perspective value?
And where are the near plane and the far plane?

I asked a similar question a few years ago and got the response below. The article linked has since changed, so I'm quoting the text since it no longer exists in the article (but there may still be other useful info).
If I'm reading it correctly, you have a pyramid with the base at [perspective px] away from the viewer. So if you want a 60° fov, you have a 30° triangle from the center to the corners and you need to find the length of the adjacent edge:
Math.pow( w/2*w/2 + h/2*h/2, 0.5 ) / Math.tan( 30 * Math.PI / 180 )
I think ;) It yields a perspective of 968 for a 1000x500 view, which seems about right from having played randomly with -webkit-perspective a fair bit
The CSS 3D Transforms Module working draft gives the following explanation:
perspective(<number>)
specifies a perspective projection matrix. This matrix maps a viewing cube onto a pyramid whose base is infinitely far away from the
viewer and whose peak represents the viewer's position. The viewable
area is the region bounded by the four edges of the viewport (the
portion of the browser window used for rendering the webpage between
the viewer's position and a point at a distance of infinity from the
viewer). The depth, given as the parameter to the function, represents
the distance of the z=0 plane from the viewer. Lower values give a
more flattened pyramid and therefore a more pronounced perspective
effect. The value is given in pixels, so a value of 1000 gives a
moderate amount of foreshortening and a value of 200 gives an extreme
amount. The matrix is computed by starting with an identity matrix and
replacing the value at row 3, column 4 with the value -1/depth. The
value for depth must be greater than zero, otherwise the function is
invalid.

Related

three.js warped boxhelper and inaccurate object position

This THREE.BoxHelper is wildly inaccurate, and the position of the cube is not accurate when drawing a line to it!?
See the proof of concept JSFiddle: https://jsfiddle.net/can35bj0/15/
cubeBox = new THREE.BoxHelper(cube, 0xffff00)
scene.add(cubeBox);
cube.position.copy(positionVector);
cubeTrace.geometry.vertices[cubeTrace.geometry.vertices.length - 1].copy(cube.position);
Why is this, and is there a way to fix this? So far I've come up empty...
p.s. scales and position need to be small vs. large
I've more or less concluded that this is due to a 'long vector' problem of THREE.js.
When an object (such as the square cube in the JSFiddle) is on a long arm from Origin compared to it's size, in this case the positionVector is roughly 100,000 units and the cube size is 0.001 units, positions become erratic and fluctuating as can be seen by the weird behavior of the Boxhelper. (Note that the cube is set to move slightly every second and the camera moves with it)

Procedural terrain generation with blocks

I am using three.js to create procedurally generated terrain using Perlin Noise.
I am creating the terrain using a series of blocks, but their heights along their borders are not corresponding to one another as you can see below.
How should I approach matching the height maps across blocks?
I'm using Perlin Noise Algorithm for generating heights; the problem is that the height of each point is indipendent from the heights of the near points. I've other noise algorithm, but i have the same problem..
There's a really good video on infinite terrain here: https://www.youtube.com/watch?v=IKB1hWWedMk
It's in processing, but the same concept can be applied to whichever noise library you're using - I'm going to assume that you're using Perlin noise. In which case, you need to look at the values you're passing into this function and change them based on how big your blocks are.
For example, imagine a 3x3 grid of blocks. If your middle block is (x, y), and each block is 10x10 units in size, if you move 'north' (for lack of a better term), you'd need to be getting (x, y - 10) from your noise function.
The video explains it way better than I can, but hopefully this has helped. Without more knowledge of the function you're using I can't really give a more detailed answer.
This answer will explain how to solve it for a single axis, x. It is then trivial to do the same for the y (z in three.js) axis.
The first step is to ensure the perlin noise is using the same random seed for each block. This will ensure that blocks share the same perlin noise map and so can transition smoothly between them.
The second part is to have a mapping between your block units and what is passed into the perlin noise function. For example your block x may be going from -512 to 512 units, so you get a height value for each x vertex by passing in -0.5 to 0.5 for each x vertex into the noise function.
E.g.
vertextHeight = perlin(vertexX / 1024, vertextY / 1024)
Your second block will then be offset so its edge interfaces with the first block. E.g. its x position will be +1024 more than the first block, and so will go from 512 to 1536.
So in this sense, block0 will have an x offset of 0, and block1 will have an x offset of 1024. 1024 being the block width/size in three.js units.
Finally, you need to give the same offsets to the noise function, but scaled based on the mapping described above. In this example, 512 would become 0.5 and 1536 would become 1.5 which looks like this:
size = 1024;
vertextHeight = perlin((vertexX + offsetX) / size, (vertextY + offsetY) / size)`
Therefore, the x value given to the noise function at the edge between block0 and block 1 will be the same, and so will return the same height value.

Understanding Matrix in SVG

I need help in deep understanding of matrix in SVG. I already know about matrix, I want to rotate and scale without using scale or rotate word. I want to use transform='matrix(a,b,c,d,e,f)'. I know 'a/d' value determine the scale, 'e/f' determines the position. tan(b),tan(c) determines the skew. cos(a),sin(b),-sin(c),cos(d) determines the angle.But I want to know how this work, I need thoroughly help in understanding matrix in SVG.
Matrix operations are composed of individual, "local" transformations (i.e. translate, rotate, scale, skew) by matrix concatenation (i.e. multiplication).
For example, if you want to rotate an object by r degrees around a point (x, y), you would translate to (x, y), rotate r degrees, then translate back to the original position (-x, -y).
By what is often referred to as "chaining" (as described above) each successive "local" transformation is combined to produce a result. Therefore, at any location in a chain of transformations, the "local" transformation space (at that location) is composed of all operations that came before.
What this implies is that when transforming some parameter of an SVG element (i.e. translate) the transform is applied to it's current transformation space. So, for example if the element is already rotated 30 degrees, then a translation of (8, 5) would not go 8 to the right and 5 down, but it would go the rotation of (8, 5) by 30 degrees - relative to the current position.
So this is a bit of a gotcha.
One way to help deal with this complication is to decompose transformation matrices into their individual, total transformations (i.e. total translation, total rotation/skew, total scale), but decomposition says nothing about what individual basic transformations went into the combined totals, nor of the order in which they occurred. This is a problem because 2D transformations are not commutative, e.g. translate(x, y)->rotate(r) is not the same as rotate(r)->translate(x, y).
The best way that I've found is to only compose transformations in a certain order and keep track of the totals in that order, then when a new transformation is introduced, used the totals that have been tracked, update the one that is being modified and recompose the entire transformation.
Like so: (pseudo-code)
// EDIT: initialize components (new SVGMatrix returns the identity matrix)
var transX=0, transY=0, rot=0, scaX=0, scaY=0, skwX=0, skwY=0, matrix = new SVGmatrix();
// example rotate
function rotate(svgEl, angle){
rot = rot + angle;
updateTransform();
applyTransform(svgEl);
};
function updateTransform(){
// the order that I've found most convenient
// (others may do it differently)
matrix.translate(transX, transY);
matrix.rotate(rot);
matrix.scale(scaX, scaY);
matrix.skewX(skwX);
matrix.skewY(skwY);
};
function applyTransform(el){
el.transform = matrix;
};
To be clear, this is not suggesting that matrices are not a good way of representing transformations, nor is it suggesting a better way - far from it.
Transformation matrices are a powerful tool, and when used appropriately, they are very effective at handling complex animations, but they are not trivial to use in all cases.
This may be a bit advanced, but for more information about animations using matrix transformations, this short code example provides a wealth of information and references to start from.
http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#matrix-decomposition
Update:
Just a note about the decomposed skew factor proposed at the above link.
Only a single skew factor ( in x ) is computed because skewing in both x and y is equivalent to a skew in x and a combined ( offset ) rotation.
Combining x skew and y skew ( with or without a rotation or translation, as in my above preferred composition order ) will result in a different x skew, rotation ( e.g. non-zero rotation if none was originally composed ), and translation ( e.g. an offset by some amount relative to the decomposed rotation in lieu of the original y skew ), but no recoverable y skew - using the linked decomposition method.
This is a limitation of composed affine matrices. So producing a final result matrix should generally be considered a one-way computation.

2d perspective skew in JS

I want to transform an image in 2 points perspective.
I guess I need to transfer to JS a formula from: http://web.iitd.ac.in/~hegde/cad/lecture/L9_persproj.pdf
But I'm humanities-minded person and I faint when I see matrices.
Here's what I need exactly:
I have a two vanishing points: X(X.x, X.y) and Z(Z.x, Z.y). And rectangle ABCD (A.x, A.y and so on)
(source: take.ms)
And I want to find new nA, nB, nC and nD points with which I can transform my rectangle like that (the points order doesn't really matter):
(source: take.ms)
Right now I'm doing weird approximate calculations: I'm looking for most distant point from X (1), then lay over an interval towards Z (2), than another interval towards X (3) and then again from Z (4):
(source: take.ms)
The result is a bit off but is alright for the precision I need, but this algorithm sometimes gives very weird results if I change vanishing points, so if there's a proper solution I'll gladly use it. Thanks!

How to subdivide a shape into sections of a given size

I'm currently trying to build a kind of pie chart / voronoi diagram hybrid (in canvas/javascript) .I don't know if it's even possible. I'm very new to this, and I haven't tried any approaches yet.
Assume I have a circle, and a set of numbers 2, 3, 5, 7, 11.
I want to subdivide the circle into sections equivalent to the numbers (much like a pie chart) but forming a lattice / honeycomb like shape.
Is this even possible? Is it ridiculously difficult, especially for someone who's only done some basic pie chart rendering?
This is my view on this after a quick look.
A general solution, assuming there are to be n polygons with k vertices/edges, will depend on the solution to n equations, where each equation has no more than 2nk, (but exactly 2k non-zero) variables. The variables in each polygon's equation are the same x_1, x_2, x_3... x_nk and y_1, y_2, y_3... y_nk variables. Exactly four of x_1, x_2, x_3... x_nk have non-zero coefficients and exactly four of y_1, y_2, y_3... y_nk have non-zero coefficients for each polygon's equation. x_i and y_i are bounded differently depending on the parent shape.. For the sake of simplicity, we'll assume the shape is a circle. The boundary condition is: (x_i)^2 + (y_i)^2 <= r^2
Note: I say no more than 2nk, because I am unsure of the lowerbound, but know that it can not be more than 2nk. This is a result of polygons, as a requirement, sharing vertices.
The equations are the collection of definite, but variable-bounded, integrals representing the area of each polygon, with the area equal for the ith polygon:
A_i = pi*r^2/S_i
where r is the radius of the parent circle and S_i is the number assigned to the polygon, as in your diagram.
The four separate pairs of (x_j,y_j), both with non-zero coefficients in a polygon's equation will yield the vertices for the polygon.
This may prove to be considerably difficult.
Is the boundary fixed from the beginning, or can you deform it a bit?
If I had to solve this, I would sort the areas from large to small. Then, starting with the largest area, I would first generate a random convex polygon (vertices along a circle) with the required size. The next area would share an edge with the first area, but would be otherwise also random and convex. Each polygon after that would choose an existing edge from already-present polygons, and would also share any 'convex' edges that start from there (where 'convex edge' is one that, if used for the new polygon, would result in the new polygon still being convex).
By evaluating different prospective polygon positions for 'total boundary approaches desired boundary', you can probably generate a cheap approximation to your initial goal. This is quite similar to what word-clouds do: place things incrementally from largest to smallest while trying to fill in a more-or-less enclosed space.
Given a set of voronio centres (i.e. a list of the coordinates of the centre for each one), we can calculate the area closest to each centre:
area[i] = areaClosestTo(i,positions)
Assume these are a bit wrong, because we haven't got the centres in the right place. So we can calculate the error in our current set by comparing the areas to the ideal areas:
var areaIndexSq = 0;
var desiredAreasMagSq = 0;
for(var i = 0; i < areas.length; ++i) {
var contrib = (areas[i] - desiredAreas[i]);
areaIndexSq += contrib*contrib;
desiredAreasMagSq += desiredAreas[i]*desiredAreas[i];
}
var areaIndex = Math.sqrt(areaIndexSq/desiredAreasMagSq);
This is the vector norm of the difference vector between the areas and the desiredAreas. Think of it like a measure of how good a least squares fit line is.
We also want some kind of honeycomb pattern, so we can call that honeycombness(positions), and get an overall measure of the quality of the thing (this is just a starter, the weighting or form of this can be whatever floats your boat):
var overallMeasure = areaIndex + honeycombnessIndex;
Then we have a mechanism to know how bad a guess is, and we can combine this with a mechanism for modifying the positions; the simplest is just to add a random amount to the x and y coords of each centre. Alternatively you can try moving each point towards neighbour areas which have an area too high, and away from those with an area too low.
This is not a straight solve, but it requires minimal maths apart from calculating the area closest to each point, and it's approachable. The difficult part may be recognising local minima and dealing with them.
Incidentally, it should be fairly easy to get the start points for the process; the centroids of the pie slices shouldn't be too far from the truth.
A definite plus is that you could use the intermediate calculations to animate a transition from pie to voronoi.

Categories