Calculating the height of a CSS 3D rotationX transform - css

I'd like to find out the height of an element, of a specific width and height, after a rotationX transform has been applied to it.
Would be ideal to be able find out the angle of an element's rotationX transform given the desired height.

rotateX() preserves the x-coordinate. The y-coordinate scales with the cosine of the rotation angle:
new_height = old_height * cos(angle)
The other way around is:
angle = arc cos(new_height / old_height)

Found 2 ways on finding dimensions of a DOM element after css 3D transforms have been applied to it.
1.By using element.getBoundingClientRect() which will return a ClientRect object with the correct dimensions of an elements after the transforms has been applied to it
{
bottom: 90.07061767578125
height: 90.07061767578125
left: 0
right: 891
top: 0
width: 891
}
A module called domvertices will return the xyz positions of 4 corner vertices for a DOM element, accounting for any transformatons:
{
a: {x: , y: , z: },
b: {x: , y: , z: },
c: {x: , y: , z: },
d: {x: , y: , z: }
}

Related

Which spatial Data structure to use when Coordinates of the features is updating frequently? I have tried R tree, 2d Grid

I have N numbers of geographical points and coordinates of the points are being updated frequently. And the number N is very big.
I need to find points within a rectangle.
I have tried searching by all points, by using 2d Array grid and R tree.
I had to remove and then insert again which is costly operation.
Indexing spatial data is a complex topic. If you just have points you can index them using a sparse grid (better memory utilization than just 2d array):
class SpatialGrid:
def __init__(self, cell_size):
self._cells: DefaultDict[Tuple, Set] = defaultdict(set)
self._cell_size = cell_size
def __len__(self):
return len(self._cells)
def _key(self, x: float, y: float) -> Tuple:
return int(x / self._cell_size), int(y / self._cell_size)
def add(self, x: float, y: float, obj: object) -> None:
i_x, i_y = self._key(x, y)
for i in (-1, 0, 1):
for j in (-1, 0, 1):
self._cells[(i_x + i, i_y + j)].add(obj)
def get(self, x: float, y: float) -> Set:
return self._cells[self._key(x, y)]
def contains(self, x: float, y: float) -> bool:
return self._key(x, y) in self._cells
if you need to find by rectangle then you can just iterate over grid cells that are in that rectangle
But of course you will need to remove and insert points whenever the coordinates change. Indexing is costly.

QT QMatrix4x4 matrix for scale and rotation with origin x and y

I am working on image transformation,I want to scale and rotate image at the same time from x origin and y origin. I tried to use separate Scale,Rotate but they work one at the same time. here is my code
function setRotation(rotation){
rt.origin.x=imagQuickitem.anchorPoint.x;
rt.origin.y=imagQuickitem.anchorPoint.y;
rt.angle=rotation
image.transform=rt;
image.transform=sc;
}
function setScale(scale){
sc.origin.x=imagQuickitem.anchorPoint.x;
sc.origin.y=imagQuickitem.anchorPoint.y;
sc.xScale=scale;
sc.yScale=scale;
image.transform=sc;
}
Scale { id:sc; }
Rotation { id:rt; }
Well, It seems the solution is QMatrix4x4,I tried to use QMatrix4x4 and found this link
Qt Transform matrix
but I have no idea how to write matrix to work for both scale and rotation, should I multiple scale matrix with rotation matrix?
There are 2 solutions I could think of so far. Solution 1: combine Rotation with Scale. Solution 2: explicitly calculate the transformation matrix. See the following code snippet:
Image {
id: img
source: "Lenna.png"
property real rotD: 45 // angle (degrees)
property real rotR: rotD * Math.PI / 180 // angle (radians)
property real scale: 2 // scaling value
property real x0: 264 // origin.x
property real y0: 264 // origin.y
// solution 1: combine 2 transforms
transform: [
Rotation{origin.x: img.x0; origin.y: img.y0; angle: img.rotD; },
Scale{origin.x: img.x0; origin.y: img.y0; xScale: img.scale; yScale: img.scale}
]
// solution 2: do the math to calculate the matrix
// transform: Matrix4x4 {
// matrix: Qt.matrix4x4(
// img.scale*Math.cos(img.rotR), -img.scale*Math.sin(img.rotR), 0, -img.scale*Math.cos(img.rotR)*img.x0 + img.scale*Math.sin(img.rotR)*img.y0 + img.x0,
// img.scale*Math.sin(img.rotR), img.scale*Math.cos(img.rotR), 0, -img.scale*Math.sin(img.rotR)*img.x0 - img.scale*Math.cos(img.rotR)*img.y0 + img.y0,
// 0, 0, 1, 0,
// 0, 0, 0, 1)
// }
}

