Blame synfig-core/src/synfig/matrix.cpp

Nikita Kitaev dc5e35
/* === S Y N F I G ========================================================= */
Nikita Kitaev dc5e35
/*!	\file matrix.cpp
Nikita Kitaev dc5e35
**	\brief Template File
Nikita Kitaev dc5e35
**
Nikita Kitaev dc5e35
**	$Id$
Nikita Kitaev dc5e35
**
Nikita Kitaev dc5e35
**	\legal
Nikita Kitaev dc5e35
**  Copyright (c) 2008 Carlos Lรณpez
Nikita Kitaev dc5e35
**  Copyright (c) 2008 Chris Moore
Nikita Kitaev dc5e35
**
Nikita Kitaev dc5e35
**	This package is free software; you can redistribute it and/or
Nikita Kitaev dc5e35
**	modify it under the terms of the GNU General Public License as
Nikita Kitaev dc5e35
**	published by the Free Software Foundation; either version 2 of
Nikita Kitaev dc5e35
**	the License, or (at your option) any later version.
Nikita Kitaev dc5e35
**
Nikita Kitaev dc5e35
**	This package is distributed in the hope that it will be useful,
Nikita Kitaev dc5e35
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
Nikita Kitaev dc5e35
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Nikita Kitaev dc5e35
**	General Public License for more details.
Nikita Kitaev dc5e35
**	\endlegal
Nikita Kitaev dc5e35
*/
Nikita Kitaev dc5e35
/* ========================================================================= */
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
/* === H E A D E R S ======================================================= */
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
#ifdef USING_PCH
Nikita Kitaev dc5e35
#	include "pch.h"
Nikita Kitaev dc5e35
#else
Nikita Kitaev dc5e35
#ifdef HAVE_CONFIG_H
Nikita Kitaev dc5e35
#	include <config.h></config.h>
Nikita Kitaev dc5e35
#endif
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
#include "matrix.h"
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
#endif
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
/* === U S I N G =========================================================== */
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
using namespace std;
Nikita Kitaev dc5e35
using namespace etl;
Nikita Kitaev dc5e35
using namespace synfig;
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
/* === M A C R O S ========================================================= */
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
/* === G L O B A L S ======================================================= */
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
/* === M E T H O D S ======================================================= */
Nikita Kitaev dc5e35
ab6e41
// Matrix2
ab6e41
ab6e41
Matrix2 &
ab6e41
Matrix2::set_scale(const value_type &sx, const value_type &sy)
Nikita Kitaev dc5e35
{
ab6e41
	m00=sx;  m01=0.0;
ab6e41
	m10=0.0; m11=sy;
ab6e41
	return *this;
ab6e41
}
ab6e41
ab6e41
Matrix2 &
ab6e41
Matrix2::set_rotate(const Angle &a)
ab6e41
{
ab6e41
	value_type c(Angle::cos(a).get());
ab6e41
	value_type s(Angle::sin(a).get());
bf609b
	m00= c; m01=s;
bf609b
	m10=-s; m11=c;
ab6e41
	return *this;
ab6e41
}
ab6e41
ab6e41
void
ab6e41
Matrix2::get_transformed(value_type &out_x, value_type &out_y, const value_type x, const value_type y)const
ab6e41
	{ out_x = x*m00+y*m10; out_y = x*m01+y*m11; }
