Translating the origin

Does anyone know if it would be possible when referring to the pixels of the OLED to translate the point of origin to somewhere other than the top left corner? There’s a few cases in which having (0,0) at the middle of the screen would be really advantageous. Ideally I would want to call a procedure like, “moveOrigin(MIDDLE)” that could be called during setup. I realize calling it could get confusing if the point of origin is moving a lot, so maybe the procedure only moves origin to the middle of the screen or back to the top left corner.

Anyone got any ideas? I have no idea where to start with this😅

There’s no built in way of doing it, but that’s not a problem.

It’s not that difficult to just translate your coordinates before calling the drawing functions.

Have a look at my platformer demo for some examples, where the camera effectively moves the origin:

If you only want the centre of the screen to be 0,0 then you’d only have to use a constant offset of half the screen’s width and height.

Though note that because the screen’s dimensions are even, the centre actually straddles 4 pixels, so unless you’re willing to deal with half-pixel coordinates you’ll just have to pick one of the four to be point zero.

4 Likes

To put it another way…

// Drawing.h
#pragma once

// For uint8_t
#include <stdint.h>

// For Arduboy2, Sprites
#include <Arduboy2.h>

namespace Drawing
{
	constexpr uint8_t halfScreenX = (Arduboy2::width() / 2);
	constexpr uint8_t halfScreenY = (Arduboy2::height() / 2);

	// Note:
	// The us of 'inline' is so this will work in a header file,
	// not an attempt to request that the function will be 'inlined'.
	// The function is small enough that the compiler should inline it anyway.
	inline void drawOverwrite(int16_t x, int16_t y, const uint8_t * bitmap, uint8_t frame)
	{
		Sprites::drawOverwrite(halfScreenX + x, halfScreenY + y, bitmap, frame);
	}

	// Et cetera, for the remaining Sprites functions

	inline void drawRect(int16_t x, int16_t y, uint8_t width, uint8_t height, uint8_t colour = WHITE)
	{
		Arduboy2Base::drawRect(halfScreenX + x, halfScreenY + y, width, height, colour);
	}

	// Et cetera, for the remaining Arduboy2Base drawing functions
}

If you wanted something more general, you could make Drawing a class instead, with offsetX and offsetY member variables, and a means to set them.

1 Like

I’m not all that familiar with namespaces so I’m not exactly sure what’s happening here. Trying to adapt this to code I’m using I get an error “cannot call member function 'void Arduboy2Base::drawRect(int16_t, int16_t, uint8_t, uint8_t, uint8_t)' without object ” during compile time.

I’ve used offsets in the past for some games and demos, and while they work well for most situations I’m trying to figure out how one could call a single function to change the origin, so that any function after it’s been called will print to the middle of the screen if coordinates are (0,0). Is this what the namespace chunk of code would do, essentially modify the existing drawing functions to take the offset into account?

There’s no reason not to read up on them, most languages have namespaces these days, and they’re quite useful:

https://www.learncpp.com/cpp-tutorial/2-9-naming-collisions-and-an-introduction-to-namespaces/

C++'s namespace have a few more features than some though.

E.g. The ability to use using to bring a name into scope so you don’t have to fully qualify it (e.g. put using std::cout; at the top of a function and you can use cout instead of std::cout from then on).

I have no idea why Arduino doesn’t make more use of namespaces. I chalk it up to having lots of C programmers trying to write C++ without actually knowing much about C++.

I particularly like to keep my images in an Images namespace and strings in a Strings namespace, so I don’t have to worry about resource names clashing with names used elsewhere in my game code.

Have you checked that your library version is up to date?
Or are you perhaps using Mr. Blinky’s homemade package?

In the former case, update your library.

In the latter case, I’m presuming the homemade package doesn’t include the latest 6.0.0 changes, one of which made drawRect a static function, in which case the error will have to be worked around one way or another.

I just realised I got it backwards and it should be + instead of -.
I was still thinking about how a camera is typically used, and a camera works in the opposite way to what you’re asking for.

If you changed the -s to +s (which I’ll do in a moment), the code in the Drawing namespace would provide functions that would draw to the screen as if pixel 64, 32 were the origin point 0, 0. Thus a call of Drawing::drawOverwrite(5, 5, image, 0); would draw the image at position 69, 37, and a call of Drawing::drawOverwrite(-4, -2, image, 0); would draw the image at position 60, 32.


// Drawing.h
#pragma once

// For uint8_t
#include <stdint.h>

// For Arduboy2, Sprites
#include <Arduboy2.h>

struct Renderer
{
	int16_t offsetX;
	uint8_t offsetY;
	
	void setOffset(int16_t x, int16_t y)
	{
		this->offsetX = x;
		this->offsetY = y;
	}

	void drawOverwrite(int16_t x, int16_t y, const uint8_t * bitmap, uint8_t frame)
	{
		Sprites::drawOverwrite(offsetX + x, offsetY + y, bitmap, frame);
	}

	// Et cetera, for the remaining Sprites functions

	void drawRect(Arduboy2Base & arduboy, int16_t x, int16_t y, uint8_t width, uint8_t height, uint8_t colour = WHITE)
	{
		arduboy.drawRect(offsetX + x, offsetY + y, width, height, colour);
	}

	// Et cetera, for the remaining Arduboy2Base drawing functions
}

Then

// Initialise with the origin in the middle of the screen
Renderer renderer { (Arduboy2::width() / 2), (Arduboy2::height() / 2) };

// Draw to the centre of the screen.
renderer.drawRect(arduboy, 0, 0, 32, 32);

// Change the origin to point 0, 0
renderer.setOffset(0, 0);

// Draw to the top left corner of the screen
renderer.drawRect(arduboy, 0, 0, 32, 32);

// Change the origin to point -16, -16
renderer.setOffset(-16, -16);

// Draw starting outside the screen
renderer.drawRect(arduboy, 0, 0, 32, 32);

Note that the caveats about there being 4 pixels that could arguably be called the ‘centre’ of the screen. I’ve opted for the simplest solution, which results in the bottom right of the four ‘centre’ pixels being considered the origin. That can be changed with a few simple subtractions in the initialisation of the object.

3 Likes

This is great! Thanks for the help, and the resources :smiley: namespaces do seem extremely useful now

3 Likes

Yep! This was definitely the issue I was having, but it was simple enough to work around, I made a fork of @Mr.Blinky 's repo with the static changes necessary here. There’s also one more change in that fork, which is not related to this, but will make @obono 's games display properly on homemade arduboys.

1 Like