If your graphing library has support for views then you should be able to set the graphing_height to -non_graphing_height. If the position of the view is the center, then it shouldn't need to be changed, if it is the bottom-left, then setting graphic_bottom_left to non_graphing_top_left + non_graphing_height should work.
This might look like this in C++ with the SFML library:
const auto& current_view = window.getView();
const auto& current_view_size = current_view.getSize();
window.setView(sf::View(current_view.getCenter(), { current_view_size.x, -current_view_size.y }));
If you are in 3D this could instead be applied to the projection matrix.
If there are no views, you should be able to recreate them with a view matrix. You should then be able to pass that view matrix into your graphics library.
If you can not pass in the view matrix then apply those to your non graphing transforms to get your graphing transforms. Once you have that you will have a transformation matrix that you can pass into your graphics library.
If your graphics library does not support passing in that either you can decompose the transformed matrix into a translation, rotation and scale.
Here is an example of a function in C++ without the SFML library that would do this with a view from 0,0 to 200,200 in the non graphics library:
/// transform the passed in position, rotation and scale
/// \param rotation radians
void transform(Vector2& position, float& rotation, Vector2& scale) {
// create a transformation matrix from the position, rotation and scale
const Matrix transform_matrix{ position, rotation, scale };
// create a upside down view matrix at origin of size 200x200
const Vector2 view_position{
0.f,
0.f
};
const Vector2 view_size{
200.f,
200.f
};
const Matrix view_matrix =
Matrix::translation({ -view_position.x, -(view_position.y + view_size.y) }) *
Matrix::scale({ 1.f / view_size.x, -1.f / view_size.y });
// multiply the transformation matrix with the view matrix
const auto multiplied = transform_matrix * view_matrix;
// update the passed in values
position = multiplied.getTranslation();
rotation = multiplied.getAngle();
scale = multiplied.getScale();
}
Note that in 3D the rotation might be multiple values or a Quaternion, so make sure you have a Matrix class that supports that.
Here is the example Vector2 and Matrix class I used for the example above:
struct Vector2 {
float x;
float y;
};
class Matrix {
public:
/// creates a matrix using passed in matrix
Matrix(const std::vector<std::vector<float>>& matrix) :
matrix_{ matrix }
{}
/// creates 2d transformation matrix
/// \param angle radians
Matrix(const Vector2& translation, const float angle, const Vector2& scale) :
Matrix({
{ scale.x * cos(angle), scale.y * sin(angle), 0.f },
{ -scale.x * sin(angle), scale.x * cos(angle), 0.f },
{ translation.x, translation.y, 1.f }
})
{}
/// create 2d translation matrix
static Matrix translation(const Vector2& translation) {
return Matrix({
{ 1.f, 0.f, 0.f },
{ 0.f, 1.f, 0.f },
{ translation.x, translation.y, 1.f }
});
}
/// create 2d scale matrix
static Matrix scale(const Vector2& scale) {
return Matrix({
{ scale.x, 0.f, 0.f },
{ 0.f, scale.y, 0.f },
{ 0.f, 0.f, 1.f }
});
}
/// multiplies two matrices together
Matrix operator*(const Matrix& rhs) const {
// get number of rows, columns and elements
const auto rows = matrix_.size();
const auto columns = rhs.matrix_[0].size();
const auto elements = rhs.matrix_.size();
// create a new matrix of the correct size, filled with zeroes
Matrix new_matrix(
std::vector<std::vector<float>>(
rows, std::vector<float>(columns, 0.f)
)
);
// go through each row of the new matrix
for (size_t row = 0; row < rows; ++row) {
// go through each column of the new matrix
for (size_t column = 0; column < columns; ++column) {
// set element in the new matrix to the dot product of this matrix's row and the rhs matrix's column
for (size_t element = 0; element < elements; ++element) {
new_matrix.matrix_[row][column] +=
matrix_[row][element] * rhs.matrix_[element][column];
}
}
}
return new_matrix;
}
/// gets the translation from the matrix
Vector2 getTranslation() const {
return { matrix_[2][0], matrix_[2][1] };
}
/// gets the positive scale from the matrix
Vector2 getScale() const {
Vector2 scale{
sqrt(pow(matrix_[0][0], 2) + pow(matrix_[0][1], 2)),
sqrt(pow(matrix_[1][0], 2) + pow(matrix_[1][1], 2))
};
if (matrix_[0][0] < 0) {
scale.x = -scale.x;
}
if (matrix_[1][1] < 0) {
scale.y = -scale.y;
}
return scale;
}
/// gets the angle from the matrix
/// \returns radians
float getAngle() const {
return atan2(matrix_[0][1], std::abs(matrix_[1][1]));
}
private:
std::vector<std::vector<float>> matrix_;
};