Last active
January 26, 2023 22:22
-
-
Save JujuAdams/3aa6402de1cffcb3c02f3b0f29cd9a55 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Transforms a position from one coordinate system to another | |
/// This function presumes you're using GameMaker's native camera/view system and are not | |
/// manually setting custom view/projection matrices. Also if you're drawing your application | |
/// surface yourself then the coordinate transform *may* not be accurate, but it usually is. | |
/// | |
/// N.B. The struct returned from this function is declared as static for the sake of efficiency. | |
/// If you want to store values, don't keep a reference to the struct as it is liable to change! | |
/// | |
/// N.B. This function does NOT take into account view_set_xport() or view_set_yport(). I've not | |
/// seen someone use those functions in many years. | |
/// | |
/// Coordinate systems are selected by using one of the following integers: | |
/// 0: Room - Same coordinate system as device_mouse_x() / device_mouse_y() | |
/// 1: GUI - Same coordinate system as device_mouse_y_to_gui() / device_mouse_y_to_gui() | |
/// 2: Device - Same coordinate system as device_mouse_raw_x() / device_mouse_raw_y() | |
/// | |
/// The "Device" coordinate system is the same as an application's window on PC. | |
/// | |
/// @param x x-coordinate of the point to transform | |
/// @param y y-coordinate of the point to transform | |
/// @param inputSystem Original coordinate system for the point (0, 1, or 2) | |
/// @param outputSystem New coordinate system to transform into (0, 1, or 2) | |
/// @param [camera] Camera to use for the room coordinate system. If not specified, the currently active camera is used. If no camera is active, the camera for view 0 is used | |
/// | |
/// @jujuadams 2023-01-26 | |
function TransformCoordSystem(_x, _y, _inputSystem, _outputSystem, _camera = undefined) | |
{ | |
static _result = { | |
x: 0, | |
y: 0, | |
}; | |
//Build out lots of cached values | |
//We use these to detect changes that might trigger a recalculation of application_get_position() | |
//Doing all this work is faster than calling application_get_position() all the time | |
static _windowW = undefined; | |
static _windowH = undefined; | |
static _appSurfW = undefined; | |
static _appSurfH = undefined; | |
static _appSurfDrawL = undefined; | |
static _appSurfDrawT = undefined; | |
static _appSurfDrawW = undefined; | |
static _appSurfDrawH = undefined; | |
static _recacheTime = -infinity; | |
if (_inputSystem != _outputSystem) //Only do MATHS if the output system is different | |
{ | |
//Only update the cached app surface draw parameters if we're going to need them | |
if ((_inputSystem == 2) || (_outputSystem == 2)) | |
{ | |
//Detect changes in application surface size | |
if ((_appSurfW != surface_get_width(application_surface)) | |
|| (_appSurfH != surface_get_height(application_surface))) | |
{ | |
_appSurfW = surface_get_width(application_surface); | |
_appSurfH = surface_get_height(application_surface); | |
//Recache application surface position immediately in this situation | |
_recacheTime = -infinity; | |
} | |
if (current_time > _recacheTime) | |
{ | |
_recacheTime = infinity; | |
var _array = application_get_position(); | |
_appSurfDrawL = _array[0]; | |
_appSurfDrawT = _array[1]; | |
_appSurfDrawW = _array[2] - _appSurfDrawL; | |
_appSurfDrawH = _array[3] - _appSurfDrawT; | |
} | |
//Detect changes in window size | |
if ((_windowW != window_get_width()) | |
|| (_windowH != window_get_height())) | |
{ | |
_windowW = window_get_width(); | |
_windowH = window_get_height(); | |
//Recache application surface position after 200ms to give GM time to do whatever it does | |
_recacheTime = current_time + 200; | |
} | |
} | |
if (_inputSystem == 0) //Input coordinate system is room-space | |
{ | |
//Grab a camera. Multiple levels of fallback here to cope with different setups | |
_camera = _camera ?? camera_get_active(); | |
if (_camera < 0) _camera = view_camera[0]; | |
if (camera_get_view_angle(_camera) == 0) //Skip expensive rotation step if we can | |
{ | |
//Reduce x/y to normalised values in the viewport | |
_x = (_x - camera_get_view_x(_camera)) / camera_get_view_width( _camera); | |
_y = (_y - camera_get_view_y(_camera)) / camera_get_view_height(_camera); | |
} | |
else | |
{ | |
//Perform a rotation, eventually ending up with normalised values as above | |
var _viewW = camera_get_view_width( _camera); | |
var _viewH = camera_get_view_height(_camera); | |
var _viewCX = camera_get_view_x(_camera) + _viewW/2; | |
var _viewCY = camera_get_view_y(_camera) + _viewH/2; | |
var _angle = camera_get_view_angle(_camera); | |
var _sin = dsin(-_angle); | |
var _cos = dcos(-_angle); | |
var _x0 = _x - _viewCX; | |
var _y0 = _y - _viewCY; | |
_x = ((_x0*_cos - _y0*_sin) + _viewCX) / _viewW; | |
_y = ((_x0*_sin + _y0*_cos) + _viewCY) / _viewH; | |
} | |
if (_outputSystem == 1) | |
{ | |
//If we're outputting to GUI-space then simply multiply up by the GUI size | |
_x *= display_get_gui_width(); | |
_y *= display_get_gui_height(); | |
} | |
else if (_outputSystem == 2) | |
{ | |
//If we're outputting to device-space then perform a transform using the cached app surface draw parameters | |
_x = _appSurfDrawW*_x + _appSurfDrawL; | |
_y = _appSurfDrawH*_y + _appSurfDrawT; | |
} | |
else | |
{ | |
show_error("Unhandled output coordinate system (" + string(_outputSystem) + ")\n ", true); | |
} | |
} | |
else if (_inputSystem == 1) //Input coordinate system is GUI-space | |
{ | |
//Reduce x/y to normalised values in GUI-space | |
_x /= display_get_gui_width(); | |
_y /= display_get_gui_height(); | |
if (_outputSystem == 0) | |
{ | |
//Grab a camera. Multiple levels of fallback here to cope with different setups | |
_camera = _camera ?? camera_get_active(); | |
if (_camera < 0) _camera = view_camera[0]; | |
if (camera_get_view_angle(_camera) == 0) //Skip expensive rotation step if we can | |
{ | |
//Expand room-space x/y from normalised values in the viewport | |
_x = camera_get_view_width( _camera)*_x + camera_get_view_x(_camera); | |
_y = camera_get_view_height(_camera)*_y + camera_get_view_y(_camera); | |
} | |
else | |
{ | |
//Perform a rotation, eventually ending up with room-space coordinates as above | |
var _viewW = camera_get_view_width( _camera); | |
var _viewH = camera_get_view_height(_camera); | |
var _viewCX = camera_get_view_x(_camera) + _viewW/2; | |
var _viewCY = camera_get_view_y(_camera) + _viewH/2; | |
var _angle = camera_get_view_angle(_camera); | |
var _sin = dsin(_angle); | |
var _cos = dcos(_angle); | |
var _x0 = _x*_viewW - _viewCX; | |
var _y0 = _y*_viewH - _viewCY; | |
_x = (_x0*_cos - _y0*_sin) + _viewCX; | |
_y = (_x0*_sin + _y0*_cos) + _viewCY; | |
} | |
} | |
else if (_outputSystem == 2) | |
{ | |
//If we're outputting to device-space then perform a transform using the cached app surface draw parameters | |
_x = _appSurfDrawW*_x + _appSurfDrawL; | |
_y = _appSurfDrawH*_y + _appSurfDrawT; | |
} | |
else | |
{ | |
show_error("Unhandled output coordinate system (" + string(_outputSystem) + ")\n ", true); | |
} | |
} | |
else if (_inputSystem == 2) //Input coordinate system is device-space | |
{ | |
_x = (_x - _appSurfDrawL) / _appSurfDrawW; | |
_y = (_y - _appSurfDrawT) / _appSurfDrawH; | |
if (_outputSystem == 1) | |
{ | |
//Reduce x/y to normalised values in GUI-space | |
_x *= display_get_gui_width(); | |
_y *= display_get_gui_height(); | |
} | |
else if (_outputSystem == 0) | |
{ | |
//Grab a camera. Multiple levels of fallback here to cope with different setups | |
_camera = _camera ?? camera_get_active(); | |
if (_camera < 0) _camera = view_camera[0]; | |
if (camera_get_view_angle(_camera) == 0) //Skip expensive rotation step if we can | |
{ | |
//Expand room-space x/y from normalised values in the viewport | |
_x = camera_get_view_width( _camera)*_x + camera_get_view_x(_camera); | |
_y = camera_get_view_height(_camera)*_y + camera_get_view_y(_camera); | |
} | |
else | |
{ | |
//Perform a rotation, eventually ending up with room-space coordinates as above | |
var _viewW = camera_get_view_width( _camera); | |
var _viewH = camera_get_view_height(_camera); | |
var _viewCX = camera_get_view_x(_camera) + _viewW/2; | |
var _viewCY = camera_get_view_y(_camera) + _viewH/2; | |
var _angle = camera_get_view_angle(_camera); | |
var _sin = dsin(_angle); | |
var _cos = dcos(_angle); | |
var _x0 = _x*_viewW - _viewCX; | |
var _y0 = _y*_viewH - _viewCY; | |
_x = (_x0*_cos - _y0*_sin) + _viewCX; | |
_y = (_x0*_sin + _y0*_cos) + _viewCY; | |
} | |
} | |
else | |
{ | |
show_error("Unhandled output coordinate system (" + string(_outputSystem) + ")\n ", true); | |
} | |
} | |
else | |
{ | |
show_error("Unhandled input coordinate system (" + string(_inputSystem) + ")\n ", true); | |
} | |
} | |
//Set values and return! | |
_result.x = _x; | |
_result.y = _y; | |
return _result; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment