cpdfposition

This commit is contained in:
John Whitington 2021-08-12 20:38:55 +01:00
parent afe80205dd
commit 671a637271
6 changed files with 210 additions and 212 deletions

View File

@ -1,6 +1,6 @@
# Build the cpdf command line tools and top level
MODS = tjutil tjutf16 tjllist tjparserMonad tjjson xmlm \
cpdfwriteJSON cpdfstrftime cpdfcoord cpdfpagespec cpdf cpdfcommand
cpdfwriteJSON cpdfstrftime cpdfcoord cpdfpagespec cpdfposition cpdf cpdfcommand
SOURCES = $(foreach x,$(MODS),$(x).ml $(x).mli) cpdfcommandrun.ml

121
cpdf.ml
View File

@ -1268,94 +1268,6 @@ let print_fonts pdf =
(* \section{Superimpose text, page numbers etc.} *)
type position =
| PosCentre of float * float
| PosLeft of float * float
| PosRight of float * float
| Top of float
| TopLeft of float
| TopRight of float
| Left of float
| BottomLeft of float
| Bottom of float
| BottomRight of float
| Right of float
| Diagonal
| ReverseDiagonal
| Centre
let string_of_position = function
| PosCentre (a, b) -> Printf.sprintf "PosCentre %f %f" a b
| PosLeft (a, b) -> Printf.sprintf "PosLeft %f %f" a b
| PosRight (a, b) -> Printf.sprintf "PosRight %f %f" a b
| Top a -> Printf.sprintf "Top %f" a
| TopLeft a -> Printf.sprintf "TopLeft %f" a
| TopRight a -> Printf.sprintf "TopRight %f" a
| Left a -> Printf.sprintf "Left %f" a
| BottomLeft a -> Printf.sprintf "BottomLeft %f" a
| Bottom a -> Printf.sprintf "Bottom %f" a
| BottomRight a -> Printf.sprintf "BottomRight %f" a
| Right a -> Printf.sprintf "Right %f" a
| Diagonal -> "Diagonal"
| ReverseDiagonal -> "Reverse Diagonal"
| Centre -> "Centre"
type orientation =
| Horizontal
| Vertical
| VerticalDown
type justification = LeftJustify | CentreJustify | RightJustify
(* Given the mediabox, calculate an absolute position for the text. *)
let calculate_position ignore_d w (xmin, ymin, xmax, ymax) orientation pos =
let rot = if orientation = VerticalDown then rad_of_deg 270. else 0. in
match pos with
| Centre ->
(xmin +. xmax) /. 2. -. w /. 2.,
(ymin +. ymax) /. 2.,
rot
| Diagonal ->
let angle = atan ((ymax -. ymin) /. (xmax -. xmin))
in let cx, cy = (xmax +. xmin) /. 2., (ymax +. ymin) /. 2. in
let dl = w /. 2. in
let dx = dl *. cos angle
in let dy = dl *. sin angle in
cx -. dx, cy -. dy, angle
| ReverseDiagonal ->
let angle = atan ((ymax -. ymin) /. (xmax -. xmin))
in let cx, cy = (xmax +. xmin) /. 2., (ymax +. ymin) /. 2. in
let dl = w /. 2. in
let dx = dl *. cos angle
in let dy = dl *. sin angle in
cx -. dx, (ymax +. ymin) -. (cy -. dy), angle -. ((2. *. pi) -. ((pi -. (2. *. angle)) *. 2.) /. 2.) +. pi
| PosLeft (x, y) -> xmin +. x, ymin +. y, rot
| PosCentre (x, y) -> xmin +. x -. (w /. 2.), ymin +. y, rot
| PosRight (x, y) -> xmin +. x -. w, ymin +. y, rot
| Top d ->
let d = if ignore_d then 0. else d in
(xmin +. xmax) /. 2. -. w /. 2., ymax -. d, rot
| TopLeft d ->
let d = if ignore_d then 0. else d in
xmin +. d, ymax -. d, rot
| TopRight d ->
let d = if ignore_d then 0. else d in
xmax -. d -. w, ymax -. d, rot
| Left d ->
let d = if ignore_d then 0. else d in
xmin +. d, (ymax +. ymin) /. 2., rot
| BottomLeft d ->
let d = if ignore_d then 0. else d in
xmin +. d, ymin +. d, rot
| Bottom d ->
let d = if ignore_d then 0. else d in
(xmin +. xmax) /. 2. -. w /. 2., ymin +. d, rot
| BottomRight d ->
let d = if ignore_d then 0. else d in
xmax -. d -. w, ymin +. d, rot
| Right d ->
let d = if ignore_d then 0. else d in
xmax -. d -. w, (ymax +. ymin) /. 2., rot
(* Process UTF8 text to /WinAnsiEncoding string. *)
let winansi_of_utf8 s =
@ -1445,9 +1357,13 @@ let ops longest_w metrics x y rotate hoffset voffset outline linewidth unique_fo
Pdfops.Op_EMC;
Pdfops.Op_Q]
type justification = LeftJustify | CentreJustify | RightJustify
(* Find the h-offset for justification based on the longest width, the current
width, the justification and the position. *)
let find_justification_offsets longest_w w position = function
let find_justification_offsets longest_w w position j =
let open Cpdfposition in
match j with
| LeftJustify ->
begin match position with
| TopLeft _ | Left _ | PosLeft _ | BottomLeft _ -> 0.
@ -1599,6 +1515,8 @@ let extract_text extract_text_font_size pdf range =
fold_left (fun x y -> x ^ (if x <> "" && y <> "" then "\n" else "") ^ y) ""
(map_pages (extract_page_text extract_text_font_size pdf) pdf range)
let addtext
metrics lines linewidth outline fast colour fontname embed bates batespad fontsize font
underneath position hoffset voffset text pages orientation cropbox opacity
@ -1696,7 +1614,7 @@ let addtext
else
Pdf.parse_rectangle page.Pdfpage.mediabox
in
let x, y, rotate = calculate_position false textwidth mediabox orientation position in
let x, y, rotate = Cpdfposition.calculate_position false textwidth mediabox orientation position in
let hoffset, voffset =
if position = Diagonal || position = ReverseDiagonal
then -. (cos ((pi /. 2.) -. rotate) *. voffset), sin ((pi /. 2.) -. rotate) *. voffset
@ -1785,6 +1703,7 @@ let
let lines = map unescape_string (split_at_newline text) in
let pdf = ref pdf in
let voffset =
let open Cpdfposition in
match position with
| Bottom _ | BottomLeft _ | BottomRight _ ->
ref (0. -. (linespacing *. fontsize *. (float (length lines) -. 1.)))
@ -1824,7 +1743,7 @@ let
iter
(fun line ->
let voff, hoff =
if orientation = Vertical then 0., -.(!voffset) else !voffset, 0.
if orientation = Cpdfposition.Vertical then 0., -.(!voffset) else !voffset, 0.
in
pdf :=
addtext metrics lines linewidth outline fast colour fontname
@ -2241,6 +2160,7 @@ let stamp_shift_of_position topline midline sw sh w h p =
else if topline then sh
else 0.
in
let open Cpdfposition in
match p with
| PosCentre (ox, oy) -> ox -. half sw, oy -. dy
| PosLeft (ox, oy) -> ox, oy -. dy
@ -2819,13 +2739,13 @@ let scale_to_fit_pdf ?(fast=false) position input_scale xylist op pdf range =
let scale = fmin fx fy *. input_scale in
let trans_x =
match position with
Left _ -> 0.
| Right _ -> (x -. (maxx *. scale))
Cpdfposition.Left _ -> 0.
| Cpdfposition.Right _ -> (x -. (maxx *. scale))
| _ -> (x -. (maxx *. scale)) /. 2.
and trans_y =
match position with
| Top _ -> (y -. (maxy *. scale))
| Bottom _ -> 0.
| Cpdfposition.Top _ -> (y -. (maxy *. scale))
| Cpdfposition.Bottom _ -> 0.
| _ -> (y -. (maxy *. scale)) /. 2.
in
(Pdftransform.matrix_of_transform
@ -2853,8 +2773,9 @@ let scale_page_contents ?(fast=false) scale position pdf pnum page =
| Some r -> r
| None -> page.Pdfpage.mediabox)
in
let sx, sy, _ = calculate_position true 0. box Horizontal position in
let sx, sy, _ = Cpdfposition.calculate_position true 0. box Horizontal position in
let tx, ty =
let open Cpdfposition in
match position with
| Top t -> 0., -.t
| TopLeft t -> t, -.t
@ -3343,14 +3264,8 @@ let dc = "http://purl.org/dc/elements/1.1/"
let rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
(* For OCaml < 4.00 *)
let string_trim s =
implode
(rev (dropwhile
Pdf.is_whitespace (rev (dropwhile Pdf.is_whitespace (explode s)))))
let combine_with_spaces strs =
string_trim
String.trim
(fold_left (fun x y -> x ^ (if x <> "" then ", " else "") ^ y) "" strs)
(* Collect all <li> elements inside a seq, bag, or alt. Combine with commas. If

View File

@ -14,23 +14,6 @@ exception HardError of string
(** Two exceptions recommended for use with the library, though currently not
raised by any function in this module. Cpdfcommand uses them extensively. *)
(** Possible positions for adding text and other uses. See cpdfmanual.pdf *)
type position =
| PosCentre of float * float
| PosLeft of float * float
| PosRight of float * float
| Top of float
| TopLeft of float
| TopRight of float
| Left of float
| BottomLeft of float
| Bottom of float
| BottomRight of float
| Right of float
| Diagonal
| ReverseDiagonal
| Centre
(** {2 Debug} *)
(** Debug: Print out a PDF in readable form to the terminal *)
@ -174,7 +157,7 @@ val combine_pages : bool -> Pdf.t -> Pdf.t -> bool -> bool -> bool -> Pdf.t
(** [stamp relative_to_cropbox position topline midline fast scale_to_fit isover range over pdf] stamps the first page of
[over] over each page of the PDF. The arguments have the same meaning as in
[combine_pages]. *)
val stamp : bool -> position -> bool -> bool -> bool -> bool -> bool -> int list -> Pdf.t -> Pdf.t -> Pdf.t
val stamp : bool -> Cpdfposition.position -> bool -> bool -> bool -> bool -> bool -> int list -> Pdf.t -> Pdf.t -> Pdf.t
(** {2 Splitting PDFs} *)
@ -194,32 +177,12 @@ val list_fonts : Pdf.t -> (int * string * string * string * string) list
(** Expand the string "now" to a PDF date string, ignoring any other string *)
val expand_date : string -> string
(** Produce a debug string of a [position] *)
val string_of_position : position -> string
(** Orientation of the string on the page *)
type orientation =
| Horizontal
| Vertical
| VerticalDown
(** Justification of multiline text *)
type justification =
| LeftJustify
| CentreJustify
| RightJustify
(** [calculate_position ignore_d w (xmin, ymin, xmax, ymax) orientation pos] calculates
the absolute position of text given its width, bounding box, orientation and
position. If [ignore_d] is true, the distance from the position (e.g 10 in
TopLeft 10) is ignored (considered zero). *)
val calculate_position :
bool ->
float ->
float * float * float * float ->
orientation -> position -> float * float * float
(** Call [add_texts metrics linewidth outline fast fontname font bates batespad colour
position linespacing fontsize underneath text pages orientation
relative_to_cropbox midline_adjust topline filename pdf]. For details see cpdfmanual.pdf *)
@ -234,13 +197,13 @@ val addtexts :
int -> (* bates number *)
int option -> (* bates padding width *)
float * float * float -> (*colour*)
position -> (*position*)
Cpdfposition.position -> (*position*)
float -> (*linespacing*)
float -> (*fontsize*)
bool -> (*underneath*)
string ->(*text*)
int list ->(*page range*)
orientation ->(*orientation*)
Cpdfposition.orientation ->(*orientation*)
bool ->(*relative to cropbox?*)
float ->(*opacity*)
justification ->(*justification*)
@ -323,10 +286,10 @@ val scale_pdf : ?fast:bool -> (float * float) list -> Pdf.t -> int list -> Pdf.t
(** [scale_to_fit_pdf fast position input_scale x y op pdf range] scales a page to fit the
page size given by (x, y) and by the [input_scale] (e.g 1.0 = scale to fit, 0.9
= scale to fit leaving a border etc.). [op] is unused. *)
val scale_to_fit_pdf : ?fast:bool -> position -> float -> (float * float) list -> 'a -> Pdf.t -> int list -> Pdf.t
val scale_to_fit_pdf : ?fast:bool -> Cpdfposition.position -> float -> (float * float) list -> 'a -> Pdf.t -> int list -> Pdf.t
(** Scale the contents of a page by a given factor centred around a given point in a given range. *)
val scale_contents : ?fast:bool -> position -> float -> Pdf.t -> int list -> Pdf.t
val scale_contents : ?fast:bool -> Cpdfposition.position -> float -> Pdf.t -> int list -> Pdf.t
val trim_marks : ?fast:bool -> Pdf.t -> int list -> Pdf.t

View File

@ -379,7 +379,7 @@ type args =
mutable fontsize : float;
mutable color : float * float * float;
mutable opacity : float;
mutable position : Cpdf.position;
mutable position : Cpdfposition.position;
mutable underneath : bool;
mutable linespacing : float;
mutable midline : bool;
@ -388,7 +388,7 @@ type args =
mutable bates : int;
mutable batespad : int option;
mutable prerotate : bool;
mutable orientation : Cpdf.orientation;
mutable orientation : Cpdfposition.orientation;
mutable relative_to_cropbox : bool;
mutable keepversion : bool;
mutable bycolumns : bool;
@ -483,7 +483,7 @@ let args =
fontsize = 12.;
color = 0., 0., 0.;
opacity = 1.;
position = Cpdf.TopLeft 100.;
position = Cpdfposition.TopLeft 100.;
underneath = false;
linespacing = 1.;
midline = false;
@ -492,7 +492,7 @@ let args =
bates = 0;
batespad = None;
prerotate = false;
orientation = Cpdf.Horizontal;
orientation = Cpdfposition.Horizontal;
relative_to_cropbox = false;
keepversion = false;
bycolumns = false;
@ -587,7 +587,7 @@ let reset_arguments () =
args.fontsize <- 12.;
args.color <- 0., 0., 0.;
args.opacity <- 1.;
args.position <- Cpdf.TopLeft 100.;
args.position <- Cpdfposition.TopLeft 100.;
args.underneath <- false;
args.linespacing <- 1.;
args.midline <- false;
@ -596,7 +596,7 @@ let reset_arguments () =
args.bates <- 0;
args.batespad <- None;
args.prerotate <- false;
args.orientation <- Cpdf.Horizontal;
args.orientation <- Cpdfposition.Horizontal;
args.relative_to_cropbox <- false;
args.keepversion <- false;
args.bycolumns <- false;
@ -1047,11 +1047,11 @@ let setaddbookmarks s =
let setstampon f =
setop (StampOn f) ();
(* Due to an earlier bad decision (default position), we have this nasty hack *)
if args.position = Cpdf.TopLeft 100. then args.position <- Cpdf.BottomLeft 0.
if args.position = Cpdfposition.TopLeft 100. then args.position <- Cpdfposition.BottomLeft 0.
let setstampunder f =
setop (StampUnder f) ();
if args.position = Cpdf.TopLeft 100. then args.position <- Cpdf.BottomLeft 0.
if args.position = Cpdfposition.TopLeft 100. then args.position <- Cpdfposition.BottomLeft 0.
let setstampasxobject f =
setop (StampAsXObject f) ()
@ -1061,58 +1061,58 @@ let setcombinepages f =
let setposcenter s =
let x, y = Cpdfcoord.parse_coordinate empty s in
args.position <- Cpdf.PosCentre (x, y)
args.position <- Cpdfposition.PosCentre (x, y)
let setposleft s =
let x, y = Cpdfcoord.parse_coordinate empty s in
args.position <- Cpdf.PosLeft (x, y)
args.position <- Cpdfposition.PosLeft (x, y)
let setposright s =
let x, y = Cpdfcoord.parse_coordinate empty s in
args.position <- Cpdf.PosRight (x, y)
args.position <- Cpdfposition.PosRight (x, y)
let settop n =
args.position <- Cpdf.Top (Cpdfcoord.parse_single_number empty n);
args.position <- Cpdfposition.Top (Cpdfcoord.parse_single_number empty n);
args.justification <- Cpdf.CentreJustify
let settopleft n =
args.position <- Cpdf.TopLeft (Cpdfcoord.parse_single_number empty n);
args.position <- Cpdfposition.TopLeft (Cpdfcoord.parse_single_number empty n);
args.justification <- Cpdf.LeftJustify
let settopright n =
args.position <- Cpdf.TopRight (Cpdfcoord.parse_single_number empty n);
args.position <- Cpdfposition.TopRight (Cpdfcoord.parse_single_number empty n);
args.justification <- Cpdf.RightJustify
let setleft n =
args.position <- Cpdf.Left (Cpdfcoord.parse_single_number empty n);
args.position <- Cpdfposition.Left (Cpdfcoord.parse_single_number empty n);
args.justification <- Cpdf.LeftJustify
let setbottomleft n =
args.position <- Cpdf.BottomLeft (Cpdfcoord.parse_single_number empty n);
args.position <- Cpdfposition.BottomLeft (Cpdfcoord.parse_single_number empty n);
args.justification <- Cpdf.LeftJustify
let setbottom n =
args.position <- Cpdf.Bottom (Cpdfcoord.parse_single_number empty n);
args.position <- Cpdfposition.Bottom (Cpdfcoord.parse_single_number empty n);
args.justification <- Cpdf.CentreJustify
let setbottomright n =
args.position <- Cpdf.BottomRight (Cpdfcoord.parse_single_number empty n);
args.position <- Cpdfposition.BottomRight (Cpdfcoord.parse_single_number empty n);
args.justification <- Cpdf.RightJustify
let setright n =
args.position <- Cpdf.Right (Cpdfcoord.parse_single_number empty n);
args.position <- Cpdfposition.Right (Cpdfcoord.parse_single_number empty n);
args.justification <- Cpdf.RightJustify
let setdiagonal n =
args.position <- Cpdf.Diagonal;
args.position <- Cpdfposition.Diagonal;
args.justification <- Cpdf.CentreJustify
let setreversediagonal n =
args.position <- Cpdf.ReverseDiagonal;
args.position <- Cpdfposition.ReverseDiagonal;
args.justification <- Cpdf.CentreJustify
let setcenter n =
args.position <- Cpdf.Centre;
args.position <- Cpdfposition.Centre;
args.justification <- Cpdf.CentreJustify
let setbatespad n =
@ -1220,7 +1220,7 @@ let setscaletofitscale f =
let setscalecontents f =
detect_duplicate_op (ScaleContents f);
args.op <- Some (ScaleContents f);
args.position <- Cpdf.Diagonal (* Will be center *)
args.position <- Cpdfposition.Diagonal (* Will be center *)
let setsqueeze () =
args.squeeze <- true;
@ -1345,10 +1345,10 @@ let setp2ppath p =
args.path_to_p2p <- p
let settextvertical () =
args.orientation <- Cpdf.Vertical
args.orientation <- Cpdfposition.Vertical
let settextverticaldown () =
args.orientation <- Cpdf.VerticalDown
args.orientation <- Cpdfposition.VerticalDown
let setfrombox s =
detect_duplicate_op CopyBox;
@ -3351,12 +3351,12 @@ let addrectangle
Pdf.parse_rectangle page.Pdfpage.mediabox
in
let x, y, _ =
Cpdf.calculate_position false w mediabox Cpdf.Horizontal position
Cpdfposition.calculate_position false w mediabox Cpdfposition.Horizontal position
in
let x, y =
match position with
Cpdf.Top _ | Cpdf.TopLeft _ | Cpdf.TopRight _ -> (x, y -. h)
| Cpdf.Centre | Cpdf.PosCentre _ -> (x, y -. (h /. 2.))
Cpdfposition.Top _ | Cpdfposition.TopLeft _ | Cpdfposition.TopRight _ -> (x, y -. h)
| Cpdfposition.Centre | Cpdfposition.PosCentre _ -> (x, y -. (h /. 2.))
| _ -> (x, y)
in
let ops =

88
cpdfposition.ml Normal file
View File

@ -0,0 +1,88 @@
open Pdfutil
type position =
| PosCentre of float * float
| PosLeft of float * float
| PosRight of float * float
| Top of float
| TopLeft of float
| TopRight of float
| Left of float
| BottomLeft of float
| Bottom of float
| BottomRight of float
| Right of float
| Diagonal
| ReverseDiagonal
| Centre
let string_of_position = function
| PosCentre (a, b) -> Printf.sprintf "PosCentre %f %f" a b
| PosLeft (a, b) -> Printf.sprintf "PosLeft %f %f" a b
| PosRight (a, b) -> Printf.sprintf "PosRight %f %f" a b
| Top a -> Printf.sprintf "Top %f" a
| TopLeft a -> Printf.sprintf "TopLeft %f" a
| TopRight a -> Printf.sprintf "TopRight %f" a
| Left a -> Printf.sprintf "Left %f" a
| BottomLeft a -> Printf.sprintf "BottomLeft %f" a
| Bottom a -> Printf.sprintf "Bottom %f" a
| BottomRight a -> Printf.sprintf "BottomRight %f" a
| Right a -> Printf.sprintf "Right %f" a
| Diagonal -> "Diagonal"
| ReverseDiagonal -> "Reverse Diagonal"
| Centre -> "Centre"
type orientation =
| Horizontal
| Vertical
| VerticalDown
(* Given the mediabox, calculate an absolute position for the text. *)
let calculate_position ignore_d w (xmin, ymin, xmax, ymax) orientation pos =
let rot = if orientation = VerticalDown then rad_of_deg 270. else 0. in
match pos with
| Centre ->
(xmin +. xmax) /. 2. -. w /. 2.,
(ymin +. ymax) /. 2.,
rot
| Diagonal ->
let angle = atan ((ymax -. ymin) /. (xmax -. xmin))
in let cx, cy = (xmax +. xmin) /. 2., (ymax +. ymin) /. 2. in
let dl = w /. 2. in
let dx = dl *. cos angle
in let dy = dl *. sin angle in
cx -. dx, cy -. dy, angle
| ReverseDiagonal ->
let angle = atan ((ymax -. ymin) /. (xmax -. xmin))
in let cx, cy = (xmax +. xmin) /. 2., (ymax +. ymin) /. 2. in
let dl = w /. 2. in
let dx = dl *. cos angle
in let dy = dl *. sin angle in
cx -. dx, (ymax +. ymin) -. (cy -. dy), angle -. ((2. *. pi) -. ((pi -. (2. *. angle)) *. 2.) /. 2.) +. pi
| PosLeft (x, y) -> xmin +. x, ymin +. y, rot
| PosCentre (x, y) -> xmin +. x -. (w /. 2.), ymin +. y, rot
| PosRight (x, y) -> xmin +. x -. w, ymin +. y, rot
| Top d ->
let d = if ignore_d then 0. else d in
(xmin +. xmax) /. 2. -. w /. 2., ymax -. d, rot
| TopLeft d ->
let d = if ignore_d then 0. else d in
xmin +. d, ymax -. d, rot
| TopRight d ->
let d = if ignore_d then 0. else d in
xmax -. d -. w, ymax -. d, rot
| Left d ->
let d = if ignore_d then 0. else d in
xmin +. d, (ymax +. ymin) /. 2., rot
| BottomLeft d ->
let d = if ignore_d then 0. else d in
xmin +. d, ymin +. d, rot
| Bottom d ->
let d = if ignore_d then 0. else d in
(xmin +. xmax) /. 2. -. w /. 2., ymin +. d, rot
| BottomRight d ->
let d = if ignore_d then 0. else d in
xmax -. d -. w, ymin +. d, rot
| Right d ->
let d = if ignore_d then 0. else d in
xmax -. d -. w, (ymax +. ymin) /. 2., rot

32
cpdfposition.mli Normal file
View File

@ -0,0 +1,32 @@
(** Possible positions for adding text and other uses. See cpdfmanual.pdf *)
type position =
PosCentre of float * float
| PosLeft of float * float
| PosRight of float * float
| Top of float
| TopLeft of float
| TopRight of float
| Left of float
| BottomLeft of float
| Bottom of float
| BottomRight of float
| Right of float
| Diagonal
| ReverseDiagonal
| Centre
(** Produce a debug string of a [position] *)
val string_of_position : position -> string
(** Orientation of the string on the page *)
type orientation = Horizontal | Vertical | VerticalDown
(** [calculate_position ignore_d w (xmin, ymin, xmax, ymax) orientation pos] calculates
the absolute position of text given its width, bounding box, orientation and
position. If [ignore_d] is true, the distance from the position (e.g 10 in
TopLeft 10) is ignored (considered zero). *)
val calculate_position :
bool ->
float ->
float * float * float * float ->
orientation -> position -> float * float * float