@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
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- The block headers: the bits before a payload, as the pair that lays each form down.
|
|
4
|
+
|
|
5
|
+
local Bits = require (script.Parent.Parent.kernel.Bits)
|
|
6
|
+
local Protocol = require (script.Parent.Parent.wire.Protocol)
|
|
7
|
+
local Words = require (script.Parent.Words)
|
|
8
|
+
|
|
9
|
+
local Frames = {}
|
|
10
|
+
|
|
11
|
+
-- Constants -----------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
-- What a header's fields are wide.
|
|
14
|
+
const STAMP = Protocol.STAMP_BITS
|
|
15
|
+
const SEQ = Bits.width (Protocol.NEWEST_WINDOW * 2 - 1)
|
|
16
|
+
const ASKED = Bits.width (Protocol.QUERY_CAP * 2 - 1)
|
|
17
|
+
const OUTCOME = Protocol.OUTCOME_BITS
|
|
18
|
+
const KIND = Bits.width (Protocol.SECTION_CLEAR)
|
|
19
|
+
|
|
20
|
+
-- The byte a run's continuation bit is written in, and what a block that has one spends on it.
|
|
21
|
+
const CONTINUED = 1
|
|
22
|
+
const ALONE = 0
|
|
23
|
+
|
|
24
|
+
-- The narrow windows a plan cuts, read from the module that cuts them. The widest is the last arm.
|
|
25
|
+
const ONE = Words.ONE
|
|
26
|
+
const PAIR = Words.PAIR
|
|
27
|
+
|
|
28
|
+
-- Types ---------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
-- One block header, as the pair that lays it down and takes it back up.
|
|
31
|
+
export type Write = (b: buffer, pos: number, of: { number }) -> ()
|
|
32
|
+
export type Read = (b: buffer, pos: number, into: { number }) -> ()
|
|
33
|
+
|
|
34
|
+
export type Head = {
|
|
35
|
+
read fields: number,
|
|
36
|
+
read bytes: number,
|
|
37
|
+
read trailer: number,
|
|
38
|
+
read write: Write,
|
|
39
|
+
read read: Read,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
-- Private -------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
-- A run of fixed-width fields, as the pair that composes it into windows and takes it apart.
|
|
45
|
+
local function run (widths: { number }): (Write, Read, number)
|
|
46
|
+
local plan = Words.plan (widths)
|
|
47
|
+
local windows = #plan
|
|
48
|
+
local held = #widths
|
|
49
|
+
|
|
50
|
+
local bytes = 0
|
|
51
|
+
for _, window in plan do
|
|
52
|
+
bytes += window.bytes
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
local function write (b: buffer, pos: number, of: { number })
|
|
56
|
+
for index = 1, windows do
|
|
57
|
+
local window = plan[index]
|
|
58
|
+
local acc = 0
|
|
59
|
+
|
|
60
|
+
for _, part in window.parts do
|
|
61
|
+
local piece = bit32.extract (of[part.field], part.shift, part.width)
|
|
62
|
+
acc = bit32.bor (acc, bit32.lshift (piece, part.offset))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
local at = pos + window.at
|
|
66
|
+
if window.bytes == ONE then
|
|
67
|
+
buffer.writeu8 (b, at, acc)
|
|
68
|
+
elseif window.bytes == PAIR then
|
|
69
|
+
buffer.writeu16 (b, at, acc)
|
|
70
|
+
else
|
|
71
|
+
buffer.writeu32 (b, at, acc)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
--[[
|
|
77
|
+
Every field cleared first, because a piece at no shift is written and every piece above it
|
|
78
|
+
is merged, and what is under a field the caller reused is whatever the last header left.
|
|
79
|
+
]]
|
|
80
|
+
local function read (b: buffer, pos: number, into: { number })
|
|
81
|
+
for index = 1, held do
|
|
82
|
+
into[index] = 0
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
for index = 1, windows do
|
|
86
|
+
local window = plan[index]
|
|
87
|
+
local at = pos + window.at
|
|
88
|
+
local acc = if window.bytes == ONE
|
|
89
|
+
then buffer.readu8 (b, at)
|
|
90
|
+
elseif window.bytes == PAIR then buffer.readu16 (b, at)
|
|
91
|
+
else buffer.readu32 (b, at)
|
|
92
|
+
|
|
93
|
+
for _, part in window.parts do
|
|
94
|
+
local piece = bit32.extract (acc, part.offset, part.width)
|
|
95
|
+
local field = part.field
|
|
96
|
+
into[field] = bit32.bor (into[field], bit32.lshift (piece, part.shift))
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
return write, read, bytes
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
-- A form's fields, as the head that writes them.
|
|
105
|
+
local function headed (widths: { number }, trailer: number): Head
|
|
106
|
+
local write, read, bytes = run (widths)
|
|
107
|
+
|
|
108
|
+
return table.freeze ({
|
|
109
|
+
fields = #widths,
|
|
110
|
+
bytes = bytes,
|
|
111
|
+
trailer = trailer,
|
|
112
|
+
write = write,
|
|
113
|
+
read = read,
|
|
114
|
+
})
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
local function named (over: number): number return Bits.width (over - 1) end
|
|
118
|
+
|
|
119
|
+
-- Public --------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
-- The defId on its own, which is the one field every form opens with.
|
|
122
|
+
function Frames.naming (over: number): Head return headed ({ named (over) }, ALONE) end
|
|
123
|
+
|
|
124
|
+
-- A packet block: `{ defId }`, or `{ defId, stamp }` where the definition declared one.
|
|
125
|
+
function Frames.packet (over: number, stamped: boolean): Head
|
|
126
|
+
local id = named (over)
|
|
127
|
+
return headed (if stamped then { id, STAMP } else { id }, CONTINUED)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
-- A newest packet block: `{ defId, seq }`, or `{ defId, stamp, seq }`.
|
|
131
|
+
function Frames.newest (over: number, stamped: boolean): Head
|
|
132
|
+
local id = named (over)
|
|
133
|
+
return headed (if stamped then { id, STAMP, SEQ } else { id, SEQ }, ALONE)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
-- A request block: `{ defId, id }`, where the id is the one its response comes back under.
|
|
137
|
+
function Frames.request (over: number): Head return headed ({ named (over), ASKED }, ALONE) end
|
|
138
|
+
|
|
139
|
+
-- A response block: `{ defId, id, outcome }`.
|
|
140
|
+
function Frames.response (over: number): Head
|
|
141
|
+
local id = named (over)
|
|
142
|
+
return headed ({ id, ASKED, OUTCOME }, ALONE)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
-- A state section's block: `{ defId, kind }`, and everything after it is the section's own grammar.
|
|
146
|
+
function Frames.section (over: number): Head return headed ({ named (over), KIND }, ALONE) end
|
|
147
|
+
|
|
148
|
+
-- A newest state section's block: `{ defId, fieldId, seq }`.
|
|
149
|
+
function Frames.stream (over: number, fields: number): Head
|
|
150
|
+
return headed ({ named (over), named (fields), SEQ }, ALONE)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
--[[
|
|
154
|
+
What one payload's block comes to, which is the header, the payload, and whatever follows it.
|
|
155
|
+
|
|
156
|
+
Stated once: the three parts are the grammar, and a caller adding two of them is one who will
|
|
157
|
+
eventually add the wrong two.
|
|
158
|
+
]]
|
|
159
|
+
function Frames.block (of: Head, payload: number): number return of.bytes + payload + of.trailer end
|
|
160
|
+
|
|
161
|
+
-- The bit that says another payload follows, in the byte alignment gives it.
|
|
162
|
+
function Frames.more (b: buffer, pos: number, yes: boolean)
|
|
163
|
+
buffer.writeu8 (b, pos, if yes then CONTINUED else ALONE)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
function Frames.continues (b: buffer, pos: number): boolean
|
|
167
|
+
return bit32.band (buffer.readu8 (b, pos), CONTINUED) == CONTINUED
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
return table.freeze (Frames)
|
package/shape/Grid.luau
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- The quantization grid every ranged and stepped kind indexes through.
|
|
4
|
+
|
|
5
|
+
local Bits = require (script.Parent.Parent.kernel.Bits)
|
|
6
|
+
local Protocol = require (script.Parent.Parent.wire.Protocol)
|
|
7
|
+
|
|
8
|
+
local Grid = {}
|
|
9
|
+
|
|
10
|
+
-- Constants -----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
--[[
|
|
13
|
+
A quarter turn, which is what an octahedral precision is read against: a component of the fold
|
|
14
|
+
runs a quarter of the way round the sphere per unit, so a precision in degrees is that many
|
|
15
|
+
ninetieths of one. A whole turn is not here, because an angle is the codec that declares one and
|
|
16
|
+
a cyclic grid takes the range it is handed rather than a turn it assumes.
|
|
17
|
+
]]
|
|
18
|
+
const QUARTER = Protocol.QUARTER_TURN
|
|
19
|
+
|
|
20
|
+
-- How far the three components a smallest-three keeps can reach.
|
|
21
|
+
const BOUND = Protocol.QUAT_BOUND
|
|
22
|
+
|
|
23
|
+
-- What a division is allowed to be out by before its answer is read as the whole number it is.
|
|
24
|
+
const HAIR = 3.552713678800501E-15
|
|
25
|
+
-- What a reading is allowed to be out by before it is read as the point it obviously is.
|
|
26
|
+
|
|
27
|
+
const READING = 9.5367431640625E-7
|
|
28
|
+
-- Types ---------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
-- A mapping between the values a declaration carries and the indices the wire carries.
|
|
31
|
+
|
|
32
|
+
export type Grid = {
|
|
33
|
+
read span: number,
|
|
34
|
+
read width: number,
|
|
35
|
+
read wrap: boolean?,
|
|
36
|
+
read toIndex: (value: any) -> number?,
|
|
37
|
+
read fromIndex: (index: number) -> any,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
-- Private -------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
-- What a number may be out by and still be the point it obviously is, at a stated width.
|
|
43
|
+
local function allowed (min: number, max: number, step: number, width: number): number
|
|
44
|
+
return math.min ((max - min) * width, step / 2)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
-- How many whole steps a range holds, which is a division and so not always the number it is.
|
|
48
|
+
local function spanned (min: number, max: number, step: number): number
|
|
49
|
+
local whole = math.floor ((max - min) / step)
|
|
50
|
+
if min + (whole + 1) * step <= max + allowed (min, max, step, HAIR) then return whole + 1 end
|
|
51
|
+
|
|
52
|
+
return whole
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
-- Public --------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
--[[
|
|
58
|
+
A midpoint rounds up. That is arbitrary and it is pinned, because two machines splitting one
|
|
59
|
+
differently disagree about the bytes.
|
|
60
|
+
]]
|
|
61
|
+
function Grid.of (min: number, max: number, step: number, wraps: boolean?): Grid
|
|
62
|
+
local span = spanned (min, max, step)
|
|
63
|
+
local width = Bits.width (span)
|
|
64
|
+
|
|
65
|
+
local function fromIndex (index: number): any return min + index * step end
|
|
66
|
+
|
|
67
|
+
--[[
|
|
68
|
+
A cyclic grid reduces the value into one turn before rounding, never the index after:
|
|
69
|
+
rounding away from zero takes a midpoint up above the bottom and down below it, so a
|
|
70
|
+
negative angle reduced afterwards lands a point off. The reduction is also the test for a
|
|
71
|
+
reading that is no place on the line, since neither an infinity nor a NaN survives it.
|
|
72
|
+
]]
|
|
73
|
+
if wraps then
|
|
74
|
+
local points = span + 1
|
|
75
|
+
local turn = points * step
|
|
76
|
+
|
|
77
|
+
local function toIndex (value: any): number?
|
|
78
|
+
if type (value) ~= "number" then return nil end
|
|
79
|
+
|
|
80
|
+
local turned = (value - min) % turn
|
|
81
|
+
if not (turned >= 0) then return nil end
|
|
82
|
+
return math.round (turned / step) % points
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
span = span,
|
|
87
|
+
width = width,
|
|
88
|
+
wrap = true,
|
|
89
|
+
toIndex = toIndex,
|
|
90
|
+
fromIndex = fromIndex,
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
--[[
|
|
95
|
+
One test rather than two rejections, so a reading that is no place falls out of it as well.
|
|
96
|
+
The index is clamped because a range holding two and a half steps rounds its top to three
|
|
97
|
+
and the grid has two, and the nearest point a grid actually has is the last one.
|
|
98
|
+
]]
|
|
99
|
+
local room = allowed (min, max, step, READING)
|
|
100
|
+
local under = min - room
|
|
101
|
+
local over = max + room
|
|
102
|
+
|
|
103
|
+
local function toIndex (value: any): number?
|
|
104
|
+
if type (value) ~= "number" then return nil end
|
|
105
|
+
if not (value >= under and value <= over) then return nil end
|
|
106
|
+
return math.clamp (math.round ((value - min) / step), 0, span)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
return { span = span, width = width, toIndex = toIndex, fromIndex = fromIndex }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
-- One bit, and the two values it is, which is the one grid holding nothing a number could be.
|
|
113
|
+
function Grid.flag (): Grid
|
|
114
|
+
local function toIndex (value: any): number?
|
|
115
|
+
if value == true then return 1 end
|
|
116
|
+
if value == false then return 0 end
|
|
117
|
+
return nil
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
local function fromIndex (index: number): any return index == 1 end
|
|
121
|
+
|
|
122
|
+
return { span = 1, width = 1, toIndex = toIndex, fromIndex = fromIndex }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
-- A ranged int over a count of things, which is where every tag and every chosen name lands.
|
|
126
|
+
function Grid.index (count: number): Grid return Grid.of (0, count - 1, 1) end
|
|
127
|
+
|
|
128
|
+
-- The names a declaration lists, indexed by where each one sits in it.
|
|
129
|
+
function Grid.named (names: { string }): Grid
|
|
130
|
+
local span = #names - 1
|
|
131
|
+
local index: { [string]: number } = {}
|
|
132
|
+
|
|
133
|
+
for place, name in names do
|
|
134
|
+
index[name] = place - 1
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
local function toIndex (value: any): number? return index[value] end
|
|
138
|
+
local function fromIndex (at: number): any return names[at + 1] end
|
|
139
|
+
|
|
140
|
+
return { span = span, width = Bits.width (span), toIndex = toIndex, fromIndex = fromIndex }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
--[[
|
|
144
|
+
One component of an octahedral mapping, which is the unit sphere folded onto a square and the
|
|
145
|
+
square read at a precision in degrees. Both components are this, and nothing else is.
|
|
146
|
+
]]
|
|
147
|
+
function Grid.plane (precision: number): Grid return Grid.of (-1, 1, precision / QUARTER) end
|
|
148
|
+
|
|
149
|
+
--[[
|
|
150
|
+
One component a smallest-three kept. The same number of points as a plane at the same precision,
|
|
151
|
+
over a range scaled down by the bound: the component dropped was the largest, so no kept one is
|
|
152
|
+
larger than it and two of them squared cannot pass one.
|
|
153
|
+
]]
|
|
154
|
+
function Grid.chord (precision: number): Grid
|
|
155
|
+
return Grid.of (-BOUND, BOUND, BOUND * precision / QUARTER)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
-- One channel of a packed colour, which is the whole of none to all of it in the bits it has.
|
|
159
|
+
function Grid.channel (bits: number): Grid return Grid.of (0, 1, 1 / (2 ^ bits - 1)) end
|
|
160
|
+
|
|
161
|
+
-- Whether a step divides a range, which is a division and so not always the answer it is.
|
|
162
|
+
function Grid.divides (reach: number, step: number): boolean
|
|
163
|
+
local points = reach / step
|
|
164
|
+
local near = math.round (points)
|
|
165
|
+
|
|
166
|
+
return math.abs (points - near) < math.max (1, points) * HAIR
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
return table.freeze (Grid)
|