@axpecter/lync 2.3.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +302 -299
  3. package/Types.luau +215 -0
  4. package/api/Define.luau +154 -0
  5. package/api/Lifecycle.luau +119 -0
  6. package/api/Packet.luau +108 -0
  7. package/api/Query.luau +149 -0
  8. package/api/Set.luau +189 -0
  9. package/channel/Blocks.luau +124 -0
  10. package/channel/Group.luau +118 -0
  11. package/channel/Link.luau +135 -0
  12. package/channel/Outbox.luau +261 -0
  13. package/channel/Recipients.luau +129 -0
  14. package/channel/Transport.luau +138 -0
  15. package/codec/Base.luau +236 -0
  16. package/codec/Composites.luau +128 -0
  17. package/codec/Datatypes.luau +162 -0
  18. package/codec/Legality.luau +61 -0
  19. package/codec/Scalars.luau +72 -0
  20. package/codec/Text.luau +89 -0
  21. package/codec/init.luau +39 -0
  22. package/index.d.ts +285 -0
  23. package/init.luau +91 -0
  24. package/kernel/Bits.luau +16 -0
  25. package/kernel/Catalog.luau +121 -0
  26. package/kernel/Heap.luau +80 -0
  27. package/kernel/Listeners.luau +95 -0
  28. package/kernel/Log.luau +197 -0
  29. package/kernel/Tally.luau +128 -0
  30. package/kernel/Threads.luau +44 -0
  31. package/kernel/Wide.luau +267 -0
  32. package/package.json +15 -32
  33. package/runtime/Boundary.luau +180 -0
  34. package/runtime/Dispatch.luau +91 -0
  35. package/runtime/Handles.luau +114 -0
  36. package/runtime/Handshake.luau +90 -0
  37. package/runtime/Pending.luau +196 -0
  38. package/runtime/Router.luau +379 -0
  39. package/runtime/Session.luau +731 -0
  40. package/shape/Compile.luau +340 -0
  41. package/shape/Folds.luau +332 -0
  42. package/shape/Frames.luau +170 -0
  43. package/shape/Grid.luau +169 -0
  44. package/shape/Ir.luau +540 -0
  45. package/shape/Joins.luau +125 -0
  46. package/shape/Legality.luau +273 -0
  47. package/shape/Lower.luau +419 -0
  48. package/shape/Order.luau +64 -0
  49. package/shape/Regions.luau +113 -0
  50. package/shape/Rotation.luau +472 -0
  51. package/shape/Serial.luau +258 -0
  52. package/shape/Words.luau +89 -0
  53. package/shape/emit/Block.luau +453 -0
  54. package/shape/emit/Column.luau +338 -0
  55. package/shape/emit/Leaf.luau +462 -0
  56. package/shape/emit/Word.luau +234 -0
  57. package/shape/emit/init.luau +222 -0
  58. package/shape/init.luau +78 -0
  59. package/state/Apply.luau +330 -0
  60. package/state/Audience.luau +126 -0
  61. package/state/Catchup.luau +218 -0
  62. package/state/Coalesce.luau +43 -0
  63. package/state/Fieldset.luau +177 -0
  64. package/state/Newest.luau +339 -0
  65. package/state/Plan.luau +441 -0
  66. package/state/Sections.luau +468 -0
  67. package/state/Store.luau +188 -0
  68. package/state/Sync.luau +762 -0
  69. package/wire/Bytes.luau +118 -0
  70. package/wire/Clock.luau +37 -0
  71. package/wire/Protocol.luau +110 -0
  72. package/wire/Sequence.luau +40 -0
  73. package/wire/Varint.luau +124 -0
  74. package/src/Types.luau +0 -180
  75. package/src/api/Group.luau +0 -156
  76. package/src/api/Packet.luau +0 -317
  77. package/src/api/Query.luau +0 -338
  78. package/src/api/Scope.luau +0 -90
  79. package/src/api/Signal.luau +0 -193
  80. package/src/codec/Base.luau +0 -136
  81. package/src/codec/composite/Array.luau +0 -473
  82. package/src/codec/composite/Map.luau +0 -485
  83. package/src/codec/composite/Optional.luau +0 -54
  84. package/src/codec/composite/Shared.luau +0 -311
  85. package/src/codec/composite/Struct.luau +0 -494
  86. package/src/codec/composite/Tagged.luau +0 -103
  87. package/src/codec/composite/Tuple.luau +0 -143
  88. package/src/codec/datatype/Buffer.luau +0 -66
  89. package/src/codec/datatype/CFrame.luau +0 -109
  90. package/src/codec/datatype/Color.luau +0 -32
  91. package/src/codec/datatype/Instance.luau +0 -52
  92. package/src/codec/datatype/IntVector.luau +0 -39
  93. package/src/codec/datatype/NumberRange.luau +0 -39
  94. package/src/codec/datatype/Ray.luau +0 -36
  95. package/src/codec/datatype/Rect.luau +0 -31
  96. package/src/codec/datatype/Region.luau +0 -62
  97. package/src/codec/datatype/Sequence.luau +0 -184
  98. package/src/codec/datatype/String.luau +0 -94
  99. package/src/codec/datatype/UDim.luau +0 -46
  100. package/src/codec/datatype/Vector.luau +0 -130
  101. package/src/codec/meta/Auto.luau +0 -318
  102. package/src/codec/meta/Bitfield.luau +0 -160
  103. package/src/codec/meta/Custom.luau +0 -33
  104. package/src/codec/meta/DeltaScalar.luau +0 -390
  105. package/src/codec/meta/Enum.luau +0 -50
  106. package/src/codec/meta/Float.luau +0 -38
  107. package/src/codec/meta/Nothing.luau +0 -13
  108. package/src/codec/meta/Unknown.luau +0 -41
  109. package/src/codec/primitive/Bool.luau +0 -26
  110. package/src/codec/primitive/Float16.luau +0 -99
  111. package/src/codec/primitive/Int.luau +0 -59
  112. package/src/codec/primitive/Number.luau +0 -24
  113. package/src/codec/primitive/Signed.luau +0 -32
  114. package/src/codec/primitive/Varint.luau +0 -165
  115. package/src/codec/primitive/Zint.luau +0 -99
  116. package/src/index.d.ts +0 -423
  117. package/src/init.luau +0 -362
  118. package/src/internal/Baseline.luau +0 -57
  119. package/src/internal/Channel.luau +0 -484
  120. package/src/internal/Middleware.luau +0 -100
  121. package/src/internal/Pool.luau +0 -66
  122. package/src/internal/Registry.luau +0 -119
  123. package/src/internal/Transport.luau +0 -28
  124. package/src/transport/Bridge.luau +0 -110
  125. package/src/transport/Client.luau +0 -97
  126. package/src/transport/Gate.luau +0 -488
  127. package/src/transport/Reader.luau +0 -331
  128. package/src/transport/Server.luau +0 -233
  129. package/src/util/Array.luau +0 -18
  130. package/src/util/Buffer.luau +0 -90
  131. package/src/util/Constants.luau +0 -30
  132. package/src/util/Log.luau +0 -68
  133. package/src/util/Player.luau +0 -14
  134. package/src/util/Quantize.luau +0 -84
  135. package/src/util/Quat.luau +0 -124
