Page 1 of 1

Non-square non-1x1 inserter giving constant error

Posted: Tue Jan 16, 2018 4:12 am
by Reika
I am trying to make an entity of type inserter which has a size of 3x6, and no matter what I do, I cannot get the game to load, as it always halts with an "invalid insert vector, not a safe distance from the tile edge" error.

No matter what positions I pick, I get this error. I have tried the originals (inherited from the stack inserter), those values offset by 1, 1.5, and 3 (to compensate for the increased hitbox size), and even ridiculous like {0, 2000}. Nothing has worked.

What exactly raises this error? What is it telling me, and what can I do to fix it (not just some magic values, but how are such 'safe' values derived)?

Image

Re: Non-square non-1x1 inserter giving constant error

Posted: Tue Jan 16, 2018 4:47 am
by eradicator
They're probably derived similar to fluid boxes (see this post).
I find those error messages pretty frustrating myself though.

Re: Non-square non-1x1 inserter giving constant error

Posted: Tue Jan 16, 2018 5:05 am
by Reika
eradicator wrote:They're probably derived similar to fluid boxes (see this post).
I find those error messages pretty frustrating myself though.
I suspected this, as I have run into that error myself, back when designing the geothermal wells.

However, among the values I tried were ones that should meet that criterion, like {0, -1.6} or {0, -2} for an entity of collision box {{-2.9, -1.4}, {2.9, 1.4}}.

EDIT:
OK, WTF: I tried rotating the collision and selection boxes 90 degrees, as a random test, and now it loads. But the insert vector is inside the collision box! How is this acceptable while its 90-degree counterpart is not?! And why do vanilla ones - which are NOT inside the box - work?
Image


Also, rotation is broken - I hit R when holding the item, but when I place a new entity, its collision box is not placed in a rotated state. Its insert vectors are, (and now in line with what I wanted), but the collision box remains "aligned to Y axis". Likewise if I rotate the insert vector in the prototype but leave the collision box unrotated:
Image
Image

Re: Non-square non-1x1 inserter giving constant error

Posted: Tue Jan 16, 2018 12:45 pm
by eradicator
Non-square entities are generally fiddly, and if you want an entity that's rotateable 4 ways after being placed they're a straight no-go. They can only rotate 2-way (i.e. flip direction). And ofc the vectors are box sensitive. Best guess: inserters don't have any code for non-square boxes. Best to ask someone (rsed?) on irc i guess.

I remember there was a bug report/fix about a non-square tank but can't find it right now. I found a topic concerning assemblers viewtopic.php?t=53310 which sounds like it supports my theory that every entity needs special code before it can handle non-square boxes.

Re: Non-square non-1x1 inserter giving constant error

