-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrelativity.red
144 lines (129 loc) · 4.27 KB
/
relativity.red
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
Red [
title: "Face coordinate systems translation mezzanines"
author: @hiiamboris
license: BSD-3
provides: relativity
usage: {
Scaling between DPI-aware logical coordinates (units) and real screen pixels:
units-to-pixels 100
units-to-pixels 100x100
pixels-to-units 100
pixels-to-units 100x100
Translation:
face-to-window point face ;) = point in window CS
window-to-face point face ;) = point in face CS
face-to-screen point face ;) = point in screen CS
face-to-screen/real point face ;) = same, but scaled into real screen pixels
screen-to-face point face ;) = point in face CS
screen-to-face/real point face ;) = same, but takes point in real screen pixels
face-to-face point face1 face2 ;) = point belonging to face1, in face2 CS
Helpers:
window-of face -- returns owning window face
parent parent-of? face -- checks if face belongs to parent
}
limitations: {
`screen-to-face` and `face-to-screen` return wrong coordinates,
because window's client area coordinates cannot be known without R/S
In `face-to-face` these effects negate each other, so this one is correct.
}
]
units-to-pixels: pixels-to-units: window-of: parent-of?:
face-to-window: window-to-face: face-to-screen: screen-to-face: face-to-face:
does [do make error! "No View module!"]
if object? :system/view [ ;-- CLI programs skip this
context [
dpi: any [attempt [system/view/metrics/dpi] 96] ;@@ temporary workaround for #4740
ppd: dpi / 96.0 ;-- pixels per (logical) dot = display scaling factor / 100%
u2p: func [x] [round/to x * ppd 1] ;-- units to pixels, one-dimensional
u2p': func [x] [x * ppd]
p2u: func [x] [round/to x / ppd 1] ;-- pixels to units, one-dimensional
p2u': func [x] [x / ppd]
set 'units-to-pixels function [
"Convert amount in virtual pixels into screen pixels"
size [pair! point2D! integer! float!]
][
switch type?/word size [
pair! [as-pair u2p size/x u2p size/y]
point2D! [u2p' size]
integer! float! [u2p size]
]
]
;; should be careful here not to turn 1 into 0 (dangers of zero division, zero sized images..)
;; does it make sense to clip the result at 1x1 (2D) and 1 (1D)?
set 'pixels-to-units function [
"Convert amount in screen pixels into virtual pixels"
size [pair! point2D! integer!]
][
switch type?/word size [
pair! [as-pair p2u size/x p2u size/y]
point2D! [p2u' size]
integer! [p2u size]
]
]
set 'window-of func [
"Get the window object of FACE"
face [object!]
][
while [all [face 'window <> face/type]] [face: face/parent]
face
]
set 'parent-of? make op! func [
"Checks if PA is a (probably deep) parent of FA"
pa [object!]
fa [object!]
][
while [fa: select fa 'parent] [if pa =? fa [return yes]]
no
]
translate: func [
"Translate coordinate XY between face FA and screen, using OP"
xy [pair! point2D!]
fa [object!]
op [op!] ":+ for face-to-screen; :- for screen-to-face"
/limit lim [word!] "Stop at this face type (default: 'screen)"
][
lim: any [lim 'screen]
while [fa/type <> lim] [
xy: xy op fa/offset
fa: fa/parent
#assert [fa "Face is not connected to window!"]
]
xy
]
set 'face-to-window func [
"Translate a point XY in FACE space into window space"
xy [pair! point2D!] face [object!]
][
translate/limit xy face :+ 'window
]
set 'window-to-face func [
"Translate a point XY in window space into FACE space"
xy [pair! point2D!] face [object!]
][
translate/limit xy face :- 'window
]
set 'face-to-screen func [
"Translate a point in face space into screen space"
xy [pair! point2D!] face [object!]
/real "Translate to screen pixels (not scaled by DPI)"
][
xy: translate xy face :+
if real [xy: units-to-pixels xy]
xy
]
set 'screen-to-face func [
"Translate a point in screen space into face space"
xy [pair! point2D!] face [object!]
/real "XY is in screen pixels (not scaled by DPI)"
][
if real [xy: pixels-to-units xy]
translate xy face :-
]
set 'face-to-face func [
"Translate a point XY from FACE1 space into FACE2 space"
xy [pair! point2D!] face1 [object!] face2 [object!]
][
screen-to-face face-to-screen xy face1 face2
]
]
];if object? :system/view [