@@ -0,0 +1,64 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- What the canonical ordering decides: the order of names, and which fields vary.
4
+
5
+ local Base = require (script.Parent.Parent.codec.Base)
6
+
7
+ local Order = {}
8
+
9
+ -- Constants -----------------------------------------------------------
10
+
11
+ -- The kinds that vary whatever they are made of.
12
+ const ALWAYS = table.freeze ({
13
+ vlq = true,
14
+ vli = true,
15
+ tagged = true,
16
+ optional = true,
17
+ inst = true,
18
+ }) :: { [string]: boolean }
19
+
20
+ -- The kinds that carry a count, which vary exactly when the two ends of that count differ.
21
+ const COUNTED = table.freeze ({
22
+ str = true,
23
+ buffer = true,
24
+ array = true,
25
+ map = true,
26
+ }) :: { [string]: boolean }
27
+
28
+ -- Public --------------------------------------------------------------
29
+
30
+ -- The names a declaration carries, in the one order two machines can both arrive at.
31
+ function Order.names <V>(of: { [string]: V }): { string }
32
+ local out: { string } = {}
33
+ for name in of do
34
+ table.insert (out, name)
35
+ end
36
+
37
+ table.sort (out)
38
+ return out
39
+ end
40
+
41
+ --[[
42
+ Whether a declaration's size depends on the value it carries, which is what decides the region a
43
+ field is written in and nothing else.
44
+ ]]
45
+ function Order.varies (of: Base.Cell): boolean
46
+ local kind = of.kind
47
+ if ALWAYS[kind] then return true end
48
+
49
+ if COUNTED[kind] then
50
+ local params = of.params
51
+ if params.min ~= params.max then return true end
52
+ end
53
+
54
+ local children = of.children
55
+ if children == nil then return false end
56
+
57
+ for _, child in children do
58
+ if Order.varies (child) then return true end
59
+ end
60
+
61
+ return false
62
+ end
63
+
64
+ return table.freeze (Order)
@@ -0,0 +1,113 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- Which of the four regions every field of a record is written in, and in what order.
4
+
5
+ local Base = require (script.Parent.Parent.codec.Base)
6
+ local Folds = require (script.Parent.Folds)
7
+ local Grid = require (script.Parent.Grid)
8
+ local Joins = require (script.Parent.Joins)
9
+ local Order = require (script.Parent.Order)
10
+ local Protocol = require (script.Parent.Parent.wire.Protocol)
11
+
12
+ local Regions = {}
13
+
14
+ -- Constants -----------------------------------------------------------
15
+
16
+ -- The widest a leaf can be and still flow with its neighbours rather than take whole bytes.
17
+ const CEILING = Protocol.BIT_CEILING
18
+
19
+ -- Types ---------------------------------------------------------------
20
+
21
+ -- One field, at the path it is reached by from the record being partitioned.
22
+ export type Entry = {
23
+ read at: { string },
24
+ read of: Base.Cell,
25
+ }
26
+
27
+ -- The four regions, each in the order it is written, and the records that dissolved on the way.
28
+ export type Split = {
29
+ read presence: { Entry },
30
+ read bits: { Entry },
31
+ read bytes: { Entry },
32
+ read vars: { Entry },
33
+ read nests: { Entry },
34
+ }
35
+
36
+ -- Private -------------------------------------------------------------
37
+
38
+ -- The leaves a declaration takes where it is a region field, and nothing where it is not.
39
+ local function held (of: Base.Cell): { Grid.Grid }?
40
+ local named = Joins.leaves (of)
41
+ if named ~= nil then return named end
42
+
43
+ local fold = Folds.fold (of)
44
+ return if fold == nil then nil else fold.leaves
45
+ end
46
+
47
+ -- Every field of a record into the region it is written in, and the fixed records under it too.
48
+ local function walk (of: { [string]: Base.Cell }, at: { string }, into: Split)
49
+ for _, name in Order.names (of) do
50
+ local child = of[name]
51
+
52
+ local path = table.clone (at)
53
+ table.insert (path, name)
54
+
55
+ local entry: Entry = table.freeze ({ at = table.freeze (path), of = child })
56
+
57
+ --[[
58
+ Both lists, because the two ask different things. A presence bit says whether the field
59
+ is there at all, and the value behind it is still one whose width is not known ahead.
60
+ ]]
61
+ if child.kind == "optional" then
62
+ table.insert (into.presence, entry)
63
+ table.insert (into.vars, entry)
64
+ continue
65
+ end
66
+
67
+ if Order.varies (child) then
68
+ table.insert (into.vars, entry)
69
+ continue
70
+ end
71
+
72
+ if child.kind == "struct" then
73
+ table.insert (into.nests, entry)
74
+ walk (child.children :: { [string]: Base.Cell }, path, into)
75
+ continue
76
+ end
77
+
78
+ if Regions.packs (child) then
79
+ table.insert (into.bits, entry)
80
+ continue
81
+ end
82
+
83
+ table.insert (into.bytes, entry)
84
+ end
85
+ end
86
+
87
+ -- Public --------------------------------------------------------------
88
+
89
+ -- Whether a declaration flows in a bit run, which is one question asked of every leaf it takes.
90
+ function Regions.packs (of: Base.Cell): boolean
91
+ local leaves = held (of)
92
+ if leaves == nil then return false end
93
+
94
+ for _, grid in leaves do
95
+ if grid.width > CEILING then return false end
96
+ end
97
+ return true
98
+ end
99
+
100
+ -- A record's fields, partitioned.
101
+ function Regions.of (of: { [string]: Base.Cell }): Split
102
+ local into: Split = { presence = {}, bits = {}, bytes = {}, vars = {}, nests = {} }
103
+ walk (of, {}, into)
104
+
105
+ table.freeze (into.presence)
106
+ table.freeze (into.bits)
107
+ table.freeze (into.bytes)
108
+ table.freeze (into.vars)
109
+ table.freeze (into.nests)
110
+ return table.freeze (into)
111
+ end
112
+
113
+ return table.freeze (Regions)
@@ -0,0 +1,472 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- The two orientation mappings: a direction as a pair on a square, a turn as three of its four.
4
+
5
+ local Grid = require (script.Parent.Grid)
6
+ local Protocol = require (script.Parent.Parent.wire.Protocol)
7
+
8
+ local Rotation = {}
9
+
10
+ -- Constants -----------------------------------------------------------
11
+
12
+ -- Hoisted so the tag a branch writes is a capture rather than a read through two tables.
13
+ const AT_X = Protocol.QUAT_X
14
+ const AT_Y = Protocol.QUAT_Y
15
+ const AT_Z = Protocol.QUAT_Z
16
+ const AT_W = Protocol.QUAT_W
17
+
18
+ -- How near two of the four have to be before the first of them wins.
19
+ const LEVEL = 1e-9
20
+
21
+ -- How much better one spelling of a rotation has to score before it is taken as better at all.
22
+ const TIGHTER = 0.999999999
23
+ -- How near a component has to sit to a grid point before it is called one.
24
+
25
+ const NEARLY = 1e-9
26
+
27
+ -- Types ---------------------------------------------------------------
28
+
29
+ --[[
30
+ A direction, either way. `plane` answers the two grid indices the wire carries, and nil for a
31
+ reading that is no direction at all. `sphere` takes those indices back to a unit vector.
32
+ ]]
33
+ export type Octahedral = {
34
+ read plane: (x: number, y: number, z: number) -> (number?, number?),
35
+ read sphere: (u: number, v: number) -> (number, number, number),
36
+ }
37
+
38
+ --[[
39
+ A turn, either way, and a pair that is a matrix in and a quaternion out. The platform builds a
40
+ CFrame from four numbers and hands none of them back, so the way in is off the rotation matrix
41
+ and the way out is the four the constructor takes.
42
+ ]]
43
+ export type Smallest = {
44
+ read parts: (
45
+ _x: number,
46
+ _y: number,
47
+ _z: number,
48
+ r00: number,
49
+ r01: number,
50
+ r02: number,
51
+ r10: number,
52
+ r11: number,
53
+ r12: number,
54
+ r20: number,
55
+ r21: number,
56
+ r22: number
57
+ ) -> (number, number?, number?, number?),
58
+ read whole: (
59
+ largest: number,
60
+ a: number,
61
+ b: number,
62
+ c: number
63
+ ) -> (number, number, number, number),
64
+ }
65
+
66
+ --[[
67
+ A turn about one declared axis, either way. `parts` answers the grid index of the angle and
68
+ nothing for a matrix that is no rotation at all, and `whole` answers the quaternion at that
69
+ index. Both speak the twelve components a frame is read with, for the reason the smallest three
70
+ does: that is one call at the boundary and nothing here then names a datatype.
71
+ ]]
72
+ export type Axial = {
73
+ read parts: (
74
+ _x: number,
75
+ _y: number,
76
+ _z: number,
77
+ r00: number,
78
+ r01: number,
79
+ r02: number,
80
+ r10: number,
81
+ r11: number,
82
+ r12: number,
83
+ r20: number,
84
+ r21: number,
85
+ r22: number
86
+ ) -> number?,
87
+ read whole: (at: number) -> (number, number, number, number),
88
+ }
89
+
90
+ -- A look direction, either way, as the same square a unit vector folds onto.
91
+ export type Facing = {
92
+ read parts: (
93
+ _x: number,
94
+ _y: number,
95
+ _z: number,
96
+ r00: number,
97
+ r01: number,
98
+ r02: number,
99
+ r10: number,
100
+ r11: number,
101
+ r12: number,
102
+ r20: number,
103
+ r21: number,
104
+ r22: number
105
+ ) -> (number?, number?),
106
+ read whole: (u: number, v: number) -> (number, number, number, number),
107
+ }
108
+
109
+ -- Private -------------------------------------------------------------
110
+
111
+ --[[
112
+ Zero counts as positive. That is what makes the fold total: a component sitting exactly on an
113
+ axis has to go the same way on both machines, and it is why neither `math.sign` nor
114
+ `vector.sign` can stand here, since both answer zero for zero and one of the two folds is then a
115
+ reflection through nothing.
116
+ ]]
117
+ local function sign (value: number): number return if value >= 0 then 1 else -1 end
118
+
119
+ --[[
120
+ The fold, which is its own inverse. The far half of the sphere reflects across the square's two
121
+ diagonals, and the same two lines carry it back, so both directions of the mapping share this
122
+ rather than each spelling a reflection out and one of them wearing a sign the other does not.
123
+ ]]
124
+ local function folded (u: number, v: number): (number, number)
125
+ return (1 - math.abs (v)) * sign (u), (1 - math.abs (u)) * sign (v)
126
+ end
127
+
128
+ -- The one of the two names a point on the square's outer edge has.
129
+ local function only (span: number, atU: number, atV: number): (number, number)
130
+ local edgeU = atU == 0 or atU == span
131
+ local edgeV = atV == 0 or atV == span
132
+
133
+ if edgeU and span - atV > atV then atV = span - atV end
134
+ if edgeV and span - atU > atU then atU = span - atU end
135
+ return atU, atV
136
+ end
137
+
138
+ --[[
139
+ The lower index of the two the value lies between, which is where a search of a grid cell
140
+ starts. Read off the nearest rather than by dividing, so the grid keeps its own arithmetic and a
141
+ reading that is on no grid at all arrives here as the nil that grid already answers.
142
+ ]]
143
+ local function under (of: Grid.Grid, value: number): number?
144
+ local at = of.toIndex (value)
145
+ if at == nil then return nil end
146
+ return if of.fromIndex (at) > value then at - 1 else at
147
+ end
148
+
149
+ -- Public --------------------------------------------------------------
150
+
151
+ --[[
152
+ The octahedral mapping over one grid, which is `vec3.unit`, the direction half of
153
+ `vec3.bounded`, and `rotation.direction`.
154
+ ]]
155
+ function Rotation.octahedral (of: Grid.Grid): Octahedral
156
+ local span, fromIndex = of.span, of.fromIndex
157
+
158
+ local function sphere (u: number, v: number): (number, number, number)
159
+ local x, y = fromIndex (u), fromIndex (v)
160
+ local z = 1 - math.abs (x) - math.abs (y)
161
+ if z < 0 then
162
+ x, y = folded (x, y)
163
+ end
164
+
165
+ local length = math.sqrt (x * x + y * y + z * z)
166
+ return x / length, y / length, z / length
167
+ end
168
+
169
+ --[[
170
+ The pair whose direction is nearest, rather than the pair nearest the projection. The
171
+ projection is no isometry, so rounding each coordinate lands as much as a sixth of a step
172
+ past the arc the precision promises, and one of the cell's four corners always does better.
173
+ A corner is scored by how far along the vector it points, which needs no length.
174
+ ]]
175
+ local function plane (x: number, y: number, z: number): (number?, number?)
176
+ local reach = math.abs (x) + math.abs (y) + math.abs (z)
177
+ local u, v = x / reach, y / reach
178
+ if z < 0 then
179
+ u, v = folded (u, v)
180
+ end
181
+
182
+ local lowU, lowV = under (of, u), under (of, v)
183
+ if lowU == nil or lowV == nil then return nil, nil end
184
+
185
+ local best, atU, atV = -math.huge, lowU, lowV
186
+ for stepU = 0, 1 do
187
+ for stepV = 0, 1 do
188
+ local tryU = math.clamp (lowU + stepU, 0, span)
189
+ local tryV = math.clamp (lowV + stepV, 0, span)
190
+
191
+ local ax, ay, az = sphere (tryU, tryV)
192
+ local along = ax * x + ay * y + az * z
193
+ if along > best then
194
+ best, atU, atV = along, tryU, tryV
195
+ end
196
+ end
197
+ end
198
+ return only (span, atU, atV)
199
+ end
200
+
201
+ return table.freeze ({ plane = plane, sphere = sphere })
202
+ end
203
+
204
+ -- The smallest-three mapping over one grid, which is `rotation.quat` alone.
205
+ function Rotation.smallest (of: Grid.Grid): Smallest
206
+ local span, fromIndex = of.span, of.fromIndex
207
+
208
+ -- The three a tag sends and the one it leaves behind, which is `laid` the other way about.
209
+ local function apart (
210
+ tag: number,
211
+ x: number,
212
+ y: number,
213
+ z: number,
214
+ w: number
215
+ ): (number, number, number, number)
216
+ -- The one of the pair whose named component is already positive.
217
+ local named = w
218
+ if tag == AT_X then
219
+ named = x
220
+ elseif tag == AT_Y then
221
+ named = y
222
+ elseif tag == AT_Z then
223
+ named = z
224
+ end
225
+
226
+ if named < 0 then
227
+ x, y, z, w = -x, -y, -z, -w
228
+ end
229
+
230
+ if tag == AT_X then return y, z, w, x end
231
+ if tag == AT_Y then return x, z, w, y end
232
+ if tag == AT_Z then return x, y, w, z end
233
+ return x, y, z, w
234
+ end
235
+
236
+ -- The three that travelled and the one rebuilt from them, back in component order.
237
+ local function laid (
238
+ largest: number,
239
+ a: number,
240
+ b: number,
241
+ c: number,
242
+ d: number
243
+ ): (number, number, number, number)
244
+ if largest == AT_X then return d, a, b, c end
245
+ if largest == AT_Y then return a, d, b, c end
246
+ if largest == AT_Z then return a, b, d, c end
247
+ return a, b, c, d
248
+ end
249
+
250
+ --[[
251
+ The dropped component is positive and is whatever is left of a unit quaternion, so a frame
252
+ claiming three that already overrun one leaves nothing for it rather than a root of a
253
+ negative. Every index pair a frame can carry answers a quaternion here, and none of them
254
+ answers a zero one, because three zeros leave the fourth at one.
255
+ ]]
256
+ local function whole (
257
+ largest: number,
258
+ a: number,
259
+ b: number,
260
+ c: number
261
+ ): (number, number, number, number)
262
+ local ka, kb, kc = fromIndex (a), fromIndex (b), fromIndex (c)
263
+ local x, y, z, w =
264
+ laid (largest, ka, kb, kc, math.sqrt (math.max (0, 1 - ka * ka - kb * kb - kc * kc)))
265
+
266
+ local length = math.sqrt (x * x + y * y + z * z + w * w)
267
+ return x / length, y / length, z / length, w / length
268
+ end
269
+
270
+ --[[
271
+ The three indices whose quaternion is nearest, for the reason the direction search gives:
272
+ the rebuilt component moves with all three others, so the triple nearest component by
273
+ component is not the triple nearest as a rotation. One of the cell's eight corners is always
274
+ the answer.
275
+ ]]
276
+ local function parts (
277
+ _x: number,
278
+ _y: number,
279
+ _z: number,
280
+ r00: number,
281
+ r01: number,
282
+ r02: number,
283
+ r10: number,
284
+ r11: number,
285
+ r12: number,
286
+ r20: number,
287
+ r21: number,
288
+ r22: number
289
+ ): (number, number?, number?, number?)
290
+ local trace = r00 + r11 + r22
291
+
292
+ --[[
293
+ Which of the four is largest, and the first of them where they are level. The branch
294
+ decides which component is dropped, so it decides the bytes, and a quarter turn makes
295
+ two of them equal to within a last bit rather than exactly. A strict comparison let that
296
+ bit choose, and the two took turns forever.
297
+ ]]
298
+ local top = math.max (trace, r00, r11, r22) - LEVEL
299
+
300
+ local largest, a, b, c, d
301
+ if trace >= top then
302
+ local s = math.sqrt (trace + 1) * 2
303
+ largest, d = AT_W, 0.25 * s
304
+ a, b, c = (r21 - r12) / s, (r02 - r20) / s, (r10 - r01) / s
305
+ elseif r00 >= top then
306
+ local s = math.sqrt (1 + r00 - r11 - r22) * 2
307
+ largest, d = AT_X, 0.25 * s
308
+ a, b, c = (r01 + r10) / s, (r02 + r20) / s, (r21 - r12) / s
309
+ elseif r11 >= top then
310
+ local s = math.sqrt (1 + r11 - r00 - r22) * 2
311
+ largest, d = AT_Y, 0.25 * s
312
+ a, b, c = (r01 + r10) / s, (r12 + r21) / s, (r02 - r20) / s
313
+ else
314
+ local s = math.sqrt (1 + r22 - r00 - r11) * 2
315
+ largest, d = AT_Z, 0.25 * s
316
+ a, b, c = (r02 + r20) / s, (r12 + r21) / s, (r10 - r01) / s
317
+ end
318
+
319
+ --[[
320
+ Held to unit length before anything is placed on a grid. A matrix carried in float32
321
+ spells a quaternion a last bit of that width off one, and a CFrame carries its matrix in
322
+ float32.
323
+ ]]
324
+ local x, y, z, w = laid (largest, a, b, c, d)
325
+
326
+ local length = math.sqrt (x * x + y * y + z * z + w * w)
327
+ x, y, z, w = x / length, y / length, z / length, w / length
328
+
329
+ --[[
330
+ Every tag tried, and the nearest answer taken. The branch above names the largest of the
331
+ exact quaternion, which cannot be the whole choice: the three that travel are rounded,
332
+ so the bytes name one tag while the value they rebuild wants another.
333
+ ]]
334
+ local best, took, atA, atB, atC = math.huge, largest, nil, nil, nil
335
+ for tag = AT_X, AT_W do
336
+ local pa, pb, pc, pd = apart (tag, x, y, z, w)
337
+
338
+ local lowA, lowB, lowC = under (of, pa), under (of, pb), under (of, pc)
339
+ if lowA == nil or lowB == nil or lowC == nil then continue end
340
+
341
+ for stepA = 0, 1 do
342
+ for stepB = 0, 1 do
343
+ for stepC = 0, 1 do
344
+ local tryA = math.clamp (lowA + stepA, 0, span)
345
+ local tryB = math.clamp (lowB + stepB, 0, span)
346
+ local tryC = math.clamp (lowC + stepC, 0, span)
347
+
348
+ local ka, kb, kc = fromIndex (tryA), fromIndex (tryB), fromIndex (tryC)
349
+
350
+ --[[
351
+ A triple that overruns the unit quaternion, which is not a spelling of
352
+ anything.
353
+ ]]
354
+ local over = ka * ka + kb * kb + kc * kc
355
+ if over > 1 then continue end
356
+
357
+ local kd = math.sqrt (math.max (0, 1 - over))
358
+ local away = (ka - pa) ^ 2 + (kb - pb) ^ 2 + (kc - pc) ^ 2 + (kd - pd) ^ 2
359
+
360
+ -- Nearer wins, and where two are level the smaller spelling does.
361
+ if away < best * TIGHTER then
362
+ best, took, atA, atB, atC = away, tag, tryA, tryB, tryC
363
+ end
364
+ end
365
+ end
366
+ end
367
+ end
368
+
369
+ if atA == nil or atB == nil or atC == nil then return largest, nil, nil, nil end
370
+
371
+ local heldA, heldB, heldC = atA :: number, atB :: number, atC :: number
372
+
373
+ --[[
374
+ The same rotation, spelled by the lowest tag that can spell it exactly. Nearest is not a
375
+ choice a rotation can be held to, since which of two tags is nearer depends on the value
376
+ asked about and the bytes then take turns.
377
+ ]]
378
+ local qx, qy, qz, qw = whole (took, heldA, heldB, heldC)
379
+ for tag = AT_X, took - 1 do
380
+ local pa, pb, pc, pd = apart (tag, qx, qy, qz, qw)
381
+
382
+ local ia, ib, ic = of.toIndex (pa), of.toIndex (pb), of.toIndex (pc)
383
+ if ia == nil or ib == nil or ic == nil then continue end
384
+
385
+ local ka, kb, kc = fromIndex (ia), fromIndex (ib), fromIndex (ic)
386
+ local over = ka * ka + kb * kb + kc * kc
387
+ if over > 1 then continue end
388
+
389
+ local kd = math.sqrt (math.max (0, 1 - over))
390
+ local off = math.abs (ka - pa) + math.abs (kb - pb) + math.abs (kc - pc)
391
+ if off + math.abs (kd - pd) > NEARLY then continue end
392
+
393
+ return tag, ia, ib, ic
394
+ end
395
+
396
+ return took, heldA, heldB, heldC
397
+ end
398
+
399
+ return table.freeze ({ parts = parts, whole = whole })
400
+ end
401
+
402
+ -- A turn about one axis the declaration named, over the cyclic grid its precision cuts.
403
+ function Rotation.axial (of: Grid.Grid, ax: number, ay: number, az: number): Axial
404
+ local length = math.sqrt (ax * ax + ay * ay + az * az)
405
+ local nx, ny, nz = ax / length, ay / length, az / length
406
+
407
+ local toIndex, fromIndex = of.toIndex, of.fromIndex
408
+
409
+ local function parts (
410
+ _x: number,
411
+ _y: number,
412
+ _z: number,
413
+ r00: number,
414
+ r01: number,
415
+ r02: number,
416
+ r10: number,
417
+ r11: number,
418
+ r12: number,
419
+ r20: number,
420
+ r21: number,
421
+ r22: number
422
+ ): number?
423
+ local along = (r21 - r12) * nx + (r02 - r20) * ny + (r10 - r01) * nz
424
+ return toIndex (math.deg (math.atan2 (along / 2, (r00 + r11 + r22 - 1) / 2)))
425
+ end
426
+
427
+ local function whole (at: number): (number, number, number, number)
428
+ local half = math.rad (fromIndex (at)) / 2
429
+ local turned = math.sin (half)
430
+
431
+ return nx * turned, ny * turned, nz * turned, math.cos (half)
432
+ end
433
+
434
+ return table.freeze ({ parts = parts, whole = whole })
435
+ end
436
+
437
+ -- A look direction on the octahedral square, and the shortest turn back onto it.
438
+ function Rotation.facing (of: Grid.Grid): Facing
439
+ local mapping = Rotation.octahedral (of)
440
+ local plane, sphere = mapping.plane, mapping.sphere
441
+
442
+ local function parts (
443
+ _x: number,
444
+ _y: number,
445
+ _z: number,
446
+ r00: number,
447
+ r01: number,
448
+ r02: number,
449
+ r10: number,
450
+ r11: number,
451
+ r12: number,
452
+ r20: number,
453
+ r21: number,
454
+ r22: number
455
+ ): (number?, number?)
456
+ return plane (-r02, -r12, -r22)
457
+ end
458
+
459
+ local function whole (u: number, v: number): (number, number, number, number)
460
+ local tx, ty, tz = sphere (u, v)
461
+ local w = 1 - tz
462
+
463
+ if w <= 0 then return 0, 1, 0, 0 end
464
+
465
+ local length = math.sqrt (ty * ty + tx * tx + w * w)
466
+ return ty / length, -tx / length, 0, w / length
467
+ end
468
+
469
+ return table.freeze ({ parts = parts, whole = whole })
470
+ end
471
+
472
+ return table.freeze (Rotation)