Problem Overview
I'm working on a website that uses CSS/JS to create this isometric, resizable cube.
Through some "logical" experimentation, I was able to determine the constants that allow for the 3 planes to stay connected together as expected:
Here are the constants used specifically
- .7049166488
- .210
- .494916649 (just the difference of the previous two)
I was wondering what the actual formula of these numbers is, given the fact that the faces are squares skewed on 22.5 degree angles. Any help in deriving them would be appreciated :)
Code Walkthrough & Diagrams
As requested, I've done my best to walk through each of the skews and shifts the code performs on each face. Calculations are as follows:
For a given xWidth x, yWidth y and cube height h (this example is performed with $x = 20, y = 40, h = 60$),
- Create a $y * x$ rectangle, this is the top face.
- Create a $x * h$ rectangle, this is the left face.
- Create a $y * h$ rectangle, this is the right face.
- Align all rectangles by their top-right corners.
Note: Here, the right face is the darkest, and the top the lightest.
Then, perform these skews:
- Skew the top and bottom edges of the left face by 22.5 degrees
- Skew the top and bottom edges of the right face by -22.5 degrees (sloping upwards instead of downwards)
- Skew all edges of the top face by 22.5 degrees
- Rotate the top face 45 degrees
Finally, perform these translational operations:
- Shift the entire block down $h + .7049x + .21y$
- Shift the top face left $.5x$
- Shift the left face left $x$
- Shift the left face down $.4949x + .21y$
- Shift the right face down $.7049x$
Code
I've provided my code here in case it helps anyone.
JSX:
import './Block.css';
export default function Block ({xWidth, yWidth, height}) {
return (
<div class="block" style={{marginTop: -height - xWidth*.7049166488 - yWidth*.210, left: 200, top: 200}}>
<figure class="top" style={{width:yWidth, height:xWidth, marginLeft:-1*xWidth/2}}></figure>
<figure class="left" style={{width:xWidth, height:height, marginLeft:-1*xWidth, marginTop:xWidth*(.494916649) + (yWidth)*(.210)}}></figure>
<figure class="right" style={{width:yWidth, height:height, marginTop:xWidth*(.7049166488)}}></figure>
</div>
)
}
CSS:
.block {
position: absolute;
}
.block > figure {
position: absolute;
-webkit-transform-origin: 50% 50% 0;
}
.left {
transform: skewY(22.5deg);
margin: 0;
padding: 0;
background-color: #00E5BB;
}
.right {
transform: skewY(-22.5deg);
margin: 0;
padding: 0;
background-color: #00CBA5;
}
.top {
transform: rotateZ(-45deg) skew(22.5deg, 22.5deg);
margin: 0;
padding: 0;
background-color: #00FFD0;
}




Short summary:
Long answer:
Throughout this post I'll be writing affine transformations as matrices acting on homogeneous coordinates.
$$ \begin{pmatrix}a&b&c\\d&e&f\\0&0&1\end{pmatrix} \cdot\begin{pmatrix}x\\y\\1\end{pmatrix} =\begin{pmatrix}ax+by+c\\dx+ey+f\\1\end{pmatrix} $$
In this notation the one in the last row is just a tool which enables us to express translations in the same framework. By not treating translations as a separate vector addition operation, we can combine multiple operations by simple matrix multiplication.
My coordinate system will have its origin in the top left corner (because all your items are aligned using top and left measurements) and will have the $y$ direction pointing down as is the convention in computer graphics, not up as the convention in mathematics. I'll write $x,y,h$ instead of
xWidth, yWidth, height, and I'll use $t:=\tan(22.5°)=\sqrt2-1$ as another abbreviation.Let's apply this to the left face, to get it into shape but not translate it yet.
$$ \begin{pmatrix} 1 & 0 & \frac x2 \\ 0 & 1 & \frac h2 \\ 0 & 0 & 1 \end{pmatrix}\cdot \begin{pmatrix} 1 & 0 & 0 \\ t & 1 & 0 \\ 0 & 0 & 1 \end{pmatrix}\cdot \begin{pmatrix} 1 & 0 & -\frac x2 \\ 0 & 1 & -\frac h2 \\ 0 & 0 & 1 \end{pmatrix}\cdot \begin{pmatrix} 0 & x & x & 0 \\ 0 & 0 & h & h \\ 1 & 1 & 1 & 1 \end{pmatrix}\\= \begin{pmatrix} 0 & x & x & 0 \\ -\frac t2x & \frac t2x & h+\frac t2x & h-\frac t2x \\ 1 & 1 & 1 & 1 \end{pmatrix} $$
Read this from right to left: the rightmost matrix consists of four columns representing the four corners of the rectangle, with a constant $1$ added in the third row. Then we apply a transformation which shifts the center of the rectangle to the origin of the coordinate system, in line with the default transform-origin (and your explicit webkit setting). Then you apply the skewY using the matrix straight from the documentation, and then you shift the origin back to where it was. The end result is again a matrix with four columns representing the four transformed corners, where you notice a change in the vertical position of all four corners.
Same for the right face:
$$ \begin{pmatrix} 1 & 0 & \frac y2 \\ 0 & 1 & \frac h2 \\ 0 & 0 & 1 \end{pmatrix}\cdot \begin{pmatrix} 1 & 0 & 0 \\ -t & 1 & 0 \\ 0 & 0 & 1 \end{pmatrix}\cdot \begin{pmatrix} 1 & 0 & -\frac y2 \\ 0 & 1 & -\frac h2 \\ 0 & 0 & 1 \end{pmatrix}\cdot \begin{pmatrix} 0 & y & y & 0 \\ 0 & 0 & h & h \\ 1 & 1 & 1 & 1 \end{pmatrix}\\= \begin{pmatrix} 0 & y & y & 0 \\ \frac t2y & -\frac t2y & h-\frac t2y & h-\frac t2y \\ 1 & 1 & 1 & 1 \end{pmatrix} $$
And lastly the top face, where you apply a skew followed by a rotate (although you used rotateZ for some reason).
For that one I use yet another abbreviation: $s:=\sin(45°)=\cos(45°)=\frac1{\sqrt2}=\frac12\sqrt2$. With this abbreviation, the matrix for the rotation alone becomes
$$ \begin{pmatrix} \cos(-45°) & -\sin(-45°) & 0 \\ \sin(-45°) & \cos(-45°) & 0 \\ 0 & 0 & 1 \end{pmatrix}= \begin{pmatrix} s & s & 0 \\ -s & s & 0 \\ 0 & 0 & 1 \end{pmatrix} $$
And then transforming four corners of a rectangle in a chain, with skew before rotation because it is written on the right both here in the matrix multiplication and in your transform property:
$$ \begin{pmatrix} 1 & 0 & \frac y2 \\ 0 & 1 & \frac x2 \\ 0 & 0 & 1 \end{pmatrix}\cdot \begin{pmatrix} s & s & 0 \\ -s & s & 0 \\ 0 & 0 & 1 \end{pmatrix}\cdot \begin{pmatrix} 1 & t & 0 \\ t & 1 & 0 \\ 0 & 0 & 1 \end{pmatrix}\cdot \begin{pmatrix} 1 & 0 & -\frac y2 \\ 0 & 1 & -\frac x2 \\ 0 & 0 & 1 \end{pmatrix}\cdot \begin{pmatrix} 0 & y & y & 0 \\ 0 & 0 & x & x \\ 1 & 1 & 1 & 1 \end{pmatrix}\\= \begin{pmatrix} -\frac12\,x & -\frac12\,x+y & \frac12\,x+y & \frac12\,x \\ (1-s)\,x+(-\frac12+s)\,y & (1-s)\,x+(\frac12-s)\,y & s\,x+(\frac12-s)\,y & s\,x+(-\frac12+s)\,y \\ 1 & 1 & 1 & 1 \end{pmatrix} $$
Note that I used the actual values of $s$ and $t$ to combine these and express the above in terms of $s$ only. Done symbolically the $t$ wouldn't simply cancel out, you need the relationship between these two to get rid of one. Specifically $t=\sqrt2-1=2s-1$.
Now we want to align all of these. Since the bottom front corner is to remain fixed, let's make that the origin. So we want the third column (bottom right corner) of the left face to move to that origin, which is a translation by
$$ \begin{pmatrix} -x\\ -h-\tfrac t2x \end{pmatrix} $$
The relevant factor there in front of the $x$ is
$$\tfrac t2=\tfrac12\tan(22.5°)=\tfrac12(\sqrt2-1)\approx 0.2071\approx 0.7049166488-0.494916649$$
So this roughly matches the combined
margin-topof theblockandleftelements, noticing that theyWidthterms in your style cancel each other out.To move the right face we move the fourth column (bottom left corner) to the origin, using a translation by
$$ \begin{pmatrix} 0 \\ -h+\frac t2y \end{pmatrix} $$
The relevant term in front of the $y$ is again
$$\tfrac t2=\tfrac12\tan(22.5°)=\tfrac12(\sqrt2-1)\approx 0.2071\approx 0.210$$
That is the term you use for the combined
margin-topofblockandright, noticing that thexWidthterms cancel each other out.Now for the top face. We want the fourth column (originally the bottom left corner, but bottom corner after the transformations) to move to $(0,-h)$. We achieve that using a translation by
$$ \begin{pmatrix} -\frac12x \\ -h-sx+\left(\tfrac12-s\right)y \end{pmatrix} = \begin{pmatrix} -\frac12x \\ -h-\tfrac12\sqrt2x-\tfrac12(\sqrt2-1)y \end{pmatrix} $$
The factor $\tfrac12(\sqrt2-1)\approx0.210$ we already discussed above. The new term is
$$\tfrac12\sqrt2\approx0.7049166488$$
where the approximate value is from your source; the last number of digits is wrong.
Note that you might be able to avoid a lot of this hassle by picking a transformation origin in such a way that it allows you exact placement of either the top front or the bottom front corner of the cube. Having the origin in the center means you have to account for the transformations for every corner, there is none that remains unaffected.
You might be wondering where the exact equation for $\tan(22.5°)=\sqrt2-1$ is coming from. There are many ways to derive that. https://en.wikipedia.org/wiki/List_of_trigonometric_identities has formulas for half or double angles which you can use. Personally I came from the inscribed angle theorem. The chord on the unit circle between $A=(1,0)$ and $B=(\frac12\sqrt2,\frac12\sqrt2)$ is subtended by a central angle of $45°$. In other words for $O=(0,0)$ you get $\measuredangle AOB=45°$. According to the theorem, the inscribed angle is half that, which you get at any point on the circle, specifically at the point $C=(-1,0)$. So $\measuredangle ACB=22.5°$. Since the edge $AC$ is horizontal, the tangens of that angle is just the slope of the line $CB$ which is
$$t= \frac{\frac12\sqrt2}{1+\frac12\sqrt2}= \frac{\frac12\sqrt2\left(1-\frac12\sqrt2\right)}{\left(1+\frac12\sqrt2\right)\left(1-\frac12\sqrt2\right)}= \frac{\frac12\sqrt2-\frac12}{1-\frac12}= \sqrt2-1$$