// SPDX-License-Identifier: BSD-3-Clause //////////////////////////////////////////////////////////////////////////////////////////////// // 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)) { 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() { // homogeneous 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); // concatenate 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); // concatenate 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 - concatenate 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) { // unit matrix return a; } // 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) { 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) { 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 // consistent 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) { 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; } } // namespace geoff_geometry