GenericMappingTools / GenericMappingTools/gmt

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

Open
#8,907 9 comments 0 reactions 1 assignee Claimed by @Esteban82 View on GitHub
feature request question
Dominant language
C
Stars
979
Forks
414
Avg merge
17h 26m
Merged PRs (30d)
54

Description

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/`

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

```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.