Files
create/src/Mod/Path/libarea/kurve/Matrix.cpp
2016-06-11 10:51:27 -05:00

625 lines
15 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////////////
// 3d geometry classes - implements some 3d stuff
//
// g.j.hawkesford August 2003
//
// This program is released under the BSD license. See the file COPYING for details.
//
////////////////////////////////////////////////////////////////////////////////////////////////
#include "geometry.h"
using namespace geoff_geometry;
#ifdef PEPSDLL
#include "vdm.h"
#include "pepsdll.h"
#include "realds.h"
#endif
////////////////////////////////////////////////////////////////////////////////////////////////
// matrix
////////////////////////////////////////////////////////////////////////////////////////////////
namespace geoff_geometry {
Matrix::Matrix(){
Unit();
}
Matrix::Matrix(double m[16]) {
memcpy(e, m, sizeof(e));
this->IsUnit();
this->IsMirrored();
}
Matrix::Matrix( const Matrix& m)
{
*this = m;
}
bool Matrix::operator==(const Matrix &m)const{
// m1 == m2
if(this->m_unit != m.m_unit || this->m_mirrored != m.m_mirrored) return false;
for(int i = 0; i < 16; i++)
if(FEQ(this->e[i], m.e[i], TIGHT_TOLERANCE) == false) return false;
return true;
}
#if 0
const Matrix& Matrix::operator=( Matrix &m) {
for(int i = 0; i < 16; i++) e[i] = m.e[i];
m_unit = m.m_unit;
m_mirrored = m.m_mirrored;
return *this;
}
#endif
void Matrix::Unit()
{
// homogenous matrix - set as unit matrix
memset(e, 0, sizeof(e));
e[0] = e[5] = e[10] = e[15] = 1;
m_unit = true;
m_mirrored = false;
}
void Matrix::Get(double* p) const
{
// copy the matrix
memcpy(p, e, sizeof(e));
}
void Matrix::Put(double* p)
{
// assign the matrix
memcpy(e, p, sizeof(e));
m_unit = false; // don't know
m_mirrored = -1; // don't know
}
void Matrix::Translate(double x, double y, double z)
{
// translation
e[3] += x;
e[7] += y;
e[11] += z;
m_unit = false;
}
void Matrix::Rotate(double angle, Vector3d *rotAxis) {
/// Rotation about rotAxis with angle
Rotate(sin(angle), cos(angle), rotAxis);
}
void Matrix::Rotate(double sinang, double cosang, Vector3d *rotAxis) {
/// Rotation about rotAxis with cp & dp
Matrix rotate;
double oneminusc = 1.0 - cosang;
rotate.e[0] = rotAxis->getx() * rotAxis->getx() * oneminusc + cosang;
rotate.e[1] = rotAxis->getx() * rotAxis->gety() * oneminusc - rotAxis->getz() * sinang;
rotate.e[2] = rotAxis->getx() * rotAxis->getz() * oneminusc + rotAxis->gety() * sinang;
rotate.e[4] = rotAxis->getx() * rotAxis->gety() * oneminusc + rotAxis->getz() * sinang;
rotate.e[5] = rotAxis->gety() * rotAxis->gety() * oneminusc + cosang;
rotate.e[6] = rotAxis->gety() * rotAxis->getz() * oneminusc - rotAxis->getx() * sinang;
rotate.e[8] = rotAxis->getx() * rotAxis->getz() * oneminusc - rotAxis->gety() * sinang;
rotate.e[9] = rotAxis->gety() * rotAxis->getz() * oneminusc + rotAxis->getx() * sinang;
rotate.e[10] = rotAxis->getz() * rotAxis->getz() * oneminusc + cosang;
Multiply(rotate); // concatinate rotation with this matrix
m_unit = false;
m_mirrored = -1; // don't know
}
void Matrix::Rotate(double angle, int Axis)
{ // Rotation (Axis 1 = x , 2 = y , 3 = z
Rotate(sin(angle), cos(angle), Axis);
}
void Matrix::Rotate(double sinang, double cosang, int Axis)
{ // Rotation (Axis 1 = x , 2 = y , 3 = z
Matrix rotate;
rotate.Unit();
switch(Axis)
{
case 1:
// about x axis
rotate.e[5] = rotate.e[10] = cosang;
rotate.e[6] = -sinang;
rotate.e[9] = sinang;
break;
case 2:
// about y axis
rotate.e[0] = rotate.e[10] = cosang;
rotate.e[2] = sinang;
rotate.e[8] = -sinang;
break;
case 3:
// about z axis
rotate.e[0] = rotate.e[5] = cosang;
rotate.e[1] = -sinang;
rotate.e[4] = sinang;
break;
}
Multiply(rotate); // concatinate rotation with this matrix
m_unit = false;
m_mirrored = -1; // don't know
}
void Matrix::Scale(double scale)
{
// add a scale
Scale(scale, scale, scale);
}
void Matrix::Scale(double scalex, double scaley, double scalez)
{
// add a scale
Matrix temp;
temp.Unit();
temp.e[0] = scalex;
temp.e[5] = scaley;
temp.e[10] = scalez;
Multiply(temp);
m_unit = false;
m_mirrored = -1; // don't know
}
void Matrix::Multiply(Matrix& m)
{
// multiply this by give matrix - concatinate
int i, k, l;
Matrix ret;
for (i = 0; i < 16; i++)
{
l = i - (k = (i % 4));
ret.e[i] = m.e[l] * e[k] + m.e[l+1] * e[k+4] + m.e[l+2] * e[k+8] + m.e[l+3] * e[k+12];
}
*this = ret;
this->IsUnit();
}
void Matrix::Transform(double p0[3], double p1[3]) const
{
// transform p0 thro' this matrix
if(m_unit)
memcpy(p1, p0, 3 * sizeof(double));
else {
p1[0] = p0[0] * e[0] + p0[1] * e[1] + p0[2] * e[2] + e[3];
p1[1] = p0[0] * e[4] + p0[1] * e[5] + p0[2] * e[6] + e[7];
p1[2] = p0[0] * e[8] + p0[1] * e[9] + p0[2] * e[10] + e[11];
}
}
void Matrix::Transform2d(double p0[2], double p1[2]) const
{
// transform p0 thro' this matrix (2d only)
if(m_unit)
memcpy(p1, p0, 2 * sizeof(double));
else {
p1[0] = p0[0] * e[0] + p0[1] * e[1] + e[3];
p1[1] = p0[0] * e[4] + p0[1] * e[5] + e[7];
}
}
void Matrix::Transform(double p0[3]) const
{
double p1[3];
if(!m_unit) {
Transform(p0, p1);
memcpy(p0, p1, 3 * sizeof(double));
}
}
int Matrix::IsMirrored()
{
// returns true if matrix has a mirror
if(m_unit)
m_mirrored = false;
else if(m_mirrored == -1) {
m_mirrored = ((e[0] * (e[5] * e[10] - e[6] * e[9])
- e[1] * (e[4] * e[10] - e[6] * e[8])
+ e[2] * (e[4] * e[9] - e[5] * e[8])) < 0);
}
return m_mirrored;
}
int Matrix::IsUnit() {
// returns true if unit matrix
for(int i = 0; i < 16; i++) {
if(i == 0 || i == 5 || i == 10 || i == 15) {
if(e[i] != 1) return m_unit = false;
}
else {
if(e[i] != 0) return m_unit = false;
}
}
m_mirrored = false;
return m_unit = true;
}
void Matrix::GetTranslate(double& x, double& y, double& z) const
{
// return translation
x = e[3];
y = e[7];
z = e[11];
}
void Matrix::GetScale(double& sx, double& sy, double& sz) const
{
// return the scale
if(m_unit) {
sx = sy = sz = 1;
}
else {
sx = sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]);
sy = sqrt(e[4] * e[4] + e[5] * e[5] + e[6] * e[6]);
sz = sqrt(e[8] * e[8] + e[9] * e[9] + e[10] * e[10]);
}
}
bool Matrix::GetScale(double& sx) const
{
// return a uniform scale (false if differential)
double sy, sz;
if(m_unit) {
sx = 1;
return true;
}
GetScale(sx, sy, sz);
return (fabs(fabs(sx) - fabs(sy)) < 0.000001)?true : false;
}
void Matrix::GetRotation(double& ax, double& ay, double& az) const
{
// return the rotations
if(m_unit) {
ax = ay = az = 0;
return;
}
double a; /* cos(bx) */
double b; /* sin(bx) */
double c; /* cos(by) */
double d; /* sin(by) */
double ee; /* cos(bz) */
double f; /* sin(bz) */
double sx, sy, sz;
GetScale(sx, sy, sz);
if(this->m_mirrored == -1) FAILURE(L"Don't know mirror - use IsMirrored method on object");
if(this->m_mirrored) sx = -sx;
// solve for d and decide case and solve for a, b, c, e and f
d = - e[8] / sz;
if((c = (1 - d) * (1 + d)) > 0.001)
{
// case 1
c = sqrt( c );
a = e[10] / sz / c;
b = e[9] / sz / c;
ee = e[0] / sx / c;
f = e[4] / sy / c;
}
else
{
// case 2
double coef;
double p, q;
d = ( d < 0 ) ? -1 : 1 ;
c = 0 ;
p = d * e[5] / sy - e[2] / sx;
q = d * e[6] / sy + e[1] / sx;
if((coef = sqrt( p * p + q * q )) > 0.001) {
a = q / coef;
b = p / coef;
ee = b;
f = -d * b;
}
else
{
/* dependent pairs */
a = e[5] / sy;
b = -e[6] / sy;
ee = 1 ;
f = 0 ;
}
}
// solve and return ax, ay and az
ax = atan2( b, a );
ay = atan2( d, c );
az = atan2( f, ee );
}
Matrix Matrix::Inverse()
{
// matrix inversion routine
// a is input matrix destroyed & replaced by inverse
// method used is gauss-jordan (ref ibm applications)
double hold , biga ;
int i , j , k , nk , kk , ij , iz ;
int ki , ji , jp , jk , kj , jq , jr , ik;
int n = 4; // 4 x 4 matrix only
Matrix a = *this;
int l[4], m[4];
if(a.m_unit) return a; // unit matrix
// search for largest element
nk = - n ;
for ( k = 0 ; k < n ; k++ ) {
nk += n ;
l [ k ] = m [ k ] = k ;
kk = nk + k ;
biga = a.e[ kk ] ;
for ( j = k ; j < n ; j++ ) {
iz = n * j ;
for ( i = k ; i < n ; i++ ) {
ij = iz + i ;
if ( fabs ( biga ) < fabs ( a.e[ ij ] ) ) {
biga = a.e[ ij ] ;
l[ k ] = i ;
m[ k ] = j ;
}
}
}
// interchange rows
j = l[ k ] ;
if ( j > k ) {
ki = k - n ;
for ( i = 0 ; i < n ; i++ ) {
ki += n ;
hold = - a.e[ ki ] ;
ji = ki - k + j ;
a.e[ ki ] = a.e[ ji ] ;
a.e[ ji ] = hold ;
}
}
// interchange columns
i = m[ k ] ;
if ( i > k ) {
jp = n * i ;
for ( j = 0 ; j < n ; j++ ) {
jk = nk + j ;
ji = jp + j ;
hold = - a.e[ jk ] ;
a.e[ jk ] = a.e[ ji ] ;
a.e[ ji ] = hold ;
}
}
// divide columns by minus pivot (value of pivot element is contained in biga)
if ( fabs ( biga ) < 1.0e-10 )FAILURE(getMessage(L"Singular Matrix - Inversion failure")); // singular matrix
for ( i = 0 ; i < n ; i++ ) {
if ( i != k ) {
ik = nk + i ;
a.e[ ik ] = - a.e[ ik ] /biga ;
}
}
// reduce matrix
for ( i = 0 ; i < n ; i++ ) {
ik = nk + i ;
hold = a.e[ ik ] ;
ij = i - n ;
for ( j = 0 ; j < n ; j++ ) {
ij = ij + n ;
if ( i != k && j != k ) {
kj = ij - i + k ;
a.e[ ij ] = hold * a.e[ kj ] + a.e[ ij ] ;
}
}
}
// divide row by pivot
kj = k - n ;
for ( j = 0 ; j < n ; j++ ) {
kj = kj + n ;
if ( j != k ) a.e[ kj] = a.e[ kj ] /biga ;
}
// replace pivot by reciprocal
a.e[ kk ] = 1 / biga ;
}
// final row and column interchange
k = n - 1 ;
while ( k > 0 ) {
i = l[ --k ] ;
if ( i > k ) {
jq = n * k ;
jr = n * i ;
for ( j = 0 ; j < n ; j++ ) {
jk = jq + j ;
hold = a.e[jk] ;
ji = jr + j ;
a.e[jk] = - a.e[ji] ;
a.e[ji] = hold ;
}
}
j = m[ k ] ;
if ( j > k ) {
ki = k - n ;
for ( i = 1 ; i <= n ; i ++ ) {
ki = ki + n ;
hold = a.e[ ki ] ;
ji = ki - k + j ;
a.e[ ki ] = - a.e[ ji ] ;
a.e[ ji ] = hold ;
}
}
}
return a;
}
#ifdef PEPSDLL
void Matrix::ToPeps(int id)
{
int set = PepsVdmMake(id, VDM_MATRIX_TYPE , VDM_LOCAL);
if(set < 0) FAILURE(L"Failed to create Matrix VDM");
struct kgm_header pepsm;
Get(pepsm.matrix);
pepsm.off_rad = 0;
pepsm.off_dir = pepsm.origin_id = 0;
PepsVdmWriteTmx(set , &pepsm );
PepsVdmClose(set);
}
void Matrix::FromPeps(int id)
{
// if(id) {
int set = PepsVdmOpen(id, VDM_MATRIX_TYPE , VDM_READ_ONLY | VDM_LOCAL);
if(set < 0) FAILURE(L"Failed to open Matrix VDM");
struct kgm_header pepsm;
PepsVdmReadTmx(set , &pepsm);
memcpy(e, pepsm.matrix, sizeof(pepsm.matrix));
m_unit = true;
for(int i = 0; i < 16; i++) {
// copy over matrix and check for unit matrix
if(i == 0 || i == 5 || i == 10 || i == 15) {
if((e[i] = pepsm.matrix[i]) != 1) m_unit = false;
}
else {
if((e[i] = pepsm.matrix[i]) != 0) m_unit = false;
}
}
PepsVdmClose(set);
m_mirrored = IsMirrored();
// }
}
#endif
Matrix UnitMatrix; // a global unit matrix
// vector
Vector2d::Vector2d(const Vector3d &v){
if(FEQZ(v.getz())) FAILURE(L"Converting Vector3d to Vector2d illegal");
dx = v.getx();
dy = v.gety();
}
bool Vector2d::operator==(const Vector2d &v)const {
return FEQ(dx, v.getx(), 1.0e-06) && FEQ(dy, v.gety(), 1.0e-06);
}
void Vector2d::Transform(const Matrix& m) {
// transform vector
if(m.m_unit == false) {
double dxt = dx * m.e[0] + dy * m.e[1];
double dyt = dx * m.e[4] + dy * m.e[5];
dx = dxt;
dy = dyt;
}
this->normalise();
}
void Vector3d::Transform(const Matrix& m) {
// transform vector
if(m.m_unit == false) {
double dxt = dx * m.e[0] + dy * m.e[1] + dz * m.e[2];
double dyt = dx * m.e[4] + dy * m.e[5] + dz * m.e[6];
double dzt = dx * m.e[8] + dy * m.e[9] + dz * m.e[10];
dx = dxt;
dy = dyt;
dz = dzt;
}
this->normalise();
}
void Vector3d::arbitrary_axes(Vector3d& x, Vector3d& y){
// arbitrary axis algorithm - acad method of generating an arbitrary but
// consistant set of axes from a single normal ( z )
// arbitrary x & y axes
if ( ( fabs ( this->getx() ) < 1.0/64.0 ) && (fabs(this->gety()) < 1.0/64.0))
x = Y_VECTOR ^ *this;
else
x = Z_VECTOR ^ *this;
y = *this ^ x;
}
int Vector3d::setCartesianAxes(Vector3d& b, Vector3d& c) {
#define a *this
// computes a RH triad of Axes (Cartesian) starting from a (normalised)
// if a & b are perpendicular then c = a ^ b
// if a & c are perpendicular then b = c ^ a
// if neither are perpendicular to a, then return arbitrary axes from a
// calling sequence for RH cartesian
// x y z
// y z x
// z x y
if(a == NULL_VECTOR) FAILURE(L"SetAxes given a NULL Vector");
double epsilon = 1.0e-09;
bool bNull = (b == NULL_VECTOR);
bool cNull = (c == NULL_VECTOR);
bool abPerp = !bNull;
if(abPerp) abPerp = (fabs(a * b) < epsilon);
bool acPerp = !cNull;
if(acPerp) acPerp = (fabs(a * c) < epsilon);
if(abPerp) {
c = a ^ b;
return 1;
}
if(acPerp) {
b = c ^ a;
return 1;
}
arbitrary_axes(b, c);
b.normalise();
c.normalise();
return 2;
}
void Plane::Mirrored(Matrix* tmMirrored) {
// calculates a mirror transformation that mirrors 2d about plane
Point3d p1 = this->Near(Point3d(0.,0.,0.));
if(tmMirrored->m_unit == false) tmMirrored->Unit();
double nx = this->normal.getx();
double ny = this->normal.gety();
double nz = this->normal.getz();
// the translation
tmMirrored->e[ 3] = -2. * nx * this->d;
tmMirrored->e[ 7] = -2. * ny * this->d;
tmMirrored->e[11] = -2. * nz * this->d;
// the rest
tmMirrored->e[ 0] = 1. - 2. * nx * nx;
tmMirrored->e[ 5] = 1. - 2. * ny * ny;
tmMirrored->e[10] = 1. - 2. * nz * nz;
tmMirrored->e[ 1] = tmMirrored->e[ 4] = -2. * nx * ny;
tmMirrored->e[ 2] = tmMirrored->e[ 8] = -2. * nz * nx;
tmMirrored->e[ 6] = tmMirrored->e[ 9] = -2. * ny * nz;
tmMirrored->m_unit = false;
tmMirrored->m_mirrored = true;
}
}