Determining the throughput of inserters is a fairly common problem in Factorio, both for players and for mod creators, yet it is a very difficult problem to solve.
This library aims to provide mod creators with reasonable throughput estimates for use in their mods.
The throughput of inserters can actually be calculated very accurately in many setups. Especially when it comes to dropping items it can all be calculated, the only tricky situations being dropping to splitters. Dropping to loaders which take items from the belt is also awkward, but this is much less common at least.
The major difficulty with calculation originates from belt item chasing. In other words, picking up items from any belt connectable. There are many variables which influence how long it takes an inserter to pick up an item from a belt, leaving just 2 options for calculation: Simulation or estimation.
Simulation is out of the question. I could just leave it at that, but if you are curious there are at least 4 reasons: Reimplementing item chasing exactly the way it works in game would take a long time, and would be so implementation dependent that any micro change to the game would invalidate the simulation. So while possible, it'd be really bad for maintainability. The second reason is performance. Without going into detail, there are inserters out there with over 100 stack sizes, requiring long simulations. The third reason is that inserters behave differently when picking up from belts depending on timing. It would require multiple simulations with different item timings on the belt to determine accurate enough throughput... but wait, at that point it's just an estimate. And lastly inserters can get stuck chasing items, never picking any up, so it would need a timeout as well. There are just too many issues with simulation making it not feasible.
That leaves estimation. More specifically a bit of math trying to get close to real values. To get closer to real values the algorithm can be parametrized and then run through iterations with different values for those parameters, comparing estimated values with real measured values. The result is a few magic values with which the algorithm is tuned to be a little bit more accurate.
Due to my lack of time to properly understand and implement belt stacking support (because my house/home got flooded), this library cannot estimate throughput when belt stacking is involved in any way.
The inserter throughput api consists of 3 parts: utility, definition creation and estimation.
The vector api mainly contains vector functions, with a few 2x2 matrix functions mixed in.
The estimation function takes a definition table which contains all necessary data for the estimation. This definition does not contain any references to entities or prototypes. Therefore the definition table gets created beforehand through definition creation functions (or manually, technically). Nearly all of these functions only set a part of the definition.
Apart from that there are a few utility functions in relation to inserter pickup and drop targets, stack sizes and default positions. They are used internally, however they are also exposed in the api for more flexibility.
The definition table consists of 3 parts: inserter data, pickup data, drop data.
The majority of the definition creation functions only set 1 of those 3 parts in the definition table. The only exceptions are 1 function which sets all 3, and a few which set pickup or drop data, and then also set the pickup or drop vector inside of the inserter data.
The estimation function does not modify the definition allowing for reuse of the same definition table. The definition creation functions also accept a given part of the definition table being set already, they will simply clear out the old data and set new data.
When working with inserter pickup and drop positions, a vector library tends to be quite useful.
It tries to strike a balance between usability and performance:
- Every function expects positions or vectors using
x
andy
fields. Arrays as positions are not accepted. - It modifies a given table instead of creating a new one whenever possible to reduce the amount of table allocations. Use
vec.copy
on the argument when modifying is undesired. - There are no metatables. Addition takes the form
vec.add(left, right)
for example.
This mod contains two files which are public api:
local inserter_throughput = require("__inserter-throughput-lib__.inserter_throughput")
local vec = require("__inserter-throughput-lib__.vector")
local inserter_throughput
is quite a long name and many times more verbose than necessary. Feel free to shorten it to local ins_throughput
or local throughput
or whatever feels right. Even local itl
for inserter throughput library is an option, but I'd warn you about using too many acronyms in code. It'll quickly become hard to read.
Aside from the documentation in this file, the mod also contains type annotations for the LuaLS. If you are using said language server I would recommend to extract this mod and put it in a place the language server can see, such as the workspace itself or in a library path of the language server. I recommend against cloning the source because it contains dev only test files which create several prototypes and modify the editor controller.
If you are not seeing type information on the return value of require("__inserter-throughput-lib__.inserter_throughput")
you can try adding ---@type InserterThroughputLib
at the end of that line (or on a new line above). Same goes for the vector
file, use ---@type VectorLib
for that. This assists with cases where the language server cannot resolve the __mod-name__
prefix in the require call.
For completeness I'll also point you to FMTK which has support for LuaLS as well, and this mod is using some of the type definitions that FMTK generates from the machine readable documentation of Factorio, such as LuaEntity, LuaEntityPrototype or MapPosition, etc.
Print inserter throughput to chat whenever the player hovers an inserter. (The hello world for this library :P)
local inserter_throughput = require("__inserter-throughput-lib__.inserter_throughput")
script.on_event(defines.events.on_selected_entity_changed, function(event)
local player = game.get_player(event.player_index) ---@cast player -nil
local selected = player.selected
if not selected then return end
local entity_type = inserter_throughput.get_real_or_ghost_entity_type(selected)
if entity_type ~= "inserter" then return end
local def = inserter_throughput.make_full_definition_for_inserter(selected)
local items_per_second = inserter_throughput.estimate_inserter_speed(def)
local is_estimate = inserter_throughput.is_estimate(def)
player.print(string.format(
"Inserter speed: %s%.3f/s",
is_estimate and "~ " or "",
items_per_second
))
end)
InserterThroughputTargetType
InserterThroughputDefinition
InserterThroughputInserterDefinition
InserterThroughputLoaderDefinitionBase
InserterThroughputPickupDefinition
InserterThroughputDropDefinition
VectorXY
MatrixIJ
pickup_from_inventory
pickup_from_ground
pickup_from_belt
pickup_from_linked_belt
pickup_from_underground
pickup_from_splitter
pickup_from_loader
pickup_from_entity
pickup_from_position
pickup_from_position_and_set_pickup_vector
pickup_from_pickup_target_of_inserter
pickup_from_pickup_target_of_inserter_and_set_pickup_vector
drop_to_inventory
drop_to_ground
drop_to_belt
drop_to_linked_belt
drop_to_underground
drop_to_splitter
drop_to_loader
drop_to_entity
drop_to_position
drop_to_position_and_set_drop_vector
drop_to_drop_target_of_inserter
drop_to_drop_target_of_inserter_and_set_drop_vector
get_real_or_ghost_entity_type
get_real_or_ghost_entity_prototype
get_target_type
is_belt_connectable_target_type
get_pickup_vector
get_drop_vector
get_default_inserter_position_in_tile
get_position_in_tile
get_stack_size_for_prototype
get_stack_size
snap_build_position
normalize_belt_speed
vec_equals
matrix_equals
copy
is_zero
get_length
set_length
set_length_safe
normalize
normalize_safe
snap_to_map
add
sub
add_scalar
sub_scalar
mul_scalar
div_scalar
mod_scalar
pow_scalar
sqrt
abs
floor
ceil
min
max
dot_product
get_radians
get_radians_safe
get_orientation
get_orientation_safe
rotate_by_radians
rotate_by_orientation
rotate_by_direction
transform_by_matrix
rotation_matrix_by_radians
rotation_matrix_by_orientation
new_matrix
new_identity_matrix
copy_matrix
compose_matrices
Alias of:
"inventory"
"ground"
"belt"
"linked-belt"
"underground"
"splitter"
"loader"
inserter
::InserterThroughputInserterDefinition
Functions setting fields in this table accept it beingnil
, they'll simply create the table. The estimate functions however require this table to be nonnil
.pickup
::InserterThroughputPickupDefinition
Functions setting fields in this table accept it beingnil
, they'll simply create the table. The estimate functions however require this table to be nonnil
.drop
::InserterThroughputDropDefinition
Functions setting fields in this table accept it beingnil
, they'll simply create the table. The estimate functions however require this table to be nonnil
.
extension_speed
::number
Tiles per tick.rotation_speed
::number
RealOrientation
per tick.stack_size
::integer
Must be at least 1.pickup_vector
::VectorXY
Relative to inserter position.drop_vector
::VectorXY
Relative to inserter position.chases_belt_items
::boolean
InserterPrototype::chases_belt_items
.direction
::defines.direction
?
The base direction of the inserter, separate from current pickup or drop vectors.
Only used and required if thepickup_vector
ordrop_vector
has a length of 0 (in other words ifvec.is_zero(vector)
).inserter_position_in_tile
::VectorXY
?
Modulo (%) 1 of x and y of the inserter's position.
Only used and required ifdrop.is_splitter
is true.
Abstract.
loader_type
::"input"|"output"?
LuaEntity::loader_type
."input"
: items flow into the loader,"output"
: items flow out of the loader.loader_belt_length
::number
?
LuaEntityPrototype::belt_length
.loader_tile_distance_from_belt_start
::integer
?
How many tiles is the drop distance away from the actual belt of the loader? When dropping onto the tile of a 1x2 loader which is next to a container, this is1
. When dropping onto the tile where items get put onto or taken from the belt, this is 0.
Inherits InserterThroughputLoaderDefinitionBase
.
target_type
::InserterThroughputTargetType
belt_speed
::number
?
Tiles per tick of each item on the belt being picked up from.
Required for all belt connectables.belt_direction
::defines.direction
?
This is the direction items flow in. Always. Even for undergrounds and loaders, be it input or output.
Required for all belt connectables.belt_shape
::"left"|"right"|"straight"
?
LuaEntity::belt_shape
. Just used for"belt"
s.linked_belt_type
::"input"|"output"
?
LuaEntity::linked_belt_type
."input"
: items go into the belt,"output"
: items come out of the belt.underground_type
::"input"|"output"
?
LuaEntity::belt_to_ground_type
."input"
: goes underground,"output"
: emerges from the ground.
Inherits InserterThroughputLoaderDefinitionBase
.
target_type
::InserterThroughputTargetType
belt_speed
::number
?
Tiles per tick of each item on the belt being dropped off to.
Required for all belt connectables.belt_direction
::defines.direction
?
Only used and required when dropping to"splitter"
s or"loader"
s.
Only accepts tables taking the xy form, not arrays.
Alias of:
Must watch (3blue1brown) https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab
ix
::number
Top left corner if you think about it like a 2x2 grid.iy
::number
Bottom left corner if you think about it like a 2x2 grid.jx
::number
Top right corner if you think about it like a 2x2 grid.jy
::number
Bottom right corner if you think about it like a 2x2 grid.
Snaps belt speeds and vectors to valid 1/256ths, because they are all related to MapPositions. Does not modify the given definition table however.
If is_unreasonable_definition(def)
then this simply returns 0.
Parameters
def
::InserterThroughputDefinition
Return values
items_per_second
::number
Whether or not the given definition can be used for accurate throughput calculation or if it is just an estimate. Under what conditions this returns true or false is not part of the public api.
If is_unreasonable_definition(def)
then this returns true
.
Parameters
def
::InserterThroughputDefinition
Return values
boolean
Is the given definition one which does not have any reasonable way to calculate or estimate inserter throughput for it? Some of these types of definitions are technically valid according to the game, some of them would not get past the prototype loading phase if a prototype were to have those values. This function does not specify which exact definitions qualify as such, as it may change. Specifically because it does not do full validation of the definition, and there could always be more things that pop up in the future.
This function is used by both estimate_inserter_speed
and is_estimate
, no need to manually call it before calling those functions, unless it requires other special handling.
Parameters
def
::InserterThroughputDefinition
Creates a new definition with def.inserter
, def.pickup
and def.drop
all being empty tables.
Return values
Sets all fields in def.inserter
, def.pickup
and def.drop
. In other words: everything.
Parameters
inserter
::LuaEntity
def_to_reuse
::InserterThroughputDefinition
?
Return values
Sets all fields in def.inserter
.
Parameters
def
::InserterThroughputDefinition
inserter_prototype
::LuaEntityPrototype
quality
::string
|LuaQualityPrototype
direction
::defines.direction
position
::VectorXY
?
Default:get_default_inserter_position_in_tile(inserter_prototype, direction)
.stack_size
::integer
Sets all fields in def.inserter
.
Parameters
def
::InserterThroughputDefinition
inserter
::LuaEntity
Sets all fields in def.pickup
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
Sets all fields in def.pickup
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
Sets all fields in def.pickup
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
belt_speed
::number
Tiles per tick that each item moves.belt_direction
::defines.direction
belt_shape
::"left"|"right"|"straight"
Example: If a belt is pointing at this belt from the left, set "left".
Sets all fields in def.pickup
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
belt_speed
::number
Tiles per tick that each item moves.belt_direction
::defines.direction
linked_belt_type
::"input"|"output"
LuaEntity::linked_belt_type
."input"
: items go into the belt,"output"
: items come out of the belt.
Sets all fields in def.pickup
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
belt_speed
::number
Tiles per tick that each item moves.belt_direction
::defines.direction
underground_type
::"input"|"output"
LuaEntity::belt_to_ground_type
."input"
: goes underground,"output"
: emerges from the ground.
Sets all fields in def.pickup
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
belt_speed
::number
Tiles per tick that each item moves.belt_direction
::defines.direction
Sets all fields in def.pickup
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
belt_speed
::number
Tiles per tick that each item moves.belt_direction
::defines.direction
loader_type
::"input"|"output"
LuaEntity::loader_type
."input"
: items flow into the loader,"output"
: items flow out of the loader.loader_belt_length
::number
LuaEntityPrototype::belt_length
.loader_tile_distance_from_belt_start
::integer
How many tiles is the drop distance away from the actual belt of the loader? When dropping onto the tile of a 1x2 loader which is next to a container, this is1
. When dropping onto the tile where items get put onto or taken from the belt, this is 0.
Sets all fields in def.pickup
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
entity
::LuaEntity
?pickup_position
::VectorXY
?
Required ifentity
is any loader.
Sets all fields in def.pickup
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
surface
::LuaSurface
position
::VectorXY
A MapPosition on the given surface.inserter
::LuaEntity
?
Used to prevent an inserter from picking up from itself, provide it if applicable.
Sets all fields in def.pickup
, unrelated fields get set to nil
.
Also sets def.inserter.pickup_vector
using position
and inserter_position
.
Parameters
def
::InserterThroughputDefinition
surface
::LuaSurface
position
::VectorXY
A MapPosition on the given surface.inserter
::LuaEntity
?
Used to prevent an inserter from picking up from itself, provide it if applicable.inserter_position
::VectorXY
?
Default:inserter.position
. Required ifinserter
isnil
.
Sets all fields in def.pickup
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
inserter
::LuaEntity
Ghost or real.
Sets all fields in def.pickup
, unrelated fields get set to nil
.
Also sets def.inserter.pickup_vector
using inserter.pickup_position
and inserter.position
.
Parameters
def
::InserterThroughputDefinition
inserter
::LuaEntity
Ghost or real.
Sets all fields in def.drop
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
Sets all fields in def.drop
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
Sets all fields in def.drop
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
belt_speed
::number
Tiles per tick that each item moves.
Sets all fields in def.drop
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
belt_speed
::number
Tiles per tick that each item moves.
Sets all fields in def.drop
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
belt_speed
::number
Tiles per tick that each item moves.
Sets all fields in def.drop
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
belt_speed
::number
Tiles per tick that each item moves.belt_direction
::defines.direction
Sets all fields in def.drop
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
belt_speed
::number
Tiles per tick that each item moves.belt_direction
::defines.direction
loader_type
::"input"|"output"
LuaEntity::loader_type
."input"
: items flow into the loader,"output"
: items flow out of the loader.loader_belt_length
::number
LuaEntityPrototype::belt_length
.loader_tile_distance_from_belt_start
::integer
How many tiles is the drop distance away from the actual belt of the loader? When dropping onto the tile of a 1x2 loader which is next to a container, this is1
. When dropping onto the tile where items get put onto or taken from the belt, this is 0.
Sets all fields in def.drop
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
entity
::LuaEntity
?drop_position
::VectorXY
?
Required ifentity
is any loader.
Sets all fields in def.drop
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
surface
::LuaSurface
position
::VectorXY
A MapPosition on the given surface.inserter
::LuaEntity
?
Used to prevent an inserter from dropping to itself, provide it if applicable.
Sets all fields in def.drop
, unrelated fields get set to nil
.
Also sets def.inserter.drop_vector
using position
and inserter_position
.
Parameters
def
::InserterThroughputDefinition
surface
::LuaSurface
position
::VectorXY
A MapPosition on the given surface.inserter
::LuaEntity
?
Used to prevent an inserter from dropping to itself, provide it if applicable.inserter_position
::VectorXY
?
Default:inserter.position
. Required ifinserter
isnil
.
Sets all fields in def.drop
, unrelated fields get set to nil
.
Parameters
def
::InserterThroughputDefinition
inserter
::LuaEntity
Ghost or real.
Sets all fields in def.drop
, unrelated fields get set to nil
.
Also sets def.inserter.drop_vector
using inserter.drop_position
and inserter.position
.
Parameters
def
::InserterThroughputDefinition
inserter
::LuaEntity
Ghost or real.
Parameters
entity
::LuaEntity
Return values
type
::string
Parameters
entity
::LuaEntity
Must not be a ghost for a tile.
Return values
Parameters
entity
::LuaEntity
?
Handles both real and ghost entities.
Return values
Parameters
target_type
::InserterThroughputTargetType
Return values
boolean
Instead of getting the pickup_position
which is an absolute position, this gets the vector from the inserter to its pickup_position
.
Parameters
inserter
::LuaEntity
position
::VectorXY
?
Prefetched position of the inserter, to reduce the amount of api calls and allocations. Only makes sense in code that runs a lot.
Return values
pickup_vector
::VectorXY
Instead of getting the drop_position
which is an absolute position, this gets the vector from the inserter to its drop_position
.
Parameters
inserter
::LuaEntity
position
::VectorXY
?
Prefetched position of the inserter, to reduce the amount of api calls and allocations. Only makes sense in code that runs a lot.
Return values
drop_vector
::VectorXY
Pretends off grid inserters are placed on the grid, so they get zero special treatment.
Parameters
prototype
::LuaEntityPrototype
direction
::defines.direction
Return values
position
::VectorXY
The position within a tile, so x and y are in the [0, 1) range.
Generics
T
of typeVectorXY
Parameters
position
::T
Return values
position_within_tile
::T
A new table.
Parameters
prototype
::LuaEntityPrototype
force
::LuaForce
?manual_override
::integer
?control_signal_id
::SignalID
?red
::LuaCircuitNetwork
?green
::LuaCircuitNetwork
?
Return values
stack_size
::integer
Uses inserter.inserter_target_pickup_count
. However get_stack_size
also handles inserters which have just been built in this tick, while inserter_target_pickup_count
returns 1
in that case in several situations.
Parameters
inserter
::LuaEntity
Ghost or real.
This appears to match the game's snapping logic perfectly.
Generics
T
of typeVectorXY
Parameters
prototype
::LuaEntityPrototype
position
::T
Gets modified.direction
::defines.direction
Return values
position
::T
The same table as theposition
parameter.
Rounds down to nearest valid number, because items on belts also use fixed point positions. Same resolution as MapPositions, so 1/256.
Parameters
belt_speed
::number
Tiles per tick.
Return values
belt_speed
::number
Parameters
Return values
boolean
Parameters
Return values
boolean
Generics
T
of typeVectorXY
Parameters
vector
::T
Return values
T
Parameters
vector
::VectorXY
Return values
boolean
true
when bothx
andy
are== 0
.
Parameters
vector
::VectorXY
Return values
number
Errors when target_length ~= 0 and is_zero(vector)
.
Generics
T
of typeVectorXY
Parameters
vector
::T
Gets modified.target_length
::number
current_length
::number
?
Precalculated length if available.
Return values
vector
::T
When the target_length
is 0, the result is going to be a 0 length vector.
Otherwise, when the given vector has a length of 0, the return value is going to be nil
.
Generics
-
T
of typeVectorXY
-
vector
::T
Gets modified. Whennil
is returned,vector
did not get modified. -
target_length
::number
-
current_length
::number
?
Precalculated length if available.
Return values
vector
::T
nil
whentarget_length ~= 0 and is_zero(vector)
.
Errors when is_zero(vector)
.
Generics
T
of typeVectorXY
Parameters
vector
::T
Gets modified.current_length
::number
?
Precalculated length if available.
Return values
vector
::T
Generics
T
of typeVectorXY
Parameters
vector
::T
Gets modified. Whennil
is returned,vector
did not get modified.current_length
::number
?
Precalculated length if available.
Return values
vector
::T
nil
whenis_zero(vector)
.
Snaps x and y to the MapPosition grid (1/256).
I don't know if the game rounds or floors, but this function is flooring.
Generics
T
of typeVectorXY
Parameters
vector
::T
Gets modified.
Return values
vector
::T
Generics
T
of typeVectorXY
Parameters
left
::T
Gets modified.right
::VectorXY
Return values
left
::T
Generics
T
of typeVectorXY
Parameters
left
::T
Gets modified.right
::VectorXY
Return values
left
::T
Generics
T
of typeVectorXY
Parameters
left
::T
Gets modified.right
::number
Return values
left
::T
Generics
T
of typeVectorXY
Parameters
left
::T
Gets modified.right
::number
Return values
left
::T
Generics
T
of typeVectorXY
Parameters
left
::T
Gets modified.right
::number
Return values
left
::T
Generics
T
of typeVectorXY
Parameters
left
::T
Gets modified.right
::number
Return values
left
::T
Generics
T
of typeVectorXY
Parameters
left
::T
Gets modified.right
::number
Return values
left
::T
Generics
T
of typeVectorXY
Parameters
left
::T
Gets modified.right
::number
Return values
left
::T
Simply calls math.sqrt
on both x
and y
.
Generics
T
of typeVectorXY
Parameters
vector
::T
Gets modified.
Return values
vector
::T
Simply calls math.abs
on both x
and y
.
Generics
T
of typeVectorXY
Parameters
vector
::T
Gets modified.
Return values
vector
::T
Simply calls math.floor
on both x
and y
.
Generics
T
of typeVectorXY
Parameters
vector
::T
Gets modified.
Return values
vector
::T
Simply calls math.ceil
on both x
and y
.
Generics
T
of typeVectorXY
Parameters
vector
::T
Gets modified.
Return values
vector
::T
Can take any amount of vectors, technically even 0 in which case it simply returns nil
. The only limitation is that there must be no gaps in the arguments.
Generics
T
of typeVectorXY
?
Parameters
Return values
vector
::T
A vector with the lowestx
and the lowesty
out of all given vectors.
Can take any amount of vectors, technically even 0 in which case it simply returns nil
. The only limitation is that there must be no gaps in the arguments.
Generics
T
of typeVectorXY
?
Parameters
Return values
vector
::T
A vector with the highestx
and the highesty
out of all given vectors.
Project right
onto left
, get that length and multiply it by the length of left
.
If they are perpendicular to each other, it is 0.
If they are pointing generally away from each other, it is negative.
You can also think about it as projecting left
onto right
and the result is the same. See https://www.3blue1brown.com/lessons/dot-products
Parameters
Return values
number
North is 0, goes clockwise, always positive.
Errors when is_zero(vector)
. Check for 0 length vectors before or see get_orientation_safe
.
Parameters
vector
::VectorXY
Return values
number
North is 0, goes clockwise, always positive.
Parameters
vector
::VectorXY
Return values
number
?
nil
whenis_zero(vector)
.
Returns a RealOrientation, so [0, 1)
where 0 is north, 0.25 is east, 0.5 is south, 0.75 is west.
Errors when is_zero(vector)
. Check for 0 length vectors before or see get_orientation_safe
.
Parameters
vector
::VectorXY
Return values
Returns a RealOrientation, so [0, 1)
where 0 is north, 0.25 is east, 0.5 is south, 0.75 is west.
Parameters
vector
::VectorXY
Return values
RealOrientation
?
nil
whenis_zero(vector)
.
Generics
T
of typeVectorXY
Parameters
vector
::T
Gets modified.radians_diff
::number
Return values
vector
::T
Generics
T
of typeVectorXY
Parameters
vector
::T
Gets modified.orientation_diff
::RealOrientation
Can exceed the usual bounds of RealOrientation.
Return values
vector
::T
Generics
T
of typeVectorXY
Parameters
vector
::T
Gets modified.direction
::defines.direction
Can take negative values, which rotate counter clockwise.
Return values
vector
::T
Right to left... because math.
Generics
T
of typeVectorXY
Parameters
matrix
::MatrixIJ
vector
::T
Gets modified.
Return values
vector
::T
Parameters
radians
::number
Return values
matrix
::MatrixIJ
Parameters
orientation
::RealOrientation
Can exceed the usual bounds of RealOrientation.
Return values
matrix
::MatrixIJ
Parameters
ix
::number
Top left corner if you think about it like a 2x2 grid.jx
::number
Top right corner if you think about it like a 2x2 grid.iy
::number
Bottom left corner if you think about it like a 2x2 grid.jy
::number
Bottom right corner if you think about it like a 2x2 grid.
Return values
matrix
::MatrixIJ
Return values
matrix
::MatrixIJ
Parameters
matrix
::MatrixIJ
Return values
Right to left... because math.
Parameters
second
::MatrixIJ
The transformation that should happen after the first one.first
::MatrixIJ
(Gets modified.) The first transformation that should happen.
Return values
first
::MatrixIJ