/// <summary>
        /// Update animation and pending changes.
        /// </summary>
        public void Update()
        {
            bool pending_changes = _pending_queue.Count() > 0;

            _animation_is_active = _animation_is_active && pending_changes;

            _current_time = time;

            if (pending_changes == false)
            {
                return;
            }
            ChangeOperation op = _pending_queue.Last();

            if (_animation_is_active)
            {
                double past_animation_time_s = _current_time - _animation_start_time;
                if (past_animation_time_s < _animation_time_s)
                {
                    // get change information
                    int axis_i = (int)op.axis;
                    int row_i  = (int)op.row;
                    var cube_i = SubCubeIndices(op.axis, op.row);

                    // update the position model matrix of the affected sub cubes
                    foreach (var i in cube_i)
                    {
                        double angle = Radians(90.0) * (op.direction == RubiksGlobal.TDirection.left ? -1.0 : 1.0);
                        angle        *= past_animation_time_s / _animation_time_s;
                        _animation[i] = CreateRotate((float)angle, RubiksGlobal.AxisVector(axis_i));
                    }

                    // Update the final model matrices of the sub cubes
                    UpdateM44Cubes();

                    return;
                }
                _animation_is_active = false;
            }
            else if (pending_changes)
            {
                _animation_is_active  = true;
                _animation_start_time = time;
                return;
            }

            _pending_queue.RemoveAt(_pending_queue.Count() - 1);
            Rotate(op);

            // Update the final model matrices of the sub cubes
            UpdateM44Cubes();
        }
        /// <summary>
        /// Calculate the rotation of a part of the Rubik's cube.
        /// Compute the new positions of the sub cubes and calculate the model matrices.
        /// </summary>
        /// <param name="op">specifies the change operation</param>
        void Rotate(ChangeOperation op)
        {
            int axis_i = (int)op.axis;
            int row_i  = (int)op.row;
            var cube_i = SubCubeIndices(op.axis, op.row);

            // update the position model matrix of the affected sub cubes
            Matrix4 rot_mat = _c_rot_mat[axis_i * 2 + (op.direction == RubiksGlobal.TDirection.left ? 0 : 1)];

            foreach (var i in cube_i)
            {
                double angle = Radians(90.0) * (op.direction == RubiksGlobal.TDirection.left ? -1.0 : 1.0);
                rot_mat         = CreateRotate((float)angle, RubiksGlobal.AxisVector(axis_i));
                _current_pos[i] = _current_pos[i] * rot_mat;  // OpenTK `*`-operator is reversed
            }

            // Recalculate the index map of the cubes
            int[,] indices = { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 2, 1 }, { 2, 2 }, { 1, 2 }, { 0, 2 }, { 0, 1 } };
            int[] current_map = (int[])_cube_map.Clone();

            for (int i_o = 0; i_o < 8; ++i_o)
            {
                int j_n = (op.direction == RubiksGlobal.TDirection.left ? i_o + 6 : i_o + 2) % 8;

                int[] ao = { 0, 0, 0 };
                int[] an = { 0, 0, 0 };

                ao[axis_i]           = row_i;
                an[axis_i]           = row_i;
                ao[(axis_i + 1) % 3] = indices[i_o, 0];
                an[(axis_i + 1) % 3] = indices[j_n, 0];
                ao[(axis_i + 2) % 3] = indices[i_o, 1];
                an[(axis_i + 2) % 3] = indices[j_n, 1];

                int ci_o = ao[0] + ao[1] * 3 + ao[2] * 9;
                int ci_n = an[0] + an[1] * 3 + an[2] * 9;

                _cube_map[ci_n] = current_map[ci_o];
            }

            // reset animation matrices
            for (int i = 0; i < RubiksGlobal.NoOfCubes; ++i)
            {
                _animation[i] = Matrix4.Identity;
            }
        }
 public ChangeOperation(Vector3 rot_axis, int sub_cube_i)
 {
     _axis      = RubiksGlobal.Axis(rot_axis);
     _direction = RubiksGlobal.Direction(rot_axis);
     _row       = RubiksGlobal.Row(_axis, sub_cube_i);
 }