@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.
- package/LICENSE +21 -0
- package/README.md +302 -299
- package/Types.luau +215 -0
- package/api/Define.luau +154 -0
- package/api/Lifecycle.luau +119 -0
- package/api/Packet.luau +108 -0
- package/api/Query.luau +149 -0
- package/api/Set.luau +189 -0
- package/channel/Blocks.luau +124 -0
- package/channel/Group.luau +118 -0
- package/channel/Link.luau +135 -0
- package/channel/Outbox.luau +261 -0
- package/channel/Recipients.luau +129 -0
- package/channel/Transport.luau +138 -0
- package/codec/Base.luau +236 -0
- package/codec/Composites.luau +128 -0
- package/codec/Datatypes.luau +162 -0
- package/codec/Legality.luau +61 -0
- package/codec/Scalars.luau +72 -0
- package/codec/Text.luau +89 -0
- package/codec/init.luau +39 -0
- package/index.d.ts +285 -0
- package/init.luau +91 -0
- package/kernel/Bits.luau +16 -0
- package/kernel/Catalog.luau +121 -0
- package/kernel/Heap.luau +80 -0
- package/kernel/Listeners.luau +95 -0
- package/kernel/Log.luau +197 -0
- package/kernel/Tally.luau +128 -0
- package/kernel/Threads.luau +44 -0
- package/kernel/Wide.luau +267 -0
- package/package.json +15 -32
- package/runtime/Boundary.luau +180 -0
- package/runtime/Dispatch.luau +91 -0
- package/runtime/Handles.luau +114 -0
- package/runtime/Handshake.luau +90 -0
- package/runtime/Pending.luau +196 -0
- package/runtime/Router.luau +379 -0
- package/runtime/Session.luau +731 -0
- package/shape/Compile.luau +340 -0
- package/shape/Folds.luau +332 -0
- package/shape/Frames.luau +170 -0
- package/shape/Grid.luau +169 -0
- package/shape/Ir.luau +540 -0
- package/shape/Joins.luau +125 -0
- package/shape/Legality.luau +273 -0
- package/shape/Lower.luau +419 -0
- package/shape/Order.luau +64 -0
- package/shape/Regions.luau +113 -0
- package/shape/Rotation.luau +472 -0
- package/shape/Serial.luau +258 -0
- package/shape/Words.luau +89 -0
- package/shape/emit/Block.luau +453 -0
- package/shape/emit/Column.luau +338 -0
- package/shape/emit/Leaf.luau +462 -0
- package/shape/emit/Word.luau +234 -0
- package/shape/emit/init.luau +222 -0
- package/shape/init.luau +78 -0
- package/state/Apply.luau +330 -0
- package/state/Audience.luau +126 -0
- package/state/Catchup.luau +218 -0
- package/state/Coalesce.luau +43 -0
- package/state/Fieldset.luau +177 -0
- package/state/Newest.luau +339 -0
- package/state/Plan.luau +441 -0
- package/state/Sections.luau +468 -0
- package/state/Store.luau +188 -0
- package/state/Sync.luau +762 -0
- package/wire/Bytes.luau +118 -0
- package/wire/Clock.luau +37 -0
- package/wire/Protocol.luau +110 -0
- package/wire/Sequence.luau +40 -0
- package/wire/Varint.luau +124 -0
- package/src/Types.luau +0 -180
- package/src/api/Group.luau +0 -156
- package/src/api/Packet.luau +0 -317
- package/src/api/Query.luau +0 -338
- package/src/api/Scope.luau +0 -90
- package/src/api/Signal.luau +0 -193
- package/src/codec/Base.luau +0 -136
- package/src/codec/composite/Array.luau +0 -473
- package/src/codec/composite/Map.luau +0 -485
- package/src/codec/composite/Optional.luau +0 -54
- package/src/codec/composite/Shared.luau +0 -311
- package/src/codec/composite/Struct.luau +0 -494
- package/src/codec/composite/Tagged.luau +0 -103
- package/src/codec/composite/Tuple.luau +0 -143
- package/src/codec/datatype/Buffer.luau +0 -66
- package/src/codec/datatype/CFrame.luau +0 -109
- package/src/codec/datatype/Color.luau +0 -32
- package/src/codec/datatype/Instance.luau +0 -52
- package/src/codec/datatype/IntVector.luau +0 -39
- package/src/codec/datatype/NumberRange.luau +0 -39
- package/src/codec/datatype/Ray.luau +0 -36
- package/src/codec/datatype/Rect.luau +0 -31
- package/src/codec/datatype/Region.luau +0 -62
- package/src/codec/datatype/Sequence.luau +0 -184
- package/src/codec/datatype/String.luau +0 -94
- package/src/codec/datatype/UDim.luau +0 -46
- package/src/codec/datatype/Vector.luau +0 -130
- package/src/codec/meta/Auto.luau +0 -318
- package/src/codec/meta/Bitfield.luau +0 -160
- package/src/codec/meta/Custom.luau +0 -33
- package/src/codec/meta/DeltaScalar.luau +0 -390
- package/src/codec/meta/Enum.luau +0 -50
- package/src/codec/meta/Float.luau +0 -38
- package/src/codec/meta/Nothing.luau +0 -13
- package/src/codec/meta/Unknown.luau +0 -41
- package/src/codec/primitive/Bool.luau +0 -26
- package/src/codec/primitive/Float16.luau +0 -99
- package/src/codec/primitive/Int.luau +0 -59
- package/src/codec/primitive/Number.luau +0 -24
- package/src/codec/primitive/Signed.luau +0 -32
- package/src/codec/primitive/Varint.luau +0 -165
- package/src/codec/primitive/Zint.luau +0 -99
- package/src/index.d.ts +0 -423
- package/src/init.luau +0 -362
- package/src/internal/Baseline.luau +0 -57
- package/src/internal/Channel.luau +0 -484
- package/src/internal/Middleware.luau +0 -100
- package/src/internal/Pool.luau +0 -66
- package/src/internal/Registry.luau +0 -119
- package/src/internal/Transport.luau +0 -28
- package/src/transport/Bridge.luau +0 -110
- package/src/transport/Client.luau +0 -97
- package/src/transport/Gate.luau +0 -488
- package/src/transport/Reader.luau +0 -331
- package/src/transport/Server.luau +0 -233
- package/src/util/Array.luau +0 -18
- package/src/util/Buffer.luau +0 -90
- package/src/util/Constants.luau +0 -30
- package/src/util/Log.luau +0 -68
- package/src/util/Player.luau +0 -14
- package/src/util/Quantize.luau +0 -84
- package/src/util/Quat.luau +0 -124
package/shape/Order.luau
ADDED
|
@@ -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)
|