Page 1 of 1

Blueprint entity positioning

Posted: Tue May 23, 2017 9:13 pm
by SyncViews
Trying to work out how the game is positioning entities in blueprints to allow scripted edits, but I am finding the game seems to use 2 different coordinate systems and the blueprint wont build correctly if I position something wrongly.

Normally for entities/tiles on a surface tiles are at integer coordinates, and entities are positioned by their centre with the tile top-left being an integer. e.g. a tile may be at 10,12 and an inserter on it would be 10.5,12.5.

But in blueprints, most of the time it seems to me the entities are shifted by -0.5 compared to what I expect, except in some cases (I think if track is present).

Does anyone have any idea exactly what is going on here? Is it always shifted for some reason if a curved-rail or straight-rail is not present?


e.g. in these the belts are both in the same place relative to the hazard tiles. But when I create a blueprint with and without track, and look at the entities in the blueprint, they are positioned differently.
Without track:

Code: Select all

0eNqNkd1qwzAMhV8l07UNW2j346foRWEXYwwnEYmYYwdbKc1C3n1y2sFgGfRSsvSdo+MZKjfiEMkzmBmoDj6BeZshUeutyz2eBgQDxNiDAm/7XHG0Pg0hsq7QMSwKyDd4BvOwvCtAz8SEF9JaTB9+7CuMMvAfQ8EQkqwFn1UFpWV0ykQFDUWsL0/lov4wy5uZ99tI8czkroa3bWgR/lHp7JeNjZaw6oiMOlLb5RDmbbkbVsXAGrD59R8KnJUrpHd3xMSFLg5XOvlWqlfiLoxcHKOtP2X6hDGt0o+7XbkvX/bPT+WyfAOl66CW

Entities:
  {entity_number=1,name="transport-belt",position={x=-1,y=1},direction=2}
  {entity_number=2,name="transport-belt",position={x= 0,y=1},direction=2}
Tiles:
  {position={x=-1,y=-2},name="hazard-concrete-right"}
  {position={x= 0,y=-2},name="hazard-concrete-right"}
With track:

Code: Select all

0eNqNkt1ugzAMhV+F+TqpCiv74Sl2UWkX0zQFsMAaBJS40xji3eek3VR1P+1NJCc53zmxM0PZ7XB0ZBmKGagarIfiaQZPjTVd2ONpRCiAGHtQYE0fKmeog0UB2RrfoUgXdVbCzlg/Do51iR0fibPlWQFaJibcm8dierG7vkQn9G+GFwg1Levor2AcvKgGG0yFtFYwySromhxW+5MsZDshZn+l+oHU61UeqekqP8+9vpj7D1a6wdQdWnEaJ40qLeZfTq35MK7WMrnKIaN2oUEQwv3anQukEiCOrjj6HAo6Iy+Rvastek508nCgk22keiRuk60z1atcfUPno+/NZpPl2X1+d5styyf088yQ
Entities:
  {entity_number=1,name="straight-rail", position={x= 0  ,y=0  },direction=2}
  {entity_number=2,name="transport-belt",position={x=-0.5,y=1.5},direction=2}
  {entity_number=3,name="transport-belt",position={x= 0.5,y=1.5},direction=2}
Tiles:
  {position={x=-1,y=-2},name="hazard-concrete-right"}
  {position={x= 0,y=-2},name="hazard-concrete-right"}
]

Re: Blueprint entity positioning

Posted: Wed May 24, 2017 1:40 am
by Rseding91
To get the actual location an entity will be built at in the world we do this logic:

Code: Select all

RealPosition Blueprint::getBuildingPosition(const RealPosition& position) const
{
  if (this->isPlaceableOffGrid())
    return position;

  uint8_t buildingGridBitShift = this->getBuildingGridBitShift();
  TilePosition tiledResult = position;
  tiledResult.roundToGridBitShift(buildingGridBitShift);
  RealPosition result(tiledResult.x, tiledResult.y);
  result.x += (1 << buildingGridBitShift) * 0.5;
  result.y += (1 << buildingGridBitShift) * 0.5;
  return result;
}

