In Xamarin.Forms (C#) I have created a ribbon, which I try to place in the upper right corner.

I know the size of the Label (height and width) before it is rotated, but when I rotate the Label the position is still the same, so I have to move the Label, so all text gets visibile.

Before rotation

After rotation

As you can see on the picture after rotation, all the text is not visible, so I have to calculcate the transformation of $X$ and $Y$ values. As you also can see on the pictures, the Rotation point is in the center of the Label.

At the moment I am calculating using the extra space (the width between the Label in the same rotation as the Label and the right side) by the following:

var extraSpace = width / Math.Cos(Rotation) - width;

I then calculate the width by

var extraWidth = Math.Cos(Rotation) / extraSpace;
TranslationX = extraWidth + Padding.Top + Content.Margin.Top;

And the same of the height

var extraHeight = Math.Sin(Rotation) / extraSpace;
TranslationY = extraHeight + (height - Padding.VerticalThickness - Content.Margin.VerticalThickness) / 2;

But there is a problem with the calculation.

I have a point on the Label ($X=0, Y=0$), which is the top of the text's start.

I also needs to calculate the width and height of the Ribbon, but is that possible?


I now figured out how to calculate the Points using the following

var upperLeft = new Point(Padding.Left, Padding.Top);
var upperRight = new Point(upperLeft.X + width - Padding.HorizontalThickness, upperLeft.Y);
var lowerLeft = new Point(upperLeft.X, upperLeft + height - Padding.VerticalThickness);
var lowerRight = new Point(upperRight.X, lowerLeft.Y);

var rotationPoint = new Point();
rotationPoint.X = (lowerRight.X - upperLeft.X) * AnchorX + upperLeft.X;
rotationPoint.Y = (lowerRight.Y - upperLeft.Y) * AnchorY + upperLeft.Y;

var rotatedUpperLeft = CalculateRotatedPoint(upperLeft, rotationPoint);
var rotatedUpperRight = CalculateRotatedPoint(upperRight, rotationPoint);
var rotatedLowerLeft = CalculateRotatedPoint(lowerLeft, rotationPoint);
var rotatedLowerRight = CalculateRotatedPoint(lowerRight, rotationPoint);

The CalculateRotatedPoint is then defined as

private Point CalculateRotatedPoint(Point p, Point rotationPoint)
   var rotation = GetRotationInRadians();

   var rotatedPoint = new Point();

   rotatedPoint.X = Math.Cos(rotation) * (p.X - rotationPoint.X) - Math.Sin(rotation) * (p.Y - rotationPoint.Y) + p.X;
   rotatedPoint.Y = Math.Sin(rotation) * (p.X - rotationPoint.X) + Math.Cos(rotation) * (p.Y - rotationPoint.Y) + p.Y;

   return rotatedPoint;

And the Translation values (the movement of $X$ and $Y$) is then set as

TranslationX = Math.Min(Math.Min(rotatedUpperLeft.X, rotatedUpperRight.X), Math.Min(rotatedLowerLeft.X, rotatedLowerRight.X));
TranslationY = -Math.Min(Math.Min(rotatedUpperLeft.Y, rotatedUpperRight.Y), Math.Min(rotatedLowerLeft.Y, rotatedLowerRight.Y));

Can somebody explain me, why I need the negative sign before $TranslationY$?

If I rotate the label more than 140 degrees. The negative sign gives a wrong result.

  • $\begingroup$ The Problem with the calculation is shown here. $\endgroup$ Jun 28 '17 at 13:55
  • $\begingroup$ Have a look at this related question. $\endgroup$
    – amd
    Jun 28 '17 at 23:30
  • $\begingroup$ I have taken a look at it, but somehow it places the Label outside the View $\endgroup$ Jun 29 '17 at 6:09
  • $\begingroup$ CalculateRotatedPoint is incorrect. You should be translating by rotationPoint instead of p. $\endgroup$
    – amd
    Jun 30 '17 at 20:26
  • $\begingroup$ Sorry for the delay on my response, but I am on vacation. Do you mean it shall be Math.Cos(rotation) * (p.X - rotationPoint.X) - Math.Sin(rotation) * (p.Y - rotationPoint.Y) + rotationPoint.X; instead? $\endgroup$ Jul 6 '17 at 19:07

For a ribbon with width $w$ and height $h$, the coordinates of its corners relative to the center of the ribbon are $(w/2,h/2)$, $(-w/2,h/2)$, $(-w/2,-h/2)$ and $(w/2,-h/2)$. The resulting coordinates after rotation through an angle $\theta$ can be found using well-known formulas. Assuming that angles are measured consistently with the orientation of the coordinate system (a positive rotation angle takes the positive $x$-axis onto the positive $y$-axis), applying a rotation to these four points produces $$\begin{align}\frac12(w,h) &\mapsto \frac12(w\cos\theta-h\sin\theta,w\sin\theta+h\cos\theta) \\ \frac12(-w,h) &\mapsto \frac12(-w\cos\theta-h\sin\theta,-w\sin\theta+h\cos\theta) \\ \frac12(-w,-h) &\mapsto \frac12(-w\cos\theta+h\sin\theta,-w\sin\theta-h\cos\theta) \\ \frac12(w,-h) &\mapsto \frac12(w\cos\theta+h\sin\theta,w\sin\theta-h\cos\theta). \end{align}$$ Taking $\max$ and $\min$ of these new $x$- and $y$- coordinates gives you an axis-aligned bounding box for the rotated ribbon, again relative to its fixed center, from which it should be fairly easy to compute how much it needs to be translated so that it remains visible.

  • $\begingroup$ Thank you for your help @amd. I tried using min to both values, and if I multiply the Y-value with -1, the Y axis is calculated correctly, when the angle is below 140 degrees. When it is above 140 degrees the -1 gives a wrong answer. What can I have done wrong? $\endgroup$ Jun 30 '17 at 5:52
  • $\begingroup$ @LasseMadsen The bounding boxes for a rotation of $\theta$ and $\pi-\theta$, e.g., 40° and 140°, are identical. If you’re not getting the same result for these two rotations, there’s obviously a bug in your code, but I’m not going to try to guess what that might be. $\endgroup$
    – amd
    Jun 30 '17 at 20:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.