Easily manage projects with monday.com

I have recently found myself wanting to tightly pack an SVG shape into an HTML container. By tight packing, I mean I want the outermost points of my shape to be right on the edges on the container, without anything going out of the container or getting cut off.

The following demo shows an example of this:

See the Pen 4 point star - tight fitted into container by Ana Tudor (@thebabydino) on CodePen.

The outermost vertices of this four-point star are situated on the edges of its container. We have made the SVG fully cover its parent, and we have picked the coordinate system and the coordinates of the star's points such that the `(0,0)`

point of the SVG canvas is right in the middle and the star's outermost vertices are on the axes of this coordinate system at the edges of the container. The demo below illustrates how the code works in this case:

See the Pen 4 point star - visual code explanation by Ana Tudor (@thebabydino) on CodePen.

Now let's say we don't want it to have a fill, but a stroke of variable width. Drag the slider to adjust the star's `stroke-width`

:

See the Pen 4 point star - variable stroke by Ana Tudor (@thebabydino) on CodePen.

As the demo above shows, increasing the `stroke-width`

creates a problem: the stroke extends both inside and outside the initial boundaries of the four-point star, making it increase in size and not fit inside its container anymore. It's obvious that we either need to adjust the position of the vertices or change the `viewBox`

attribute on our `<svg>`

element.

### Solution: adjusting the `viewBox`

The following demo illustrates how adjusting the `viewBox`

would work:

See the Pen 4 point star - adjusting the viewBox by Ana Tudor (@thebabydino) on CodePen.

Dragging the slider thumb to the left decreases the `viewBox`

width and height, keeping them equal at all times, and changes the coordinates of the top left corner of the SVG canvas such that its `(0, 0)`

point is always right in the middle. This is like a "zooming in" effect.

Dragging the slider thumb to the right increases the `viewBox`

width and height, keeping them equal at all times, and changes the coordinates of the top left corner of the SVG canvas such that its `(0, 0)`

point is always right in the middle. This is like a "zooming out" effect.

In our case, in order to fit our four-point star after increasing its `stroke-width`

, we need to zoom out, so we need to increase the `viewBox`

dimensions. But, by how much do we need to increase them in order to have the star just touching the edges? If we don't zoom out enough, then our star still gets cropped. If we zoom out too much, then it won't touch the edges anymore. So, what are the `viewBox`

dimensions for the right amount of zoom and how do we get them?

In order to figure this out, we need to pick an exact value we want to increase the `stroke-width`

to—let's say `20`

. We'll now take a closer look at what happens around the outer vertices after increasing the `stroke-width`

to `20`

:

In the figure above, we have our star represented twice, both before and after increasing the `stroke-width`

. We can see that, after increasing the `stroke-width`

, our star extends outside the initial boundaries by a distance *d* in all directions. This means that we should increase the viewbox dimensions by twice this distance *d*.

All right, but how can we get *d*? Well, in order to do this, we'll focus on just one of the outer vertices, let's say the point at `(250,0)`

.

We'll call this point *P*. We'll also consider another point, *Q*, which is the rightmost point of the star after we have increased its `stroke-width`

. It's situated on the `x`

axis, just like *P*, only at a distance *d* to the right of *P*. If we draw a perpendicular through *P* onto one of the star's edges, we get the right triangle *PQR*, where *PQ* is equal to *d* and *PR* is equal to half the known `stroke-width`

we have applied.

In this right triangle, *PQ* is the hypotenuse, and the sine of the angle *α* is the ratio between *PR* and *PQ*. *PR* is equal to half of our known `stroke-width`

(`20/2`

= `10`

), while *PQ* is *d*. So, from here we get that *d* equals the ratio between half the `stroke-width`

(`10`

) and the sine of the angle *α*.

But, it looks like we just replaced one unknown with another, because we don't know α. However, we can compute it.

Our star is symmetrical with respect to the axes of coordinates, meaning that any of the two axes split our star into two perfectly mirrored halves. In addition to this, when a transversal line cuts two or more parallel lines, the corresponding angles (the angles occupying the same position at each intersection) are equal, as the demo below shows:

See the Pen corresponding angles by Ana Tudor (@thebabydino) on CodePen.

All these mean that, in the figure below, all marked angles are equal. Those above the `x`

