Merge pull request #4461 from Beep6581/custom-working-profiles
added possibility to specify extra working spaces via a json file
This commit is contained in:
@@ -286,7 +286,7 @@ void rtengine::HaldCLUT::splitClutFilename(
|
||||
profile_name = "sRGB";
|
||||
|
||||
if (!name.empty()) {
|
||||
for (const auto& working_profile : rtengine::ICCStore::getWorkingProfiles()) {
|
||||
for (const auto& working_profile : rtengine::ICCStore::getInstance()->getWorkingProfiles()) {
|
||||
if (
|
||||
!working_profile.empty() // This isn't strictly needed, but an empty wp name should be skipped anyway
|
||||
&& std::search(name.rbegin(), name.rend(), working_profile.rbegin(), working_profile.rend()) == name.rbegin()
|
||||
|
||||
@@ -37,30 +37,36 @@ namespace
|
||||
|
||||
DCPProfile::Matrix invert3x3(const DCPProfile::Matrix& a)
|
||||
{
|
||||
const double res00 = a[1][1] * a[2][2] - a[2][1] * a[1][2];
|
||||
const double res10 = a[2][0] * a[1][2] - a[1][0] * a[2][2];
|
||||
const double res20 = a[1][0] * a[2][1] - a[2][0] * a[1][1];
|
||||
|
||||
const double det = a[0][0] * res00 + a[0][1] * res10 + a[0][2] * res20;
|
||||
|
||||
if (std::fabs(det) < 1.0e-10) {
|
||||
DCPProfile::Matrix res = a;
|
||||
if (!invertMatrix(a, res)) {
|
||||
std::cerr << "DCP matrix cannot be inverted! Expect weird output." << std::endl;
|
||||
return a;
|
||||
}
|
||||
|
||||
DCPProfile::Matrix res;
|
||||
|
||||
res[0][0] = res00 / det;
|
||||
res[0][1] = (a[2][1] * a[0][2] - a[0][1] * a[2][2]) / det;
|
||||
res[0][2] = (a[0][1] * a[1][2] - a[1][1] * a[0][2]) / det;
|
||||
res[1][0] = res10 / det;
|
||||
res[1][1] = (a[0][0] * a[2][2] - a[2][0] * a[0][2]) / det;
|
||||
res[1][2] = (a[1][0] * a[0][2] - a[0][0] * a[1][2]) / det;
|
||||
res[2][0] = res20 / det;
|
||||
res[2][1] = (a[2][0] * a[0][1] - a[0][0] * a[2][1]) / det;
|
||||
res[2][2] = (a[0][0] * a[1][1] - a[1][0] * a[0][1]) / det;
|
||||
|
||||
return res;
|
||||
|
||||
// const double res00 = a[1][1] * a[2][2] - a[2][1] * a[1][2];
|
||||
// const double res10 = a[2][0] * a[1][2] - a[1][0] * a[2][2];
|
||||
// const double res20 = a[1][0] * a[2][1] - a[2][0] * a[1][1];
|
||||
|
||||
// const double det = a[0][0] * res00 + a[0][1] * res10 + a[0][2] * res20;
|
||||
|
||||
// if (std::fabs(det) < 1.0e-10) {
|
||||
// std::cerr << "DCP matrix cannot be inverted! Expect weird output." << std::endl;
|
||||
// return a;
|
||||
// }
|
||||
|
||||
// DCPProfile::Matrix res;
|
||||
|
||||
// res[0][0] = res00 / det;
|
||||
// res[0][1] = (a[2][1] * a[0][2] - a[0][1] * a[2][2]) / det;
|
||||
// res[0][2] = (a[0][1] * a[1][2] - a[1][1] * a[0][2]) / det;
|
||||
// res[1][0] = res10 / det;
|
||||
// res[1][1] = (a[0][0] * a[2][2] - a[2][0] * a[0][2]) / det;
|
||||
// res[1][2] = (a[1][0] * a[0][2] - a[0][0] * a[1][2]) / det;
|
||||
// res[2][0] = res20 / det;
|
||||
// res[2][1] = (a[2][0] * a[0][1] - a[0][0] * a[2][1]) / det;
|
||||
// res[2][2] = (a[0][0] * a[1][1] - a[1][0] * a[0][1]) / det;
|
||||
|
||||
// return res;
|
||||
}
|
||||
|
||||
DCPProfile::Matrix multiply3x3(const DCPProfile::Matrix& a, const DCPProfile::Matrix& b)
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "iccstore.h"
|
||||
|
||||
#include "iccmatrices.h"
|
||||
@@ -35,6 +37,8 @@
|
||||
#include "../rtgui/options.h"
|
||||
#include "../rtgui/threadutils.h"
|
||||
|
||||
#include "cJSON.h"
|
||||
|
||||
namespace rtengine
|
||||
{
|
||||
|
||||
@@ -274,7 +278,7 @@ public:
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
wProfiles[wpnames[i]] = createFromMatrix(wprofiles[i]);
|
||||
wProfilesGamma[wpnames[i]] = createFromMatrix(wprofiles[i], true);
|
||||
// wProfilesGamma[wpnames[i]] = createFromMatrix(wprofiles[i], true);
|
||||
wMatrices[wpnames[i]] = wprofiles[i];
|
||||
iwMatrices[wpnames[i]] = iwprofiles[i];
|
||||
}
|
||||
@@ -287,11 +291,11 @@ public:
|
||||
cmsCloseProfile(p.second);
|
||||
}
|
||||
}
|
||||
for (auto &p : wProfilesGamma) {
|
||||
if (p.second) {
|
||||
cmsCloseProfile(p.second);
|
||||
}
|
||||
}
|
||||
// for (auto &p : wProfilesGamma) {
|
||||
// if (p.second) {
|
||||
// cmsCloseProfile(p.second);
|
||||
// }
|
||||
// }
|
||||
for (auto &p : fileProfiles) {
|
||||
if(p.second) {
|
||||
cmsCloseProfile(p.second);
|
||||
@@ -334,6 +338,9 @@ public:
|
||||
|
||||
defaultMonitorProfile = settings->monitorProfile;
|
||||
|
||||
loadWorkingSpaces(rtICCDir);
|
||||
loadWorkingSpaces(userICCDir);
|
||||
|
||||
// initialize the alarm colours for lcms gamut checking -- we use bright green
|
||||
cmsUInt16Number cms_alarm_codes[cmsMAXCHANNELS] = { 0, 65535, 65535 };
|
||||
cmsSetAlarmCodes(cms_alarm_codes);
|
||||
@@ -349,16 +356,16 @@ public:
|
||||
: wProfiles.find("sRGB")->second;
|
||||
}
|
||||
|
||||
cmsHPROFILE workingSpaceGamma(const Glib::ustring& name) const
|
||||
{
|
||||
// cmsHPROFILE workingSpaceGamma(const Glib::ustring& name) const
|
||||
// {
|
||||
|
||||
const ProfileMap::const_iterator r = wProfilesGamma.find(name);
|
||||
// const ProfileMap::const_iterator r = wProfilesGamma.find(name);
|
||||
|
||||
return
|
||||
r != wProfilesGamma.end()
|
||||
? r->second
|
||||
: wProfilesGamma.find("sRGB")->second;
|
||||
}
|
||||
// return
|
||||
// r != wProfilesGamma.end()
|
||||
// ? r->second
|
||||
// : wProfilesGamma.find("sRGB")->second;
|
||||
// }
|
||||
|
||||
TMatrix workingSpaceMatrix(const Glib::ustring& name) const
|
||||
{
|
||||
@@ -582,17 +589,276 @@ public:
|
||||
defaultMonitorProfile = name;
|
||||
}
|
||||
|
||||
std::vector<Glib::ustring> getWorkingProfiles()
|
||||
{
|
||||
std::vector<Glib::ustring> res;
|
||||
|
||||
// for (unsigned int i = 0; i < sizeof(wpnames) / sizeof(wpnames[0]); i++) {
|
||||
// res.push_back(wpnames[i]);
|
||||
// }
|
||||
for (const auto &p : wProfiles) {
|
||||
res.push_back(p.first);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
using CVector = std::array<double, 3>;
|
||||
using CMatrix = std::array<CVector, 3>;
|
||||
struct PMatrix {
|
||||
double matrix[3][3];
|
||||
PMatrix(): matrix{} {}
|
||||
PMatrix(const CMatrix &m)
|
||||
{
|
||||
set(m);
|
||||
}
|
||||
|
||||
CMatrix toMatrix() const
|
||||
{
|
||||
CMatrix ret;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
ret[i][j] = matrix[i][j];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void set(const CMatrix &m)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
matrix[i][j] = m[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool computeWorkingSpaceMatrix(const Glib::ustring &path, const Glib::ustring &filename, PMatrix &out)
|
||||
{
|
||||
Glib::ustring fullpath = filename;
|
||||
if (!Glib::path_is_absolute(fullpath)) {
|
||||
fullpath = Glib::build_filename(path, filename);
|
||||
}
|
||||
ProfileContent content(fullpath);
|
||||
cmsHPROFILE prof = content.toProfile();
|
||||
if (!prof) {
|
||||
return false;
|
||||
}
|
||||
if (cmsGetColorSpace(prof) != cmsSigRgbData) {
|
||||
cmsCloseProfile(prof);
|
||||
return false;
|
||||
}
|
||||
if (!cmsIsMatrixShaper(prof)) {
|
||||
cmsCloseProfile(prof);
|
||||
return false;
|
||||
}
|
||||
cmsCIEXYZ *white = static_cast<cmsCIEXYZ *>(cmsReadTag(prof, cmsSigMediaWhitePointTag));
|
||||
cmsCIEXYZ *red = static_cast<cmsCIEXYZ *>(cmsReadTag(prof, cmsSigRedMatrixColumnTag));
|
||||
cmsCIEXYZ *green = static_cast<cmsCIEXYZ *>(cmsReadTag(prof, cmsSigGreenMatrixColumnTag));
|
||||
cmsCIEXYZ *blue = static_cast<cmsCIEXYZ *>(cmsReadTag(prof, cmsSigBlueMatrixColumnTag));
|
||||
|
||||
if (!white || !red || !green || !blue) {
|
||||
cmsCloseProfile(prof);
|
||||
return false;
|
||||
}
|
||||
|
||||
// do the Bradford adaptation to D50
|
||||
// matrices from Bruce Lindbloom's webpage
|
||||
static constexpr CMatrix bradford_MA = {
|
||||
CVector({0.8951000, 0.2664000, -0.1614000}),
|
||||
CVector({-0.7502000, 1.7135000, 0.0367000}),
|
||||
CVector({0.0389000, -0.0685000, 1.0296000})
|
||||
};
|
||||
static constexpr CMatrix bradford_MA_inv = {
|
||||
CVector({0.9869929, -0.1470543, 0.1599627}),
|
||||
CVector({0.4323053, 0.5183603, 0.0492912}),
|
||||
CVector({-0.0085287, 0.0400428, 0.9684867})
|
||||
};
|
||||
static constexpr CVector bradford_MA_dot_D50 = {
|
||||
0.99628443, 1.02042736, 0.81864437
|
||||
};
|
||||
|
||||
CVector srcw = dotProduct(bradford_MA, CVector({ white->X, white->Y, white->Z }));
|
||||
CMatrix m = {
|
||||
CVector({ bradford_MA_dot_D50[0]/srcw[0], 0.0, 0.0 }),
|
||||
CVector({ 0.0, bradford_MA_dot_D50[1]/srcw[1], 0.0 }),
|
||||
CVector({ 0.0, 0.0, bradford_MA_dot_D50[2]/srcw[2] })
|
||||
};
|
||||
CMatrix adapt = dotProduct(dotProduct(bradford_MA_inv, m), bradford_MA);
|
||||
|
||||
m[0][0] = red->X; m[0][1] = green->X; m[0][2] = blue->X;
|
||||
m[1][0] = red->Y; m[1][1] = green->Y; m[1][2] = blue->Y;
|
||||
m[2][0] = red->Z; m[2][1] = green->Z; m[2][2] = blue->Z;
|
||||
|
||||
m = dotProduct(adapt, m);
|
||||
out.set(m);
|
||||
|
||||
cmsCloseProfile(prof);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadWorkingSpaces(const Glib::ustring &path)
|
||||
{
|
||||
Glib::ustring fileName = Glib::build_filename(path, "workingspaces.json");
|
||||
FILE* const f = g_fopen(fileName.c_str(), "r");
|
||||
|
||||
if (settings->verbose) {
|
||||
std::cout << "trying to load extra working spaces from " << fileName << std::flush;
|
||||
}
|
||||
|
||||
if (!f) {
|
||||
if (settings->verbose) {
|
||||
std::cout << " FAIL" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long length = ftell(f);
|
||||
if (length <= 0) {
|
||||
if (settings->verbose) {
|
||||
std::cout << " FAIL" << std::endl;
|
||||
}
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
char *buf = new char[length + 1];
|
||||
fseek(f, 0, SEEK_SET);
|
||||
length = fread(buf, 1, length, f);
|
||||
buf[length] = 0;
|
||||
|
||||
fclose(f);
|
||||
|
||||
cJSON_Minify(buf);
|
||||
cJSON *root = cJSON_Parse(buf);
|
||||
|
||||
if (!root) {
|
||||
if (settings->verbose) {
|
||||
std::cout << " FAIL" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
delete[] buf;
|
||||
|
||||
cJSON *js = cJSON_GetObjectItem(root, "working_spaces");
|
||||
if (!js) {
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
for (js = js->child; js != nullptr; js = js->next) {
|
||||
cJSON *ji = cJSON_GetObjectItem(js, "name");
|
||||
std::unique_ptr<PMatrix> m(new PMatrix);
|
||||
std::string name;
|
||||
|
||||
if (!ji || ji->type != cJSON_String) {
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
name = ji->valuestring;
|
||||
|
||||
if (wProfiles.find(name) != wProfiles.end()) {
|
||||
continue; // already there -- ignore
|
||||
}
|
||||
|
||||
bool found_matrix = false;
|
||||
|
||||
ji = cJSON_GetObjectItem(js, "matrix");
|
||||
if (ji) {
|
||||
if (ji->type != cJSON_Array) {
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
ji = ji->child;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (int j = 0; j < 3; ++j, ji = ji->next) {
|
||||
if (!ji || ji->type != cJSON_Number) {
|
||||
goto parse_error;
|
||||
}
|
||||
m->matrix[i][j] = ji->valuedouble;
|
||||
}
|
||||
}
|
||||
|
||||
if (ji) {
|
||||
goto parse_error;
|
||||
}
|
||||
found_matrix = true;
|
||||
} else {
|
||||
ji = cJSON_GetObjectItem(js, "file");
|
||||
if (!ji || ji->type != cJSON_String) {
|
||||
goto parse_error;
|
||||
}
|
||||
found_matrix = computeWorkingSpaceMatrix(path, ji->valuestring, *m);
|
||||
}
|
||||
|
||||
if (!found_matrix) {
|
||||
if (settings->verbose) {
|
||||
std::cout << "Could not find suitable matrix for working space: " << name << std::endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
pMatrices.emplace_back(std::move(m));
|
||||
TMatrix w = pMatrices.back()->matrix;
|
||||
|
||||
CMatrix b = {};
|
||||
if (!rtengine::invertMatrix(pMatrices.back()->toMatrix(), b)) {
|
||||
if (settings->verbose) {
|
||||
std::cout << "Matrix for working space: " << name << " is not invertible, skipping" << std::endl;
|
||||
}
|
||||
pMatrices.pop_back();
|
||||
} else {
|
||||
wMatrices[name] = w;
|
||||
pMatrices.emplace_back(new PMatrix(b));
|
||||
TMatrix iw = pMatrices.back()->matrix;
|
||||
iwMatrices[name] = iw;
|
||||
wProfiles[name] = createFromMatrix(w);
|
||||
|
||||
if (settings->verbose) {
|
||||
std::cout << "Added working space: " << name << std::endl;
|
||||
std::cout << " matrix: [";
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
std::cout << " [";
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
std::cout << " " << w[i][j];
|
||||
}
|
||||
std::cout << "]";
|
||||
}
|
||||
std::cout << " ]" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
if (settings->verbose) {
|
||||
std::cout << " OK" << std::endl;
|
||||
}
|
||||
return true;
|
||||
|
||||
parse_error:
|
||||
if (settings->verbose) {
|
||||
std::cout << " ERROR in parsing " << fileName << std::endl;
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
return false;
|
||||
}
|
||||
|
||||
using ProfileMap = std::map<Glib::ustring, cmsHPROFILE>;
|
||||
using MatrixMap = std::map<Glib::ustring, TMatrix>;
|
||||
using ContentMap = std::map<Glib::ustring, ProfileContent>;
|
||||
using NameMap = std::map<Glib::ustring, Glib::ustring>;
|
||||
|
||||
ProfileMap wProfiles;
|
||||
ProfileMap wProfilesGamma;
|
||||
// ProfileMap wProfilesGamma;
|
||||
MatrixMap wMatrices;
|
||||
MatrixMap iwMatrices;
|
||||
|
||||
std::vector<std::unique_ptr<PMatrix>> pMatrices;
|
||||
|
||||
// These contain profiles from user/system directory(supplied on init)
|
||||
Glib::ustring profilesDir;
|
||||
Glib::ustring userICCDir;
|
||||
@@ -630,10 +896,10 @@ cmsHPROFILE rtengine::ICCStore::workingSpace(const Glib::ustring& name) const
|
||||
return implementation->workingSpace(name);
|
||||
}
|
||||
|
||||
cmsHPROFILE rtengine::ICCStore::workingSpaceGamma(const Glib::ustring& name) const
|
||||
{
|
||||
return implementation->workingSpaceGamma(name);
|
||||
}
|
||||
// cmsHPROFILE rtengine::ICCStore::workingSpaceGamma(const Glib::ustring& name) const
|
||||
// {
|
||||
// return implementation->workingSpaceGamma(name);
|
||||
// }
|
||||
|
||||
rtengine::TMatrix rtengine::ICCStore::workingSpaceMatrix(const Glib::ustring& name) const
|
||||
{
|
||||
@@ -736,14 +1002,7 @@ rtengine::ICCStore::~ICCStore() = default;
|
||||
|
||||
std::vector<Glib::ustring> rtengine::ICCStore::getWorkingProfiles()
|
||||
{
|
||||
|
||||
std::vector<Glib::ustring> res;
|
||||
|
||||
for (unsigned int i = 0; i < sizeof(wpnames) / sizeof(wpnames[0]); i++) {
|
||||
res.push_back(wpnames[i]);
|
||||
}
|
||||
|
||||
return res;
|
||||
return implementation->getWorkingProfiles();
|
||||
}
|
||||
|
||||
std::vector<Glib::ustring> rtengine::ICCStore::getGamma()
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
void init(const Glib::ustring& usrICCDir, const Glib::ustring& stdICCDir, bool loadAll);
|
||||
|
||||
cmsHPROFILE workingSpace(const Glib::ustring& name) const;
|
||||
cmsHPROFILE workingSpaceGamma(const Glib::ustring& name) const;
|
||||
// cmsHPROFILE workingSpaceGamma(const Glib::ustring& name) const;
|
||||
TMatrix workingSpaceMatrix(const Glib::ustring& name) const;
|
||||
TMatrix workingSpaceInverseMatrix(const Glib::ustring& name) const;
|
||||
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
std::uint8_t getOutputIntents(const Glib::ustring& name) const;
|
||||
std::uint8_t getProofIntents(const Glib::ustring& name) const;
|
||||
|
||||
static std::vector<Glib::ustring> getWorkingProfiles();
|
||||
/*static*/ std::vector<Glib::ustring> getWorkingProfiles();
|
||||
static std::vector<Glib::ustring> getGamma();
|
||||
|
||||
static void getGammaArray(const procparams::ColorManagementParams& icm, GammaValues& ga);
|
||||
|
||||
@@ -3997,7 +3997,7 @@ void RawImageSource::colorSpaceConversion_ (Imagefloat* im, const ColorManagemen
|
||||
|
||||
}
|
||||
} else {
|
||||
const bool working_space_is_prophoto = (cmp.working == "ProPhoto");
|
||||
bool working_space_is_prophoto = (cmp.working == "ProPhoto");
|
||||
|
||||
// use supplied input profile
|
||||
|
||||
@@ -4066,6 +4066,33 @@ void RawImageSource::colorSpaceConversion_ (Imagefloat* im, const ColorManagemen
|
||||
cmsHPROFILE prophoto = ICCStore::getInstance()->workingSpace("ProPhoto"); // We always use Prophoto to apply the ICC profile to minimize problems with clipping in LUT conversion.
|
||||
bool transform_via_pcs_lab = false;
|
||||
bool separate_pcs_lab_highlights = false;
|
||||
|
||||
// check if the working space is fully contained in prophoto
|
||||
if (!working_space_is_prophoto) {
|
||||
TMatrix toxyz = ICCStore::getInstance()->workingSpaceMatrix(cmp.working);
|
||||
TMatrix torgb = ICCStore::getInstance()->workingSpaceInverseMatrix("ProPhoto");
|
||||
float rgb[3] = {0.f, 0.f, 0.f};
|
||||
for (int i = 0; i < 2 && !working_space_is_prophoto; ++i) {
|
||||
rgb[i] = 1.f;
|
||||
float x, y, z;
|
||||
|
||||
Color::rgbxyz(rgb[0], rgb[1], rgb[2], x, y, z, toxyz);
|
||||
Color::xyz2rgb(x, y, z, rgb[0], rgb[1], rgb[2], torgb);
|
||||
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
if (rgb[j] < 0.f || rgb[j] > 1.f) {
|
||||
working_space_is_prophoto = true;
|
||||
prophoto = ICCStore::getInstance()->workingSpace(cmp.working);
|
||||
if (settings->verbose) {
|
||||
std::cout << "colorSpaceConversion_: converting directly to " << cmp.working << " instead of passing through ProPhoto" << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
rgb[j] = 0.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lcmsMutex->lock ();
|
||||
|
||||
switch (camera_icc_type) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
namespace rtengine
|
||||
{
|
||||
@@ -137,4 +138,67 @@ constexpr std::uint8_t uint16ToUint8Rounded(std::uint16_t i)
|
||||
return ((i + 128) - ((i + 128) >> 8)) >> 8;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
bool invertMatrix(const std::array<std::array<T, 3>, 3> &in, std::array<std::array<T, 3>, 3> &out)
|
||||
{
|
||||
const T res00 = in[1][1] * in[2][2] - in[2][1] * in[1][2];
|
||||
const T res10 = in[2][0] * in[1][2] - in[1][0] * in[2][2];
|
||||
const T res20 = in[1][0] * in[2][1] - in[2][0] * in[1][1];
|
||||
|
||||
const T det = in[0][0] * res00 + in[0][1] * res10 + in[0][2] * res20;
|
||||
|
||||
if (std::abs(det) < 1.0e-10) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out[0][0] = res00 / det;
|
||||
out[0][1] = (in[2][1] * in[0][2] - in[0][1] * in[2][2]) / det;
|
||||
out[0][2] = (in[0][1] * in[1][2] - in[1][1] * in[0][2]) / det;
|
||||
out[1][0] = res10 / det;
|
||||
out[1][1] = (in[0][0] * in[2][2] - in[2][0] * in[0][2]) / det;
|
||||
out[1][2] = (in[1][0] * in[0][2] - in[0][0] * in[1][2]) / det;
|
||||
out[2][0] = res20 / det;
|
||||
out[2][1] = (in[2][0] * in[0][1] - in[0][0] * in[2][1]) / det;
|
||||
out[2][2] = (in[0][0] * in[1][1] - in[1][0] * in[0][1]) / det;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::array<std::array<T, 3>, 3> dotProduct(const std::array<std::array<T, 3>, 3> &a, const std::array<std::array<T, 3>, 3> &b)
|
||||
{
|
||||
std::array<std::array<T, 3>, 3> res;
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
res[i][j] = 0;
|
||||
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
res[i][j] += a[i][k] * b[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
std::array<T, 3> dotProduct(const std::array<std::array<T, 3>, 3> &a, const std::array<T, 3> &b)
|
||||
{
|
||||
std::array<T, 3> res;
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
res[i] = 0;
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
res[i] += a[i][k] * b[k];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user