Posted: Tue Jan 16, 2018 8:16 pm
by Therax
Reika wrote:However, among the values I tried were ones that should meet that criterion, like {0, -1.6} or {0, -2} for an entity of collision box {{-2.9, -1.4}, {2.9, 1.4}}.
Insert positions must not be on tile boundaries, i.e. having final coordinates that are integers or close to integers. (I've found >=0.2 and <=0.8 to be safest.)

For this example, the collision box has even size in the X direction, and odd size in the Y direction, so the center of the entity will be something like (0, 0.5). Adding the insert vector to this, we get (0, -1.1), and (0, -1.5). These are both invalid in the X direction. It's unclear to the engine whether dropped items should be inserted into tile (0,-1) or (-1, -1).

Vanilla inserters are 1x1, giving them a conceptual center of (0.5, 0.5) relative to the tile grid, and integer pickup/drop positions are always safely within the bounds of a tile.
Reika wrote:EDIT:
OK, WTF: I tried rotating the collision and selection boxes 90 degrees, as a random test, and now it loads. But the insert vector is inside the collision box! How is this acceptable while its 90-degree counterpart is not?! And why do vanilla ones - which are NOT inside the box - work?
Being inside/outside the collision box is not important, as long as the drop_position is clearly inside a tile, not near a tile boundary.

Rotating the collision box changes the conceptual center of the entity to (0.5, 0). If you did not change the drop_position, we now have a sum drop position of:

(0.5, -1.1) or (0.5, -1.5), which do not contain integer values on either axis, and are thus valid.

From the screenshots, it looks like you're trying to make a train unloader with inserters, possibly inspired by Miniloader? :) I ran into very similar problems tweaking drop positions to put items into the different lanes of a belt.

Re: Non-square non-1x1 inserter giving constant error

Posted: Tue Jan 16, 2018 8:33 pm
by eradicator
@Therax:
Do you have any "proof"/anectodes that that "conceptual" center of entities is relevant to vectors? I assumed that entities are always "centered" on 0,0 of their own collision box and thus the vectors should be based on that and not on if the game places them on a tile boundary or not?
(Not saying you're wrong. I'm just struggling with that s*** myself and want to understand it better...)

Re: Non-square non-1x1 inserter giving constant error

Posted: Tue Jan 16, 2018 8:42 pm
by Klonan
eradicator wrote:@Therax:
Do you have any "proof"/anectodes that that "conceptual" center of entities is relevant to vectors? I assumed that entities are always "centered" on 0,0 of their own collision box and thus the vectors should be based on that and not on if the game places them on a tile boundary or not?
(Not saying you're wrong. I'm just struggling with that s*** myself and want to understand it better...)
Maybe you can glean some insight from this:

Code: Select all

bool InserterPrototype::isPickupDropoffPositionValid(Vector checkVector)
{
  double intPart;
  float intPartFloat;
  checkVector.dx = modff(modf(modf(checkVector.dx, &intPart) + double(1.0), &intPart), &intPartFloat);
  checkVector.dy = modff(modf(modf(checkVector.dy, &intPart) + double(1.0), &intPart), &intPartFloat);
  return !(checkVector.dx <= ItemEntityPrototype::size.getDouble() + 0.01 ||
           checkVector.dy <= ItemEntityPrototype::size.getDouble() + 0.01 ||
           checkVector.dx >= 1 - ItemEntityPrototype::size.getDouble() - 0.01 ||
           checkVector.dy >= 1 - ItemEntityPrototype::size.getDouble() - 0.01);
}

Re: Non-square non-1x1 inserter giving constant error

Posted: Tue Jan 16, 2018 9:14 pm
by Therax
eradicator wrote:@Therax:
Do you have any "proof"/anectodes that that "conceptual" center of entities is relevant to vectors? I assumed that entities are always "centered" on 0,0 of their own collision box and thus the vectors should be based on that and not on if the game places them on a tile boundary or not?
(Not saying you're wrong. I'm just struggling with that s*** myself and want to understand it better...)
Anecdotal evidence: Reika rotated the collision_box of an entity without changing the vectors, and it went from non-working to working. ;)

I'm assuming that collision boxes are centered around 0,0, which I believe is true for all vanilla entities and followed by most mod authors. I've never tried to make a collision box that wasn't centered on 0,0, so I can't say what the behavior might be in that case. When I say "conceptual center", all I'm referring to is whether the center of an entity, its "position", lies on a tile boundary or not.

The "position" of an entity is the center of the collision box, but the size of the collision box affects where "position" lies in relation to the tile grid. Check "position" of a 1x1 entity (inserter), or a 3x3 entity (storage-tank), and you'll always find that the position has the form (XX.5, YY.5). Do the same for an entity with an even dimension, like a pump or a decider combinator (1x2), and you'll find that the position is always (XX.0, YY.5) or (XX.5, YY.0), depending on its rotation.

Do the same for a 2x2 entity like a straight-rail or a train-stop, and the position is always (XX.0, YY.0), because the center of the entity is exactly the center of 4 tiles, on a "corner" of the tile grid.

Put another way, Factorio's coordinate system is anchored at the top-left corner of each tile, so tile boundaries are integer coordinates, while a 1x1 entity aligned with the grid has its position in the center of a tile, at (0.5, 0.5) relative to the top-left corner of its tile.

Re: Non-square non-1x1 inserter giving constant error