How to get coordinates of closest free spot?

I have a space:
[.][.][.][.][.]
[.][.][.][.][.]
[.][.][x][.][.]
[.][.][.][.][.]
[.][.][.][.][.]
Each [.] represents a spot in 2D space. (need it for 3D, but doesn't matter for now)
By [x] I marked "current location" (let's say it's [0, 0, 0])
So I need to find what is the position of closest "not busy" spot.
For example if I had such area:
[.][.][.][.][.]
[.][b][b][b][.]
[.][b][x][.][.]
[.][b][b][b][.]
[.][.][.][.][.]
([b] for "busy")
Then I would need to get a result (x: 0, y: 1, z: 0) (because it's closest one that is free).
For now thinking to do something like this:
current_location = {x: 0, y: 0, z: 0}
radius = 0
closest_record = nil
begin
radius = radius + 1
# some way to iterate over each [x, y, z] spot within that radius until
# closest_record = Record.where(x: x1, y: y1, z: z1).first returns nil (meaning it's free)
end until record.present?
# (that's Ruby syntax, but doesn't matter really)
Is there some formula to do that?
I'm not familiar with Ruby, but in C++, you might do something like this:
for(int x=(radius*-1); x<=radius; ++x) {
for(int y=(radius*-1); y<=radius; ++y) {
for(int z=(radius*-1); z<=radius; ++z) {
if(x==0 && y==0 && z==0) {
continue;
} else {
//check location[x,y,z];
}
}
}
}
Are you asking for the distance formula? Not sure I understand the question, but the distance between two points with coordinates (x1,y1,z1) and (x2,y2,z2) is:
distance = sqrt((x1-x2)^2 + (y1-y2)^2 + (z1-z2)^2)

pixel coordinates on diamond

I got an image with a couple of diamond put side by side like on the image below
The only coordinates I know on the image are the top corners (green text).
When I click on the image I get the coordinates of that point, but I'm not able to get which diamond I'm on.
For example I click on the red dot, how do I know that x:260, y:179 = the top diamond ?
And the blue belongs to the left ? etc...
Thank you very much for your help.
EDIT:
I finally used Canvas, but I think SVG would have worked as well for what I needed to do.
I see two possible approaches: direct check whether a point is inside a diamond and using affine transformations. I will describe both.
Direct point position check
To determine whether a point is inside a diamond you have to check its deviation from the middle point of a diamond. You have to put the X and Y deviations in proportion with the X and Y extents of the diamond, you will get two factors. For all points inside the diamond the sum of the modulo values for these factors is smaller or equal 1. In code this looks like this:
var dx = Math.abs(coords[0] - middle[0]);
var dy = Math.abs(coords[1] - middle[1]);
if (dx / size[0] + dy / size[1] <= 1)
alert("Inside diamond");
else
alert("Outside diamond");
So all you have to do now is determining the middle point for each diamond (size is the same in all cases) and checking whether the point you are testing is located inside them.
Working example: http://jsfiddle.net/z98hr/
Affine transformations
Using affine transformations you can change the corner coordinates of your top diamond into (0,0), (1,0), (0,1) and (1,1). If you then apply the same transformation to the point you need to test, determining which diamond it belongs to becomes trivial.
First you will need a translation vector to move the (225,2) point into the origin of coordinates. Let's say that you have four coordinates determining your top diamond (left and right coordinate, top and bottom coordinate):
var topDiamond = [[113, 2], [337, 227]];
Then the translation vector to move the top point of the diamond to the zero coordinate would be:
var translationVector = [-(topDiamond[0][0] + topDiamond[1][0]) / 2,
-topDiamond[0][1]];
You can apply it to the original coordinates like this:
function add(vector1, vector2)
{
return [vector1[0] + vector2[0], vector1[1] + vector2[1]];
}
topDiamond = [add(topDiamond[0], translationVector),
add(topDiamond[1], translationVector)];
Then you will need a rotation matrix:
var angle = -Math.atan2(topDiamond[1][1] - topDiamond[0][1],
topDiamond[1][0] - topDiamond[0][0]);
var rotMatrix = [[Math.cos(angle), -Math.sin(angle)],
[Math.sin(angle), Math.cos(angle)]];
After the multiplication with this matrix the points (225,2) and (337,114.5) are aligned on the X axis. But what you have now is a trapeze, you now need a horizontal shear transformation to get the other side of the diamond aligned on the Y axis:
function multiply(matrix, vector)
{
return [matrix[0][0] * vector[0] + matrix[0][1] * vector[1],
matrix[1][0] * vector[0] + matrix[1][1] * vector[1]];
}
var point = [topDiamond[0][0], (topDiamond[0][1] + topDiamond[1][1]) / 2];
point = multiply(rotMatrix, point);
var shearMatrix = [[1, -point[0] / point[1]], [0, 1]];
After multiplication with this matrix you have a rectangle now. Now you only need a scaling matrix to make sure that the X and Y coordinates of the corners have the value 0 and 1:
point = multiply(shearMatrix, point);
var point2 = [topDiamond[1][0], (topDiamond[0][1] + topDiamond[1][1]) / 2];
point2 = multiply(rotMatrix, point2);
point2 = multiply(shearMatrix, point2);
var scaleMatrix = [[1/point2[0], 0], [0, 1/point[1]]];
And there you have it, now you can apply these transformations to any point:
alert(
multiply(scaleMatrix,
multiply(shearMatrix,
multiply(rotMatrix,
add(translationVector, [260, 179])
)
)
)
);
This gives you 0.94,0.63 - both values are in the (0..1) range meaning that it is the top diamond. With [420,230] as input you get 1.88,0.14 - X in (1..2) range and Y in 0..1 range means right diamond. And so on.
Working example: http://jsfiddle.net/FzWHe/
In the retrospective, this was probably too much work for a simple geometrical figure like a diamond.
Essentially, what you have there is possibly an isometric view of 4 tiles (based on your comment about the diamonds appearing as trapezoids).
One quick way of doing this is to create 2 lines that are parallel with the "axes" of the "diamonds" (but still are crossing with each other...this is important as well). In the example image given, that would mean two lines that are vertical to each other but rotated by 45 degrees. In the isometric case, the lines will not be vertical to each other but at some other angle depending on your view.
Once you have these two lines you can create a "hitTest()" function that will be taking the coordinates of the point that was clicked and will be evaluating the two line equations. You are not really interested on the actual number returned by the line equations but only the signs. The sign shows you which side of the line does your point resides.
This means that your "diamonds" will correspond to these sign pairs (one sign for each line equation) [-,-], [-,+], [+,-], [+,+].
(Please note that the sign depends on the way that the line was defined, in other words for a given point P, the sign from some line equation (L) will be different if the line was defined as running "from left to right" or "from right to left", or more generally the sign will be the reverse for reciprocal directions.)
A bit more information about the form of the line equation you need can be obtained from here
Using matrices, you can derive a quick formula for which diamond is selected.
You want a transformation from (x,y) into "diamond-space". That is, a coordinate system where (0,0) is the top diamond, (1,0) is the one below to the right, and (0,1) below to the left.
A * x = y
where A is the transformation, x is the image coordinates, and y is the diamond-coordinates. To deal with the translation ((0,0) not being the same point in both spaces), you can add another row to the vectors, which is always 1.
You can transform multiple vectors at the same time, by putting them beside each other, so they form a matrix.
[ a b dx ] [ 225 337 113 ] [ 0 1 0 ]
[ c d dy ] * [ 2 114 114 ] = [ 0 0 1 ]
[ 0 0 1 ] [ 1 1 1 ] [ 1 1 1 ]
^ ^ ^-left ^-^-^--- new coordinates for each point
| '-right
'-top diamond
To solve for the coefficients in the first matrix, you need to divide by the second matrix (or multiply by the inverse).
[ a b dx ] [ 0 1 0 ] [ 225 337 113 ]^-1
[ c d dy ] = [ 0 0 1 ] * [ 2 114 114 ]
[ 0 0 1 ] [ 1 1 1 ] [ 1 1 1 ]
The result is:
[ a b dx ] [ (1/224) (1/224) (-227/224) ]
[ c d dy ] = [ (-1/224) (1/224) (223/224) ]
[ 0 0 1 ] [ 0 0 1 ]
To put this into program code:
function getDiamond(x, y) {
return [(x + y - 227) / 224, (-x + y + 223) / 224];
}
Example:
> getDiamond(260,179); // red
[0.9464285714285714, 0.6339285714285714]
> getDiamond(250,230); // green
[1.1294642857142858, 0.90625]
> getDiamond(189,250); // blue
[0.9464285714285714, 1.2678571428571428]
> getDiamond(420,230); // yellow
[1.8883928571428572, 0.14732142857142858]
If you look at the integer parts, you can see which diamond the coordinate corresponds to. The red one is at (0.94, 0.63) which is in region (0,0) pretty close to the edge of (1,0).
NB. The blue and green points in OP is drawn in the wrong location (or given wrong coordinates), so the result of my function places them in a different relative location.
If you do the calculations symbolically, you end up with this:
[ a b dx ] [ (y2 - y0)/M -(x2 - x0)/M -(x0*y2 - y0*x2)/M ]
[ c d dy ] = [-(y1 - y0)/M (x1 - x0)/M (x0*y1 - y0*x1)/M ]
[ 0 0 1 ] [ 0 0 1 ]
where M = x1*y2 - x2*y1 - y0*x1 + y0*x2 + x0*y1 - x0*y2.
Point 0 being the position of top diamond, point 1 being the position of right diamond, and point 2 being the position of left diamond.
Here is a function to calculate this:
function DiamondMaker(topx,topy, leftx,lefty, rightx,righty)
{
var M = topx*lefty - topx*righty +
leftx*righty - leftx*topy +
rightx*topy - rightx*lefty;
var a = -(topy - righty)/M;
var b = (topx - rightx)/M;
var dx = -(topx*righty - topy*rightx)/M;
var c = (topy - lefty)/M;
var d = -(topx - leftx)/M;
var dy = (topx*lefty - topy*leftx)/M;
return function(x, y) {
return [a * x + b * y + dx, c * x + d * y + dy];
};
}
var getDiamond = DiamondMaker(225,2, 337,114, 113,114);
// (same example as before)
All you need - just stady what is roration. Here is link: http://en.wikipedia.org/wiki/Rotation_(mathematics)
You should rotate you point in order to make sides of squares in parrallel with coordinate's grid. Point of rotaion should be 1 corner of dimonds you will threat as 0,0 diamond. After rotaion you can easily define how many daimond you point away from 0,0

Calculate correct sprite direction image in bird's view game? (Math here might be speed vector to degrees angle?)

Background: I have 8 images for every sprite in my bird's view JavaScript game, representing top, top-right, right, right-bottom etc., depending on the player's space ship speed.
Question: Given the values sprite.speed.x and sprite.speed.y (which could be something like 4 and -2.5, or 2 and 0 for instance), how do I get the correct angle in degrees? Given that angle, I could then have a lookup for which degrees value represents which sprite image. Or perhaps there's an even easier way. (Currently I'm just using something like "if x below zero use left image" etc. which will result in diagonal images used almost all of the time.)
Searching around, I found ...
angle = Math.atan2(speed.y, speed.x);
... but somehow I'm still missing something.
PS: Zero speed can be ignored, these sprites will just use whatever was the last valid direction image.
Thanks so much for any help!
Good question! I liked tom10's answer (on the mark, +1), but wondered if it can be done without much trigonometry. Here's a solution in short, followed by an explanation.
// slope is a constant, 0.414...; calculate it just once
var slope = Math.tan(Math.PI/8);
// do this for each x,y point
var s1 = x * slope + y > 0 ? 0 : 1;
var s2 = y * slope + x > 0 ? 0 : 1;
var s3 = y * slope - x < 0 ? 0 : 1;
var s4 = x * slope - y > 0 ? 0 : 1;
var segment = 4 * s4 + 2 * (s2 ^ s4) + (s1 ^ s2 ^ s3 ^ s4);
This sets the value of segment between 0 and 7. Here's an example with 2000 random points (full source code at the end of the answer). Using the x,y values of the sprite's speed, you can use the segment value to pick up the appropriate sprite image.
Tadaa!
So how does this work? Our segment expression does look a bit cryptic.
Observation one: we want to split the circle around the point into 8 segments of equal angular dimension. 360/8 = 45 degrees per segment. Four of the 8 segments are centered on one of the two sides of the x and y axes, sliced at 45/2 = 22.5 degrees each.
Observation two: The equation of a line on a plane, a*x + b*y + c = 0, when turned into an inequality, a*x + b*y + c > 0 can be used to test on which side of the line a point is located. All our four lines cross the origin (x=0, y=0), and hence force c=0. Further, they are all at a 22.5 degrees angle from either the x or the y axis. This gets us the four line equations:
y = x * tan(22.5); y = -x * tan(22.5);
x = y * tan(22.5); x = -y * tan(22.5)
Turned into inequalities we get:
x * tan(22.5) - y > 0;
x * tan(22.5) + y > 0;
y * tan(22.5) - x > 0;
y * tan(22.5) + x > 0
Testing the inequalities for a given point lets us know on each side of each line it lies:
Observation three: we can combine the test results to obtain the segment number pattern we want. Here's a visual breakdown:
In sequence: 4 * s4, 2 * (s2 ^ s4) and the sum 4 * s4 + 2 * (s2 ^ s4)
(The ^ symbol is the Javascript XOR operator.)
And here is s1 ^ s2 ^ s3 ^ s4, first on its own, and then added to 4 * s4 + 2 * (s2 ^ s4)
Extra credit: can we tweak the calculation to use only integer arithmetic? Yes -- if x and y are known to be integers, we could multiply both sides of the inequalities by some constant (and round off), resulting in completely integer math. (This would be lost, however, on Javascript, whose numbers are always double precision floating point.):
var s1 = x * 414 + y * 1000 > 0 ? 0 : 1;
var s2 = y * 414 + x * 1000 > 0 ? 0 : 1;
var s3 = y * 414 - x * 1000 < 0 ? 0 : 1;
var s4 = x * 414 - y * 1000 > 0 ? 0 : 1;
Full source code for our sample above: (just drop it in a new html file, and open in any browser)
(see as a live demo on jsbin)
<html>
<head>
<style type="text/css">
.dot { position: absolute; font: 10px Arial }
.d0 { color: #FF0000; }
.d1 { color: #FFBF00; }
.d2 { color: #7fcc00; }
.d3 { color: #00FF7F; }
.d4 { color: #00FFFF; }
.d5 { color: #5555FF; }
.d6 { color: #aF00FF; }
.d7 { color: #FF00BF; }
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
var $canvas = $("#canvas");
var canvasSize = 300;
var count = 2000;
var slope = Math.tan(Math.PI/8);
$canvas.css({ width: canvasSize, height: canvasSize });
for (var i = 0; i < count; ++i) {
// generate a random point
var x = Math.random() - 0.5;
var y = Math.random() - 0.5;
// draw our point
var $point = $("<div class='dot'></div>")
.css({
left: Math.floor((x + 0.5) * canvasSize) - 3,
top: Math.floor((y + 0.5) * canvasSize) - 6 })
.appendTo($canvas);
// figure out in what segment our point lies
var s1 = x * slope + y > 0 ? 0 : 1;
var s2 = y * slope + x > 0 ? 0 : 1;
var s3 = y * slope - x < 0 ? 0 : 1;
var s4 = x * slope - y > 0 ? 0 : 1;
var segment = 4 * s4 + 2 * (s2 ^ s4) + (s1 ^ s2 ^ s3 ^ s4);
// modify the point's html content and color
// (via its CSS class) to indicate its segment
$point
.text(segment)
.addClass("d" + segment);
}
});
</script>
</head>
<body>
<div id="canvas" style="position: absolute; border: 1px solid blue">
</div>
</body>
</html>
What you suggest is exactly right! Note that the result of Math.atan2 is in radians, and you're probably more familiar with degrees; you can convert using angle_degrees = angle*(180./pi).
(Note also that you don't need to normalize as RCIX suggested, though you can if you want to. What you have, angle = Math.atan2(speed.y, speed.x);, should work just fine.)
You were on the right track. Normalize your speed vector (check for both components being 0 first) , call atan2 on it, and then convert the radians value you get to some sort of friendly direction enum or something that you can use to pick the right sprite.

Resources