0
\$\begingroup\$

So, I have a line: enter image description here

And I skewed it with x += y * cos(angle)

and got something like:

enter image description here

My question is, if I have another line segment with that same skew, how can I transform it so that it is pointing up again like:

I tried just applying a rotation (reverse of the skew angle) to the line's x,y coordinates, but that did not seem to give me the effect I am going for.

How can I do this? enter image description here

EDIT

So, if I start with a rectangular like:

enter image description here

and I apply:

x1 = x1 + (y2 - y1) * cos(angle);
x2 = x2 + (y2 - y1) * cos(angle);

I get:

enter image description here

I am not quite clear why my x1 is wrong? (as you can see, it extends outside of the rectangle)..

But, regardless of that, this still is not the direction I am trying to skew by... What I really want is something that looks like this: enter image description here

\$\endgroup\$
2
  • \$\begingroup\$ Are you trying to rotate around one of the ends of the line? \$\endgroup\$
    – Theraot
    Nov 14 '19 at 8:18
  • \$\begingroup\$ @Theraot, yes... \$\endgroup\$
    – patrick
    Nov 14 '19 at 15:28
2
\$\begingroup\$

Skew

So, you say you have applied this transformation:

x += y * cos(angle)

That is:

x = x + y * cos(angle)

This means that if you have a line that goes from (x1, y1) to (x2, y2)...

After transformation it goes from (x1 + y1 * cos(angle), y1) to (x2 + y2 * cos(angle)).

Note: I am assuming angle is a paramater of the transformation.


There are two things to notice here:

  • Only x is affected.
  • The transformation depends on y.

This means that:

  • To reverse it, you must only affect x.
  • You must have y into account to reverse it.

Reverse Skew

We can figure out the inverse function... first, let us label old_x and new_x:

new_x = old_x + y * cos(angle)

Solve for old_x:

new_x = old_x + y * cos(angle)

=>

new_x - y * cos(angle) = old_x

=>

old_x = new_x - y * cos(angle)

That is your inverse function:

x = x - y * cos(angle)

We can check by reversing (x1 + y1 * cos(angle), y1) to (x2 + y2 * cos(angle)).

We get (x1 + y1 * cos(angle) - y1 * cos(angle), y1) to (x2 + y2 * cos(angle) - y2 * cos(angle)).

Which is just (x1, y1) to (x2, y2).


Skew with a fixed point

Now, if we are trying to keep a point fixed, the usual approach is to translate it to the origin.

Thus, if we want the point (x1, y1) to be fixed, we will use the following transformation:

x = x - x1
y = y - y1

So that (x1, y1) gets transformed into (0, 0). Then we do the transformation we want. Followed by the inverse of the translation above, so that (0, 0) is (x1, y1) again.

Alright, that means that if we want to do x = x + y * cos(angle) while keeping (x1, y1) in place, we do:

x = x - x1
y = y - y1
x = x + y * cos(angle)
x = x + x1
y = y + y1

We can inline this transformation like this:

x = ((x - x1) + (y - y1) * cos(angle)) + x1

Simplify:

x = x + (y - y1) * cos(angle)

The alternative approach is to apply the inverse transformation for the fixed point. That is:

x = x + y * cos(angle)
x = x - y1 * cos(angle)

In a single line:

x = x + y * cos(angle) - y1 * cos(angle)

Simplifly:

x = x + (y - y1) * cos(angle)

That is the same result.


Reverse Skew with fixed point

We, of course, can figure out the inverse function for this transformation. Label your old_x and new_x:

new_x = old_x + (y - y1) * cos(angle)

And solve for old_x:

new_x = old_x + (y - y1) * cos(angle)

=>

new_x - (y - y1) * cos(angle) = old_x

=>

old_x = new_x - (y - y1) * cos(angle)

=>

old_x = new_x + -(y - y1) * cos(angle)

=>

old_x = new_x + (-y - -y1) * cos(angle)

=>

old_x = new_x + (-y + y1) * cos(angle)

=>

old_x = new_x + (y1 - y) * cos(angle)

Finally, you have a transformation that keeps (x1, y1) fixed:

x = x + (y - y1) * cos(angle)

And its inverse, which, of course, also keeps (x1, y1) fixed:

x = x + (y1 - y) * cos(angle)

Rotate

If you only have lines, for each one the skew will be equivalent to a rotation and a scaling. This is no longer true once you have 2d shapes.

A rotation looks like this:

x = x * cos(rotation_angle) - y * sin(rotation_angle)
y = y * cos(rotation_angle) + x * sin(rotation_angle)

Scaling looks like this:

x = x * scale_factor
y = y * scale_factor

Combined:

x = x * cos(rotation_angle) * scale_factor - y * sin(rotation_angle) * scale_factor
y = y * cos(rotation_angle) * scale_factor + x * sin(rotation_angle) * scale_factor

We are trying to parametrize the combination, such that:

x = x + y * cos(angle)

That is:

x + y * cos(angle) = x * cos(rotation_angle) * scale_factor - y * sin(rotation_angle) * scale_factor

Break it by variables:

x = x * cos(rotation_angle) * scale_factor
y * cos(angle) = - y * sin(rotation_angle) * scale_factor

Two equations and two unknowns, we can do this...

Clearly cos(rotation_angle) and scale_factor must cancel out (because x = x). Thus

scale_factor = 1/cos(rotation_angle)

Which means:

y * cos(angle) = - y * sin(rotation_angle) * 1/cos(rotation_angle)

By trigonometric identity, that is tangent:

y * cos(angle) = - y * tan(rotation_angle)

Solve for rotation_angle:

y * cos(angle) = - y * tan(rotation_angle)

=>

- y * cos(angle) = y * tan(rotation_angle)

=>

- y * cos(angle) / y = tan(rotation_angle)

=>

- cos(angle) = tan(rotation_angle)

=>

atan(-cos(angle)) = rotation_angle

=>

rotation_angle = atan(-cos(angle))

=>

rotation_angle = -atan(cos(angle))

Replace in scale_factor = 1/cos(rotation_angle):

scale_factor = 1/cos(rotation_angle)

=>

scale_factor = 1/cos(-atan(cos(angle)))

By 1/cos(-atan(cos(θ))) in Wolfram|Alpha:

scale_factor = 1/cos(-atan(cos(angle)))

=>

scale_factor = sqrt((cos(angle * 2) + 3) / 2)

This means that if you rotate a point by -atan(cos(angle)) and scale by sqrt((cos(angle * 2) + 3) / 2) it's x will end in the same place as if you had done x = x + y * cos(angle).

Thus, if you want to undo the rotation (and keep the scaling), you can rotate by atan(cos(angle)).


Rotate with fixed point

And of course, if you want to keep a fixed point while rotating, you can do the thing where the point is translated to the origin and then back.

So you do this:

x = x - x1
y = y - y1
x = x * cos(rotation_angle) - y * sin(rotation_angle)
y = y * cos(rotation_angle) + x * sin(rotation_angle)
x = x + x1
y = y + y1

Inline:

x = (x - x1) * cos(rotation_angle) - (y - y1) * sin(rotation_angle) + x1
y = (y - y1) * cos(rotation_angle) + (x - x1) * sin(rotation_angle) + y1

And, of course, since the rotation_angle you want is atan(cos(angle)) you do:

x = (x - x1) * cos(atan(cos(angle))) - (y - y1) * sin(atan(cos(angle))) + x1
y = (y - y1) * cos(atan(cos(angle))) + (x - x1) * sin(atan(cos(angle))) + y1

You can ask Wolfram|Alpha for alternatives for this trigonometric combos if you want to.

\$\endgroup\$
2
  • \$\begingroup\$ thank you for that very detailed answer.. I updated my question because I am still a little unclear on what exactly I need to do to achieve the transform I am going for... \$\endgroup\$
    – patrick
    Nov 14 '19 at 21:19
  • \$\begingroup\$ @patrick about your wrong x1, you probably have a typo somewhere in your code. About the transformation you show... that is two different skews, one horizontal (that affects only x) and one vertical (that affects only y). Now... is that why you use an angle? I suspect you want to do x += y * cos(angle) and then y += x * sin(angle) or something like that (in particular, I'm not sure if cos and sin are flipped). \$\endgroup\$
    – Theraot
    Nov 14 '19 at 21:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .