Trouble with go.world_to_local_position – seems to be adding positions together?

I am trying to determine where on a game object the user has clicked, in terms of that game object’s local space. (For example, if the user clicks the game object dead on its local origin, I want the returned position to be (0, 0).) I’m using go.world_to_local_position, which I think is what I want, but it’s not returning the position I’d expect. Am I using the function incorrectly?

My actual project uses defold-input’s cursor together with defold-orthographic, but I’m still getting the behaviour in a minimal reproduction project that uses neither. I’m using Defold 1.10.0 on macOS, though as far as I can tell, go.world_to_local_position hasn’t changed since it was originally merged in last year.

These three green squares’ bottom left corners (marked in red) are at their local origins. Each has a collision shape that covers their sprites. One is placed at world origin, the second is at (250,250), and the third is at (550,0) with Z-rotation of 45.0°.

There is a simple cursor game object with a small kinematic collision object (and a white dot particle sprite) that follows the user’s mouse pointer. It is scripted thus:

function init(self)
	msg.post(".", "acquire_input_focus")
	self.touching = nil
end

function update(self, dt)
	self.touching = nil
end

function on_message(self, message_id, message, sender)
	if message_id == hash("collision_response") then
		self.touching = message.other_id
	end
end

function on_input(self, action_id, action)
	if action.x and action.y then
		go.set_position(vmath.vector3(action.x, action.y, 0))
	end

	if action_id == hash("touch") and action.pressed then
		print("Click action.x/y: ", action.x, action.y)
		local cursor_world_position = go.get_world_position(".")
		print("Cursor world position x/y: ", cursor_world_position.x, cursor_world_position.y)
		if self.touching then
			local object_world_position = go.get_world_position(self.touching)
			print("Clicked object: " .. self.touching .. " (object world position: ", object_world_position.x, object_world_position.y, ")")

			local click_local_position = go.world_to_local_position(cursor_world_position, self.touching)
			print("Object was clicked at local position: ", click_local_position.x, click_local_position.y)
		end
	end
end

When I run the game and click each square close to each one’s local origin:

DEBUG:SCRIPT: Click action.x/y: 	13.5	12.5
DEBUG:SCRIPT: Cursor world position x/y: 	13.5	12.5
DEBUG:SCRIPT: Clicked object: [/square_origin] (object world position: 	0	0	)
DEBUG:SCRIPT: Object was clicked at local position: 	13.5	12.5

DEBUG:SCRIPT: Click action.x/y: 	268.5	264.5
DEBUG:SCRIPT: Cursor world position x/y: 	268.5	264.5
DEBUG:SCRIPT: Clicked object: [/square_translated] (object world position: 	250	250	)
DEBUG:SCRIPT: Object was clicked at local position: 	518.5	514.5

DEBUG:SCRIPT: Click action.x/y: 	553.5	25.5
DEBUG:SCRIPT: Cursor world position x/y: 	553.5	25.5
DEBUG:SCRIPT: Clicked object: [/square_rotated] (object world position: 	550	0	)
DEBUG:SCRIPT: Object was clicked at local position: 	1103.5	25.5

I’ll be the first to admit that vector math’s not my strong suit, but a click at world (268.5, 264.5) atop a GO that has its own origin at world (250, 250) should mean the click’s at (18.5, 14.5) in that GO’s local space, not (250 + 268.5, 250 + 264.5) = (518.5, 514.5), right?

I feel like I must be missing something obvious. If anyone can set me right, I’d appreciate it.

This is how I’m getting local click coordinates in the meantime:

local object_world = go.get_world_transform(object_url)
local object_inverse = vmath.ortho_inv(object_world)
local cursor_world = go.get_world_transform("/cursor")
local cursor_from_object = object_inverse * cursor_world
local cursor_local_position = vmath.vector3(cursor_from_object.m03, cursor_from_object.m13, cursor_from_object.m23)