4

I am trying to adapt the solution here, which places text in 3D on the xy plane, so that the text can be placed in any orientation. So, I wrapped it in three Rotate statements, first by tilt around the x axis, then by elevation around the y-axis, then by longitude around the z-axis. However, this rotation happens after the text has been placed, away from the center of the axes. Trying to put the Rotation statements inside the meat of the function runs into the problem that I cannot tell from where the x and y coefficients are deriving so as to manipulate them using Rotate. Anyway, the original is

xyText[str_, scaling_: 1, location_: {0, 0, 0}] := 
 Module[{mesh = 
    DiscretizeGraphics[Text[Style[str, FontFamily -> "Times"]], _Text,
      MaxCellMeasure -> 0.2]}, 
  Join[{EdgeForm[]}, 
   MeshPrimitives[mesh, 
     2] /. {x_?NumberQ, 
      y_?NumberQ} :> (scaling {x, y, 0} + location), {Black}, 
   MeshPrimitives[BoundaryMesh[mesh], 
     1] /. {x_?NumberQ, 
      y_?NumberQ} :> (scaling {x, y, 0} + location)]]

and my inadequate adaptation is

ClearAll[xyText]
xyText[str_, scaling_: 1, location_: {0, 0, 0}, longitude_, 
  elevation_, tilt_] := 
 Module[{mesh = 
    DiscretizeGraphics[Text[Style[str, FontFamily -> "Times"]], _Text,
      MaxCellMeasure -> 0.2]}, Rotate[
   Rotate[
    Rotate[
     Join[{EdgeForm[]}, 
      MeshPrimitives[mesh, 
        2] /. {x_?NumberQ, 
         y_?NumberQ} :> (scaling {x, y, 0} + location)
      , {Black}, 
      MeshPrimitives[BoundaryMesh[mesh], 
        1] /. {x_?NumberQ, 
         y_?NumberQ} :> (scaling {x, y, 0} + location)], 
     tilt, {1, 0, 0}], elevation, {0, 1, 0}], longitude, {0, 0, 1}]]

The objective is for the yellow and the red "Dodecahedron" texts to intersect each other in this:

meshedSol = 
 Graphics3D[{Antialiasing -> True, Yellow, Opacity[.8], 
   PolyhedronData["Dodecahedron", "Faces"], 
   xyText["Dodecahedron", 0.2, {1, 1, 1}, 0, 0, 0],
   Red, xyText["Dodecahedron", 0.2, {1, 1, .9}, 0, 0, \[Pi]/2]}, 
  ViewPoint -> {-2, -2, 2}]

But instead, the red one is rotated after having been placed.

Update:

I gathered that x and y come from MeshPrimitives, so I made that 3D and wove in rotation matrices. Till we get a more elegant answer, I moved the result to be an answer.

Nicholas G
  • 1,981
  • 10
  • 15

1 Answers1

7

I wove rotation matrices in the code and increased the resolution and it works, and now with Chip Hurst's improvement not so slow at all. Maybe it will get to be more elegant yet:

text3D[str_, location_: {0, 0, 0}, 
  scaling : {_?NumericQ, _?NumericQ} : { 1, 1}, 
  longitude : _?NumericQ : 0, 
  elevation : _?NumericQ : 0, 
  tilt : _?NumericQ : 0, 
  opt : OptionsPattern[]]:=Module[{
mesh=DiscretizeGraphics[
   Text[Style[str, opt, FontFamily -> "Times", 
   FontSize -> 12]], _Text, 
   MaxCellMeasure -> 0.04],
rotmatrx=RotationMatrix[longitude, {0, 0, 1}].
   RotationMatrix[-elevation, {0, 1, 0}].RotationMatrix[tilt, {1, 0, 0}]}, 
Join[{EdgeForm[]}, 
  MeshPrimitives[mesh,2] /. {x_?NumberQ, y_?NumberQ} :> (
      rotmatrx.(scaling~Join~{1} {x, y, 0}) + location)
(*
   ,{Black},
   MeshPrimitives[BoundaryMesh[mesh],1]/.{x_?NumberQ,y_?NumberQ}\[RuleDelayed]
   (rotmatrx.(scaling~Join~{1} {x,y,0})+location)
REMOVE REM IF NEEDING OUTLINES*)
]]

Then:

offset = 12; scale = {.5, .5};
Graphics3D[{
  Yellow,
  text3D["width\[LongRightArrow]", {offset, 0, 0}, scale, 
    FontSlant -> Italic],
  Blue,
  text3D["depth\[LongRightArrow]", {0, offset, 0}, scale, \[Pi]/2, 
    FontWeight -> Bold],
  Black,
  text3D["height\[LongRightArrow]", {0, 0, offset}, {.5, .8}, 
    0, \[Pi]/2, \[Pi]/2, FontFamily -> "Brush Script Std"],
  Red,
  Sphere[{offset, offset, offset/4}, 3]
}, Boxed -> False]

produces:

enter image description here

Nicholas G
  • 1,981
  • 10
  • 15
  • 2
    I get about a 10 times speed up by precomputing M = scaling*RotationMatrix[longitude,{0,0,1}].RotationMatrix[-elevation,{0,1,0}].RotationMatrix[tilt,{1,0,0}] as a Module variable, i.e. this quantity only needs to be computed once. As you have it, it's being computed for every triangle created. – Greg Hurst Nov 23 '16 at 02:05
  • What a huge difference! Thanks. Adding it. – Nicholas G Nov 23 '16 at 02:12
  • 1
    Just a stylistic suggestion. In your projected text, you could use \[Rule] or \[LongRightArrow] instead of ->. – Greg Hurst Nov 23 '16 at 03:51
  • The italic Roman turns out not to have \[Rule], LOL, but it does have \[LongRightArrow]. – Nicholas G Nov 23 '16 at 10:14
  • What remains is for someone to enable left- and right-alignment of the text. As it is, the coordinates for its placement are at the center of the text. – Nicholas G Nov 23 '16 at 12:14
  • 1
    Why not use EulerMatrix[{longitude, -elevation, tilt}, {3, 2, 1}]? And another option is to use the replace on the mesh primitives to transform it to a 3D object quickly ({x, y} -> {x, y, 0}), and then just work with Translate and Rotate (or GeometricTransformation with EulerMatrix). Alternatively, you can do all of that at once with TransformedRegion supplying a function that does both the embedding from 2D into 3D and the transformation. (I'll try to write an answer after working this out.) – Tom Verhoeff Feb 19 '21 at 20:28
  • This is SO useful. Thanks. – David G. Stork Feb 22 '21 at 22:41
  • This has been so very valuable in my figure preparation... thanks again.... however there's a problem. I prefer to use: Text[Style["x",Italic],...] which works with text3D but the option BackgroundColor -> Green does not. Is there any way that option can be incorporated into your nice code? – David G. Stork Feb 24 '21 at 01:36
  • ... also, subscripts don't seem to work. – David G. Stork Feb 24 '21 at 02:11
  • Sorry about the subscripts and the background. I do not know why subscripts do not get rendered and I realize that the workaround of placing them with a separate text3D statement is cumbersome. For background, however, you can easily place a Rectangle behind the text3D. – Nicholas G Feb 24 '21 at 08:24