Skip to content

Native support for perspective-transformed text rendering (PostScript-level transformation) #8907

@Esteban82

Description

@Esteban82

I would like to share a PostScript prototype I implemented and ask for feedback before attempting to integrate this into the GMT source code.

This is related to #8816, but I decided to post it separately for clarity.

I implemented a PostScript prototype that applies a backward rotation around the bottom edge of a text object, followed by a perspective projection. The visual effect is similar to the perspective tool in GIMP. The prototype is intended for text elements such as titles, for now.

ImageImage2

How it would be used

The user would only need to provide a rotation angle and a Field-of-View (FOV) (as in GIMP).
All other parameters (text bounding box, rotation axis, focal distance, vanishing point, depth scaling) are computed automatically.
The rotation axis and vanishing point are hire-wire the the bottom edge and the center of figure respectively.

How it would be used

The user would only need to provide:

  • A rotation angle
  • A Field-of-View (FOV)

Conceptually, something like:

+p<angle>/<fov>

All other parameters (bounding box, focal distance, depth scaling, etc.) are computed automatically.

Currently, the rotation axis and vanishing point are hard-wired to:

  • The bottom edge of the text (rotation axis)
  • The center of the figure (vanishing point)

However, both could be made configurable without major changes.

Current Question

Before moving toward a C-level implementation inside GMT (likely within PSL or the text rendering logic), I would appreciate feedback on the PostScript prototype.

In particular:

  • Are there obvious inefficiencies in the current path transformation approach?
  • Is there an existing PSL mechanism that could simplify this?
  • Any recommendations to improve performance or numerical stability?

My goal is to ensure the PostScript logic is clean and efficient before porting it into the GMT source code.

Postscript prototype

Output:

Image

File:

%!PS-Adobe-3.0
%%BoundingBox: 0 0 245 100

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 1. USER PARAMETERS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

/ang_rotation 30 def     % Rotation angle (degrees)
/fov 45 def              % Field of view (perspective angle in degrees)

/texto {
  (Episode IV)
} def                    % Text to render

/Times-Roman findfont 54 scalefont setfont  % Font configuration

/z_near 0 def            % Canvas is located at z = 0 (added just in case)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 2. BASIC MATH UTILITIES 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Basic trigonometric functions. This could be done in C.
/tan { dup sin exch cos div } bind def
/tan_f { fov 2 div tan } bind def
/cos_ang_rotation { ang_rotation cos } bind def
/sin_ang_rotation { ang_rotation sin } bind def

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 3. TEXT BOUNDING BOX
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Computes bounding box and dimensions of the text path

/Get_Text_Bounding_Box {
  true charpath
  pathbbox
  /top exch def
  /right exch def
  /bottom exch def
  /left exch def
  /height top bottom sub def
  /width right left sub def
} bind def

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 4. ROTATION SETUP
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

/Rotation_Setup {
  % Use bottom edge as rotation axis (it can be modified)
  /axisOriginY bottom def
} bind def

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 5. PERSPECTIVE SETUP
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

/Calculate_Focal_distance {
  /focal height 2 div tan_f div def
} bind def

/Set_Vanishing_point {                    % Set at the center of the figure
  /Cx right left add 2 div def        % Vanishing point X (center)
  /Cy top bottom add 2 div def        % Vanishing point Y (center)
} bind def

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 6. PATH TRANSFORMATION UTILITIES from Bill Casselman.
%% https://personal.math.ubc.ca/~cass/graphics/manual/transform.inc
%% I have his permission to use it.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% The original file includes a subdivide procedure. I am not using it. 
% But maybe it can be included.

/ctransform {
  load
  8 dict begin
  /f exch def
  [
    { [ 3 1 roll f {moveto} ] }
    { [ 3 1 roll f {lineto} ] }
    { [ 7 1 roll f 6 2 roll f 6 2 roll f 6 2 roll {curveto} ] }
    { [ {closepath} ] }
    pathforall
  ]
  newpath
  { aload pop exec } forall
  end
} def

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 7. ROTATION + PERSPECTIVE PROJECTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Transforms (x, y) -> (xp, yp)

/rotate-project {
  /y exch def
  /x exch def

  % Compute depth (z') after rotation
  y axisOriginY sub
  sin_ang_rotation mul
  z_near add
  /z exch def

  % Perspective scaling factor (depends on Z)
  /s z focal add focal exch div def

  % Transform X
  x Cx sub s mul Cx add

  % Rotate Y around axis, then apply perspective
  y axisOriginY sub
  cos_ang_rotation mul
  axisOriginY add
  Cy sub s mul Cy add
} bind def

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 8. RENDERING
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

0 62 translate
0.6 setgray

% --- Compute text metrics ---
newpath
0 0 moveto
texto

Get_Text_Bounding_Box
Rotation_Setup
Calculate_Focal_distance
Set_Vanishing_point

% --- Draw original text (gray) ---
newpath
0 -50 moveto
texto
show

% --- Draw perspective-transformed text (red) ---
newpath
0 0 moveto
texto
true charpath
/rotate-project ctransform
1 0 0 setrgbcolor
fill

showpage

Metadata

Metadata

Assignees

Labels

feature requestRequest a new featurequestionFurther information is requested

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions