sourcedemos/rosetta/Stone.xtl

1⍝# Stone: the Rosetta stone's geometry and faces (docs/rosetta.md), a 2⍝# library beside the demo ("st:" u_se< "Stone"). The stone is three 3⍝# carousels sharing one place: the whole stone rolling about the x 4⍝# axis through the idioms (roll it up or down for the previous or next 5⍝# idiom), and its two halves, closed boxes, each turning about the y 6⍝# axis through the languages, one for the top half of the facing 7⍝# idiom, one for the bottom. Each is a square prism, so the geometry 8⍝# never changes: the facing side shows the current item and the sides 9⍝# at right angles show its neighbors, found by index; the lid of the 10⍝# top half and the floor of the bottom name the idioms before and 11⍝# after. The halves turn independently, which is the impossible part: 12⍝# no solid turns like this, and none needs to. 13⍝# Points are Geometry3D's (3 rows), pictures Svg's. 14ᵍ⁼u̲se< "Geometry3D" 15ᵛ⁼u̲se< "Svg" 16 17⍝# The stone's size: width, height (the halves are each half of it) and 18⍝# depth, in scene units; the viewer's distance; the picture's size in 19⍝# pixels and its scale. 20ˡwidth ← 2.4 21ˡheight ← 2.0 22ˡdepth ← 2.0 23ˡeye ← 7.0 24ˡsize ← 420.0 25zoom ← 112.0 26 27⍝# The corners of a box centered on the origin: w h d in a 3-vector, 28⍝# 3 x 8 (near face then far face, each from the top left anticlockwise, 29⍝# as g:c_ube orders them). 30ˡb̲ox ← { whd → (3 8 r̲eshape 8 r̲eplicate 0.5 × whd) × ᵍc̲ube 2 } 31 32⍝# The view: the stone turned a little about x and y so three sides 33⍝# show; every solid below is given in the view's frame. 34ˡv̲iew ← { points → ((ᵍr̲otX 22.0) ᵍt̲urn ᵍr̲otY -28.0) ᵍt̲urn points } 35 36⍝# Points in the view's frame projected and placed in the picture, y 37⍝# up: a 3 x n matrix of points to a 2 x n matrix of pixels. 38ˡs̲creen ← { points → 39 p ← ˡeye ᵍp̲roject points 40 xs ← (ˡsize ÷ 2) + zoom × 1 s̲elect p 41 ys ← (ˡsize ÷ 2) − zoom × 2 s̲elect p 42 (2 c̲at t̲ally xs) r̲eshape xs c̲at ys 43} 44 45⍝# How the prisms turn: an axis advanced by a degrees (90 per item) is 46⍝# drawn turned by only the part of a quarter turn under way, `a m_od 47⍝# 90`, and its sides show the items counted from the one the turn 48⍝# started at, f̲loor a ÷ 90 steps from the first. So a turn ends 49⍝# with the same picture it would have begun from, the sides relabel 50⍝# only at that instant, and the halves always stand upright where 51⍝# they can turn. 52ˡb̲ase ← { a → f̲loor a ÷ 90.0 } 53ˡp̲art ← { a → a − 90.0 × f̲loat ˡb̲ase a } 54 55⍝# The six sides of a language half advanced by a degrees: the closed 56⍝# box of one half (1 the top half, -1 the bottom), its front, 57⍝# right, back and left sides, then its lid and its floor, as a 58⍝# 6 x 3 x 4 array, rolled with the whole stone by d degrees about x. 59⍝# The next language comes round from the right, so the half turns by 60⍝# -a about y. ˢᵗringOffsets says which language each side shows, 61⍝# counted from st:b_ase: 0, 1, 2, -1. The lid and the floor are 62⍝# ordered so that text reads upright as they roll round to the front: 63⍝# the next idiom comes up from below, so the stone rolls by -d about 64⍝# x; ˢᵗcapOffsets says which idiom each shows, counted from 65⍝# st:b_ase: -1 on the lid, 1 on the floor (so the top half's lid and 66⍝# the bottom half's floor name the stone's neighbors; the two caps 67⍝# between the halves are hidden until a half turns). A half is a 68⍝# closed box so that nothing shows through it while it turns. 69⍝# half (a d) ˢᵗr̲ing @. 70⍝# >> "st:" u_se< "Stone" 71⍝# >> s_hape 1 st:r_ing 0.0 0.0 72⍝# 6 3 4 73⍝# >> s_hape -1 st:r_ing 45.0 30.0 74⍝# 6 3 4 75ˡr̲ing ← { half ad → 76 box ← ˡb̲ox ˡwidth c̲at (ˡheight ÷ 2) c̲at ˡdepth 77 lifted ← box + 3 8 r̲eshape 8 r̲eplicate 0.0 c̲at (half × ˡheight ÷ 4) c̲at 0.0 78 sides ← 6 4 r̲eshape 1 2 3 4 2 6 7 3 6 5 8 7 5 1 4 8 5 6 2 1 4 3 7 8 79 turned ← (ᵍr̲otY n̲eg ˡp̲art 1 s̲elect ad) ᵍt̲urn lifted 80 sides ᵍs̲olid ˡv̲iew (ᵍr̲otX n̲eg ˡp̲art 2 s̲elect ad) ᵍt̲urn turned 81} 82ˡringOffsets ← 0 1 2 -1 83ˡcapOffsets ← -1 1 84 85⍝# A face as a panel in the picture: its corners (top left, top right, 86⍝# bottom right, bottom left; 3 x 4) turned by the view, filled, with 87⍝# the given lines drawn on it in face coordinates (0 to 1 across and 88⍝# down), each line a boxed text of markup (runs from v:s_pan, or text 89⍝# through v:e_scape), the first line small (a title), the rest in the 90⍝# code size. A face turned away from the viewer gets its fill only: 91⍝# nearer faces cover it, but text laid on a face seen nearly edge-on 92⍝# could poke out past it. (fill c̲at lines) ˢᵗp̲anel face. 93ˡp̲anel ← { style f → 94 s ← ˡs̲creen f 95 fill ← d̲isclose 1 s̲elect style 96 lines ← 1 d̲rop style 97 shape ← ((ᵛf̲ill fill) c̲at "#30363d" ᵛs̲troke 1.2) ᵛp̲olygon f̲loor 0.5 + s 98 n̲ot ˡf̲acing s ? shape 99 on ← ˡa̲ffine 1 2 4 s̲elect₂ s 100 texts ← ʰj̲oin '{ k → k ˡl̲ine d̲isclose k s̲elect lines } m̲ap r̲ange t̲ally lines 101 shape c̲at on ᵛg̲roup texts 102} 103 104⍝# Picture coordinates to a thousandth of a pixel: the last digits of a 105⍝# projected coordinate differ between machines (their sine and cosine 106⍝# differ in the last place), and the pictures are pinned as text. 107⍝# >> "st:" u_se< "Stone" 108⍝# >> st:r_ounded 1.23456789 100.0004 109⍝# 1.235 100.0 110ˡr̲ounded ← { xs → (f̲loat f̲loor 0.5 + 1000.0 × xs) ÷ 1000.0 } 111 112⍝# The transform attribute mapping the unit square onto a face from its 113⍝# top left, top right and bottom left corners (2 x 3, picture 114⍝# coordinates), as v:m_atrix does, the six numbers rounded after the 115⍝# differences are taken (rounding before them leaves float noise). 116⍝# >> "st:" u_se< "Stone" 117⍝# >> st:a_ffine 2 3 r_eshape 10.0 30.0 10.0 20.0 20.0 40.0 118⍝# transform="matrix(20.0 0.0 0.0 20.0 10.0 20.0)" 119ˡa̲ffine ← { c → 120 tl ← 1 s̲elect₂ c 121 tr ← (2 s̲elect₂ c) − tl 122 bl ← (3 s̲elect₂ c) − tl 123 "transform" ᵛa̲ttr "matrix(" c̲at (f̲ormat ˡr̲ounded tr) c̲at " " c̲at (f̲ormat ˡr̲ounded bl) c̲at " " c̲at (f̲ormat ˡr̲ounded tl) c̲at ")" 124} 125 126⍝# Whether a face's corners (2 x 4 in the picture, y down) go round 127⍝# clockwise on the screen, which is how a face toward the viewer 128⍝# reads (its signed area is positive); a face turned away goes round 129⍝# the other way. 130⍝# >> "st:" u_se< "Stone" 131⍝# >> st:f_acing 2 4 r_eshape 0 1 1 0 0 0 1 1 132⍝# 1 133⍝# >> st:f_acing 2 4 r_eshape 0 0 1 1 0 1 1 0 134⍝# 0 135ˡf̲acing ← { s → 136 xs ← 1 s̲elect s 137 ys ← 2 s̲elect s 138 0 < '+ r̲/ (xs × 1 o̲- ys) − ys × 1 o̲- xs 139} 140 141⍝# One line of markup on a face: line k from the top; the first line 142⍝# is the title, small and gray, the others code, bold and black in a 143⍝# monospace face a tenth of the face high (the faces are small on a 144⍝# phone, so the code is the boldest thing on them). 145ˡl̲ine ← { k t → 146 size ← "k = 1" i̲f< "\"0.06\"; \"0.1\""
i̲f< expands to
({ @ → (k = 1) ? "0.06"◆ "0.1" } @)
147 y ← "k = 1" i̲f< "0.13; 0.14 + 0.16 * f_loat k"
i̲f< expands to
({ @ → (k = 1) ? 0.13◆ 0.14 + 0.16 × f̲loat k } @)
148 color ← "k = 1" i̲f< "\"#4b5563\"; \"#000000\""
i̲f< expands to
({ @ → (k = 1) ? "#4b5563"◆ "#000000" } @)
149 weight ← "k = 1" i̲f< "\"500\"; \"700\""
i̲f< expands to
({ @ → (k = 1) ? "500"◆ "700" } @)
150 ((ᵛa̲t 0.06 c̲at y) c̲at ("font-size" ᵛa̲ttr size) c̲at ("font-weight" ᵛa̲ttr weight) c̲at (ᵛf̲ill color) c̲at "font-family" ᵛa̲ttr "ui-monospace, monospace") ᵛm̲arkup t 151} 152 153⍝# The faces of a solid (F x 3 x 4, in the view's frame) in the order 154⍝# to paint them: far to near, so nearer faces cover farther ones. 155ˡp̲ainting ← { solid → ᵍo̲rder solid } 156 157⍝ Boxed texts joined into one (none is the empty text). 158ʰj̲oin ← { b → 159 0 = t̲ally b ? "" 160 d̲isclose '{ x y → e̲nclose (d̲isclose x) c̲at d̲isclose y } r̲/ b 161}