/src/core/MathUtils.cpp

http://github.com/imageworks/OpenColorIO · C++ · 622 lines · 470 code · 110 blank · 42 comment · 41 complexity · e8f424aa4f1d31cc19edd210490bced5 MD5 · raw file

  1. /*
  2. Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
  3. All Rights Reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are
  6. met:
  7. * Redistributions of source code must retain the above copyright
  8. notice, this list of conditions and the following disclaimer.
  9. * Redistributions in binary form must reproduce the above copyright
  10. notice, this list of conditions and the following disclaimer in the
  11. documentation and/or other materials provided with the distribution.
  12. * Neither the name of Sony Pictures Imageworks nor the names of its
  13. contributors may be used to endorse or promote products derived from
  14. this software without specific prior written permission.
  15. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  16. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  17. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  18. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  19. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  20. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  21. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  22. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  23. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  25. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. #include <OpenColorIO/OpenColorIO.h>
  28. #include <cstring>
  29. #include "MathUtils.h"
  30. OCIO_NAMESPACE_ENTER
  31. {
  32. namespace
  33. {
  34. const float FLTMIN = std::numeric_limits<float>::min();
  35. }
  36. bool IsScalarEqualToZero(float v)
  37. {
  38. return equalWithAbsError(v, 0.0f, FLTMIN);
  39. }
  40. bool IsScalarEqualToZeroFlt(double v)
  41. {
  42. return equalWithAbsError(float(v), 0.0f, FLTMIN);
  43. }
  44. bool IsScalarEqualToOne(float v)
  45. {
  46. return equalWithAbsError(v, 1.0f, FLTMIN);
  47. }
  48. bool IsScalarEqualToOneFlt(double v)
  49. {
  50. return equalWithAbsError(float(v), 1.0f, FLTMIN);
  51. }
  52. float GetSafeScalarInverse(float v, float defaultValue)
  53. {
  54. if(IsScalarEqualToZero(v)) return defaultValue;
  55. return 1.0f / v;
  56. }
  57. bool IsVecEqualToZero(const float* v, int size)
  58. {
  59. for(int i=0; i<size; ++i)
  60. {
  61. if(!IsScalarEqualToZero(v[i])) return false;
  62. }
  63. return true;
  64. }
  65. bool IsVecEqualToOne(const float* v, int size)
  66. {
  67. for(int i=0; i<size; ++i)
  68. {
  69. if(!IsScalarEqualToOne(v[i])) return false;
  70. }
  71. return true;
  72. }
  73. bool IsVecEqualToOneFlt(const double* v, int size)
  74. {
  75. for(int i=0; i<size; ++i)
  76. {
  77. if(!IsScalarEqualToOneFlt(v[i])) return false;
  78. }
  79. return true;
  80. }
  81. bool VecContainsZero(const float* v, int size)
  82. {
  83. for(int i=0; i<size; ++i)
  84. {
  85. if(IsScalarEqualToZero(v[i])) return true;
  86. }
  87. return false;
  88. }
  89. bool VecContainsOne(const float* v, int size)
  90. {
  91. for(int i=0; i<size; ++i)
  92. {
  93. if(IsScalarEqualToOne(v[i])) return true;
  94. }
  95. return false;
  96. }
  97. bool VecsEqualWithRelError(const float* v1, int size1,
  98. const float* v2, int size2,
  99. float e)
  100. {
  101. if(size1 != size2) return false;
  102. for(int i=0; i<size1; ++i)
  103. {
  104. if(!equalWithRelError(v1[i], v2[i], e)) return false;
  105. }
  106. return true;
  107. }
  108. double ClampToNormHalf(double val)
  109. {
  110. if(val < -GetHalfMax())
  111. {
  112. return -GetHalfMax();
  113. }
  114. if(val > -GetHalfNormMin() && val<GetHalfNormMin())
  115. {
  116. return 0.0;
  117. }
  118. if(val > GetHalfMax())
  119. {
  120. return GetHalfMax();
  121. }
  122. return val;
  123. }
  124. bool IsM44Identity(const float* m44)
  125. {
  126. int index=0;
  127. for(unsigned int j=0; j<4; ++j)
  128. {
  129. for(unsigned int i=0; i<4; ++i)
  130. {
  131. index = 4*j+i;
  132. if(i==j)
  133. {
  134. if(!IsScalarEqualToOne(m44[index])) return false;
  135. }
  136. else
  137. {
  138. if(!IsScalarEqualToZero(m44[index])) return false;
  139. }
  140. }
  141. }
  142. return true;
  143. }
  144. bool IsM44Diagonal(const float* m44)
  145. {
  146. for(int i=0; i<16; ++i)
  147. {
  148. if((i%5)==0) continue; // If we're on the diagonal, skip it
  149. if(!IsScalarEqualToZero(m44[i])) return false;
  150. }
  151. return true;
  152. }
  153. void GetM44Diagonal(float* out4, const float* m44)
  154. {
  155. for(int i=0; i<4; ++i)
  156. {
  157. out4[i] = m44[i*5];
  158. }
  159. }
  160. // We use an intermediate double representation to make sure
  161. // there is minimal float precision error on the determinant's computation
  162. // (We have seen IsScalarEqualToZero sensitivities here on 32-bit
  163. // virtual machines)
  164. bool GetM44Inverse(float* inverse_out, const float* m_)
  165. {
  166. double m[16];
  167. for(unsigned int i=0; i<16; ++i) m[i] = (double)m_[i];
  168. double d10_21 = m[4]*m[9] - m[5]*m[8];
  169. double d10_22 = m[4]*m[10] - m[6]*m[8];
  170. double d10_23 = m[4]*m[11] - m[7]*m[8];
  171. double d11_22 = m[5]*m[10] - m[6]*m[9];
  172. double d11_23 = m[5]*m[11] - m[7]*m[9];
  173. double d12_23 = m[6]*m[11] - m[7]*m[10];
  174. double a00 = m[13]*d12_23 - m[14]*d11_23 + m[15]*d11_22;
  175. double a10 = m[14]*d10_23 - m[15]*d10_22 - m[12]*d12_23;
  176. double a20 = m[12]*d11_23 - m[13]*d10_23 + m[15]*d10_21;
  177. double a30 = m[13]*d10_22 - m[14]*d10_21 - m[12]*d11_22;
  178. double det = a00*m[0] + a10*m[1] + a20*m[2] + a30*m[3];
  179. if(IsScalarEqualToZero((float)det)) return false;
  180. det = 1.0/det;
  181. double d00_31 = m[0]*m[13] - m[1]*m[12];
  182. double d00_32 = m[0]*m[14] - m[2]*m[12];
  183. double d00_33 = m[0]*m[15] - m[3]*m[12];
  184. double d01_32 = m[1]*m[14] - m[2]*m[13];
  185. double d01_33 = m[1]*m[15] - m[3]*m[13];
  186. double d02_33 = m[2]*m[15] - m[3]*m[14];
  187. double a01 = m[9]*d02_33 - m[10]*d01_33 + m[11]*d01_32;
  188. double a11 = m[10]*d00_33 - m[11]*d00_32 - m[8]*d02_33;
  189. double a21 = m[8]*d01_33 - m[9]*d00_33 + m[11]*d00_31;
  190. double a31 = m[9]*d00_32 - m[10]*d00_31 - m[8]*d01_32;
  191. double a02 = m[6]*d01_33 - m[7]*d01_32 - m[5]*d02_33;
  192. double a12 = m[4]*d02_33 - m[6]*d00_33 + m[7]*d00_32;
  193. double a22 = m[5]*d00_33 - m[7]*d00_31 - m[4]*d01_33;
  194. double a32 = m[4]*d01_32 - m[5]*d00_32 + m[6]*d00_31;
  195. double a03 = m[2]*d11_23 - m[3]*d11_22 - m[1]*d12_23;
  196. double a13 = m[0]*d12_23 - m[2]*d10_23 + m[3]*d10_22;
  197. double a23 = m[1]*d10_23 - m[3]*d10_21 - m[0]*d11_23;
  198. double a33 = m[0]*d11_22 - m[1]*d10_22 + m[2]*d10_21;
  199. inverse_out[0] = (float) (a00*det);
  200. inverse_out[1] = (float) (a01*det);
  201. inverse_out[2] = (float) (a02*det);
  202. inverse_out[3] = (float) (a03*det);
  203. inverse_out[4] = (float) (a10*det);
  204. inverse_out[5] = (float) (a11*det);
  205. inverse_out[6] = (float) (a12*det);
  206. inverse_out[7] = (float) (a13*det);
  207. inverse_out[8] = (float) (a20*det);
  208. inverse_out[9] = (float) (a21*det);
  209. inverse_out[10] = (float) (a22*det);
  210. inverse_out[11] = (float) (a23*det);
  211. inverse_out[12] = (float) (a30*det);
  212. inverse_out[13] = (float) (a31*det);
  213. inverse_out[14] = (float) (a32*det);
  214. inverse_out[15] = (float) (a33*det);
  215. return true;
  216. }
  217. void GetM44M44Product(float* mout, const float* m1_, const float* m2_)
  218. {
  219. float m1[16];
  220. float m2[16];
  221. memcpy(m1, m1_, 16*sizeof(float));
  222. memcpy(m2, m2_, 16*sizeof(float));
  223. mout[ 0] = m1[ 0]*m2[0] + m1[ 1]*m2[4] + m1[ 2]*m2[ 8] + m1[ 3]*m2[12];
  224. mout[ 1] = m1[ 0]*m2[1] + m1[ 1]*m2[5] + m1[ 2]*m2[ 9] + m1[ 3]*m2[13];
  225. mout[ 2] = m1[ 0]*m2[2] + m1[ 1]*m2[6] + m1[ 2]*m2[10] + m1[ 3]*m2[14];
  226. mout[ 3] = m1[ 0]*m2[3] + m1[ 1]*m2[7] + m1[ 2]*m2[11] + m1[ 3]*m2[15];
  227. mout[ 4] = m1[ 4]*m2[0] + m1[ 5]*m2[4] + m1[ 6]*m2[ 8] + m1[ 7]*m2[12];
  228. mout[ 5] = m1[ 4]*m2[1] + m1[ 5]*m2[5] + m1[ 6]*m2[ 9] + m1[ 7]*m2[13];
  229. mout[ 6] = m1[ 4]*m2[2] + m1[ 5]*m2[6] + m1[ 6]*m2[10] + m1[ 7]*m2[14];
  230. mout[ 7] = m1[ 4]*m2[3] + m1[ 5]*m2[7] + m1[ 6]*m2[11] + m1[ 7]*m2[15];
  231. mout[ 8] = m1[ 8]*m2[0] + m1[ 9]*m2[4] + m1[10]*m2[ 8] + m1[11]*m2[12];
  232. mout[ 9] = m1[ 8]*m2[1] + m1[ 9]*m2[5] + m1[10]*m2[ 9] + m1[11]*m2[13];
  233. mout[10] = m1[ 8]*m2[2] + m1[ 9]*m2[6] + m1[10]*m2[10] + m1[11]*m2[14];
  234. mout[11] = m1[ 8]*m2[3] + m1[ 9]*m2[7] + m1[10]*m2[11] + m1[11]*m2[15];
  235. mout[12] = m1[12]*m2[0] + m1[13]*m2[4] + m1[14]*m2[ 8] + m1[15]*m2[12];
  236. mout[13] = m1[12]*m2[1] + m1[13]*m2[5] + m1[14]*m2[ 9] + m1[15]*m2[13];
  237. mout[14] = m1[12]*m2[2] + m1[13]*m2[6] + m1[14]*m2[10] + m1[15]*m2[14];
  238. mout[15] = m1[12]*m2[3] + m1[13]*m2[7] + m1[14]*m2[11] + m1[15]*m2[15];
  239. }
  240. namespace
  241. {
  242. void GetM44V4Product(float* vout, const float* m, const float* v_)
  243. {
  244. float v[4];
  245. memcpy(v, v_, 4*sizeof(float));
  246. vout[0] = m[ 0]*v[0] + m[ 1]*v[1] + m[ 2]*v[2] + m[ 3]*v[3];
  247. vout[1] = m[ 4]*v[0] + m[ 5]*v[1] + m[ 6]*v[2] + m[ 7]*v[3];
  248. vout[2] = m[ 8]*v[0] + m[ 9]*v[1] + m[10]*v[2] + m[11]*v[3];
  249. vout[3] = m[12]*v[0] + m[13]*v[1] + m[14]*v[2] + m[15]*v[3];
  250. }
  251. void GetV4Sum(float* vout, const float* v1, const float* v2)
  252. {
  253. for(int i=0; i<4; ++i)
  254. {
  255. vout[i] = v1[i] + v2[i];
  256. }
  257. }
  258. } // anon namespace
  259. // All m(s) are 4x4. All v(s) are size 4 vectors.
  260. // Return mout, vout, where mout*x+vout == m2*(m1*x+v1)+v2
  261. // mout = m2*m1
  262. // vout = m2*v1 + v2
  263. void GetMxbCombine(float* mout, float* vout,
  264. const float* m1_, const float* v1_,
  265. const float* m2_, const float* v2_)
  266. {
  267. float m1[16];
  268. float v1[4];
  269. float m2[16];
  270. float v2[4];
  271. memcpy(m1, m1_, 16*sizeof(float));
  272. memcpy(v1, v1_, 4*sizeof(float));
  273. memcpy(m2, m2_, 16*sizeof(float));
  274. memcpy(v2, v2_, 4*sizeof(float));
  275. GetM44M44Product(mout, m2, m1);
  276. GetM44V4Product(vout, m2, v1);
  277. GetV4Sum(vout, vout, v2);
  278. }
  279. namespace
  280. {
  281. void GetMxbResult(float* vout, float* m, float* x, float* v)
  282. {
  283. GetM44V4Product(vout, m, x);
  284. GetV4Sum(vout, vout, v);
  285. }
  286. } // anon namespace
  287. bool GetMxbInverse(float* mout, float* vout,
  288. const float* m_, const float* v_)
  289. {
  290. float m[16];
  291. float v[4];
  292. memcpy(m, m_, 16*sizeof(float));
  293. memcpy(v, v_, 4*sizeof(float));
  294. if(!GetM44Inverse(mout, m)) return false;
  295. for(int i=0; i<4; ++i)
  296. {
  297. v[i] = -v[i];
  298. }
  299. GetM44V4Product(vout, mout, v);
  300. return true;
  301. }
  302. }
  303. OCIO_NAMESPACE_EXIT
  304. ///////////////////////////////////////////////////////////////////////////////
  305. #ifdef OCIO_UNIT_TEST
  306. OCIO_NAMESPACE_USING
  307. #include "UnitTest.h"
  308. OIIO_ADD_TEST(MathUtils, M44_is_diagonal)
  309. {
  310. {
  311. float m44[] = { 1.0f, 0.0f, 0.0f, 0.0f,
  312. 0.0f, 1.0f, 0.0f, 0.0f,
  313. 0.0f, 0.0f, 1.0f, 0.0f,
  314. 0.0f, 0.0f, 0.0f, 1.0f };
  315. bool isdiag = IsM44Diagonal(m44);
  316. OIIO_CHECK_EQUAL(isdiag, true);
  317. m44[1] += 1e-8f;
  318. isdiag = IsM44Diagonal(m44);
  319. OIIO_CHECK_EQUAL(isdiag, false);
  320. }
  321. }
  322. OIIO_ADD_TEST(MathUtils, IsScalarEqualToZero)
  323. {
  324. OIIO_CHECK_EQUAL(IsScalarEqualToZero(0.0f), true);
  325. OIIO_CHECK_EQUAL(IsScalarEqualToZero(-0.0f), true);
  326. OIIO_CHECK_EQUAL(IsScalarEqualToZero(-1.072883670794056e-09f), false);
  327. OIIO_CHECK_EQUAL(IsScalarEqualToZero(1.072883670794056e-09f), false);
  328. OIIO_CHECK_EQUAL(IsScalarEqualToZero(-1.072883670794056e-03f), false);
  329. OIIO_CHECK_EQUAL(IsScalarEqualToZero(1.072883670794056e-03f), false);
  330. OIIO_CHECK_EQUAL(IsScalarEqualToZero(-1.072883670794056e-01f), false);
  331. OIIO_CHECK_EQUAL(IsScalarEqualToZero(1.072883670794056e-01f), false);
  332. }
  333. OIIO_ADD_TEST(MathUtils, GetM44Inverse)
  334. {
  335. // This is a degenerate matrix, and shouldnt be invertible.
  336. float m[] = { 0.3f, 0.3f, 0.3f, 0.0f,
  337. 0.3f, 0.3f, 0.3f, 0.0f,
  338. 0.3f, 0.3f, 0.3f, 0.0f,
  339. 0.0f, 0.0f, 0.0f, 1.0f };
  340. float mout[16];
  341. bool invertsuccess = GetM44Inverse(mout, m);
  342. OIIO_CHECK_EQUAL(invertsuccess, false);
  343. }
  344. OIIO_ADD_TEST(MathUtils, M44_M44_product)
  345. {
  346. {
  347. float mout[16];
  348. float m1[] = { 1.0f, 2.0f, 0.0f, 0.0f,
  349. 0.0f, 1.0f, 1.0f, 0.0f,
  350. 1.0f, 0.0f, 1.0f, 0.0f,
  351. 0.0f, 1.0f, 3.0f, 1.0f };
  352. float m2[] = { 1.0f, 1.0f, 0.0f, 0.0f,
  353. 0.0f, 1.0f, 0.0f, 0.0f,
  354. 0.0f, 0.0f, 1.0f, 0.0f,
  355. 2.0f, 0.0f, 0.0f, 1.0f };
  356. GetM44M44Product(mout, m1, m2);
  357. float mcorrect[] = { 1.0f, 3.0f, 0.0f, 0.0f,
  358. 0.0f, 1.0f, 1.0f, 0.0f,
  359. 1.0f, 1.0f, 1.0f, 0.0f,
  360. 2.0f, 1.0f, 3.0f, 1.0f };
  361. for(int i=0; i<16; ++i)
  362. {
  363. OIIO_CHECK_EQUAL(mout[i], mcorrect[i]);
  364. }
  365. }
  366. }
  367. OIIO_ADD_TEST(MathUtils, M44_V4_product)
  368. {
  369. {
  370. float vout[4];
  371. float m[] = { 1.0f, 2.0f, 0.0f, 0.0f,
  372. 0.0f, 1.0f, 1.0f, 0.0f,
  373. 1.0f, 0.0f, 1.0f, 0.0f,
  374. 0.0f, 1.0f, 3.0f, 1.0f };
  375. float v[] = { 1.0f, 2.0f, 3.0f, 4.0f };
  376. GetM44V4Product(vout, m, v);
  377. float vcorrect[] = { 5.0f, 5.0f, 4.0f, 15.0f };
  378. for(int i=0; i<4; ++i)
  379. {
  380. OIIO_CHECK_EQUAL(vout[i], vcorrect[i]);
  381. }
  382. }
  383. }
  384. OIIO_ADD_TEST(MathUtils, V4_add)
  385. {
  386. {
  387. float vout[4];
  388. float v1[] = { 1.0f, 2.0f, 3.0f, 4.0f };
  389. float v2[] = { 3.0f, 1.0f, 4.0f, 1.0f };
  390. GetV4Sum(vout, v1, v2);
  391. float vcorrect[] = { 4.0f, 3.0f, 7.0f, 5.0f };
  392. for(int i=0; i<4; ++i)
  393. {
  394. OIIO_CHECK_EQUAL(vout[i], vcorrect[i]);
  395. }
  396. }
  397. }
  398. OIIO_ADD_TEST(MathUtils, mxb_eval)
  399. {
  400. {
  401. float vout[4];
  402. float m[] = { 1.0f, 2.0f, 0.0f, 0.0f,
  403. 0.0f, 1.0f, 1.0f, 0.0f,
  404. 1.0f, 0.0f, 1.0f, 0.0f,
  405. 0.0f, 1.0f, 3.0f, 1.0f };
  406. float x[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  407. float v[] = { 1.0f, 2.0f, 3.0f, 4.0f };
  408. GetMxbResult(vout, m, x, v);
  409. float vcorrect[] = { 4.0f, 4.0f, 5.0f, 9.0f };
  410. for(int i=0; i<4; ++i)
  411. {
  412. OIIO_CHECK_EQUAL(vout[i], vcorrect[i]);
  413. }
  414. }
  415. }
  416. OIIO_ADD_TEST(MathUtils, Combine_two_mxb)
  417. {
  418. float m1[] = { 1.0f, 0.0f, 2.0f, 0.0f,
  419. 2.0f, 1.0f, 0.0f, 1.0f,
  420. 0.0f, 1.0f, 2.0f, 0.0f,
  421. 1.0f, 0.0f, 0.0f, 1.0f };
  422. float v1[] = { 1.0f, 2.0f, 3.0f, 4.0f };
  423. float m2[] = { 2.0f, 1.0f, 0.0f, 0.0f,
  424. 0.0f, 1.0f, 0.0f, 0.0f,
  425. 1.0f, 0.0f, 3.0f, 0.0f,
  426. 1.0f,1.0f, 1.0f, 1.0f };
  427. float v2[] = { 0.0f, 2.0f, 1.0f, 0.0f };
  428. float tolerance = 1e-9f;
  429. {
  430. float x[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  431. float vout[4];
  432. // Combine two mx+b operations, and apply to test point
  433. float mout[16];
  434. float vcombined[4];
  435. GetMxbCombine(mout, vout, m1, v1, m2, v2);
  436. GetMxbResult(vcombined, mout, x, vout);
  437. // Sequentially apply the two mx+b operations.
  438. GetMxbResult(vout, m1, x, v1);
  439. GetMxbResult(vout, m2, vout, v2);
  440. // Compare outputs
  441. for(int i=0; i<4; ++i)
  442. {
  443. OIIO_CHECK_CLOSE(vcombined[i], vout[i], tolerance);
  444. }
  445. }
  446. {
  447. float x[] = { 6.0f, 0.5f, -2.0f, -0.1f };
  448. float vout[4];
  449. float mout[16];
  450. float vcombined[4];
  451. GetMxbCombine(mout, vout, m1, v1, m2, v2);
  452. GetMxbResult(vcombined, mout, x, vout);
  453. GetMxbResult(vout, m1, x, v1);
  454. GetMxbResult(vout, m2, vout, v2);
  455. for(int i=0; i<4; ++i)
  456. {
  457. OIIO_CHECK_CLOSE(vcombined[i], vout[i], tolerance);
  458. }
  459. }
  460. {
  461. float x[] = { 26.0f, -0.5f, 0.005f, 12.1f };
  462. float vout[4];
  463. float mout[16];
  464. float vcombined[4];
  465. GetMxbCombine(mout, vout, m1, v1, m2, v2);
  466. GetMxbResult(vcombined, mout, x, vout);
  467. GetMxbResult(vout, m1, x, v1);
  468. GetMxbResult(vout, m2, vout, v2);
  469. // We pick a not so small tolerance, as we're dealing with
  470. // large numbers, and the error for CHECK_CLOSE is absolute.
  471. for(int i=0; i<4; ++i)
  472. {
  473. OIIO_CHECK_CLOSE(vcombined[i], vout[i], 1e-3);
  474. }
  475. }
  476. }
  477. OIIO_ADD_TEST(MathUtils, mxb_invert)
  478. {
  479. {
  480. float m[] = { 1.0f, 2.0f, 0.0f, 0.0f,
  481. 0.0f, 1.0f, 1.0f, 0.0f,
  482. 1.0f, 0.0f, 1.0f, 0.0f,
  483. 0.0f, 1.0f, 3.0f, 1.0f };
  484. float x[] = { 1.0f, 0.5f, -1.0f, 60.0f };
  485. float v[] = { 1.0f, 2.0f, 3.0f, 4.0f };
  486. float vresult[4];
  487. float mout[16];
  488. float vout[4];
  489. GetMxbResult(vresult, m, x, v);
  490. bool invertsuccess = GetMxbInverse(mout, vout, m, v);
  491. OIIO_CHECK_EQUAL(invertsuccess, true);
  492. GetMxbResult(vresult, mout, vresult, vout);
  493. float tolerance = 1e-9f;
  494. for(int i=0; i<4; ++i)
  495. {
  496. OIIO_CHECK_CLOSE(vresult[i], x[i], tolerance);
  497. }
  498. }
  499. {
  500. float m[] = { 0.3f, 0.3f, 0.3f, 0.0f,
  501. 0.3f, 0.3f, 0.3f, 0.0f,
  502. 0.3f, 0.3f, 0.3f, 0.0f,
  503. 0.0f, 0.0f, 0.0f, 1.0f };
  504. float v[] = { 0.0f, 0.0f, 0.0f, 0.0f };
  505. float mout[16];
  506. float vout[4];
  507. bool invertsuccess = GetMxbInverse(mout, vout, m, v);
  508. OIIO_CHECK_EQUAL(invertsuccess, false);
  509. }
  510. }
  511. #endif