1
$\begingroup$

I am using JavaScript to create a shape using clip-path. I have no issues with the code but my issue lies with the math. I can get close to my goal but it fails once the shape's height or width is manipulated. I want to create a shape like this simple polygon.
My problem is that I do not know how to calculate where to place points within this rectangular polygon that will result in a copy of the polygon within itself, but with each of the interior polygon's edges being x units away from the surrounding polygon. The resulting inner polygon needs to maintain this distance when the outer polygon changes height or width. Using the simple polygon image as an example the outer edges of the image are the outer polygon, and the inner polygon is removing the image creating a hole the shape of the outer polygon.

In the image, and the context I am graphing this polygon, the top left is (0,0). The points are also color coded to match the coordinates bellow which are percent based in this case and separated by a single space with commas separating each point. For example the first listed coordinate at the bottom (dark orange) is the coordinate for the upper most left point. For this point x = 25% = polygon height * 0.25 and y = 0. (non-percentages are allowed)

My issue is once the shape changes height, width, or thickness it seems to fail as seen by my tests here. Thickness is how many units smaller the inner polygon should be. These are my best results so far as the inner points always seem to be off to some degree.

The first shape is 100x100 pixels with a thickness of 5 pixels. The second is 100x50, and the last is 100x10.

My thinking is to use these triangles in the polygon, get the points on the hypotenuse that are closest to the desired location, then attempt to shift it inward however many units equal to the thickness variable.

I have these variables:
height = polygon height, width = polygon width
h = height (triangle rise), w = width * 0.25 (triangle run), thickness = 5,
angle = atan(h/w),
hypotenuse = sqrt(h^2 + w^2)
pX1 = thickness * cos(angle), pY1 = thickness * sin(angle)
pX2 = hypotenuse * cos(angle), pY2 = hypotenuse * sin(angle)

Back to this image, here are the points I am trying to get:
dark blue = point1; dark pink = point2; grey = point3; cyan = point4

point1 = (pX2 + thickness, height - pY2 + thickness)
point2 = (width - pX1 - thickness, pY1)
point3 = ( (width * 0.5) + pX2 - thickness, pY2 - thickness )
point4 = (pX1, height - pY1)

This was mostly just a bunch of trial and error + research into right triangles so apologies if the math is messy. I am also weak in trigonometry. I also translated this all from JavaScript so let me know if any more clarification is needed.

Here you can see a live view of the math in action. (If you're not familiar with web dev just focus on the JS section at the top right, that's where all the math is)

$\endgroup$

1 Answer 1

2
$\begingroup$

Here's a conceptual answer. I'll leave it to OP to convert to CSS-appropriate coordinates and such.


We have parallelogram $ABCD$ and inset parallelogram $A'B'C'D'$. The outer figure's "shear" is determined by a $p$-$q$-$r$ right triangle, with $p$ and $q$ the horizontal and vertical offsets from $A$ to $D$, and $r:=\sqrt{p^2+q^2}$.

enter image description here

Proportional copies of that triangle —say, with lengths $p'$, $q'$, $r'$— appear in the corners of the parallelogrammatic ring. In the context of the problem at hand, $q'$ (being the vertical distance between the bottom-outer and -inner edges of the ring) is the thickness of the ring; we leverage proportionality to calculate the other lengths: $$\frac{\phantom{'}p'}{p} = \frac{\phantom{'}q'}{q} = \frac{\phantom{'}r'}{r} \qquad\to\qquad p' = \frac{\phantom{'}pq'}{q} \qquad r' = \frac{\phantom{'}rq'}{q} $$

Tracing horizontal and vertical paths along those triangles from each outer vertex to its inner counterpart, we derive these coordinate relations:

$$A' \;=\; A \,+\, \left(p'+r', q' \right) \qquad B' \;=\; B \,+\, \left(p'-r',q' \right)$$ $$C' \;=\; C \,-\, \left(p'+r', q' \right) \qquad D' \;=\; D \,-\, \left(p'-r',q' \right)$$

Since OP is tracing the ring as a simple polygon instead of a holey one, an auxiliary point, $Z$, is given by $$Z \;=\; A \,+\, \left( r', 0\right)$$ so that the ring can be traced as the polygon $AZD'C'B'A'ZBCDA$.

That should just about do it!


Addendum. Here's the figure with a different aspect ratio, showing that the inherent geometry remains the same.

enter image description here

$\endgroup$
6
  • $\begingroup$ Appreciate the reply and all the visuals but unfortunately this seems to fail when height is not equal to width. I may misinterpreted something but if you'd like you can see the math in action here. The blue set is following your example. (All the math is in the JS section) $\endgroup$ Commented Jul 31, 2024 at 6:41
  • $\begingroup$ @Panthera: I've added a figure with a different aspect ratio to show that the geometry is quite flexible here. It "should" work, but I guarantee nothing. :) ... I'm afraid I can barely understand my own code the day after I write it, so reading someone else's isn't likely to be fruitful. (Although: In line 21, shouldn't proportion be defined as thickness/h to correspond to my q'/q? And in lines 21-29, if your ps and rs are like my $p'$s and $r'$s, shouldn't innerB and innerD use p-r?) ... Maybe I typo'd in my presentation; I'll take another look at it with fresh eyes later. $\endgroup$ Commented Jul 31, 2024 at 7:29
  • $\begingroup$ Sorry for the double comment, I don't know the etiquette around here but my question was closed for lack of details or clarity but I don't know what I'm missing. Any ideas for what I need to add? I also seem to not be able to tag you so hopefully you see these replies. $\endgroup$ Commented Aug 1, 2024 at 5:12
  • 1
    $\begingroup$ It Works! Thank you for the help. I've really been stuck on this all because I wanted some fancy buttons on my website. Yes I incorrectly defined 'proportion', in my tests the math still wasn't quite working so through experimenting I used all +'s. What the issue actually seemed to be is that on the web the coordinate plane is positive going down and right. I had to adjust the definitions of A'-D' to account for this. I tried to make the math more obvious in the example. (Blue works and Red is my last experiment with all +'s wrong A'-D' values) $\endgroup$ Commented Aug 1, 2024 at 5:51
  • 1
    $\begingroup$ Thanks. Looks like you replied while I was typing up my last reply. Glad I got it working just in time to save you the trouble. It was me who needed to double check my work. $\endgroup$ Commented Aug 1, 2024 at 6:02

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.