ab6e41
ab6e41
Matrix2
3a4aaf
Matrix2::operator*(const Matrix2 &rhs) const
ab6e41
{
3a4aaf
	return Matrix2( *this * rhs.row_x(),
3a4aaf
					*this * rhs.row_y() );
Nikita Kitaev dc5e35
}
Nikita Kitaev dc5e35
813bad
bool
ab6e41
Matrix2::operator==(const Matrix2 &rhs) const
813bad
{
d9ebd0
	return approximate_equal(m00, rhs.m00)
d9ebd0
		&& approximate_equal(m01, rhs.m01)
d9ebd0
		&& approximate_equal(m10, rhs.m10)
d9ebd0
		&& approximate_equal(m11, rhs.m11);
813bad
}
813bad
3a4aaf
Matrix2&
ab6e41
Matrix2::operator*=(const value_type &rhs)
Nikita Kitaev dc5e35
{
ab6e41
	m00*=rhs;
ab6e41
	m01*=rhs;
ab6e41
ab6e41
	m10*=rhs;
ab6e41
	m11*=rhs;
ab6e41
ab6e41
	return *this;
Nikita Kitaev dc5e35
}
Nikita Kitaev dc5e35
3a4aaf
Matrix2&
ab6e41
Matrix2::operator+=(const Matrix2 &rhs)
Nikita Kitaev dc5e35
{
ab6e41
	m00+=rhs.m00;
ab6e41
	m01+=rhs.m01;
ab6e41
ab6e41
	m10+=rhs.m10;
ab6e41
	m11+=rhs.m11;
ab6e41
ab6e41
	return *this;
Nikita Kitaev dc5e35
}
Nikita Kitaev dc5e35
34a685
Matrix2::value_type
34a685
Matrix2::det() const
34a685
	{ return m00*m11 - m01*m10; }
34a685
ab6e41
bool
34a685
Matrix2::is_invertible() const
34a685
	{ return approximate_not_zero(det()); }
ab6e41
ab6e41
Matrix2&
ab6e41
Matrix2::invert()
Nikita Kitaev dc5e35
{
34a685
	value_type d = det();
34a685
	if (approximate_not_zero(d)) {
34a685
		value_type k = 1/d;
ab6e41
		std::swap(m00, m11);
ab6e41
		std::swap(m01, m10);
ab6e41
		m00 *= k; m01 *= -k;
ab6e41
		m11 *= k; m10 *= -k;
34a685
	} else
34a685
	if (m00*m00 + m01*m01 > m10*m10 + m11*m11) {
ab6e41
		m10 = m01; m01 = 0; m11 = 0;
34a685
	} else {
ab6e41
		m01 = m10; m00 = 0; m10 = 0;
ab6e41
	}
ab6e41
	return *this;
Nikita Kitaev dc5e35
}
Nikita Kitaev dc5e35
ab6e41
String
ab6e41
Matrix2::get_string(int spaces, String before, String after)const
ab6e41
{
ab6e41
	return etl::strprintf("%*s [%7.2f %7.2f] %s\n%*s [%7.2f %7.2f]\n",
ab6e41
						  spaces, before.c_str(), m00, m01, after.c_str(),
ab6e41
						  spaces, "",			  m10, m11);
ab6e41
}
ab6e41
ab6e41
// Matrix3
ab6e41
3a4aaf
Matrix3&
ab6e41
Matrix3::set_scale(const value_type &sx, const value_type &sy)
ab6e41
{
ab6e41
	m00=sx;  m01=0.0; m02=0.0;
ab6e41
	m10=0.0; m11=sy;  m12=0.0;
ab6e41
	m20=0.0; m21=0.0; m22=1.0;
ab6e41
	return *this;
ab6e41
}
ab6e41
3a4aaf
Matrix3&
ab6e41
Matrix3::set_rotate(const Angle &a)
Nikita Kitaev dc5e35
{
Nikita Kitaev dc5e35
	value_type c(Angle::cos(a).get());
Nikita Kitaev dc5e35
	value_type s(Angle::sin(a).get());
bf609b
	m00= c;   m01=s;   m02=0.0;
bf609b
	m10=-s;   m11=c;   m12=0.0;
bf609b
	m20= 0.0; m21=0.0; m22=1.0;
ab6e41
	return *this;
Nikita Kitaev dc5e35
}
Nikita Kitaev dc5e35
3a4aaf
Matrix3&
ab6e41
Matrix3::set_translate(value_type x, value_type y)
Nikita Kitaev dc5e35
{
Nikita Kitaev dc5e35
	m00=1.0; m01=0.0; m02=0.0;
Nikita Kitaev dc5e35
	m10=0.0; m11=1.0; m12=0.0;
Nikita Kitaev dc5e35
	m20=x  ; m21=y  ; m22=1.0;
ab6e41
	return *this;
Nikita Kitaev dc5e35
}
Nikita Kitaev dc5e35
8005ee
void
3a4aaf
Matrix3::get_transformed(
3a4aaf
	value_type &out_x, value_type &out_y, value_type &out_z,
3a4aaf
	const value_type x, const value_type y, const value_type z )const
8005ee
{
3a4aaf
	out_x = x*m00 + y*m10 + z*m20;
3a4aaf
	out_y = x*m01 + y*m11 + z*m21;
48c23f
	out_z = x*m02 + y*m12 + z*m22;
8005ee
}
8005ee
ab6e41
bool
ab6e41
Matrix3::operator==(const Matrix3 &rhs) const
ab6e41
{
d9ebd0
	return approximate_equal(m00, rhs.m00)
d9ebd0
		&& approximate_equal(m01, rhs.m01)
d9ebd0
		&& approximate_equal(m02, rhs.m02)
d9ebd0
		&& approximate_equal(m10, rhs.m10)
d9ebd0
		&& approximate_equal(m11, rhs.m11)
d9ebd0
		&& approximate_equal(m12, rhs.m12)
d9ebd0
		&& approximate_equal(m20, rhs.m20)
d9ebd0
		&& approximate_equal(m21, rhs.m21)
d9ebd0
		&& approximate_equal(m22, rhs.m22);
ab6e41
}
ab6e41
ab6e41
Matrix3
3a4aaf
Matrix3::operator*(const Matrix3 &rhs) const
Nikita Kitaev dc5e35
{
3a4aaf
	return Matrix3( *this * rhs.row_x(),
3a4aaf
			        *this * rhs.row_y(),
3a4aaf
					*this * rhs.row_z() );
Nikita Kitaev dc5e35
}
Nikita Kitaev dc5e35
3a4aaf
Matrix3&
ab6e41
Matrix3::operator*=(const value_type &rhs)
Nikita Kitaev dc5e35
{
Nikita Kitaev dc5e35
	m00*=rhs;
Nikita Kitaev dc5e35
	m01*=rhs;
Nikita Kitaev dc5e35
	m02*=rhs;
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
	m10*=rhs;
Nikita Kitaev dc5e35
	m11*=rhs;
Nikita Kitaev dc5e35
	m12*=rhs;
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
	m20*=rhs;
Nikita Kitaev dc5e35
	m21*=rhs;
Nikita Kitaev dc5e35
	m22*=rhs;
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
	return *this;
Nikita Kitaev dc5e35
}
Nikita Kitaev dc5e35
3a4aaf
Matrix3&
ab6e41
Matrix3::operator+=(const Matrix3 &rhs)
Nikita Kitaev dc5e35
{
Nikita Kitaev dc5e35
	m00+=rhs.m00;
Nikita Kitaev dc5e35
	m01+=rhs.m01;
Nikita Kitaev dc5e35
	m02+=rhs.m02;
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
	m10+=rhs.m10;
Nikita Kitaev dc5e35
	m11+=rhs.m11;
Nikita Kitaev dc5e35
	m12+=rhs.m12;
Nikita Kitaev dc5e35
Nikita Kitaev dc5e35
	m20+=rhs.m20;
Nikita Kitaev dc5e35
	m21+=rhs.m21;
Nikita Kitaev dc5e35
	m22+=rhs.m22;
Nikita Kitaev dc5e35
ab6e41
	return *this;
Nikita Kitaev dc5e35
}
Nikita Kitaev dc5e35
23f768
Matrix3::value_type
23f768
Matrix3::det()const
23f768
{
23f768
	return m00*(m11*m22 - m12*m21)
23f768
		 - m01*(m10*m22 - m12*m20)
23f768
		 + m02*(m10*m21 - m11*m20);
23f768
}
23f768
23f768
Nikita Kitaev dc5e35
bool
ab6e41
Matrix3::is_invertible()const
23f768
	{ return approximate_not_equal(det(), 0.0); }
Nikita Kitaev dc5e35
23f768
Matrix3
23f768
Matrix3::get_inverted()const
Nikita Kitaev dc5e35
{
23f768
	value_type d = det();
24d709
	if (approximate_equal(d, value_type(0))) {
24d709
		// result of transformation is not 3d
24d709
		// all points always transforms into one plane (2d), or line (1d), or dot (0d)
24d709
luz.paz 99f3ef
		// we cannot do the real back transform, but we will try
24d709
		// to make valid matrix for X-axis only or for Y-axis only
24d709
24d709
		// try to make back transform for X-axis
24d709
		if ( approximate_equal(row_x()[2], value_type(0))
24d709
		  && row_y().is_equal_to(Vector3::zero())
24d709
		  && approximate_equal(row_z()[2], value_type(1)) )
24d709
		{
24d709
			d = axis_x().mag_squared();
24d709
			if (d > real_precision<value_type>()) {</value_type>
24d709
				d = 1.0/d;
24d709
				return Matrix3(
24d709
				    d*m00,                 0.0, 0.0,
24d709
				    d*m01,                 0.0, 0.0,
24d709
				   -d*(m20*m00 + m21*m01), 0.0, 1.0 );
24d709
			}
24d709
		}
24d709
24d709
		// try to make back transform for Y-axis
24d709
		if ( row_x().is_equal_to(Vector3::zero())
24d709
		  && approximate_equal(row_y()[2], value_type(0))
24d709
		  && approximate_equal(row_z()[2], value_type(1)) )
24d709
		{
24d709
			d = axis_y().mag_squared();
24d709
			if (d > real_precision<value_type>()) {</value_type>
24d709
				d = 1.0/d;
24d709
				return Matrix3(
24d709
				    0.0,  d*m10,                 0.0,
24d709
					0.0,  d*m11,                 0.0,
24d709
					0.0, -d*(m20*m10 + m21*m11), 1.0 );
24d709
			}
24d709
		}
24d709
24d709
		// give up
23f768
		return Matrix3( 0.0, 0.0, 0.0,
23f768
				        0.0, 0.0, 0.0,
23f768
						0.0, 0.0, 0.0 );
24d709
	}
24d709
24d709
	// proper inversion
23f768
	value_type p = 1.0/d;
23f768
	value_type m = -p;
23f768
	return Matrix3(
23f768
		p*(m11*m22 - m12*m21), // row0
23f768
		m*(m01*m22 - m02*m21),
23f768
		p*(m01*m12 - m02*m11),
23f768
		m*(m10*m22 - m12*m20), // row1
23f768
		p*(m00*m22 - m02*m20),
23f768
		m*(m00*m12 - m02*m10),
23f768
		p*(m10*m21 - m11*m20), // row2
23f768
		m*(m00*m21 - m01*m20),
23f768
		p*(m00*m11 - m01*m10) );
Nikita Kitaev dc5e35
}
Nikita Kitaev dc5e35
34a685
Matrix3&
fc7a04
Matrix3::normalize_by_det()
34a685
{
fc7a04
	Real d = det();
fc7a04
	if (approximate_not_zero(d)) *this *= 1/cbrt(fabs(d));
34a685
	return *this;
34a685
}
34a685
34a685
Nikita Kitaev dc5e35
String
ab6e41
Matrix3::get_string(int spaces, String before, String after)const
Nikita Kitaev dc5e35
{
Nikita Kitaev dc5e35
	return etl::strprintf("%*s [%7.2f %7.2f %7.2f] %s\n%*s [%7.2f %7.2f %7.2f]\n%*s [%7.2f %7.2f %7.2f]\n",
Nikita Kitaev dc5e35
						  spaces, before.c_str(), m00, m01, m02, after.c_str(),
Nikita Kitaev dc5e35
						  spaces, "",			  m10, m11, m12,
Nikita Kitaev dc5e35
						  spaces, "",			  m20, m21, m22);
Nikita Kitaev dc5e35
}