void TilePosition::roundToGridBitShift(uint8_t shift)
{
  this->x = (this->x >> shift) << shift;
  this->y = (this->y >> shift) << shift;
}

Where this->isPlaceableOffGrid() is true when all tiles and all entities in the blueprint have the prototype flag placeable off grid (http://lua-api.factorio.com/latest/LuaE ... e.has_flag).

Where uint8_t buildingGridBitShift = this->getBuildingGridBitShift(); is the max grid bit shift value of every entity prototype (http://lua-api.factorio.com/latest/LuaE ... _bit_shift).

And then everything in the blueprint is shifted by that result and put in the world.

Re: Blueprint entity positioning

Posted: Wed May 24, 2017 9:44 am
by SyncViews
At least from the entities I tested, is 1 for train-stop, curved-rail and straight-rail and zero elsewhere, but is coded to the type, not the prototype data (e.g. I saw no property to allow a train stop which has shift=1 on any tile along a straight track like signals which have shift=0)?

Code: Select all

withoutTrack->getBuildingPosition({1,1})
  buildingGridBitShift = 0
  roundToGridBitShift = (1 >> 0) << 0 = 1
  result.x = 1 + (1 << 0) * 0.5 = 0.5
  return {1.5,1.5}
  
  (0.5,0.5) extra shift

withTrack->getBuildingPosition({1,1})
  buildingGridBitShift = 1
  roundToGridBitShift = (1 >> 1) << 1 = 0
  result.x = 0 + (1 << 1) * 0.5 = 0 + 1 = 1
  return {1,1}
  
  No extra shift
Which then looks to give me the same results for both blueprints if I adjust the entities by that, and I round the shift down to an integer for tiles (so both {0,0} in this case)? The tile thing still slightly confuses me, since in the two blueprints I looked at, the relative positions of tiles and belts changed.

There also seems to be some extra rounding of individual entities when placing a blueprint if I got it wrong. I think this is what I ran into before, entities jump or get skipped, but the pre-place outline and preview look right. Which would be what create_entity does on a per entity basis.

Then when creating (or updating/editing) a blueprint I can just use the inverse of those shifts for a {1,1} origin to adjust it (will test when home)?

Re: Blueprint entity positioning

Posted: Fri Jul 21, 2017 7:35 pm
by Reika
I realize this thread is a couple months inactive, but I find it relevant once again:

I am facing an issue due to item-in-blueprint placement, in my case involving walls and turrets (though I suspect other entities would end up being the same).

Basically, if I inject the items into the blueprint directly, using the above-supplied logic, then the blueprint "place preview" looks correct, but the turrets are placed off-position (usually failing to place at all due to intersection):
Image

If I manually adjust the positions, however, the preview now breaks, but the placement is correct:
Image

However, that latter case only works for one rotation; rotating the blueprint breaks placement as well:
Image
Image


Also, the blueprint needs to be saved and reopened for the turrets to display at all; the 'first preview' only shows the walls:
Image
Image


Relevant code:

Code: Select all

local function roundToGridBitShift(position, shift)
	position.x = bit32.lshift(bit32.rshift(position.x, shift), shift)
	position.y = bit32.lshift(bit32.rshift(position.y, shift), shift)
	return position
end

local function getPositionForBPEntity(entity)
	local position = entity.position
	
	if (entity.has_flag("placeable-off-grid")) then
		return position
	end

	local buildingGridBitShift = entity.building_grid_bit_shift
	local tiledResult = position
	tiledResult = roundToGridBitShift(tiledResult, buildingGridBitShift)
	local result = {x=tiledResult.x, y=tiledResult.y}
	result.x = result.x + bit32.lshift(1, buildingGridBitShift) * 0.5
	result.y = result.y + bit32.lshift(1, buildingGridBitShift) * 0.5
	return result
end

script.on_event(defines.events.on_player_setup_blueprint, function(event)
	local player = game.players[event.player_index]
	local surface = player.surface
	local bp = player.cursor_stack
	local flag = false
	local entities = surface.find_entities_filtered({force = player.force, area = event.area})
	local forbp = {}
	local avgpos = {x=0, y=0}
	for _,entity in pairs(entities) do
		local ename = entity.name
		local pos = entity.position
		if string.find(entity.name, "rangeboost") then
			flag = true
			ename = getTurretBaseName(entity)
			--pos.x = pos.x+1 --These are the lines that break/fix the preview and placement
			--pos.y = pos.y+1
		end
		table.insert(forbp, {entity_number=#forbp, name=ename, position=pos, direction=entity.direction})
		avgpos.x = avgpos.x+entity.position.x
		avgpos.y = avgpos.y+entity.position.y
	end
	if not flag then return end
	avgpos = {x=avgpos.x/#forbp, y=avgpos.y/#forbp}
	for _,entry in pairs(forbp) do
		entry.position.x = entry.position.x-avgpos.x
		entry.position.y = entry.position.y-avgpos.y
	end
	bp.set_stack({name=event.item, count=1}) --since always invalid
	game.print("BPing " .. #forbp .. " entities:")
	for _,v in pairs(forbp) do game.print(v.name .. " @ " .. v.position.x .. ", " .. v.position.y) end
	bp.clear_blueprint()
	bp.set_blueprint_entities(forbp)
end)

Re: Blueprint entity positioning

Posted: Fri Jul 21, 2017 8:23 pm
by Reika
Two updates:

One, the broken offset seems to apply to all 2x2 entities, not just turrets (it also affects the logistics expander next to the coal belt).
Two, decimal "correction offsets" yield interesting results: 0.5 makes the "default" orientation work in both preview and place, but does not fix the rotated versions. Anything else (including 0.33, 0.25, 0.1875 and similar) act the same as zero.

Re: Blueprint entity positioning

Posted: Fri Jul 21, 2017 10:54 pm
by SyncViews
Would have to check turrets, but dont recall them being special, only rails. Because adding such an item might need to change the entire blueprint, I coded my logic to use "real coordinates" (as they would end up in the world if you placed the blueprint at 0,0), and applied the shift only on import/export. Worked for all the blueprints / placeable objects I tried allthough it is still really strange since it changes the relative coordinates of tiles and entities.

Factorio seems to generally snap invalid coordinates to grid, but does so differentlyvfor preview and actual placement. If some of these snapped positions cause collisions, those entities are not placed.

Re: Blueprint entity positioning

Posted: Sat Jul 22, 2017 3:32 am
by Reika
SyncViews wrote:I coded my logic to use "real coordinates" (as they would end up in the world if you placed the blueprint at 0,0), and applied the shift only on import/export.
Can you elaborate? I cannot find any hooks for placing (ie exporting) blueprints and I followed the above post by Rseding exactly (as you presumably did) yet got poor results.

Re: Blueprint entity positioning

Posted: Sat Jul 22, 2017 1:25 pm
by SyncViews
So as in before I did any transforms/alterations, I took all the entities in the blueprint and adjusted them as described.

Then i added,removed,etc entities using those positions.

Then did the reverse transform to put them back in the blueprint.


To do stuff in mods the relevant methods are on the LuaItemStack object (where the stack contains a blueprint).

Re: Blueprint entity positioning

Posted: Sat Jul 22, 2017 5:53 pm
by Reika
SyncViews wrote:So as in before I did any transforms/alterations, I took all the entities in the blueprint and adjusted them as described.
Again, what do you mean exactly? I "adjusted them as described" and it did not work.
SyncViews wrote: Then i added,removed,etc entities using those positions. Then did the reverse transform to put them back in the blueprint.
....Wait, what?