4
$\begingroup$

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}]

outcome for aspect ratio of 1/2

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

$\endgroup$
5
  • 1
    $\begingroup$ 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}]? $\endgroup$
    – kglr
    Mar 13, 2023 at 18:30
  • 1
    $\begingroup$ @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.) $\endgroup$
    – Alan
    Mar 13, 2023 at 19:20
  • $\begingroup$ In other words : we shouldn't have to inject the AspectRatio in the code that contains the graphic primitives. No ? $\endgroup$
    – andre314
    Mar 13, 2023 at 19:28
  • $\begingroup$ @andre314 Right! $\endgroup$
    – Alan
    Mar 13, 2023 at 19:34
  • $\begingroup$ @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. $\endgroup$
    – Alan
    Mar 13, 2023 at 19:36

3 Answers 3

2
$\begingroup$

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}]
$\endgroup$
1
  • $\begingroup$ People who have used TikZ know what a good user interface for this common need looks like. $\endgroup$
    – Alan
    Apr 7 at 22:07
0
$\begingroup$
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.

$\endgroup$
2
  • $\begingroup$ See my comments to @kglr. $\endgroup$
    – Alan
    Mar 13, 2023 at 19:37
  • $\begingroup$ OK, I missed something. Namely, it was answered in the comments. $\endgroup$
    – anon
    Mar 13, 2023 at 20:19
0
$\begingroup$
Manipulate[
 With[{f = x |-> (x + 0.4)^2, x0 = 1, x1 = 2}, 
  Module[{y0 = f[x0], y1 = f[x1]}, 
   Graphics[{Arrowheads[{{Automatic, .5, 
        Graphics[{Text["test", {0, 0}, {0, -2}]}]}}], 
     Arrow[{{x0, y0}, {x1, y1}}]}, PlotRange -> {{0, 6}, {0, 6}}, 
    Axes -> True, AspectRatio -> a, AxesOrigin -> {0, 0}]]], 
{{a, 1}, 1/2, 1}]

enter image description here

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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