Posted: Tue Jan 16, 2018 9:26 pm
by Therax
Klonan wrote:Maybe you can glean some insight from this:

Code: Select all

bool InserterPrototype::isPickupDropoffPositionValid(Vector checkVector)
{
  double intPart;
  float intPartFloat;
  checkVector.dx = modff(modf(modf(checkVector.dx, &intPart) + double(1.0), &intPart), &intPartFloat);
  checkVector.dy = modff(modf(modf(checkVector.dy, &intPart) + double(1.0), &intPart), &intPartFloat);
  return !(checkVector.dx <= ItemEntityPrototype::size.getDouble() + 0.01 ||
           checkVector.dy <= ItemEntityPrototype::size.getDouble() + 0.01 ||
           checkVector.dx >= 1 - ItemEntityPrototype::size.getDouble() - 0.01 ||
           checkVector.dy >= 1 - ItemEntityPrototype::size.getDouble() - 0.01);
}
Thanks Klonan!

So taking "s" as the size of the "item-on-ground" entity (plus a floating-point rounding-compensation factor of 0.01), it takes the fractional part of both x and y of checkVector and makes sure they're in the range s <= fractional_part <= 1-s. The addition of 1.0 ensures that fractional_part always ends up with a positive value in the range 0 <= x < 1.

I'm fairly sure that checkVector is simply position + drop_position. What eradicator and I are discussing is how tile alignment determines the inserter's position from its collision_box.

Re: Non-square non-1x1 inserter giving constant error

Posted: Wed Jan 17, 2018 1:41 am
by eradicator
Therax wrote:What eradicator and I are discussing is how tile alignment determines the inserter's position from its collision_box.
Not really. You misunderstood the question. It looked like you were calculating the correct vector values off the map position of an entity whereas i assume that the map position should be (more or less) irrelevant. Thinking about it the map position has to be relevant to prevent dropping on tile borders.... I'm farily sure i know how the positioning works though (see my description here).

@Klonan:
Thanks for the snippet. I have to admit i have no c experience... but comparing it with @Theraxes explanation (thanks for that too) i think i get it now. A vector is invalid if it would put an item-on-the-ground so close to the tile border that the items collision box would overlap with the tile border. It also doesn't seem to consider the collision box at all?

@Therax:
I always thought of the 0.01 as more of a collision-prevention so things directly adjacent things don't collide. I.e. two lines [0,1] and [1,2] collide at the point 1, but two lines [0,0.99] and [1.01,2] don't collide.

So that would mean a valid vector for a 6x3 entity should be...eer, {0,3.5} or {0.5,2} for pickup and {0,3.7} or {0.5,2.2} for insert? Time for @Reika to do some more tests :P

Re: Non-square non-1x1 inserter giving constant error

Posted: Wed Jan 17, 2018 8:25 pm
by Reika
eradicator wrote:Time for @Reika to do some more tests :P
Because of these and other problems, I ended up scrapping the entire entity, at least for now.

Re: Non-square non-1x1 inserter giving constant error

Posted: Tue Nov 05, 2019 9:23 pm
by PyroFire
Seems like having certain prototypes completely unrelated to the inserter (e.g. data.raw.item-entity) with incorrect collision boxes also cause this error.

Weird.

Re: Non-square non-1x1 inserter giving constant error

Posted: Tue Nov 05, 2019 9:29 pm
by Therax
PyroFire wrote: Tue Nov 05, 2019 9:23 pm Seems like having certain prototypes completely unrelated to the inserter (e.g. data.raw.item-entity) with incorrect collision boxes also cause this error.

Weird.
Not that weird. As discussed:
Therax wrote: Tue Jan 16, 2018 9:26 pm So taking "s" as the size of the "item-on-ground" entity (plus a floating-point rounding-compensation factor of 0.01), it takes the fractional part of both x and y of checkVector and makes sure they're in the range s <= fractional_part <= 1-s. The addition of 1.0 ensures that fractional_part always ends up with a positive value in the range 0 <= x < 1.
Changing the size of the item-on-ground entity changes the math of how close to the edge of a tile an inserter's drop position is allowed to be.