axis are each equal to the ones right below them because the star is symmetrical. All those on one side of the axis are again equal, as the dotted lines on the same side of the axis are parallel (and the transversal line is the `x`

axis).

Since all these angles are equal, we can compute α from another right triangle—the one formed by the point *P*, the next vertex of our star (that we'll call *S*), and its projection onto the `x`

axis (that we'll call *T*). The projection of a point onto a line is obtained at the intersection between the line and a perpendicular on it drawn through the point.

We know the coordinates of these three points. From the code we have used to create the star, point *P* is situated at `(250,0)`

, and point S is situated at `(20,20)`

.

Point *T*, the projection of point *S* onto the `x`

axis, has the same `x`

coordinate as *S*, while its `y`

coordinate is `0`

. This means that *T* is situated at `(20,0)`

.

Knowing these coordinates, we can compute the lengths of the *ST* and *PT* segments. The length of the vertical *ST* segment is `20`

, while the length of the horizontal *PT* segment is `250 - 20 = 230.`

The *PST* triangle is a right triangle, so the tangent of *α* is the ratio between the *ST* and the *PT* segments. Both these segments are known, which means we can compute *α* as the arctangent of their ratio: `atan(20/230)`

.

Now that we have computed *α*, we can replace it in the relation that gives us the distance *d* we were looking for: `10/sin(atan(20/230)) ≈ 115`

(by the way, I simply googled the result of that). This means that the coordinates of the top left point will be `(-250 - 115,-250 - 115) = (-365, -365)`

and that the viewbox width and height will be `500 + 2*115 = 500 + 230 = 730.`

In this case, after changing the `viewBox`

(and leaving everything else unchanged), our code becomes:

```
<svg viewBox='-365 -365 730 730'>
<polygon points='250,0 20,20 0,250 -20,20 -250,0 -20,-20 0,-250 20,-20'/>
</svg>
```

You can see the result in the pen below:

See the Pen 4 point star - viewBox correction applied by Ana Tudor (@thebabydino) on CodePen.

### Problems

You've probably realized the demo above doesn't look right. The star still looks cropped, even if it now fits inside the visible part of the SVG canvas. Why is this happening, and how can we fix it?

`stroke-linejoin`

Well, whenever we have a shape made up of various segments that meet at corners, we have a few options about how that's going to look. These options are controlled by a property called `stroke-linejoin`

. This property can take one of the following four values:

`miter`

: (the default) — the segments meet at a sharp angle`round`

: the corner is rounded`bevel`

: looks something like miter, except the vertex is cut off`inherit`

: whatever value was used for its parent group

This pen provides some actual examples:

See the Pen stroke-linejoin values by Ana Tudor (@thebabydino) on CodePen.

Now if we look at the pen above carefully, we'll notice something interesting about the last join on the first column. It has `stroke-linejoin: miter`

, but the visual result looks different from those of the other joins on the first column (the `miter`

column). In fact, it looks just the result we get for the last join on the last column (the `bevel`

column). And, it looks just like the problem we have with the star at this point. Our line join *should* be a `miter`

because we haven't set `stroke-linejoin`

to anything else. But, what we see is a `bevel`

.

`stroke-miterlimit`

This is because of a property called `stroke-miterlimit`

. As the name may suggest, this property imposes a limit on how much the miter can extend. When this limit is exceeded, the join is simply converted from a miter to a bevel.

*Note: SVG2 specifies that line-join will get a couple more possible values, one of them being miter-clip, which will make the join be clipped at the limit value and not to the bevel.*

The demo below shows that the smaller the angle between two segments gets, the more the `miter`

would normally extend *if* the set `stroke-miterlimit`

was high enough so that it never got converted to a `bevel`

. It also shows how the angle at which the `miter`

is converted to a bevel *decreases* as the value of `stroke-miterlimit`

*increases*:

See the Pen stroke-miterlimit by Ana Tudor (@thebabydino) on CodePen.

All right, but what's the connection between the `stroke-miterlimit`

and the actual miter length? In the previous demo, the values of the `stroke-miterlimit`

range from `2`

to `15`

, while the actual miter lengths are over `50`

and can go well into the hundreds. The miter length also depends on the `stroke-width`

, so it's pretty obvious that the imposed limit is not on the actual miter length itself. Instead, this limit is on the ratio between the miter length and the stroke width. And, as the following image shows, this ratio is equal to 1 over the sine of half the angle between the lines that are joined.

The sine of an acute angle (`< 90°`

) increases as the angle increases, so 1 over the sine (the ratio on which we impose the limit set by `stroke-miterlimit`

) is going to increase as the angle decreases.

See the Pen

How sin(θ) and 1/sin(θ) change with θ by Ana Tudor (@thebabydino) on CodePen.

This kind of explains why the join transforms from `miter`

to `bevel`

. If the angle gets *really* close to `0`

, then its sine also gets really close to `0`

, making the inverse of this value a very large number and having the miter extend a lot. And, while I don't have any actual metrics on that, I'm thinking it can't possibly be good for performance.

### Fixing the star demo

Now coming back to our star, its angles are not that close to zero and all we need to do in order to prevent the joins from being transformed into bevels is set `stroke-miterlimit`

to 1 over the sine of the α angle we had previously computed. α was `atan(20/230)`

, so the value we want is `1/sin(atan(20/230)) ≈ 11.5`

and we'll set `stroke-miterlimit: 12`

just to be sure. The following pen shows how simply adding this one line to the CSS fixes everything.

See the Pen 4 point star - viewBox & stroke-mitterlimit corrections applied by Ana Tudor (@thebabydino) on CodePen.

*Note: If we don't feel like doing all these computations, then we can simply set stroke-miterlimit to a ridiculously high value. Just how high? Well, 60 is enough for a 2° angle between the lines that are being joined, and it's pretty unlikely we'll often need more than that.*

### Problems

All right, but this method of refitting the star inside its container by changing the `viewBox`

and therefore zooming out has also made the stroke look thinner than we initially intended it to be. We could try to fix this with `vector-effect: non-scaling-stroke`

, but this only creates more problems as the stroke width won't scale anymore as the SVG's container, and consequently the SVG itself, change size.

So if we want to solve this, we have to leave the `viewBox`

unchanged and just tweak the coordinates of our star's vertices.

### Solution: tweaking the coordinates of the vertices

We know by how much the star expands outwards if we set `stroke-width: 20`

. We have computed this distance *d* to be about `115`

. This means we have to move the outermost vertices inside by `115`

, from `250`

along the axes to `250 - 115 = 135`

. We can't change just the coordinates of our outermost vertices, as that would distort our star and, the same time, changing the angles would change how much the miter extends, and the star wouldn't be tightly packed anymore anyway.

See the Pen 4 point star - adjusting the outer vertices by Ana Tudor (@thebabydino) on CodePen.

Instead, what we need to do is first compute a scaling factor which will be the ratio between the new non-zero coordinate of an outermost vertex and the old one: `135/250 = .54`

. Then we multiply the old coordinates of the other vertices by this factor in order to get their new coordinates `(20*.54 = 10.8)`

.

See the Pen 4 point star - adjusting all vertices by Ana Tudor (@thebabydino) on CodePen.

And... this is it! You can see the final demo here:

See the Pen 4 point star - vertex corrections applied by Ana Tudor (@thebabydino) on CodePen.

### Future Solution: `stroke-alignment`

Of course, the future should bring a lot of good things, including avoiding all the pain that these computations might cause for some. SVG2 will allow us to specify a `stroke-alignment`

— that's right, controlling whether increasing the `stroke-width`

will make the stroke expand inwards, outwards or on both sides of the element's outline is going to be posible with just one line of code.

In our case, that would be `stroke-alignment: inner`

. And poof! No other tricks required, no zooming by tweaking the `viewBox`

, no repositioning of the points of our shape... none of that anymore.

But, in the meanwhile, let's see what I've been using this for.

### Use case: 3D!

More than two years ago, I started playing with CSS 3D transforms in order to build various 3D shapes. The non-rectangular flat faces (with three, five, six, or ten edges) were created by nesting 2D transformed HTML elements and cutting out unwanted parts with `overflow: hidden`

. Over the past few months, I've also started playing with SVG and began thinking about redoing those 3D shapes, this time using an SVG `<polygon>`

for the non-rectangular faces.

*Note: If you need a basic geometry refresher, this Pen explains what a polygon is and gives some examples.*

This is where I ran into the problem presented above, because how much I move the 2D faces in 3D depends on their 2D dimensions. So, the simplest solution to that in my mind was to have the circumcircle (the circle on which all the vertices are situated) of my regular polygons dead in the middle of the SVG, and at least one of its vertices right on the edge. Since the SVG element would have the same dimensions as its HTML container, this would mean that half the dimension of the HTML container would be equal to the circumradius of the SVG polygon inside.

*Note that not every polygon has a circumcircle (but all regular polygons and all triangles do).*

So let's see how I dealt with it all by creating the simplest 3D shapes—prisms! Prisms are polyhedra (3D solids with flat faces and straight edges) with two *n*-polygonal base faces, connected by *n* quadrilateral faces. For simplicity, we'll consider all the lateral faces rectangular and the base polygons regular (all angles are equal and all edge lengths are equal). The demo below allows creating and also flattening a number of prisms.

See the Pen build a prism by Ana Tudor (@thebabydino) on CodePen.

The rectangular faces are easy to get. HTML elements are rectangles by default, so we'll just use one element for each rectangular face. But what about the bases?

Well, for each base, we'll take a square (equal `width`

and `height`

, preferably in `vmin`

units, so that they scale with the viewport) HTML element with an SVG inside. The `<svg>`

element covers its container completely (`width`

and `height`

are both set to be `100%`

), and the `polygon`

inside touches the edges, but nothing gets cut out. Since the two bases are identical, we just define the `polygon`

once and then reference it for each base.

```
<svg height='0' width='0'>
<defs>
<polygon id='basepoly'/>
</defs>
</svg>
<div class='prism'>
<div class='prism__face--base'>
<svg>
<use xlink:href='#basepoly'/>
</svg>
</div>
<div class='prism__face--base'>
<svg>
<use xlink:href='#basepoly'/>
</svg>
</div>
<div class='prism__face--lateral'></div>
<div class='prism__face--lateral'></div>
<div class='prism__face--lateral'></div>
</div>
```

However, in this case we'd end up setting the same `viewBox`

on both SVG elements inside the base faces so a small improvement over this version would be to use `<symbol>`

, so that we only have to set the `viewBox`

in one place (on the `<symbol>`

element).

```
<svg height='0' width='0'>
<symbol id='basepoly'>
<polygon/>
</symbol>
</svg>
<div class='prism'>
<div class='prism__face--base'>
<svg>
<use xlink:href='#basepoly'/>
</svg>
</div>
<div class='prism__face--base'>
<svg>
<use xlink:href='#basepoly'/>
</svg>
</div>
<div class='prism__face--lateral'></div>
<div class='prism__face--lateral'></div>
<div class='prism__face--lateral'></div>
<!-- more lateral faces if needed -->
</div>
```

The basic CSS would position all the geometric elements absolutely in the middle of their container. We'll make the parent of our 3D shape, in our case the `<body>`

element be the scene by setting a `perspective`

on it. Since the prism is absolutely positioned, we'll also have to set a `height`

on the `<body>`

and, because we have no other content, we'll make the body cover the entire viewport. We'll also give the face elements explicit width and height and make sure they're dead in the middle by using negative margins. And given that we might want to animate the prism itself in 3D, we'll set `transform-style: preserve-3d`

on it.

```
body {
height: 100vh;
perspective: 32em;
}
[class*=prism] {
position: absolute;
top: 50%; left: 50%;
}
.prism { transform-style: preserve-3d; }
.prism__face--base {
margin: -13vmin;
width: 26vmin; height: 26vmin;
}
.prism__face--base svg {
width: 100%;
height: 100%;
}
.prism__face--lateral {
margin: -16vmin -13vmin;
width: 26vmin; height: 32vmin;
}
```

Now let's see how we can get our tight-fitted regular polygon. We start from the fact that all the vertices of a regular polygon are situated on a circle called the circumcircle. But how exactly do we position the vertices on this circle?

Before anything else, we need to know what a circle looks like. A full circle has `360°`

and we start from the + of the `x`

axis (3 o'clock).

See the Pen full circle - responsive SVG explanation by Ana Tudor (@thebabydino) on CodePen.

If our polygon has *n* equal length edges, then the arc corresponding to one edge is going to have the number of degrees of a full circle (`360°`

) over *n*. In the case of an equilateral triangle, *n* is 3, so the angle is `120°`

. For a square, *n* is 4, so the angle is `90°`

. For a regular pentagon, *n* is `5`

, so the angle is `72°`

.

Now if we go around the circle, starting from `0°`

, in *n* steps, then at every step we have a polygon vertex, as illustrated in the demo below.

See the Pen construct regular polygon by Ana Tudor (@thebabydino) on CodePen.

For a triangle, *n* is `3`

, so each step is `360°/3 = 120°`

. This puts the three vertices of our equilateral triangle at `0°,`

`120°`

, and `240°`

. For a square, *n* is `4`

, making each step `360°/4 = 90°`

, and puting the four vertices at `0°`

,` 90°`

, `180°`

, and `270°`

. For a regular pentagon, *n* is `5`

, the angular step is `360°/5 = 72°`

and the vertices are situated at `0°`

, `72°`

, `144°`

, `216°`

, and `288°`

.

Now we need the coordinates of these vertices in order to draw our polygon. The position of a point in a plane is given by the length of the segment connecting the point to the origin (in our case, the radius `r`

of our circle) and the angle between that segment and the `x`

axis (in our case, `i`

times the angular step, where `i`

is the index of the vertex).

```
xi = r*cos(i*360°/n);
yi = r*sin(i*360°/n);
```

If we know the number of vertices/edges, then it's easy to compute the angles, but how much is the radius? Well, not taking into account the `stroke-width`

of our `<polygon>`

element, this would be half the dimension of our square SVG `viewBox`

(which, let's say, we'll take to be `800`

). Adjusting for the `stroke-width`

is going to mean subtracting half the miter length from this value, just like we did for the four-point star earlier.

In order to do this, we need to compute the angle of our regular polygon, because the miter length depends on the `stroke-width`

and on the polygon angle. We know that the angles in a triangle always add up to `180°`

(you can drag the vertices to play with the triangle in the demo).

See the Pen triangle angles add up to 180° by Ana Tudor (@thebabydino) on CodePen.

Knowing this, and knowing that we have three angles in a triangle, we get that the angle of a regular (or equilateral) triangle is `60°`

. But what about other regular polygons? Well, if we connect the first vertex of our polygon to all the other vertices, we can see that we've split our polygon into `n - 2`

triangles.

See the Pen every convex n-polygon can be split into n-2 triangles by Ana Tudor (@thebabydino) on CodePen.

This means that the angles in a polygon with *n* vertices/edges add up to `(n - 2)*180°`

. Given that all *n* angles of this regular polygon are equal, we get that each angle is `(n - 2)*180°/n`

. If we actually do the computations, we find that we have `60°`

for an equilateral triangle, `90°`

for a square, `108°`

for a regular pentagon, `120°`

for a regular hexagon, and so on...

We now have all the data we need for setting the `points`

attribute of our base polygon.

```
var r = 400 /* circumradius, no correction, half of 800 */,
sw = 20 /* stroke-width */,
n = 3 /* number of edges */,
sym = document.getElementById('basepoly'),
poly = sym.querySelector('polygon'),
vb = [-r, -r, 2*r, 2*r],
points_attr_text = '',
base_angle = 2*Math.PI/n,
poly_angle = (n - 2)*Math.PI/n,
correction = .5*sw/Math.sin(.5*poly_angle),
r_final = r - correction;
/* set the viewBox */
/* "viewBox", not "viewbox" (won't work) */
sym.setAttribute('viewBox', vb.join(' '));
for(var i = 0; i < n; i++) {
curr_angle = i*base_angle;
x = r_final*Math.cos(curr_angle);
y = r_final*Math.sin(curr_angle);
points_attr_text += x + ',' + y + ' ';
}
poly.setAttribute('points', points_attr_text);
poly.setAttribute('stroke-width', sw);
```

You can see this code working in the following pen. You can tweak the values for the number of edges/ vertices, circumradius or `stroke-width`

in order to get different results. We could pack these polygons even tighter through various methods, but then we'd lose the advantage of having a simple and consistent method of doing it for any number of edges/vertices.

See the Pen n-polygon touching the edges of the SVG container by Ana Tudor (@thebabydino) on CodePen.

Alternatively, we could get the exact same result using Jade.

```
- var n = 3;
- var sw = 20;
- var r = 400;
- var base_angle = 2*Math.PI/n;
- var poly_angle = (n - 2)*Math.PI/n;
- var correction = .5*sw/Math.sin(.5*poly_angle);
- var r_final = r - correction;
mixin polygon(n)
- var points = '';
- for(var i = 0; i < n; i++) {
- var curr_angle = i*base_angle;
- var x = r_final*Math.cos(curr_angle)
- var y = r_final*Math.sin(curr_angle);
- points += ~~x + ',' + ~~y + ' ';
- }
polygon(points=points stroke-width='#{sw}')
svg(width='0' height='0')
symbol(id='basepoly'
viewbox='#{-r} #{-r} #{2*r} #{2*r}')
+polygon(n)
svg(class='vis')
use(xlink:href='#basepoly')
```

The next step we take is to combine this with the initial code we had and also auto-generate the lateral faces while we create the polygon. You can see the result in the pen below (or, if you prefer to do this the Jade way, be sure to check out this version).

See the Pen generate prism faces (JS) by Ana Tudor (@thebabydino) on CodePen.

All right, but at this point, all our faces are simply stacked one on top of the other in the middle of the screen, so we need to position them in 3D such that they form a prism.

Let's start with the base faces. Currently, they're in the vertical plane of the screen and we want them to be in a horizontal plane perpendicular onto the screen; one facing up, and the other one facing down. To do this, we'll apply a `rotateX(90deg)`

transform for the one we want to face up, and a `rotateX(-90deg)`

transform for the one we want to face down. Applying a rotation on an element also rotates its system of coordinates, so now the `z`

axis, the one that was initially coming out of the screen, towards us, is pointing up and down respectively for the two rotated faces. These faces are now horizontal, but they're cutting the vertical lateral faces in the middle. So, what we need to do is to translate them vertically (one up and the other one down) by half the height of the lateral faces (which we have taken to be `32vmin`

in this case, so half of that is `16vmin`

). Translating vertically after the rotation means translating along the `z`

axis (which was made vertical by the rotation), so we'll have to apply a `translateY`

transform. The code for this will be:

```
.prism__face--base:nth-child(1) {
transform: rotateX(90deg) translateZ(16vmin); /* up */
}
.prism__face--base:nth-child(2) {
transform: rotateX(-90deg) translateZ(16vmin); /* down */
}
```

After adding these two rule sets, the two base faces are now in place.

See the Pen position prism bases (JS) by Ana Tudor (@thebabydino) on CodePen.

Positioning the lateral faces means first rotating them around their `y`

axis (with a `rotateY`

transform) such that they each face an edge of the bases, then translating them outwards (with a `translateZ()`

) by the inradius. But what the heck is the inradius? Well, it's the radius of a circle (called the incircle) that touches each edge of a polygon at precisely one point (each edge is tangent to the incircle). Just like in the case of the circumcircle, not all polygons have an incircle, but all regular ones, like those we are dealing with in the case of our prism bases, do.

Moreover, in the case of regular polygons, the circumcircle and the incircle are centred at the same point, while the circumradius and the inradius split the polygon's angles and respective edges into two equal halves. Representing it all graphically gives us something like in this Pen:

See the Pen in/circumcircle of a regular polygon - WORK IN PROGRESS (SVG version) by Ana Tudor (@thebabydino) on CodePen.

Here, the circumradii are the segments connecting the central point to the polygon vertices, and the inradii are the segments connecting the central point to the midpoints of the polygon's edges. Given that the polygon's edges are tangent to the incircle, the inradii drawn in the demo are perpendicular onto the polygon's edges.

If we take a triangle formed by an inradius, a circumradius, and half a polygon edge, this triangle is a right triangle and allows us to compute the inradius relative to the circumradius and the polygon's angle.

See the Pen relation betwen circumradius, inradius, edge length, polygon angle by Ana Tudor (@thebabydino) on CodePen.

In the demo above, this is the *OVM* triangle. *OV* is the circumradius, *OM* is the inradius and *VM* is half the regular's polygon edge. The *VMO* angle is a right angle (which makes the opposing edge, *OV*, the hypotenuse) and the *OVM* angle is half the polygon's angle. The sine of the *OVM* angle is the ratio between *OM* and *OV* and we'll use this relation to compute *OM*, as we already know *OV* and the *OVM* angle.

The circumradius is half the dimension of the base face that we have set in CSS and we already have the angle of the polygon, so this is all we need for the value of the inradius:

```
base_face = prism.querySelector('.prism__face--base');
cradius = .5*getComputedStyle(base_face).width.split('px')[0];
iradius = cradius*Math.sin(.5*poly_angle);
```

And now all we have left to do is to set the proper transforms on each lateral face:

```
curr_y_rot = i*base_angle + .5*poly_angle;
curr_lat_face.style.transform =
'rotateY(' + curr_y_rot +
'translateZ(' + iradius + 'px)';
```

All right, this is getting really close:

See the Pen position prism faces stage #1 (JS) by Ana Tudor (@thebabydino) on CodePen.

The only problem we still have is that the widths of the lateral faces are still not right. We have taken them to be equal to the dimension of the base face, but the base face is twice the circumradius of the base polygon and that's not equal to the edge of the base polygon. But we can compute the edge from the same triangle as the inradius:

`edge_len = 2*cradius*Math.cos(.5*poly_angle);`

... then set the proper width and margin-left for the lateral faces:

```
curr_lat_face.style.width = edge_len + 'px';
curr_lat_face.style.marginLeft = -.5*edge_len + 'px';
```

And now we have a nice prism, which you can check out in the Pen below (or in its Jade vesion)

See the Pen position prism faces full (JS) by Ana Tudor (@thebabydino) on CodePen.

Best of all, changing the number of edges is as simple as just changing the `n`

variable in the JS—try that for yourself! There are still a couple of little problems to be ironed out. We don't really need to set the same `width`

and `margin-left`

inline on every lateral face— we could simply put these into a style element. Also, on resize, we need to update these values, as well as the transforms on the lateral faces, because they depend on the pixel dimensions of the bases, which we have set in viewport units so that the prisms scale on resize. This final pen fixes all such little problems:

See the Pen position prism faces final (JS) by Ana Tudor (@thebabydino) on CodePen.

Ana Tudor, once again, blowing minds of many. Incredible and yet such clean and consistent math/code. Really learnt a great deal of trigonometry thanks to your posts. Amazing work, Ana!

all of the wow! Amazing work Ana.

As soon as a saw some complicated maths going on there, I was all TL;DR;

This is incredibly dismissive given the clear amount of effort that went into the explanation and examples here.

Personally, I completely disagree, I found the effort that went into the visuals paid off massively & kept me engaged where plain text wouldn’t.

I was only joking to be fair.

She’s done an amazing job explaining everything.

Respect.

Noice

This merits post a bookmark so I can come back and read through it carefully. The miter limit has always seemed confusing to me and I’ve always applied arbitrary values to it. Perhaps we can have a greater control over it with a greater understanding of how it works.

Good work

Ana this is a really nice article, technical and clear at the same time. This would be a great study for many kinds of people, math, programming, and design. I think it should be part of a curriculum for students!

Incredible as always. I’ll have to have to reread this post several times to absorb all the information. Thanks so much!

I’m a math geek, so I love all the math that went into this (after all, my avatar was generated purely in Sass). Very cool demos.

(As a side note, as amazing as the demos are, the JS makes me cringe. The terse variable names, tightly spaced code, usage of obscure tricks like double-tilde, and completely procedural writing style makes it feel like an ancient demo written in C.)

Ha! C was my first programming language on a PC more than 10 years ago. Before that, there was the era of the HC and BASIC-S. Wouldn’t think it actually influenced my JS style as much as Java did later on. Or as much as working on a small screen for years and needing to fit as much code as possible in sight because it’s a pain in the rear to keep scrolling. In this particular case, I wanted to have as little code as possible because I know I personally dread and don’t have the patience for huge walls of code in articles.

Ah, not too often you run into web developers that started with something like C. Speaking of terse code, ever tried writing a (moderately complex) program on a TI calculator? Single letter variable names and list abuse galore.