public BreslavTextureTransition Update(Camera cam, Transform parentObjectTransform)
        {
            var baseWeights = ComputeWeightsOfPoints(_samples, cam, parentObjectTransform);

            var that    = this;
            var weights = Enumerable.Range(0, _samples.Count).Select(i =>
            {
                var oldW = that._lastFrameInfo.Weights[i];
                var newW = baseWeights[i];
                if (oldW > 0 && newW > 0) // we only consider points that are visible in this and previous frames
                {
                    return(newW);
                }
                else
                {
                    return(0);
                }
            }).ToList();
            var anyNotZero = weights.Any(c => c > 0);
            //Debug.Log(anyNotZero + );

            var screenPosOfPoints = _samples.Select(c => cam.WorldToViewportPoint(parentObjectTransform.localToWorldMatrix.MultiplyPoint(c.Position))).Select(c => new Vector2(c.x, c.y)).ToList();

            if (!anyNotZero)
            {
                _lastFrameInfo.Weights = baseWeights;
                _lastFrameInfo.SamplePointsScreenPositions = screenPosOfPoints;
                return(_lastTransition);
            }

            var currentCentertoid     = screenPosOfPoints.Select((c, i) => c * weights[i]).Aggregate((a, b) => a + b) / weights.Sum();
            var currentDeltaPositions = screenPosOfPoints.Select(c => c - currentCentertoid).ToList();

            var previousCentertoid     = _lastFrameInfo.SamplePointsScreenPositions.Select((c, i) => c * weights[i]).Aggregate((a, b) => a + b) / weights.Sum();
            var previousDeltaPositions = _lastFrameInfo.SamplePointsScreenPositions.Select(c => c - previousCentertoid).ToList();

            Vector2 z;
            float   zScaleMultiplier = 0;

            if (_configuration.ZComputingMode == BreslavZComputingMode.PaperMode)
            {
                var left = Enumerable.Range(0, _samples.Count).Select(i =>
                                                                      new Vector2(
                                                                          Vector2.Dot(weights[i] * previousDeltaPositions[i], currentDeltaPositions[i]),
                                                                          VectorUtils.CrossProduct(weights[i] * previousDeltaPositions[i], currentDeltaPositions[i])
                                                                          )).Aggregate((a, b) => a + b);

                var right = Enumerable.Range(0, _samples.Count).Select(i => weights[i] * Math.Pow(previousDeltaPositions[i].magnitude, 2))
                            .Aggregate((a, b) => a + b);
                var _z = left / (float)right;

                var s = (1 / _z.magnitude) * Math.Sqrt(
                    Enumerable.Range(0, _samples.Count).Select(i => weights[i] * Math.Pow(previousDeltaPositions[i].magnitude, 2))
                    .Aggregate((a, b) => a + b) /
                    Enumerable.Range(0, _samples.Count).Select(i => weights[i] * Math.Pow(currentDeltaPositions[i].magnitude, 2))
                    .Aggregate((a, b) => a + b)
                    );

                if (!_configuration.DoRotation)
                {
                    _z = new Vector2(_z[0], 0);
                }

                zScaleMultiplier = _z.magnitude;
                _z *= (float)s;

                z = _z;
            }
            else
            {
                double  so = 0;
                double  sn = 0;
                Vector2 _z = new Vector2(0, 0);
                for (int i = 0; i < _samples.Count; i++)
                {
                    if (weights[i] > 0)
                    {
                        var   m_o = previousDeltaPositions[i];
                        var   m_n = currentDeltaPositions[i];
                        float m_w = weights[i];

                        _z += new Vector2((float)Cm2(m_o, m_n), (float)Det(m_o, m_n)) * m_w;
                        so += m_o.sqrMagnitude * m_w;
                        sn += m_n.sqrMagnitude * m_w;
                    }
                }

                if (_configuration.DoSymmetricScalling)
                {
                    _z = _z.normalized * Mathf.Sqrt((float)(sn / so));
                }
                else
                {
                    _z /= ((float)so);
                }
                zScaleMultiplier = _z.magnitude;
                z = _z;

                if (!_configuration.DoRotation)
                {
                    z = new Vector2(z[0], 0);
                }

                double EPSILON = 0.00000000001;
                if (Math.Abs(so) <= EPSILON || Math.Abs(sn) <= EPSILON || _z.magnitude <= EPSILON)
                {
                    _lastFrameInfo = new BreslavFrameInfo()
                    {
                        O = _lastFrameInfo.O,
                        U = _lastFrameInfo.U,
                        V = _lastFrameInfo.V,
                        SamplePointsScreenPositions = screenPosOfPoints,
                        Weights = baseWeights,
                        LodInfo = _lastFrameInfo.LodInfo
                    };

                    return(_lastTransition);
                }
            }

            var lodInfo = _lastFrameInfo.LodInfo.Clone();

            lodInfo.Scale *= zScaleMultiplier;

            // interpolation parameter between 2 LOD scales
            float st = 0;

            var o = currentCentertoid + ComplexMultiplication(_lastFrameInfo.O - previousCentertoid, z);
            var u = ComplexMultiplication(_lastFrameInfo.U, z);
            var v = ComplexMultiplication(_lastFrameInfo.V, z);

            if (_configuration.UseLod)
            {
                float locScale = lodInfo.Scale;
                Preconditions.Assert(locScale > 0, $"LocScale <= 0: {locScale}");
                while (locScale >= 2) //Ensuring s is [1,2)
                {
                    locScale /= 2;
                }
                while (locScale < 1)
                {
                    locScale *= 2;
                }

                if (locScale < _configuration.Lod_St0)
                {
                    st = 0;
                }
                else if (locScale > _configuration.Lod_St1)
                {
                    st = 1;
                }
                else
                {
                    st = (locScale - _configuration.Lod_St0) / (_configuration.Lod_St1 - _configuration.Lod_St0);
                }
                u = u.normalized * locScale;
                v = v.normalized * locScale;
            }

            _lastFrameInfo = new BreslavFrameInfo()
            {
                O = o,
                U = u,
                V = v,
                SamplePointsScreenPositions = screenPosOfPoints,
                Weights = baseWeights,
                LodInfo = lodInfo
            };
            return(new BreslavTextureTransition()
            {
                V = v,
                O = o,
                St = st,
                U = u,
                LodScale = lodInfo.Scale
            });
        }