# How to rotate text in user coordinate system?

Applying Rotate to text rotates it relative to the Cartesian page coordinates. I want it rotated relative to the current user coordinate system. As a simple illustration (of a more general problem), consider the following manipulation. When the AspectRatio is 1, all is good; but otherwise not. (I've included a plot for an apect ratio of 1/2, to illustrate.) How can I keep the text on the line?

Manipulate[With[{f = x |-> (x + 0.4)^2, x0 = 1, x1 = 2},
Module[{y0 = f[x0], y1 = f[x1]},
Graphics[{
Line[{{x0, y0}, {x1, y1}}],
Text[
Rotate["test", ArcTan[1, (y1 - y0)/(x1 - x0)]], {(x0 + x1)/2, (y0 + y1)/2}, {0, -2}]
},
PlotRange -> {{0, 6}, {0, 6}},
Axes -> True, AspectRatio -> a,
AxesOrigin -> {0, 0}]]],
{{a, 1}, 1/2, 1}]


More generally, it seems that Mathematica has much to learn from TikZ about making annotation easy for users.

• try Rotate[Text["test", {(x0 + x1)/2, (y0 + y1)/2}, {0, -2}], ArcTan[1, (y1 - y0)/(x1 - x0)]] or Text[Rotate["test", ArcTan[1, a (y1 - y0)/(x1 - x0)]], {(x0 + x1)/2, (y0 + y1)/ 2}, {0, -2 a}]?
– kglr
Commented Mar 13, 2023 at 18:30
• @kglr You are correct that when I set the aspect ratio I can use it to scale the angle, but I am seeking a general solution for rotation in "user coordinates". (Like in PostScript.) Or alternatively, a way to say "place this text near the end of this line, aligned with the line". (Like in TikZ.)
– Alan
Commented Mar 13, 2023 at 19:20
• In other words : we shouldn't have to inject the AspectRatio in the code that contains the graphic primitives. No ? Commented Mar 13, 2023 at 19:28
• @andre314 Right!
– Alan
Commented Mar 13, 2023 at 19:34
• @kglr I forgot to add: if you create the text and then rotate it, it distorts. That's why I show rotating it and then placing it.
– Alan
Commented Mar 13, 2023 at 19:36

Well, if @kglr doesn't know a direct way to make Text[]/Inset[] work, I guess there isn't one. First, the 4th argument to Text[] makes the use of Rotate[] unnecessary. Second, the 4th argument doesn't work any better than Rotate[]. Third, the angle depends on both the PlotRange and the AspectRatio, so the solution in the comments and @anon's answer work only if the PlotRange has equal horizontal and vertical lengths.

For decades I did this as a two step process (or forced the graphics to work with AspectRatio -> Automatic). But @b3m2a1's function getBoxSize[] allows us to combine the steps into a single function. The poorly named withAspectRatio[] calculates the correction factor (also incorrectly called $aspectRatio) and stores it in a global variable, which may appear in the Graphics[..] code; the graphics will be returned with the correction factor replacing the global variable. The user may use a different variable name and pass this as the second argument to withAspectRatio[]. (* @b2m2a1, https://mathematica.stackexchange.com/a/166772/46569 *) getBoxSize[c_Cell] := {#[[1]], Total@#[[2 ;;]]} &@ First@FrontEndExecute@GetBoundingBoxSizePacket[c]; getBoxSize[c_CellObject] := getBoxSize[NotebookRead[c]]; getBoxSize[b_BoxObject] := getBoxSize[ Cell[BoxData@NotebookRead[b], "Output", PageWidth -> Infinity, ShowCellBracket -> False, CellMargins -> {{0, 0}, {0, 0}}]]; getBoxSize[e_] := getBoxSize[ Cell[BoxData@ToBoxes[e], "Output", PageWidth -> Infinity, ShowCellBracket -> False, CellMargins -> {{0, 0}, {0, 0}}]]; withAspectRatio // ClearAll; withAspectRatio // Attributes = {HoldRest}; withAspectRatio[g_, a_Symbol :$aspectRatio] := Block[{a},
a = 1;
With[{boxes =
Show[g, Frame -> None, Ticks -> None, ImagePadding -> 0] //
MakeBoxes[#, StandardForm] &},
a = First@
Ratios[getBoxSize@Cell[BoxData@boxes, "Output"]/
Flatten[Differences /@ PlotRange@g]];
g
]];

Manipulate[
withAspectRatio[
Plot[{Exp[x], Exp[a] (x - a + 1)}
, {x, a - 1, a + 1}
, Epilog -> {
Text[
"my tangent line is parallel to this",
{a, Exp[a]}, {0, 2}, {1, $aspectRatio * Exp[a]}]} , AspectRatio -> 2.3 + a] ] , {{a, 0}, -2, 1}]  Also for decades, I became used to this and assumed that the 4th argument was supposed to behave this way. This question made the scales fall, and I don't see why this behavior is at all convenient. Furthermore I think the community should consider it a bug and bug WRI to fix it. The OP's code refactored: Manipulate[ With[{f = x |-> (x + 0.4)^2, x0 = 1, x1 = 2}, Module[{y0 = f[x0], y1 = f[x1]}, withAspectRatio[ Graphics[{Line[{{x0, y0}, {x1, y1}}], Text[ "test test test", {(x0 + x1)/2, (y0 + y1)/2}, {0, -2}, {x1 - x0,$aspectRatio (y1 - y0)}]}
, PlotRange -> {{0, 6}, {0, 6}}, Axes -> True, AspectRatio -> a,
AxesOrigin -> {0, 0}]
]
]]
, {{a, 1}, 1/2, 2}]

• People who have used TikZ know what a good user interface for this common need looks like.
– Alan
Commented Apr 7 at 22:07
Manipulate[With[{f = Function[x, (x + 0.4)^2], x0 = 1, x1 = 2},
Module[{y0 = f[x0], y1 = f[x1]},
Graphics[{Line[{{x0, y0}, {x1, y1}}],
Text[Rotate["test", ArcTan[x1 - x0, a*(y1 - y0)]],
{(x0 + x1)/2, (y0 + y1)/2}, {0, -2*a}]},
PlotRange -> {{0, 6}, {0, 6}}, Axes -> True,
AspectRatio -> a, AxesOrigin -> {0, 0}]]],
{{a, 1}, 1/2, 1}]


The two argument form of ArcTan handles getting the rotation angle into the correct quadrant.

The AspectRatio effectively scales the y coordinate. Therefore, another correction is needed in the ArcTan function evocation. For similar reasons I also scaled the offset parameter.

Of course, I may have missed something.

• See my comments to @kglr.
– Alan
Commented Mar 13, 2023 at 19:37
• OK, I missed something. Namely, it was answered in the comments.
– anon
Commented Mar 13, 2023 at 20:19
Manipulate[
With[{f = x |-> (x + 0.4)^2, x0 = 1, x1 = 2},
Module[{y0 = f[x0], y1 = f[x1]},