public static void ConsistentWithMatrixRotationAndShiftTest() => TrafoTesting.GenericTest(rnd => { var trans = rnd.UniformV3d() * 10; var axis = rnd.UniformV3dDirection(); var angle = rnd.UniformDouble() * Constant.PiTimesTwo; var m = M44d.Translation(trans) * M44d.Rotation(axis, angle); var e = new Euclidean3d(Rot3d.Rotation(axis, angle), trans); var p = rnd.UniformV3d() * rnd.UniformInt(1000); var res = m.TransformPos(p); var res2 = e.TransformPos(p); TrafoTesting.AreEqual(res, res2); });
public AngleBetweenDouble() { var rnd = new RandomSystem(1); A.SetByIndex(i => rnd.UniformV3dDirection()); angles.SetByIndex(i => rnd.UniformDouble() * (double)Constant.Pi); B.SetByIndex(i => { V3d v; do { v = rnd.UniformV3dDirection(); }while (v.Dot(A[i]).IsTiny()); V3d axis = v.Cross(A[i]).Normalized; return(M44d.Rotation(axis, angles[i]).TransformDir(A[i])); }); }
/// <summary> /// Build a geometry transformation from the given parameters as specified in Transform /// http://gun.teipir.gr/VRML-amgem/spec/part1/nodesRef.html#Transform /// </summary> public static Trafo3d BuildVrmlGeometryTrafo(V3d center, V4d rotation, V3d scale, V4d scaleOrientation, V3d translation) { // create composite trafo (naming taken from vrml97 spec) M44d C = M44d.Translation(center), Ci = M44d.Translation(-center); M44d SR = M44d.Rotation(scaleOrientation.XYZ, scaleOrientation.W), SRi = M44d.Rotation(scaleOrientation.XYZ, -scaleOrientation.W); M44d T = M44d.Translation(translation), Ti = M44d.Translation(-translation); //if (m_aveCompatibilityMode) r.W = -r.W; M44d R = M44d.Rotation(rotation.XYZ, rotation.W), Ri = M44d.Rotation(rotation.XYZ, -rotation.W); // in case some axis scales by 0 the best thing for the inverse scale is also 0 var si = new V3d(scale.X.IsTiny() ? 0 : 1 / scale.X, scale.Y.IsTiny() ? 0 : 1 / scale.Y, scale.Z.IsTiny() ? 0 : 1 / scale.Z); M44d S = M44d.Scale(scale), Si = M44d.Scale(si); return(new Trafo3d( T * C * R * SR * S * SRi * Ci, C * SR * Si * SRi * Ri * Ci * Ti)); }
public void MatrixInverseTest(int rounds, int count, bool doLuM = true, bool doLuV = true, bool doLu2 = true, bool doGj2 = false, bool doQrI = true, bool doQr2 = true) { bool doMul = true; double luEpsilon = 1e-5; double qrEpsilon = 4e-5; Test.Begin("matrix inverse tests"); Report.Line("epsilon for lu tests: {0:e0}", luEpsilon); Report.Line("epsilon for qr tests: {0:e0}", qrEpsilon); bool showWorst = true; bool showTypes = true; var rnd = new RandomSystem(19680713); var tc = 4; var typeStatsOpt = Stats <M44d> .ComputeCountMaxMean; Stats <M44d>[] luiTypeStats = new Stats <M44d> [tc].Set(typeStatsOpt); Stats <M44d>[] lumTypeStats = new Stats <M44d> [tc].Set(typeStatsOpt); Stats <M44d>[] luvTypeStats = new Stats <M44d> [tc].Set(typeStatsOpt); Stats <M44d>[] lu2TypeStats = new Stats <M44d> [tc].Set(typeStatsOpt); Stats <M44d>[] gj2TypeStats = new Stats <M44d> [tc].Set(typeStatsOpt); Stats <M44d>[] qriTypeStats = new Stats <M44d> [tc].Set(typeStatsOpt); Stats <M44d>[] qr2TypeStats = new Stats <M44d> [tc].Set(typeStatsOpt); string[] typenames = new string[] { "Rotation", "Scale", "Translation", "Mixed" }; var histoStatsOpt = StatsOptions.MaxMean; var luiHistoStats = new HistogramAndStats <M44d>(-16, 0, 16, histoStatsOpt); var lumHistoStats = new HistogramAndStats <M44d>(-16, 0, 16, histoStatsOpt); var luvHistoStats = new HistogramAndStats <M44d>(-16, 0, 16, histoStatsOpt); var lu2HistoStats = new HistogramAndStats <M44d>(-16, 0, 16, histoStatsOpt); var gj2HistoStats = new HistogramAndStats <M44d>(-16, 0, 16, histoStatsOpt); var qriHistoStats = new HistogramAndStats <M44d>(-16, 0, 16, histoStatsOpt); var qr2HistoStats = new HistogramAndStats <M44d>(-16, 0, 16, histoStatsOpt); M44d[] mats = new M44d[count]; M44d[] luis = new M44d[count]; M44d[] lums = new M44d[count]; M44d[] luvs = new M44d[count]; M44d[] lu2s = new M44d[count]; M44d[] gj2s = new M44d[count]; M44d[] qris = new M44d[count]; M44d[] qr2s = new M44d[count]; int[] types = new int[count].SetByIndex(i => - 1); bool failedLuM = false, failedLuV = false, failedLu2 = false; bool failedQr2 = false; var chainStats = new Stats <bool>(StatsOptions.MaxMean); for (int j = 0; j < rounds; j++) { using (Report.JobTimed("creating {0} matrices round {1} of {2}", count, j + 1, rounds)) for (int i = 0; i < count; i++) { M44d mat = M44d.Identity; var chainLength = 0; do { int type = rnd.UniformInt(3); if (types[i] == -1) { types[i] = type; } else if (types[i] != type) { types[i] = 3; //mixed type } switch (type) { case 0: V3d axis = V3d.Zero; double squaredLength = 0.0; do { axis = new V3d(2 * rnd.UniformDoubleFullClosed() - 1, 2 * rnd.UniformDoubleFullClosed() - 1, 2 * rnd.UniformDoubleFullClosed() - 1); squaredLength = axis.LengthSquared; }while (squaredLength == 0); axis *= 1.0 / Fun.Sqrt(squaredLength); double alpha = 2 * Constant.Pi * rnd.UniformDoubleFull() - Constant.Pi; M44d rot = M44d.Rotation(axis, alpha); mat = rot * mat; chainLength++; break; case 1: var s0 = rnd.UniformDouble() < 0.5 ? -4.0 : 4.0; var s1 = rnd.UniformDouble() < 0.5 ? -4.0 : 4.0; var s2 = rnd.UniformDouble() < 0.5 ? -4.0 : 4.0; M44d scale = M44d.Scale( s0 * (1.0 - rnd.UniformDoubleFull()), s1 * (1.0 - rnd.UniformDoubleFull()), s2 * (1.0 - rnd.UniformDoubleFull())); mat = scale * mat; chainLength++; break; case 2: M44d shift = M44d.Translation( 32 * rnd.UniformDoubleFullClosed() - 16, 32 * rnd.UniformDoubleFullClosed() - 16, 32 * rnd.UniformDoubleFullClosed() - 16); mat = shift * mat; chainLength++; break; default: break; } }while (rnd.UniformDouble() > 0.25); mats[i] = mat; chainStats.Add(chainLength); } if (doQr2) { using (Report.JobTimed("qr [,] factorization")) for (int i = 0; i < count; i++) { qr2s[i] = mats[i].QrInverse2(); } } if (doQrI) { using (Report.JobTimed("qr factorization")) for (int i = 0; i < count; i++) { qris[i] = mats[i].QrInverse(); } } if (doGj2) { using (Report.JobTimed("gauss jordan")) for (int i = 0; i < count; i++) { gj2s[i] = mats[i].NumericallyInstableGjInverse2(); } } if (doLu2) { using (Report.JobTimed("lu [,] factorization")) for (int i = 0; i < count; i++) { lu2s[i] = mats[i].LuInverse2(); } } if (doLuV) { using (Report.JobTimed("lu vector solve factorization")) for (int i = 0; i < count; i++) { luvs[i] = mats[i].LuInverseV(); } } if (doLuM) { using (Report.JobTimed("lu matrix solve factorization")) for (int i = 0; i < count; i++) { lums[i] = mats[i].LuInverseM(); } } using (Report.JobTimed("lu factorization")) for (int i = 0; i < count; i++) { luis[i] = mats[i].LuInverse(); } Test.Begin("analysis"); for (int i = 0; i < count; i++) { M44d luid = mats[i] * luis[i]; double plErr = M44d.DistanceMax(luid, M44d.Identity); Test.IsTrue(plErr < luEpsilon); luiHistoStats.AddLog10Hist(plErr, mats[i]); luiTypeStats[types[i]].Add(plErr); if (doMul) { var m0 = new Matrix <double>((double[])mats[i], 4, 4); var m1 = new Matrix <double>((double[])luis[i], 4, 4); var id = m0.Multiply(m1); double deltaErr = M44d.Distance1(luid, new M44d(id.Data)); Test.IsTrue(deltaErr == 0.0); } if (doLuM) { M44d msid = mats[i] * lums[i]; double error = M44d.DistanceMax(msid, M44d.Identity); lumHistoStats.AddLog10Hist(error, mats[i]); lumTypeStats[types[i]].Add(error); double deltaErr = M44d.Distance1(luis[i], lums[i]); if (!Test.IsTrue(deltaErr == 0.0)) { failedLuM = true; } } if (doLuV) { M44d vsid = mats[i] * luvs[i]; double error = M44d.DistanceMax(vsid, M44d.Identity); luvHistoStats.AddLog10Hist(error, mats[i]); luvTypeStats[types[i]].Add(error); double deltaErr = M44d.Distance1(luis[i], luvs[i]); if (!Test.IsTrue(deltaErr == 0.0)) { failedLuV = true; } } if (doLu2) { M44d a2id = mats[i] * lu2s[i]; double error = M44d.DistanceMax(a2id, M44d.Identity); lu2HistoStats.AddLog10Hist(error, mats[i]); lu2TypeStats[types[i]].Add(error); double deltaErr = M44d.Distance1(luis[i], lu2s[i]); if (!Test.IsTrue(deltaErr == 0.0)) { failedLu2 = true; } } if (doGj2) { M44d gjid = mats[i] * gj2s[i]; double error = M44d.DistanceMax(gjid, M44d.Identity); gj2HistoStats.AddLog10Hist(error, mats[i]); gj2TypeStats[types[i]].Add(error); } if (doQrI) { M44d qrid = mats[i] * qris[i]; double error = M44d.DistanceMax(qrid, M44d.Identity); qriHistoStats.AddLog10Hist(error, mats[i]); qriTypeStats[types[i]].Add(error); Test.IsTrue(error < qrEpsilon); } if (doQr2) { M44d qrid = mats[i] * qr2s[i]; double error = M44d.DistanceMax(qrid, M44d.Identity); qr2HistoStats.AddLog10Hist(error, mats[i]); qr2TypeStats[types[i]].Add(error); double deltaErr = M44d.Distance1(qris[i], qr2s[i]); if (!Test.IsTrue(deltaErr == 0.0)) { failedQr2 = true; } } } Test.End(); } Report.Value("matrix chain lengths", chainStats); Report.Value("lu factorization error", luiHistoStats.Stats); if (showTypes) { for (int ti = 0; ti < tc; ti++) { Report.Value("lu " + typenames[ti] + " matrix factorization error", luiTypeStats[ti]); } } Report.Value("lu factorization log error histogram", luiHistoStats.Histogram); if (showWorst) { using (Report.Job("worst matrix")) WriteMat(luiHistoStats.Stats.MaxData); using (Report.Job("worst inverse")) WriteMat(luiHistoStats.Stats.MaxData.LuInverse()); using (Report.Job("worst result")) WriteMat(luiHistoStats.Stats.MaxData * luiHistoStats.Stats.MaxData.LuInverse()); } if (failedLuM) { Report.Value("lu matrix solve factorization error", lumHistoStats.Stats); if (showTypes) { for (int ti = 0; ti < tc; ti++) { Report.Value("lu matrix solve " + typenames[ti] + " matrix factorization error", lumTypeStats[ti]); } } Report.Value("lu matrix solve factorization log error histogram", lumHistoStats.Histogram); if (showWorst) { using (Report.Job("worst matrix")) WriteMat(lumHistoStats.Stats.MaxData); using (Report.Job("worst inverse")) WriteMat(lumHistoStats.Stats.MaxData.LuInverseM()); using (Report.Job("worst result")) WriteMat(lumHistoStats.Stats.MaxData * lumHistoStats.Stats.MaxData.LuInverseM()); } } if (failedLuV) { Report.Value("lu vector solve factorization error", luvHistoStats.Stats); if (showTypes) { for (int ti = 0; ti < tc; ti++) { Report.Value("lu vector solve " + typenames[ti] + " matrix factorization error", luvTypeStats[ti]); } } Report.Value("lu vector solve factorization log error histogram", luvHistoStats.Histogram); if (showWorst) { using (Report.Job("worst matrix")) WriteMat(luvHistoStats.Stats.MaxData); using (Report.Job("worst inverse")) WriteMat(luvHistoStats.Stats.MaxData.LuInverseV()); using (Report.Job("worst result")) WriteMat(luvHistoStats.Stats.MaxData * luvHistoStats.Stats.MaxData.LuInverseV()); } } if (failedLu2) { Report.Value("lu [,] factorization error", lu2HistoStats.Stats); if (showTypes) { for (int ti = 0; ti < tc; ti++) { Report.Value("lu [,] " + typenames[ti] + " matrix factorization error", lu2TypeStats[ti]); } } Report.Value("lu [,] factorization log error histogram", lu2HistoStats.Histogram); if (showWorst) { using (Report.Job("worst matrix")) WriteMat(lu2HistoStats.Stats.MaxData); using (Report.Job("worst inverse")) WriteMat(lu2HistoStats.Stats.MaxData.LuInverse2()); using (Report.Job("worst result")) WriteMat(lu2HistoStats.Stats.MaxData * lu2HistoStats.Stats.MaxData.LuInverse2()); } } if (doGj2) { Report.Value("gauss jordan error", gj2HistoStats.Stats); if (showTypes) { for (int ti = 0; ti < tc; ti++) { Report.Value("gj " + typenames[ti] + " matrix factorization error", gj2TypeStats[ti]); } } Report.Value("gauss jordan log error histogram", gj2HistoStats.Histogram); if (showWorst) { using (Report.Job("worst matrix")) WriteMat(gj2HistoStats.Stats.MaxData); using (Report.Job("worst inverse")) WriteMat(gj2HistoStats.Stats.MaxData.NumericallyInstableGjInverse2()); using (Report.Job("worst result")) WriteMat(gj2HistoStats.Stats.MaxData * gj2HistoStats.Stats.MaxData.NumericallyInstableGjInverse2()); } } if (doQrI) { Report.Value("qr factorization error", qr2HistoStats.Stats); if (showTypes) { for (int ti = 0; ti < tc; ti++) { Report.Value("qr " + typenames[ti] + " matrix factorization error", qriTypeStats[ti]); } } Report.Value("qr factorization log error histogram", qriHistoStats.Histogram); if (showWorst) { using (Report.Job("worst matrix")) WriteMat(qriHistoStats.Stats.MaxData); using (Report.Job("worst inverse")) WriteMat(qriHistoStats.Stats.MaxData.QrInverse()); using (Report.Job("worst result")) WriteMat(qriHistoStats.Stats.MaxData * qriHistoStats.Stats.MaxData.QrInverse()); } } if (failedQr2) { Report.Value("qr [,] factorization error", qr2HistoStats.Stats); if (showTypes) { for (int ti = 0; ti < tc; ti++) { Report.Value("qr [,] " + typenames[ti] + " matrix factorization error", qr2TypeStats[ti]); } } Report.Value("qr [,] factorization log error histogram", qr2HistoStats.Histogram); if (showWorst) { using (Report.Job("worst matrix")) WriteMat(qr2HistoStats.Stats.MaxData); using (Report.Job("worst inverse")) WriteMat(qr2HistoStats.Stats.MaxData.QrInverse2()); using (Report.Job("worst result")) WriteMat(qr2HistoStats.Stats.MaxData * qr2HistoStats.Stats.MaxData.QrInverse2()); } } Test.End(); }