Blueprint entity positioning

Place to get help with not working mods / modding interface.
Post Reply
SyncViews
Filter Inserter
Filter Inserter
Posts: 295
Joined: Thu Apr 21, 2016 3:17 pm
Contact:

Blueprint entity positioning

Post 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"}
]
Attachments
factorio-blueprint-tracks.png
factorio-blueprint-tracks.png (194.66 KiB) Viewed 3705 times

Rseding91
Factorio Staff
Factorio Staff
Posts: 13209
Joined: Wed Jun 11, 2014 5:23 am
Contact:

Re: Blueprint entity positioning

Post 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.
If you want to get ahold of me I'm almost always on Discord.

SyncViews
Filter Inserter
Filter Inserter
Posts: 295
Joined: Thu Apr 21, 2016 3:17 pm
Contact:

Re: Blueprint entity positioning

Post 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)?

User avatar
Reika
Filter Inserter
Filter Inserter
Posts: 582
Joined: Tue May 19, 2015 1:56 am
Contact:

Re: Blueprint entity positioning

Post 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)
Image

User avatar
Reika
Filter Inserter
Filter Inserter
Posts: 582
Joined: Tue May 19, 2015 1:56 am
Contact:

Re: Blueprint entity positioning

Post 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.
Image

SyncViews
Filter Inserter
Filter Inserter
Posts: 295
Joined: Thu Apr 21, 2016 3:17 pm
Contact:

Re: Blueprint entity positioning

Post 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.

User avatar
Reika
Filter Inserter
Filter Inserter
Posts: 582
Joined: Tue May 19, 2015 1:56 am
Contact:

Re: Blueprint entity positioning

Post 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.
Image

SyncViews
Filter Inserter
Filter Inserter
Posts: 295
Joined: Thu Apr 21, 2016 3:17 pm
Contact:

Re: Blueprint entity positioning

Post 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).

User avatar
Reika
Filter Inserter
Filter Inserter
Posts: 582
Joined: Tue May 19, 2015 1:56 am
Contact:

Re: Blueprint entity positioning

Post 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?
Image

Post Reply

Return to “Modding help”