<commit_before>//              Implementation of the AAA algorithm
//
//   Here, we implement the triple-A algorithm [1] for the rational approximation
//   of complex-valued functions,
//
//          p(z)/q(z) ≈ f(z).
//
//   In this file, we always assume f(z) = z^{-α}. The triple-A algorithm
//   provides a robust, accurate approximation in rational barycentric form.
//   This representation must be transformed into a partial fraction
//   representation in order to be used to solve a spectral FPDE.
//
//   More specifically, we first expand the numerator in terms of the zeros of
//   the rational approximation,
//
//          p(z) ∝ Π_i (z - z_i),
//
//   and expand the denominator in terms of the poles of the rational
//   approximation,
//
//          q(z) ∝ Π_i (z - p_i).
//
//   We then use these zeros and poles to derive the partial fraction
//   expansion
//
//          f(z) ≈ p(z)/q(z) = Σ_i c_i / (z - p_i).
//
//
//   REFERENCE
//
//   [1] Nakatsukasa, Y., Sète, O., & Trefethen, L. N. (2018). The AAA
//       algorithm for rational approximation. SIAM Journal on Scientific
//       Computing, 40(3), A1494-A1522.

#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace std;
using namespace mfem;

/**
 * RationalApproximation_AAA : compute the rational approximation (RA) of
 * data @a val at the set of points @a pt.
 *
 *  INPUT:  @a val       = vector of data values
 *          @a pt        = vector of sample points
 *          @a tol       = relative tolerance
 *          @a max_order = maximum number of terms (order) of the RA
 *
 *  OUTPUT: @a z = support points of the RA in rational barycentric form
 *          @a f = data values at support points @a z
 *          @a w = weights of the RA in rational barycentric form
 *
 * See pg. A1501 of Nakatsukasa et al. [1].
 */
void RationalApproximation_AAA(const Vector &val, const Vector &pt,
                               Array<double> &z, Array<double> &f, Vector &w,
                               double tol, int max_order)
{

   // number of sample points
   int size = val.Size();
   MFEM_VERIFY(pt.Size() == size, "size mismatch");

   // Initializations
   Array<int> J(size);
   for (int i = 0; i < size; i++) { J[i] = i; }
   z.SetSize(0);
   f.SetSize(0);

   DenseMatrix C, Ctemp, A, Am;
   // auxiliary arrays and vectors
   Vector f_vec;
   Array<double> c_i;

   // mean of the value vector
   Vector R(val.Size());
   double mean_val = val.Sum()/size;

   for (int i = 0; i<R.Size(); i++) { R(i) = mean_val; }

   for (int k = 0; k < max_order; k++)
   {
      // select next support point
      int idx = 0;
      double tmp_max = 0;
      for (int j = 0; j < size; j++)
      {
         double tmp = abs(val(j)-R(j));
         if (tmp > tmp_max)
         {
            tmp_max = tmp;
            idx = j;
         }
      }

      // Append support points and data values
      z.Append(pt(idx));
      f.Append(val(idx));

      // Update index vector
      J.DeleteFirst(idx);

      // next column in Cauchy matrix
      Array<double> C_tmp(size);
      for (int j = 0; j < size; j++)
      {
         C_tmp[j] = 1.0/(pt(j)-pt(idx));
      }
      c_i.Append(C_tmp);
      int h_C = C_tmp.Size();
      int w_C = k+1;
      C.UseExternalData(c_i.GetData(),h_C,w_C);

      Ctemp = C;

      f_vec.SetDataAndSize(f.GetData(),f.Size());
      Ctemp.InvLeftScaling(val);
      Ctemp.RightScaling(f_vec);

      A.SetSize(C.Height(), C.Width());
      Add(C,Ctemp,-1.0,A);
      A.LeftScaling(val);

      int h_Am = J.Size();
      int w_Am = A.Width();
      Am.SetSize(h_Am,w_Am);
      for (int i = 0; i<h_Am; i++)
      {
         int ii = J[i];
         for (int j = 0; j<w_Am; j++)
         {
            Am(i,j) = A(ii,j);
         }
      }

#ifdef MFEM_USE_LAPACK
      DenseMatrixSVD svd(Am,false,true);
      svd.Eval(Am);
      DenseMatrix &v = svd.RightSingularvectors();
      v.GetRow(k,w);
#else
      mfem_error("Compiled without LAPACK");
#endif

      // N = C*(w.*f); D = C*w; % numerator and denominator
      Vector aux(w);
      aux *= f_vec;
      Vector N(C.Height()); // Numerator
      C.Mult(aux,N);
      Vector D(C.Height()); // Denominator
      C.Mult(w,D);

      R = val;
      for (int i = 0; i<J.Size(); i++)
      {
         int ii = J[i];
         R(ii) = N(ii)/D(ii);
      }

      Vector verr(val);
      verr-=R;

      if (verr.Normlinf() <= tol*val.Normlinf()) { break; }
   }
}

/**
 * ComputePolesAndZeros : computes the @a poles and @a zeros of the rational
 * rational function f(z) = C p(z)/q(z) from its ration barycentric form.
 *
 *  INPUT:  @a z = support points in rational barycentric form
 *          @a f = data values at support points @a z
 *          @a w = weights in rational barycentric form
 *
 *  OUTPUT: @a poles = array of poles (roots of p(z))
 *          @a zeros = array of zeros (roots of q(z))
 *          @a scale = scaling constant in f(z) = C p(z)/q(z)
 *
 * See pg. A1501 of Nakatsukasa et al. [1].
 */
void ComputePolesAndZeros(const Vector &z, const Vector &f, const Vector &w,
                          Array<double> & poles, Array<double> & zeros, double &scale)
{
   // Initialization
   poles.SetSize(0);
   zeros.SetSize(0);

   // Compute the poles
   int m = w.Size();
   DenseMatrix B(m+1); B = 0.;
   DenseMatrix E(m+1); E = 0.;
   for (int i = 1; i<=m; i++)
   {
      B(i,i) = 1.;
      E(0,i) = w(i-1);
      E(i,0) = 1.;
      E(i,i) = z(i-1);
   }

#ifdef MFEM_USE_LAPACK
   DenseMatrixGeneralizedEigensystem eig1(E,B);
   eig1.Eval();
   Vector & evalues = eig1.EigenvaluesRealPart();
   for (int i = 0; i<evalues.Size(); i++)
   {
      if (IsFinite(evalues(i)))
      {
         poles.Append(evalues(i));
      }
   }
#else
   mfem_error("Compiled without LAPACK");
#endif
   // compute the zeros
   B = 0.;
   E = 0.;
   for (int i = 1; i<=m; i++)
   {
      B(i,i) = 1.;
      E(0,i) = w(i-1) * f(i-1);
      E(i,0) = 1.;
      E(i,i) = z(i-1);
   }

#ifdef MFEM_USE_LAPACK
   DenseMatrixGeneralizedEigensystem eig2(E,B);
   eig2.Eval();
   evalues = eig2.EigenvaluesRealPart();
   for (int i = 0; i<evalues.Size(); i++)
   {
      if (IsFinite(evalues(i)))
      {
         zeros.Append(evalues(i));
      }
   }
#else
   mfem_error("Compiled without LAPACK");
#endif

   scale = w * f / w.Sum();
}

/**
 * PartialFractionExpansion : computes the partial fraction expansion
 * of the rational rational function f(z) = Σ_i c_i / (z - p_i) from its
 * @a poles and @a zeros.
 *
 *  INPUT:  @a poles = array of poles (same as p_i above)
 *          @a zeros = array of zeros
 *          @a scale = scaling constant
 *
 *  OUTPUT: @a coeffs = coefficients c_i
 *
 */
void PartialFractionExpansion(double scale, Array<double> & poles,
                              Array<double> & zeros, Array<double> & coeffs)
{
   int psize = poles.Size();
   int zsize = zeros.Size();
   coeffs.SetSize(psize);
   coeffs = scale;

   for (int i=0; i<psize; i++)
   {
      double tmp_numer=1.0;
      for (int j=0; j<zsize; j++)
      {
         tmp_numer *= poles[i]-zeros[j];
      }

      double tmp_denom=1.0;
      for (int k=0; k<psize; k++)
      {
         if (k != i) { tmp_denom *= poles[i]-poles[k]; }
      }
      coeffs[i] *= tmp_numer / tmp_denom;
   }
}

/**
 * ComputePartialFractionApproximation : computes a rational approximation (RA) of the function
 * f(z) = z^{-a}, 0 < a < 1, in partial fraction form f(z) ≈ Σ_i c_i / (z - p_i).
 *
 *  INPUT:  @a alpha     = exponent in f(z) = z^-a
 *          @a lmin, @a lmax, @a npoints
 *                       = f(z) is uniformly sampled @a npoints times on
 *                         the interval [ @a lmin, @a lmax]
 *          @a tol       = relative tolerance
 *          @a max_order = maximum number of terms (order) of the RA
 *
 *  OUTPUT: @a coeffs = coefficients c_i
 *          @a poles  = poles p_i
 *
 * See pg. A1501 of Nakatsukasa et al. [1].
 */
void ComputePartialFractionApproximation(double alpha,
                                         Array<double> & coeffs, Array<double> & poles,
                                         double lmin = 1., double lmax = 1000.,
                                         double tol=1e-10,  int npoints = 1000,
                                         int max_order = 100)
{
#ifndef MFEM_USE_LAPACK
   mfem::out
         << "MFEM is compiled without LAPACK. Using precomputed PartialFractionApproximation"
         << std::endl;

   MFEM_ASSERT(alpha < 1., "alpha must be less than 1");
   MFEM_ASSERT(alpha > 0., "alpha must be greater than 0");

   if (alpha == 0.33)
   {
      coeffs = Array<double> ({2002.55,  99.7691,  29.0575,  12.9842,
                               6.90263,  3.96688,  2.36013,  1.42565,
                               0.867623, 0.529436, 0.317975, 0.0891797});
      poles = Array<double> ({-47928.,  -3451.45, -996.636, -388.58,
                              -168.228, -75.3162, -33.6235, -14.5403,
                              -5.84563, -1.9975, -0.434608,  0.});
   }
   else if (alpha == 0.99)
   {
      coeffs = Array<double>({0.0292386, 0.0143338, 0.0109015, 0.00976958,
                              0.00943417, 0.00948077, 0.00985799, 0.010889,
                              0.0138588, 0.0263289, 0.96953});
      poles = Array<double> ({-10085.1, -1652.6, -524.342, -199.521,
                              -80.2328, -32.3926, -12.7148, -4.63855,
                              -1.39884, -0.221076, 0.});
   }
   else
   {
      if (alpha != 0.5)
      {
         mfem::out << "Using default value of alpha = 0.5" << std::endl;
      }
      coeffs = Array<double>({209.629, 24.2714, 9.24812, 4.93138,
                              3.02653, 1.98265, 1.34293, 0.931714,
                              0.664382, 0.492972, 0.39114, 0.177527});
      poles = Array<double>({-26466.7, -2673.76, -800.03, -312.646,
                             -134.551, -59.8651, -26.6607, -11.5603,
                             -4.67241, -1.59503, -0.332738, 0.});
   }
   return;
#endif

   Vector x(npoints);
   Vector val(npoints);
   for (int i = 0; i<npoints; i++)
   {
      x(i) = (double)i+1.0;
      val(i) = pow(x(i),1.-alpha);
   }

   // Apply triple-A algorithm to f(x) = x^{1-a}
   Array<double> z, f;
   Vector w;
   RationalApproximation_AAA(val,x,z,f,w,tol,max_order);

   Vector vecz, vecf;
   vecz.SetDataAndSize(z.GetData(), z.Size());
   vecf.SetDataAndSize(f.GetData(), f.Size());

   // Compute poles and zeros for RA of f(x) = x^{1-a}
   double scale;
   Array<double> zeros;
   ComputePolesAndZeros(vecz, vecf, w, poles, zeros, scale);

   // Introduce one extra pole at x=0, thus, delivering a
   // RA for f(x) = x^{-a}
   poles.Append(0.0);

   // Compute partial fraction approximation of f(x) = x^{-a}
   PartialFractionExpansion(scale, poles, zeros, coeffs);
}
<commit_msg>remove lmin and use lmax. Remove zero instead of add pole.<commit_after>//              Implementation of the AAA algorithm
//
//   Here, we implement the triple-A algorithm [1] for the rational approximation
//   of complex-valued functions,
//
//          p(z)/q(z) ≈ f(z).
//
//   In this file, we always assume f(z) = z^{-α}. The triple-A algorithm
//   provides a robust, accurate approximation in rational barycentric form.
//   This representation must be transformed into a partial fraction
//   representation in order to be used to solve a spectral FPDE.
//
//   More specifically, we first expand the numerator in terms of the zeros of
//   the rational approximation,
//
//          p(z) ∝ Π_i (z - z_i),
//
//   and expand the denominator in terms of the poles of the rational
//   approximation,
//
//          q(z) ∝ Π_i (z - p_i).
//
//   We then use these zeros and poles to derive the partial fraction
//   expansion
//
//          f(z) ≈ p(z)/q(z) = Σ_i c_i / (z - p_i).
//
//
//   REFERENCE
//
//   [1] Nakatsukasa, Y., Sète, O., & Trefethen, L. N. (2018). The AAA
//       algorithm for rational approximation. SIAM Journal on Scientific
//       Computing, 40(3), A1494-A1522.

#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace std;
using namespace mfem;

/**
 * RationalApproximation_AAA : compute the rational approximation (RA) of
 * data @a val at the set of points @a pt.
 *
 *  INPUT:  @a val       = vector of data values
 *          @a pt        = vector of sample points
 *          @a tol       = relative tolerance
 *          @a max_order = maximum number of terms (order) of the RA
 *
 *  OUTPUT: @a z = support points of the RA in rational barycentric form
 *          @a f = data values at support points @a z
 *          @a w = weights of the RA in rational barycentric form
 *
 * See pg. A1501 of Nakatsukasa et al. [1].
 */
void RationalApproximation_AAA(const Vector &val, const Vector &pt,
                               Array<double> &z, Array<double> &f, Vector &w,
                               double tol, int max_order)
{

   // number of sample points
   int size = val.Size();
   MFEM_VERIFY(pt.Size() == size, "size mismatch");

   // Initializations
   Array<int> J(size);
   for (int i = 0; i < size; i++) { J[i] = i; }
   z.SetSize(0);
   f.SetSize(0);

   DenseMatrix C, Ctemp, A, Am;
   // auxiliary arrays and vectors
   Vector f_vec;
   Array<double> c_i;

   // mean of the value vector
   Vector R(val.Size());
   double mean_val = val.Sum()/size;

   for (int i = 0; i<R.Size(); i++) { R(i) = mean_val; }

   for (int k = 0; k < max_order; k++)
   {
      // select next support point
      int idx = 0;
      double tmp_max = 0;
      for (int j = 0; j < size; j++)
      {
         double tmp = abs(val(j)-R(j));
         if (tmp > tmp_max)
         {
            tmp_max = tmp;
            idx = j;
         }
      }

      // Append support points and data values
      z.Append(pt(idx));
      f.Append(val(idx));

      // Update index vector
      J.DeleteFirst(idx);

      // next column in Cauchy matrix
      Array<double> C_tmp(size);
      for (int j = 0; j < size; j++)
      {
         C_tmp[j] = 1.0/(pt(j)-pt(idx));
      }
      c_i.Append(C_tmp);
      int h_C = C_tmp.Size();
      int w_C = k+1;
      C.UseExternalData(c_i.GetData(),h_C,w_C);

      Ctemp = C;

      f_vec.SetDataAndSize(f.GetData(),f.Size());
      Ctemp.InvLeftScaling(val);
      Ctemp.RightScaling(f_vec);

      A.SetSize(C.Height(), C.Width());
      Add(C,Ctemp,-1.0,A);
      A.LeftScaling(val);

      int h_Am = J.Size();
      int w_Am = A.Width();
      Am.SetSize(h_Am,w_Am);
      for (int i = 0; i<h_Am; i++)
      {
         int ii = J[i];
         for (int j = 0; j<w_Am; j++)
         {
            Am(i,j) = A(ii,j);
         }
      }

#ifdef MFEM_USE_LAPACK
      DenseMatrixSVD svd(Am,false,true);
      svd.Eval(Am);
      DenseMatrix &v = svd.RightSingularvectors();
      v.GetRow(k,w);
#else
      mfem_error("Compiled without LAPACK");
#endif

      // N = C*(w.*f); D = C*w; % numerator and denominator
      Vector aux(w);
      aux *= f_vec;
      Vector N(C.Height()); // Numerator
      C.Mult(aux,N);
      Vector D(C.Height()); // Denominator
      C.Mult(w,D);

      R = val;
      for (int i = 0; i<J.Size(); i++)
      {
         int ii = J[i];
         R(ii) = N(ii)/D(ii);
      }

      Vector verr(val);
      verr-=R;

      if (verr.Normlinf() <= tol*val.Normlinf()) { break; }
   }
}

/**
 * ComputePolesAndZeros : computes the @a poles and @a zeros of the rational
 * rational function f(z) = C p(z)/q(z) from its ration barycentric form.
 *
 *  INPUT:  @a z = support points in rational barycentric form
 *          @a f = data values at support points @a z
 *          @a w = weights in rational barycentric form
 *
 *  OUTPUT: @a poles = array of poles (roots of p(z))
 *          @a zeros = array of zeros (roots of q(z))
 *          @a scale = scaling constant in f(z) = C p(z)/q(z)
 *
 * See pg. A1501 of Nakatsukasa et al. [1].
 */
void ComputePolesAndZeros(const Vector &z, const Vector &f, const Vector &w,
                          Array<double> & poles, Array<double> & zeros, double &scale)
{
   // Initialization
   poles.SetSize(0);
   zeros.SetSize(0);

   // Compute the poles
   int m = w.Size();
   DenseMatrix B(m+1); B = 0.;
   DenseMatrix E(m+1); E = 0.;
   for (int i = 1; i<=m; i++)
   {
      B(i,i) = 1.;
      E(0,i) = w(i-1);
      E(i,0) = 1.;
      E(i,i) = z(i-1);
   }

#ifdef MFEM_USE_LAPACK
   DenseMatrixGeneralizedEigensystem eig1(E,B);
   eig1.Eval();
   Vector & evalues = eig1.EigenvaluesRealPart();
   for (int i = 0; i<evalues.Size(); i++)
   {
      if (IsFinite(evalues(i)))
      {
         poles.Append(evalues(i));
      }
   }
#else
   mfem_error("Compiled without LAPACK");
#endif
   // compute the zeros
   B = 0.;
   E = 0.;
   for (int i = 1; i<=m; i++)
   {
      B(i,i) = 1.;
      E(0,i) = w(i-1) * f(i-1);
      E(i,0) = 1.;
      E(i,i) = z(i-1);
   }

#ifdef MFEM_USE_LAPACK
   DenseMatrixGeneralizedEigensystem eig2(E,B);
   eig2.Eval();
   evalues = eig2.EigenvaluesRealPart();
   for (int i = 0; i<evalues.Size(); i++)
   {
      if (IsFinite(evalues(i)))
      {
         zeros.Append(evalues(i));
      }
   }
#else
   mfem_error("Compiled without LAPACK");
#endif

   scale = w * f / w.Sum();
}

/**
 * PartialFractionExpansion : computes the partial fraction expansion
 * of the rational rational function f(z) = Σ_i c_i / (z - p_i) from its
 * @a poles and @a zeros.
 *
 *  INPUT:  @a poles = array of poles (same as p_i above)
 *          @a zeros = array of zeros
 *          @a scale = scaling constant
 *
 *  OUTPUT: @a coeffs = coefficients c_i
 *
 */
void PartialFractionExpansion(double scale, Array<double> & poles,
                              Array<double> & zeros, Array<double> & coeffs)
{
   int psize = poles.Size();
   int zsize = zeros.Size();
   coeffs.SetSize(psize);
   coeffs = scale;

   for (int i=0; i<psize; i++)
   {
      double tmp_numer=1.0;
      for (int j=0; j<zsize; j++)
      {
         tmp_numer *= poles[i]-zeros[j];
      }

      double tmp_denom=1.0;
      for (int k=0; k<psize; k++)
      {
         if (k != i) { tmp_denom *= poles[i]-poles[k]; }
      }
      coeffs[i] *= tmp_numer / tmp_denom;
   }
}

/**
 * ComputePartialFractionApproximation : computes a rational approximation (RA) in partial fraction
 * form, e.g., f(z) ≈ Σ_i c_i / (z - p_i), from sampled values of the function f(z) = z^{-a}, 0 < a < 1.
 *
 *  INPUT:  @a alpha     = exponent a in f(z) = z^-a
 *          @a lmax, @a npoints
 *                       = f(z) is uniformly sampled @a npoints times in the interval [ 0, @a lmax ]
 *          @a tol       = relative tolerance
 *          @a max_order = maximum number of terms (order) of the RA
 *
 *  OUTPUT: @a coeffs = coefficients c_i
 *          @a poles  = poles p_i
 *
 *
 *  NOTES:  When LAPACK is not compiled, only @a alpha = 0.33, 0.5, and 0.99 are possible.
 *          In this case, if @a alpha != 0.33 and @a alpha != 0.99, then @a alpha = 0.5 is used
 *          by default.
 *
 * See pg. A1501 of Nakatsukasa et al. [1].
 */
void ComputePartialFractionApproximation(double alpha,
                                         Array<double> & coeffs, Array<double> & poles,
                                         double lmax = 1000.,
                                         double tol=1e-10, int npoints = 1000,
                                         int max_order = 100)
{
   MFEM_VERIFY(alpha < 1., "alpha must be less than 1");
   MFEM_VERIFY(alpha > 0., "alpha must be greater than 0");
   MFEM_VERIFY(npoints > 2, "npoints must be greater than 2");
   MFEM_VERIFY(lmax > 0,  "lmin must be greater than 0");
   MFEM_VERIFY(tol > 0,  "tol must be greater than 0");

#ifndef MFEM_USE_LAPACK
   mfem::out
         << "MFEM is compiled without LAPACK. Using precomputed values for PartialFractionApproximation"
         << std::endl;

   if (alpha == 0.33)
   {
      coeffs = Array<double> ({2002.55,  99.7691,  29.0575,  12.9842,
                               6.90263,  3.96688,  2.36013,  1.42565,
                               0.867623, 0.529436, 0.317975, 0.0891797});
      poles = Array<double> ({-47928.,  -3451.45, -996.636, -388.58,
                              -168.228, -75.3162, -33.6235, -14.5403,
                              -5.84563, -1.9975, -0.434608,  0.});
   }
   else if (alpha == 0.99)
   {
      coeffs = Array<double>({0.0292386, 0.0143338, 0.0109015, 0.00976958,
                              0.00943417, 0.00948077, 0.00985799, 0.010889,
                              0.0138588, 0.0263289, 0.96953});
      poles = Array<double> ({-10085.1, -1652.6, -524.342, -199.521,
                              -80.2328, -32.3926, -12.7148, -4.63855,
                              -1.39884, -0.221076, 0.});
   }
   else
   {
      if (alpha != 0.5)
      {
         mfem::out << "Using default value of alpha = 0.5" << std::endl;
      }
      coeffs = Array<double>({209.629, 24.2714, 9.24812, 4.93138,
                              3.02653, 1.98265, 1.34293, 0.931714,
                              0.664382, 0.492972, 0.39114, 0.177527});
      poles = Array<double>({-26466.7, -2673.76, -800.03, -312.646,
                             -134.551, -59.8651, -26.6607, -11.5603,
                             -4.67241, -1.59503, -0.332738, 0.});
   }
   return;
#endif

   Vector x(npoints);
   Vector val(npoints);
   double dx = lmax / (double)(npoints-1);
   for (int i = 0; i<npoints; i++)
   {
      x(i) = dx * (double)i;
      val(i) = pow(x(i),1.-alpha);
   }

   // Apply triple-A algorithm to f(x) = x^{1-a}
   Array<double> z, f;
   Vector w;
   RationalApproximation_AAA(val,x,z,f,w,tol,max_order);

   Vector vecz, vecf;
   vecz.SetDataAndSize(z.GetData(), z.Size());
   vecf.SetDataAndSize(f.GetData(), f.Size());

   // Compute poles and zeros for RA of f(x) = x^{1-a}
   double scale;
   Array<double> zeros;
   ComputePolesAndZeros(vecz, vecf, w, poles, zeros, scale);

   // Remove the zero at x=0, thus, delivering a RA for f(x) = x^{-a}
   zeros.DeleteFirst(0.0);

   // Compute partial fraction approximation of f(x) = x^{-a}
   PartialFractionExpansion(scale, poles, zeros, coeffs);
}<|endoftext|>


<commit_before>//                                MFEM Example 1
//
// Compile with: make ex1
//
// Sample runs:  ex1 -m ../data/square-disc.mesh
//               ex1 -m ../data/star.mesh
//               ex1 -m ../data/star-mixed.mesh
//               ex1 -m ../data/escher.mesh
//               ex1 -m ../data/fichera.mesh
//               ex1 -m ../data/fichera-mixed.mesh
//               ex1 -m ../data/toroid-wedge.mesh
//               ex1 -m ../data/square-disc-p2.vtk -o 2
//               ex1 -m ../data/square-disc-p3.mesh -o 3
//               ex1 -m ../data/square-disc-nurbs.mesh -o -1
//               ex1 -m ../data/star-mixed-p2.mesh -o 2
//               ex1 -m ../data/disc-nurbs.mesh -o -1
//               ex1 -m ../data/pipe-nurbs.mesh -o -1
//               ex1 -m ../data/fichera-mixed-p2.mesh -o 2
//               ex1 -m ../data/star-surf.mesh
//               ex1 -m ../data/square-disc-surf.mesh
//               ex1 -m ../data/inline-segment.mesh
//               ex1 -m ../data/amr-quad.mesh
//               ex1 -m ../data/amr-hex.mesh
//               ex1 -m ../data/fichera-amr.mesh
//               ex1 -m ../data/mobius-strip.mesh
//               ex1 -m ../data/mobius-strip.mesh -o -1 -sc
//
// Device sample runs:
//               ex1 -pa -d cuda
//               ex1 -pa -d raja-cuda
//               ex1 -pa -d occa-cuda
//               ex1 -pa -d raja-omp
//               ex1 -pa -d occa-omp
//               ex1 -pa -d ceed-cpu
//               ex1 -pa -d ceed-cuda
//               ex1 -m ../data/beam-hex.mesh -pa -d cuda
//               ex1 -m ../data/beam-tet.mesh -pa -d ceed-cpu
//
// Description:  This example code demonstrates the use of MFEM to define a
//               simple finite element discretization of the Laplace problem
//               -Delta u = 1 with homogeneous Dirichlet boundary conditions.
//               Specifically, we discretize using a FE space of the specified
//               order, or if order < 1 using an isoparametric/isogeometric
//               space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
//               NURBS mesh, etc.)
//
//               The example highlights the use of mesh refinement, finite
//               element grid functions, as well as linear and bilinear forms
//               corresponding to the left-hand side and right-hand side of the
//               discrete linear system. We also cover the explicit elimination
//               of essential boundary conditions, static condensation, and the
//               optional connection to the GLVis tool for visualization.

#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace std;
using namespace mfem;

int main(int argc, char *argv[])
{
   // 1. Parse command-line options.
   const char *mesh_file = "../data/star.mesh";
   int order = 1;
   bool static_cond = false;
   bool pa = false;
   const char *device_config = "cpu";
   bool visualization = true;

   OptionsParser args(argc, argv);
   args.AddOption(&mesh_file, "-m", "--mesh",
                  "Mesh file to use.");
   args.AddOption(&order, "-o", "--order",
                  "Finite element order (polynomial degree) or -1 for"
                  " isoparametric space.");
   args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
                  "--no-static-condensation", "Enable static condensation.");
   args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
                  "--no-partial-assembly", "Enable Partial Assembly.");
   args.AddOption(&device_config, "-d", "--device",
                  "Device configuration string, see Device::Configure().");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
   args.Parse();
   if (!args.Good())
   {
      args.PrintUsage(cout);
      return 1;
   }
   args.PrintOptions(cout);

   // 2. Enable hardware devices such as GPUs, and programming models such as
   //    CUDA, OCCA, RAJA and OpenMP based on command line options.
   Device device(device_config);
   device.Print();

   // 3. Read the mesh from the given mesh file. We can handle triangular,
   //    quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
   //    the same code.
   Mesh *mesh = new Mesh(mesh_file, 1, 1);
   int dim = mesh->Dimension();

   // 4. Refine the mesh to increase the resolution. In this example we do
   //    'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
   //    largest number that gives a final mesh with no more than 50,000
   //    elements.
   {
      int ref_levels =
         (int)floor(log(50000./mesh->GetNE())/log(2.)/dim);
      for (int l = 0; l < ref_levels; l++)
      {
         mesh->UniformRefinement();
      }
   }

   // 5. Define a finite element space on the mesh. Here we use continuous
   //    Lagrange finite elements of the specified order. If order < 1, we
   //    instead use an isoparametric/isogeometric space.
   FiniteElementCollection *fec;
   if (order > 0)
   {
      fec = new H1_FECollection(order, dim);
   }
   else if (mesh->GetNodes())
   {
      fec = mesh->GetNodes()->OwnFEC();
      cout << "Using isoparametric FEs: " << fec->Name() << endl;
   }
   else
   {
      fec = new H1_FECollection(order = 1, dim);
   }
   FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
   cout << "Number of finite element unknowns: "
        << fespace->GetTrueVSize() << endl;

   // 6. Determine the list of true (i.e. conforming) essential boundary dofs.
   //    In this example, the boundary conditions are defined by marking all
   //    the boundary attributes from the mesh as essential (Dirichlet) and
   //    converting them to a list of true dofs.
   Array<int> ess_tdof_list;
   if (mesh->bdr_attributes.Size())
   {
      Array<int> ess_bdr(mesh->bdr_attributes.Max());
      ess_bdr = 1;
      fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
   }

   // 7. Set up the linear form b(.) which corresponds to the right-hand side of
   //    the FEM linear system, which in this case is (1,phi_i) where phi_i are
   //    the basis functions in the finite element fespace.
   LinearForm *b = new LinearForm(fespace);
   ConstantCoefficient one(1.0);
   b->AddDomainIntegrator(new DomainLFIntegrator(one));
   b->Assemble();

   // 8. Define the solution vector x as a finite element grid function
   //    corresponding to fespace. Initialize x with initial guess of zero,
   //    which satisfies the boundary conditions.
   GridFunction x(fespace);
   x = 0.0;

   // 9. Set up the bilinear form a(.,.) on the finite element space
   //    corresponding to the Laplacian operator -Delta, by adding the Diffusion
   //    domain integrator.
   BilinearForm *a = new BilinearForm(fespace);
   if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
   a->AddDomainIntegrator(new DiffusionIntegrator(one));

   // 10. Assemble the bilinear form and the corresponding linear system,
   //     applying any necessary transformations such as: eliminating boundary
   //     conditions, applying conforming constraints for non-conforming AMR,
   //     static condensation, etc.
   if (static_cond) { a->EnableStaticCondensation(); }
   a->Assemble();

   OperatorPtr A;
   Vector B, X;
   a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);

   cout << "Size of linear system: " << A->Height() << endl;

   // 11. Solve the linear system A X = B.
   if (!pa)
   {
#ifndef MFEM_USE_SUITESPARSE
      // Use a simple symmetric Gauss-Seidel preconditioner with PCG.
      GSSmoother M((SparseMatrix&)(*A));
      PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
#else
      // If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
      UMFPackSolver umf_solver;
      umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
      umf_solver.SetOperator(*A);
      umf_solver.Mult(B, X);
#endif
   }
   else // Jacobi preconditioning in partial assembly mode
   {
      if (UsesTensorBasis(*fespace))
      {
         OperatorJacobiSmoother M(*a, ess_tdof_list);
         PCG(*A, M, B, X, 1, 400, 1e-12, 0.0);
      }
      else
      {
         CG(*A, B, X, 1, 400, 1e-12, 0.0);
      }
   }

   // 12. Recover the solution as a finite element grid function.
   a->RecoverFEMSolution(X, *b, x);

   // 13. Save the refined mesh and the solution. This output can be viewed later
   //     using GLVis: "glvis -m refined.mesh -g sol.gf".
   ofstream mesh_ofs("refined.mesh");
   mesh_ofs.precision(8);
   mesh->Print(mesh_ofs);
   ofstream sol_ofs("sol.gf");
   sol_ofs.precision(8);
   x.Save(sol_ofs);

   // 14. Send the solution by socket to a GLVis server.
   if (visualization)
   {
      char vishost[] = "localhost";
      int  visport   = 19916;
      socketstream sol_sock(vishost, visport);
      sol_sock.precision(8);
      sol_sock << "solution\n" << *mesh << x << flush;
   }

   // 15. Free the used memory.
   delete a;
   delete b;
   delete fespace;
   if (order > 0) { delete fec; }
   delete mesh;

   return 0;
}
<commit_msg>Modify ex1 to work Element Assembly.<commit_after>//                                MFEM Example 1
//
// Compile with: make ex1
//
// Sample runs:  ex1 -m ../data/square-disc.mesh
//               ex1 -m ../data/star.mesh
//               ex1 -m ../data/star-mixed.mesh
//               ex1 -m ../data/escher.mesh
//               ex1 -m ../data/fichera.mesh
//               ex1 -m ../data/fichera-mixed.mesh
//               ex1 -m ../data/toroid-wedge.mesh
//               ex1 -m ../data/square-disc-p2.vtk -o 2
//               ex1 -m ../data/square-disc-p3.mesh -o 3
//               ex1 -m ../data/square-disc-nurbs.mesh -o -1
//               ex1 -m ../data/star-mixed-p2.mesh -o 2
//               ex1 -m ../data/disc-nurbs.mesh -o -1
//               ex1 -m ../data/pipe-nurbs.mesh -o -1
//               ex1 -m ../data/fichera-mixed-p2.mesh -o 2
//               ex1 -m ../data/star-surf.mesh
//               ex1 -m ../data/square-disc-surf.mesh
//               ex1 -m ../data/inline-segment.mesh
//               ex1 -m ../data/amr-quad.mesh
//               ex1 -m ../data/amr-hex.mesh
//               ex1 -m ../data/fichera-amr.mesh
//               ex1 -m ../data/mobius-strip.mesh
//               ex1 -m ../data/mobius-strip.mesh -o -1 -sc
//
// Device sample runs:
//               ex1 -pa -d cuda
//               ex1 -pa -d raja-cuda
//               ex1 -pa -d occa-cuda
//               ex1 -pa -d raja-omp
//               ex1 -pa -d occa-omp
//               ex1 -pa -d ceed-cpu
//               ex1 -pa -d ceed-cuda
//               ex1 -m ../data/beam-hex.mesh -pa -d cuda
//               ex1 -m ../data/beam-tet.mesh -pa -d ceed-cpu
//
// Description:  This example code demonstrates the use of MFEM to define a
//               simple finite element discretization of the Laplace problem
//               -Delta u = 1 with homogeneous Dirichlet boundary conditions.
//               Specifically, we discretize using a FE space of the specified
//               order, or if order < 1 using an isoparametric/isogeometric
//               space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
//               NURBS mesh, etc.)
//
//               The example highlights the use of mesh refinement, finite
//               element grid functions, as well as linear and bilinear forms
//               corresponding to the left-hand side and right-hand side of the
//               discrete linear system. We also cover the explicit elimination
//               of essential boundary conditions, static condensation, and the
//               optional connection to the GLVis tool for visualization.

#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace std;
using namespace mfem;

int main(int argc, char *argv[])
{
   // 1. Parse command-line options.
   const char *mesh_file = "../data/star.mesh";
   int order = 1;
   bool static_cond = false;
   bool pa = false;
   bool ea = false;
   const char *device_config = "cpu";
   bool visualization = true;

   OptionsParser args(argc, argv);
   args.AddOption(&mesh_file, "-m", "--mesh",
                  "Mesh file to use.");
   args.AddOption(&order, "-o", "--order",
                  "Finite element order (polynomial degree) or -1 for"
                  " isoparametric space.");
   args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
                  "--no-static-condensation", "Enable static condensation.");
   args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
                  "--no-partial-assembly", "Enable Partial Assembly.");
   args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
                  "--no-element-assembly", "Enable Element Assembly.");
   args.AddOption(&device_config, "-d", "--device",
                  "Device configuration string, see Device::Configure().");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
   args.Parse();
   if (!args.Good())
   {
      args.PrintUsage(cout);
      return 1;
   }
   args.PrintOptions(cout);

   // 2. Enable hardware devices such as GPUs, and programming models such as
   //    CUDA, OCCA, RAJA and OpenMP based on command line options.
   Device device(device_config);
   device.Print();

   // 3. Read the mesh from the given mesh file. We can handle triangular,
   //    quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
   //    the same code.
   Mesh *mesh = new Mesh(mesh_file, 1, 1);
   int dim = mesh->Dimension();

   // 4. Refine the mesh to increase the resolution. In this example we do
   //    'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
   //    largest number that gives a final mesh with no more than 50,000
   //    elements.
   {
      int ref_levels =
         (int)floor(log(50000./mesh->GetNE())/log(2.)/dim);
      for (int l = 0; l < ref_levels; l++)
      {
         mesh->UniformRefinement();
      }
   }

   // 5. Define a finite element space on the mesh. Here we use continuous
   //    Lagrange finite elements of the specified order. If order < 1, we
   //    instead use an isoparametric/isogeometric space.
   FiniteElementCollection *fec;
   if (order > 0)
   {
      fec = new H1_FECollection(order, dim);
   }
   else if (mesh->GetNodes())
   {
      fec = mesh->GetNodes()->OwnFEC();
      cout << "Using isoparametric FEs: " << fec->Name() << endl;
   }
   else
   {
      fec = new H1_FECollection(order = 1, dim);
   }
   FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
   cout << "Number of finite element unknowns: "
        << fespace->GetTrueVSize() << endl;

   // 6. Determine the list of true (i.e. conforming) essential boundary dofs.
   //    In this example, the boundary conditions are defined by marking all
   //    the boundary attributes from the mesh as essential (Dirichlet) and
   //    converting them to a list of true dofs.
   Array<int> ess_tdof_list;
   if (mesh->bdr_attributes.Size())
   {
      Array<int> ess_bdr(mesh->bdr_attributes.Max());
      ess_bdr = 1;
      fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
   }

   // 7. Set up the linear form b(.) which corresponds to the right-hand side of
   //    the FEM linear system, which in this case is (1,phi_i) where phi_i are
   //    the basis functions in the finite element fespace.
   LinearForm *b = new LinearForm(fespace);
   ConstantCoefficient one(1.0);
   b->AddDomainIntegrator(new DomainLFIntegrator(one));
   b->Assemble();

   // 8. Define the solution vector x as a finite element grid function
   //    corresponding to fespace. Initialize x with initial guess of zero,
   //    which satisfies the boundary conditions.
   GridFunction x(fespace);
   x = 0.0;

   // 9. Set up the bilinear form a(.,.) on the finite element space
   //    corresponding to the Laplacian operator -Delta, by adding the Diffusion
   //    domain integrator.
   BilinearForm *a = new BilinearForm(fespace);
   if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
   else if (ea) { a->SetAssemblyLevel(AssemblyLevel::ELEMENT); }
   a->AddDomainIntegrator(new DiffusionIntegrator(one));

   // 10. Assemble the bilinear form and the corresponding linear system,
   //     applying any necessary transformations such as: eliminating boundary
   //     conditions, applying conforming constraints for non-conforming AMR,
   //     static condensation, etc.
   if (static_cond) { a->EnableStaticCondensation(); }
   a->Assemble();

   OperatorPtr A;
   Vector B, X;
   a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);

   cout << "Size of linear system: " << A->Height() << endl;

   // 11. Solve the linear system A X = B.
   if (!pa && !ea)
   {
#ifndef MFEM_USE_SUITESPARSE
      // Use a simple symmetric Gauss-Seidel preconditioner with PCG.
      GSSmoother M((SparseMatrix&)(*A));
      PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
#else
      // If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
      UMFPackSolver umf_solver;
      umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
      umf_solver.SetOperator(*A);
      umf_solver.Mult(B, X);
#endif
   }
   else // Jacobi preconditioning in partial assembly mode
   {
      if (UsesTensorBasis(*fespace))
      {
         OperatorJacobiSmoother M(*a, ess_tdof_list);
         PCG(*A, M, B, X, 1, 400, 1e-12, 0.0);
      }
      else
      {
         CG(*A, B, X, 1, 400, 1e-12, 0.0);
      }
   }

   // 12. Recover the solution as a finite element grid function.
   a->RecoverFEMSolution(X, *b, x);

   // 13. Save the refined mesh and the solution. This output can be viewed later
   //     using GLVis: "glvis -m refined.mesh -g sol.gf".
   ofstream mesh_ofs("refined.mesh");
   mesh_ofs.precision(8);
   mesh->Print(mesh_ofs);
   ofstream sol_ofs("sol.gf");
   sol_ofs.precision(8);
   x.Save(sol_ofs);

   // 14. Send the solution by socket to a GLVis server.
   if (visualization)
   {
      char vishost[] = "localhost";
      int  visport   = 19916;
      socketstream sol_sock(vishost, visport);
      sol_sock.precision(8);
      sol_sock << "solution\n" << *mesh << x << flush;
   }

   // 15. Free the used memory.
   delete a;
   delete b;
   delete fespace;
   if (order > 0) { delete fec; }
   delete mesh;

   return 0;
}
<|endoftext|>


<commit_before>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (C) 2009 Sacha Schutz <istdasklar@free.fr>
 * Copyright (C) 2009-2011 Guillaume Martres <smarter@ubuntu.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "sound.h"

#include "engine.h"

#include <sndfile.h>

#include <core/debughelper.h>
#include "alure/include/AL/alure.h"
#include <sndfile.h>

using namespace GluonAudio;

class Sound::SoundPrivate
{
    public:
        SoundPrivate()
        {
            init();
        }
        ~SoundPrivate()
        {
        }

        bool newError()
        {
            QLatin1String error = QLatin1String(alureGetErrorString());
            if (error != QLatin1String("No error")) {
                DEBUG_BLOCK
                DEBUG_TEXT2("Alure error: %1", error)
                isValid = false;
                return true;
            }
            return false;
        }

        void init()
        {
            isValid = true;
            isStreamed = false;
            isPaused = false;
            isStopped = true;
            isLooping = false;
            stream = 0;
            source = 0;
            position = QVector3D( 0, 0, 0 );
            volume = 1.0f;
            pitch = 1.0f;
            radius = 10000.0f;

        }

        bool setupSource()
        {
            if (path.isEmpty()) {
                return false;
            }

            alGenSources( 1, &source );
            if (source == 0) {
                DEBUG_BLOCK
                DEBUG_TEXT2("Empty source, OpenAL error: %1", alGetError())
                return false;
            }

            ALfloat sourcePosition[] = { position.x(), position.y() , position.z() };
            alSourcefv( source, AL_POSITION, sourcePosition );
            alSourcef( source, AL_GAIN, volume );
            alSourcef( source, AL_PITCH, pitch );
            alSourcef( source, AL_REFERENCE_DISTANCE, radius );
        }

        void _k_deleteSource()
        {
            if (source != 0) {
                alureStopSource(source, false);
                alDeleteSources(1, &source);
                source = 0;
            }
            isValid = false;
            isStopped = true;
            if (isStreamed) {
                alureDestroyStream(stream, 0, 0);
            }
            stream = 0;
        }

        QString path;

        bool isValid;
        bool isStreamed;
        bool isPaused;
        bool isStopped;
        bool isLooping;
        alureStream* stream;
        ALuint source;
        QVector3D position;
        ALfloat volume;
        ALfloat pitch;
        ALfloat radius;
};

Sound::Sound()
    : QObject( Engine::instance() )
    , d( new SoundPrivate )
{
    d->isValid = false;
}

Sound::Sound(const QString& fileName)
    : QObject( Engine::instance() )
    , d( new SoundPrivate )
{
    loadFile(fileName);
}

Sound::Sound(const QString& fileName, bool toStream)
    : QObject( Engine::instance() )
    , d( new SoundPrivate )
{
    loadFile(fileName, toStream);
}

Sound::~Sound()
{
    d->_k_deleteSource();
    delete d;
}

bool Sound::isValid() const
{
    return d->isValid;
}

bool Sound::loadFile(const QString &fileName)
{
    SF_INFO sfinfo;
    memset(&sfinfo, 0, sizeof(sfinfo));
    SNDFILE *sndfile = sf_open(fileName.toLocal8Bit().data(), SFM_READ, &sfinfo);
    sf_close(sndfile);

    double duration = (double)sfinfo.frames / sfinfo.samplerate;
    bool toStream = ( duration >= (double)(Engine::instance()->bufferLength()*Engine::instance()->buffersPerStream())/1e6 );

    return loadFile(fileName, toStream);
}

bool Sound::loadFile(const QString& fileName, bool toStream)
{
    if (fileName.isEmpty()) {
        d->isValid = false;
        return false;
    }
    if (!d->path.isEmpty()) {
        d->_k_deleteSource();
    }
    d->path = fileName;
    d->setupSource();
    d->isStreamed = toStream;
    if (d->isStreamed) {
        alureStreamSizeIsMicroSec(true);
        d->stream = alureCreateStreamFromFile(d->path.toLocal8Bit().constData(), Engine::instance()->bufferLength(), 0, 0);
    } else {
        ALuint buffer = Engine::instance()->genBuffer(d->path);
        if ( d->newError() ) {
            d->isValid = false;
            return false;
        }
        alSourcei(d->source, AL_BUFFER, buffer);
    }

    d->isValid = !d->newError();
    return d->isValid;
}

ALfloat Sound::elapsedTime() const
{
    ALfloat seconds = 0.f;
    alGetSourcef( d->source, AL_SEC_OFFSET, &seconds );
    return seconds;
}

ALint Sound::status() const
{
    ALint status;
    alGetSourcei( d->source, AL_SOURCE_STATE, &status );
    return status;
}

bool Sound::isLooping() const
{
    return d->isLooping;
}

bool Sound::isPlaying() const
{
    return !d->isStopped;
}

void Sound::setLoop( bool enabled )
{
    d->isLooping = enabled;
    if (!d->isStreamed) {
        alSourcei( d->source, AL_LOOPING, enabled );
    }
}

void Sound::clear()
{
    DEBUG_BLOCK
    DEBUG_TEXT("Clear")
    d->_k_deleteSource();
}

QVector3D Sound::position() const
{
    return d->position;
}

ALfloat Sound::x() const
{
    return d->position.x();
}

ALfloat Sound::y() const
{
    return d->position.y();
}

ALfloat Sound::z() const
{
    return d->position.z();
}

ALfloat Sound::volume() const
{
    return d->volume;
}

ALfloat Sound::pitch() const
{
    return d->pitch;
}

ALfloat Sound::radius() const
{
    return d->radius;
}

void Sound::setPosition( ALfloat x, ALfloat y, ALfloat z )
{
    setPosition( QVector3D(x, y, z) );
}

void Sound::setPosition( QVector3D position )
{
    d->position = position;
    ALfloat sourcePosition[] = { position.x(), position.y() , position.z() };
    alSourcefv( d->source, AL_POSITION, sourcePosition );
}

void Sound::setVolume( ALfloat volume )
{
    d->volume = volume;
    alSourcef( d->source, AL_GAIN, volume );
}

void Sound::setPitch( ALfloat pitch )
{
    d->pitch = pitch;
    alSourcef( d->source, AL_PITCH, pitch );
}

void Sound::setRadius( ALfloat radius )
{
    d->radius = radius;
    alSourcef( d->source, AL_REFERENCE_DISTANCE, radius );
}

void callbackStopped(void *object, ALuint source)
{
    static_cast<GluonAudio::Sound*>(object)->stopped();
}

void Sound::stopped()
{
    d->isStopped = true;
    if (d->isLooping) {
        play();
    }
}

void Sound::play()
{
    if (d->isPaused) {
         alureResumeSource(d->source);
    }

    if (!d->isStopped) {
        stop();
    }

    if (d->isStreamed) {
        int loopCount = (d->isLooping ? -1 : 0);
        alurePlaySourceStream(d->source, d->stream, Engine::instance()->buffersPerStream(), loopCount, callbackStopped, this);
    } else {
        alurePlaySource(d->source, callbackStopped, this);
    }
    d->isStopped = false;
    d->newError();
}

void Sound::pause()
{
    alurePauseSource(d->source);
    d->isPaused = true;
    d->newError();
}

void Sound::stop()
{
    alureStopSource(d->source, false);
    d->isStopped = true;
    d->newError();
}

void Sound::rewind()
{
    if (d->isStreamed) {
        alureRewindStream(d->stream);
    } else {
        alSourceRewind(d->source);
    }
    d->newError();
}

void Sound::setMinVolume( ALfloat min )
{
    alSourcef( d->source, AL_MIN_GAIN, min );
}

void Sound::setMaxVolume( ALfloat max )
{
    alSourcef( d->source, AL_MAX_GAIN, max );
}

void Sound::setVelocity( ALfloat vx, ALfloat vy, ALfloat vz )
{
    ALfloat velocity[] = { vx, vy, vz };
    alSourcefv( d->source, AL_VELOCITY, velocity );
}

void Sound::setDirection( ALfloat dx, ALfloat dy, ALfloat dz )
{
    ALfloat direction[] = { dx, dy, dz };
    alSourcefv( d->source, AL_POSITION, direction );
}

void Sound::setTimePosition( ALfloat time )
{
    alSourcef( d->source, AL_SEC_OFFSET, time );

}

// ALfloat Sound::duration() const
// {
//     return d->buffer->duration();
// }

#include "sound.moc"
<commit_msg>GluonAudio::Sound: remove debug message<commit_after>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (C) 2009 Sacha Schutz <istdasklar@free.fr>
 * Copyright (C) 2009-2011 Guillaume Martres <smarter@ubuntu.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "sound.h"

#include "engine.h"

#include <sndfile.h>

#include <core/debughelper.h>
#include "alure/include/AL/alure.h"
#include <sndfile.h>

using namespace GluonAudio;

class Sound::SoundPrivate
{
    public:
        SoundPrivate()
        {
            init();
        }
        ~SoundPrivate()
        {
        }

        bool newError()
        {
            QLatin1String error = QLatin1String(alureGetErrorString());
            if (error != QLatin1String("No error")) {
                DEBUG_BLOCK
                DEBUG_TEXT2("Alure error: %1", error)
                isValid = false;
                return true;
            }
            return false;
        }

        void init()
        {
            isValid = true;
            isStreamed = false;
            isPaused = false;
            isStopped = true;
            isLooping = false;
            stream = 0;
            source = 0;
            position = QVector3D( 0, 0, 0 );
            volume = 1.0f;
            pitch = 1.0f;
            radius = 10000.0f;

        }

        bool setupSource()
        {
            if (path.isEmpty()) {
                return false;
            }

            alGenSources( 1, &source );
            if (source == 0) {
                DEBUG_BLOCK
                DEBUG_TEXT2("Empty source, OpenAL error: %1", alGetError())
                return false;
            }

            ALfloat sourcePosition[] = { position.x(), position.y() , position.z() };
            alSourcefv( source, AL_POSITION, sourcePosition );
            alSourcef( source, AL_GAIN, volume );
            alSourcef( source, AL_PITCH, pitch );
            alSourcef( source, AL_REFERENCE_DISTANCE, radius );
        }

        void _k_deleteSource()
        {
            if (source != 0) {
                alureStopSource(source, false);
                alDeleteSources(1, &source);
                source = 0;
            }
            isValid = false;
            isStopped = true;
            if (isStreamed) {
                alureDestroyStream(stream, 0, 0);
            }
            stream = 0;
        }

        QString path;

        bool isValid;
        bool isStreamed;
        bool isPaused;
        bool isStopped;
        bool isLooping;
        alureStream* stream;
        ALuint source;
        QVector3D position;
        ALfloat volume;
        ALfloat pitch;
        ALfloat radius;
};

Sound::Sound()
    : QObject( Engine::instance() )
    , d( new SoundPrivate )
{
    d->isValid = false;
}

Sound::Sound(const QString& fileName)
    : QObject( Engine::instance() )
    , d( new SoundPrivate )
{
    loadFile(fileName);
}

Sound::Sound(const QString& fileName, bool toStream)
    : QObject( Engine::instance() )
    , d( new SoundPrivate )
{
    loadFile(fileName, toStream);
}

Sound::~Sound()
{
    d->_k_deleteSource();
    delete d;
}

bool Sound::isValid() const
{
    return d->isValid;
}

bool Sound::loadFile(const QString &fileName)
{
    SF_INFO sfinfo;
    memset(&sfinfo, 0, sizeof(sfinfo));
    SNDFILE *sndfile = sf_open(fileName.toLocal8Bit().data(), SFM_READ, &sfinfo);
    sf_close(sndfile);

    double duration = (double)sfinfo.frames / sfinfo.samplerate;
    bool toStream = ( duration >= (double)(Engine::instance()->bufferLength()*Engine::instance()->buffersPerStream())/1e6 );

    return loadFile(fileName, toStream);
}

bool Sound::loadFile(const QString& fileName, bool toStream)
{
    if (fileName.isEmpty()) {
        d->isValid = false;
        return false;
    }
    if (!d->path.isEmpty()) {
        d->_k_deleteSource();
    }
    d->path = fileName;
    d->setupSource();
    d->isStreamed = toStream;
    if (d->isStreamed) {
        alureStreamSizeIsMicroSec(true);
        d->stream = alureCreateStreamFromFile(d->path.toLocal8Bit().constData(), Engine::instance()->bufferLength(), 0, 0);
    } else {
        ALuint buffer = Engine::instance()->genBuffer(d->path);
        if ( d->newError() ) {
            d->isValid = false;
            return false;
        }
        alSourcei(d->source, AL_BUFFER, buffer);
    }

    d->isValid = !d->newError();
    return d->isValid;
}

ALfloat Sound::elapsedTime() const
{
    ALfloat seconds = 0.f;
    alGetSourcef( d->source, AL_SEC_OFFSET, &seconds );
    return seconds;
}

ALint Sound::status() const
{
    ALint status;
    alGetSourcei( d->source, AL_SOURCE_STATE, &status );
    return status;
}

bool Sound::isLooping() const
{
    return d->isLooping;
}

bool Sound::isPlaying() const
{
    return !d->isStopped;
}

void Sound::setLoop( bool enabled )
{
    d->isLooping = enabled;
    if (!d->isStreamed) {
        alSourcei( d->source, AL_LOOPING, enabled );
    }
}

void Sound::clear()
{
    d->_k_deleteSource();
}

QVector3D Sound::position() const
{
    return d->position;
}

ALfloat Sound::x() const
{
    return d->position.x();
}

ALfloat Sound::y() const
{
    return d->position.y();
}

ALfloat Sound::z() const
{
    return d->position.z();
}

ALfloat Sound::volume() const
{
    return d->volume;
}

ALfloat Sound::pitch() const
{
    return d->pitch;
}

ALfloat Sound::radius() const
{
    return d->radius;
}

void Sound::setPosition( ALfloat x, ALfloat y, ALfloat z )
{
    setPosition( QVector3D(x, y, z) );
}

void Sound::setPosition( QVector3D position )
{
    d->position = position;
    ALfloat sourcePosition[] = { position.x(), position.y() , position.z() };
    alSourcefv( d->source, AL_POSITION, sourcePosition );
}

void Sound::setVolume( ALfloat volume )
{
    d->volume = volume;
    alSourcef( d->source, AL_GAIN, volume );
}

void Sound::setPitch( ALfloat pitch )
{
    d->pitch = pitch;
    alSourcef( d->source, AL_PITCH, pitch );
}

void Sound::setRadius( ALfloat radius )
{
    d->radius = radius;
    alSourcef( d->source, AL_REFERENCE_DISTANCE, radius );
}

void callbackStopped(void *object, ALuint source)
{
    static_cast<GluonAudio::Sound*>(object)->stopped();
}

void Sound::stopped()
{
    d->isStopped = true;
    if (d->isLooping) {
        play();
    }
}

void Sound::play()
{
    if (d->isPaused) {
         alureResumeSource(d->source);
    }

    if (!d->isStopped) {
        stop();
    }

    if (d->isStreamed) {
        int loopCount = (d->isLooping ? -1 : 0);
        alurePlaySourceStream(d->source, d->stream, Engine::instance()->buffersPerStream(), loopCount, callbackStopped, this);
    } else {
        alurePlaySource(d->source, callbackStopped, this);
    }
    d->isStopped = false;
    d->newError();
}

void Sound::pause()
{
    alurePauseSource(d->source);
    d->isPaused = true;
    d->newError();
}

void Sound::stop()
{
    alureStopSource(d->source, false);
    d->isStopped = true;
    d->newError();
}

void Sound::rewind()
{
    if (d->isStreamed) {
        alureRewindStream(d->stream);
    } else {
        alSourceRewind(d->source);
    }
    d->newError();
}

void Sound::setMinVolume( ALfloat min )
{
    alSourcef( d->source, AL_MIN_GAIN, min );
}

void Sound::setMaxVolume( ALfloat max )
{
    alSourcef( d->source, AL_MAX_GAIN, max );
}

void Sound::setVelocity( ALfloat vx, ALfloat vy, ALfloat vz )
{
    ALfloat velocity[] = { vx, vy, vz };
    alSourcefv( d->source, AL_VELOCITY, velocity );
}

void Sound::setDirection( ALfloat dx, ALfloat dy, ALfloat dz )
{
    ALfloat direction[] = { dx, dy, dz };
    alSourcefv( d->source, AL_POSITION, direction );
}

void Sound::setTimePosition( ALfloat time )
{
    alSourcef( d->source, AL_SEC_OFFSET, time );

}

// ALfloat Sound::duration() const
// {
//     return d->buffer->duration();
// }

#include "sound.moc"
<|endoftext|>


<commit_before>// Copyright (C) 2015 Elviss Strazdins
// This file is part of the Ouzel engine.

#include <algorithm>
#include "Node.h"
#include "Engine.h"
#include "SceneManager.h"
#include "Layer.h"
#include "Animator.h"
#include "Camera.h"
#include "Utils.h"
#include "MathUtils.h"

namespace ouzel
{
    Node::Node()
    {

    }

    Node::~Node()
    {

    }

    void Node::draw()
    {
        if (_transformDirty)
        {
            calculateTransform();
        }
    }

    void Node::update(float delta)
    {
        if (_currentAnimator)
        {
            _currentAnimator->update(delta);
        }

        if (_transformDirty)
        {
            calculateTransform();
        }
    }

    bool Node::addChild(const NodePtr& node)
    {
        if (NodeContainer::addChild(node))
        {
            node->addToLayer(_layer);
            node->setParentVisible(_visible && _parentVisible);

            if (_transformDirty)
            {
                calculateTransform();
            }
            else
            {
                node->updateTransform(_transform);
            }

            return true;
        }
        else
        {
            return false;
        }
    }

    bool Node::removeFromParent()
    {
        if (NodeContainerPtr parent = _parent.lock())
        {
            parent->removeChild(std::static_pointer_cast<Node>(shared_from_this()));
            return true;
        }

        return false;
    }

    void Node::setZ(float z)
    {
        _z = z;

        if (LayerPtr layer = _layer.lock())
        {
            layer->reorderNodes();
        }

        markTransformDirty();
    }

    void Node::setPosition(const Vector2& position)
    {
        _position = position;

        markTransformDirty();
    }

    void Node::setRotation(float rotation)
    {
        _rotation = rotation;

        markTransformDirty();
    }

    void Node::setScale(const Vector2& scale)
    {
        _scale = scale;

        markTransformDirty();
    }

    void Node::setFlipX(bool flipX)
    {
        _flipX = flipX;

        markTransformDirty();
    }

    void Node::setFlipY(bool flipY)
    {
        _flipY = flipY;

        markTransformDirty();
    }

    void Node::setVisible(bool visible)
    {
        if (visible != _visible)
        {
            _visible = visible;

            setParentVisible(_parentVisible);
        }
    }

    void Node::addToLayer(const LayerWeakPtr& layer)
    {
        _layer = layer;

        if (LayerPtr layer = _layer.lock())
        {
            layer->addNode(std::static_pointer_cast<Node>(shared_from_this()));

            for (NodePtr child : _children)
            {
                child->addToLayer(layer);
            }
        }
    }

    void Node::removeFromLayer()
    {
        if (LayerPtr layer = _layer.lock())
        {
            layer->removeNode(std::static_pointer_cast<Node>(shared_from_this()));

            for (NodePtr child : _children)
            {
                child->removeFromLayer();
            }
        }
    }

    bool Node::pointOn(const Vector2& position) const
    {
        return _boundingBox.containPoint(convertWorldToLocal(position));
    }

    bool Node::rectangleOverlaps(const Rectangle& rectangle) const
    {
        if (_boundingBox.isEmpty())
        {
            return false;
        }

        Vector3 localLeftBottom = Vector3(rectangle.x, rectangle.y, 0.0f);

        const Matrix4& inverseTransform = getInverseTransform();
        inverseTransform.transformPoint(&localLeftBottom);

        Vector3 localRightTop =  Vector3(rectangle.x + rectangle.width, rectangle.y + rectangle.height, 0.0f);

        inverseTransform.transformPoint(&localRightTop);

        if (localLeftBottom.x > _boundingBox.max.x ||
            localLeftBottom.y > _boundingBox.max.y ||
            localRightTop.x < _boundingBox.min.x ||
            localRightTop.y < _boundingBox.min.y)
        {
            return false;
        }

        return true;
    }

    const Matrix4& Node::getTransform() const
    {
        if (_transformDirty)
        {
            calculateTransform();
        }

        return _transform;
    }

    const Matrix4& Node::getInverseTransform() const
    {
        if (_transformDirty)
        {
            calculateTransform();
        }

        if (_inverseTransformDirty)
        {
            calculateInverseTransform();
        }

        return _inverseTransform;
    }

    void Node::updateTransform(const Matrix4& parentTransform)
    {
        _parentTransform = parentTransform;
        _transformDirty = true;
        _inverseTransformDirty = true;
    }

    Vector2 Node::convertWorldToLocal(const Vector2& position) const
    {
        Vector3 localPosition = position;

        const Matrix4& inverseTransform = getInverseTransform();
        inverseTransform.transformPoint(&localPosition);

        return Vector2(localPosition.x, localPosition.y);
    }

    Vector2 Node::convertLocalToWorld(const Vector2& position) const
    {
        Vector3 worldPosition = position;

        const Matrix4& transform = getTransform();
        transform.transformPoint(&worldPosition);

        return Vector2(worldPosition.x, worldPosition.y);
    }

    bool Node::checkVisibility() const
    {
        if (LayerPtr layer = _layer.lock())
        {
            if (_boundingBox.isEmpty() || _boundingRadius == 0.0f)
            {
                return true;
            }

            Matrix4 mvp = layer->getProjection() * layer->getCamera()->getTransform() * _transform;

            Vector3 position;

            mvp.transformPoint(&position);

            float radius = _boundingRadius * std::max(_scale.x, _scale.y);
            radius /= layer->getCamera()->getZoom();

            Vector2 radiusAxis(radius / (2.0f * Engine::getInstance()->getRenderer()->getSize().width - 1.0f),
                               radius / (2.0f * Engine::getInstance()->getRenderer()->getSize().height - 1.0f));

            if (position.x >= -(1.0f + radiusAxis.x) && position.x <= (1.0f + radiusAxis.x) && position.y >= -(1.0f + radiusAxis.y) && position.y <= (1.0f + radiusAxis.y))
            {
                /*Vector3 corners[4] = {
                    Vector3(_boundingBox.min.x, _boundingBox.min.y, 0.0f),
                    Vector3(_boundingBox.max.x, _boundingBox.min.y, 0.0f),
                    Vector3(_boundingBox.max.x, _boundingBox.max.y, 0.0f),
                    Vector3(_boundingBox.min.x, _boundingBox.max.y, 0.0f)
                };

                uint8_t inCorners = 0;

                for (Vector3& corner : corners)
                {
                    mvp.transformPoint(&corner);

                    if (corner.x >= -1.0f && corner.x <= 1.0f && corner.y >= -1.0f && corner.y <= 1.0f)
                    {
                        return true;
                    }

                    if (corner.x < -1.0f && corner.y < -1.0f) inCorners |= 0x01;
                    if (corner.x > 1.0f && corner.y < -1.0f) inCorners |= 0x02;
                    if (corner.x > 1.0f && corner.y > 1.0f) inCorners |= 0x04;
                    if (corner.x < -1.0f && corner.y > 1.0f) inCorners |= 0x08;
                }

                // bounding box is bigger than screen
                if (inCorners == 0x0F)
                {
                    return true;
                }

                for (uint32_t current = 0; current < 4; ++current)
                {
                    uint32_t next = (current == 3) ? 0 : current + 1;

                    if (linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), Vector2(-1.0f, 1.0f), Vector2(1.0f, 1.0f)) || // top
                        linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), Vector2(-1.0f, -1.0f), Vector2(1.0f, -1.0f)) || // bottom
                        linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), Vector2(1.0f, -1.0f), Vector2(1.0f, 1.0f)) || // right
                        linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), Vector2(-1.0f, -1.0f), Vector2(-1.0f, 1.0f))) // left
                    {
                        return true;
                    }
                }*/

                return true;
            }
        }

        return false;
    }

    void Node::animate(const AnimatorPtr& animator)
    {
        _currentAnimator = animator;

        if (_currentAnimator)
        {
            _currentAnimator->start(std::static_pointer_cast<Node>(shared_from_this()));
        }
    }

    void Node::stopAnimation()
    {
        if (_currentAnimator)
        {
            _currentAnimator->stop();
            _currentAnimator.reset();
        }
    }

    void Node::calculateTransform() const
    {
        Matrix4 translation;
        translation.translate(Vector3(_position.x, _position.y, 0.0f));

        Matrix4 rotation;
        rotation.rotate(Vector3(0.0f, 0.0f, -1.0f), _rotation);

        Vector3 realScale = Vector3(_scale.x * (_flipX ? -1.0f : 1.0f),
                                    _scale.y * (_flipY ? -1.0f : 1.0f),
                                    1.0f);

        Matrix4 scale;
        scale.scale(realScale);

        _transform = _parentTransform * translation * rotation * scale;
        _transformDirty = false;

        for (NodePtr child : _children)
        {
            child->updateTransform(_transform);
        }
    }

    void Node::calculateInverseTransform() const
    {
        if (_transformDirty)
        {
            calculateTransform();
        }

        _inverseTransform = _transform;
        _inverseTransform.invert();
        _inverseTransformDirty = false;
    }

    void Node::markTransformDirty() const
    {
        _transformDirty = true;
        _inverseTransformDirty = true;
    }

    void Node::setParentVisible(bool parentVisible)
    {
        _parentVisible = parentVisible;

        for (const NodePtr& child : _children)
        {
            child->setParentVisible(_visible && _parentVisible);
        }
    }
}
<commit_msg>Use markTransformDirty() instead of manually setting dirty flags<commit_after>// Copyright (C) 2015 Elviss Strazdins
// This file is part of the Ouzel engine.

#include <algorithm>
#include "Node.h"
#include "Engine.h"
#include "SceneManager.h"
#include "Layer.h"
#include "Animator.h"
#include "Camera.h"
#include "Utils.h"
#include "MathUtils.h"

namespace ouzel
{
    Node::Node()
    {

    }

    Node::~Node()
    {

    }

    void Node::draw()
    {
        if (_transformDirty)
        {
            calculateTransform();
        }
    }

    void Node::update(float delta)
    {
        if (_currentAnimator)
        {
            _currentAnimator->update(delta);
        }

        if (_transformDirty)
        {
            calculateTransform();
        }
    }

    bool Node::addChild(const NodePtr& node)
    {
        if (NodeContainer::addChild(node))
        {
            node->addToLayer(_layer);
            node->setParentVisible(_visible && _parentVisible);

            if (_transformDirty)
            {
                calculateTransform();
            }
            else
            {
                node->updateTransform(_transform);
            }

            return true;
        }
        else
        {
            return false;
        }
    }

    bool Node::removeFromParent()
    {
        if (NodeContainerPtr parent = _parent.lock())
        {
            parent->removeChild(std::static_pointer_cast<Node>(shared_from_this()));
            return true;
        }

        return false;
    }

    void Node::setZ(float z)
    {
        _z = z;

        if (LayerPtr layer = _layer.lock())
        {
            layer->reorderNodes();
        }

        markTransformDirty();
    }

    void Node::setPosition(const Vector2& position)
    {
        _position = position;

        markTransformDirty();
    }

    void Node::setRotation(float rotation)
    {
        _rotation = rotation;

        markTransformDirty();
    }

    void Node::setScale(const Vector2& scale)
    {
        _scale = scale;

        markTransformDirty();
    }

    void Node::setFlipX(bool flipX)
    {
        _flipX = flipX;

        markTransformDirty();
    }

    void Node::setFlipY(bool flipY)
    {
        _flipY = flipY;

        markTransformDirty();
    }

    void Node::setVisible(bool visible)
    {
        if (visible != _visible)
        {
            _visible = visible;

            setParentVisible(_parentVisible);
        }
    }

    void Node::addToLayer(const LayerWeakPtr& layer)
    {
        _layer = layer;

        if (LayerPtr layer = _layer.lock())
        {
            layer->addNode(std::static_pointer_cast<Node>(shared_from_this()));

            for (NodePtr child : _children)
            {
                child->addToLayer(layer);
            }
        }
    }

    void Node::removeFromLayer()
    {
        if (LayerPtr layer = _layer.lock())
        {
            layer->removeNode(std::static_pointer_cast<Node>(shared_from_this()));

            for (NodePtr child : _children)
            {
                child->removeFromLayer();
            }
        }
    }

    bool Node::pointOn(const Vector2& position) const
    {
        return _boundingBox.containPoint(convertWorldToLocal(position));
    }

    bool Node::rectangleOverlaps(const Rectangle& rectangle) const
    {
        if (_boundingBox.isEmpty())
        {
            return false;
        }

        Vector3 localLeftBottom = Vector3(rectangle.x, rectangle.y, 0.0f);

        const Matrix4& inverseTransform = getInverseTransform();
        inverseTransform.transformPoint(&localLeftBottom);

        Vector3 localRightTop =  Vector3(rectangle.x + rectangle.width, rectangle.y + rectangle.height, 0.0f);

        inverseTransform.transformPoint(&localRightTop);

        if (localLeftBottom.x > _boundingBox.max.x ||
            localLeftBottom.y > _boundingBox.max.y ||
            localRightTop.x < _boundingBox.min.x ||
            localRightTop.y < _boundingBox.min.y)
        {
            return false;
        }

        return true;
    }

    const Matrix4& Node::getTransform() const
    {
        if (_transformDirty)
        {
            calculateTransform();
        }

        return _transform;
    }

    const Matrix4& Node::getInverseTransform() const
    {
        if (_transformDirty)
        {
            calculateTransform();
        }

        if (_inverseTransformDirty)
        {
            calculateInverseTransform();
        }

        return _inverseTransform;
    }

    void Node::updateTransform(const Matrix4& parentTransform)
    {
        _parentTransform = parentTransform;
        markTransformDirty();
    }

    Vector2 Node::convertWorldToLocal(const Vector2& position) const
    {
        Vector3 localPosition = position;

        const Matrix4& inverseTransform = getInverseTransform();
        inverseTransform.transformPoint(&localPosition);

        return Vector2(localPosition.x, localPosition.y);
    }

    Vector2 Node::convertLocalToWorld(const Vector2& position) const
    {
        Vector3 worldPosition = position;

        const Matrix4& transform = getTransform();
        transform.transformPoint(&worldPosition);

        return Vector2(worldPosition.x, worldPosition.y);
    }

    bool Node::checkVisibility() const
    {
        if (LayerPtr layer = _layer.lock())
        {
            if (_boundingBox.isEmpty() || _boundingRadius == 0.0f)
            {
                return true;
            }

            Matrix4 mvp = layer->getProjection() * layer->getCamera()->getTransform() * _transform;

            Vector3 position;

            mvp.transformPoint(&position);

            float radius = _boundingRadius * std::max(_scale.x, _scale.y);
            radius /= layer->getCamera()->getZoom();

            Vector2 radiusAxis(radius / (2.0f * Engine::getInstance()->getRenderer()->getSize().width - 1.0f),
                               radius / (2.0f * Engine::getInstance()->getRenderer()->getSize().height - 1.0f));

            if (position.x >= -(1.0f + radiusAxis.x) && position.x <= (1.0f + radiusAxis.x) && position.y >= -(1.0f + radiusAxis.y) && position.y <= (1.0f + radiusAxis.y))
            {
                /*Vector3 corners[4] = {
                    Vector3(_boundingBox.min.x, _boundingBox.min.y, 0.0f),
                    Vector3(_boundingBox.max.x, _boundingBox.min.y, 0.0f),
                    Vector3(_boundingBox.max.x, _boundingBox.max.y, 0.0f),
                    Vector3(_boundingBox.min.x, _boundingBox.max.y, 0.0f)
                };

                uint8_t inCorners = 0;

                for (Vector3& corner : corners)
                {
                    mvp.transformPoint(&corner);

                    if (corner.x >= -1.0f && corner.x <= 1.0f && corner.y >= -1.0f && corner.y <= 1.0f)
                    {
                        return true;
                    }

                    if (corner.x < -1.0f && corner.y < -1.0f) inCorners |= 0x01;
                    if (corner.x > 1.0f && corner.y < -1.0f) inCorners |= 0x02;
                    if (corner.x > 1.0f && corner.y > 1.0f) inCorners |= 0x04;
                    if (corner.x < -1.0f && corner.y > 1.0f) inCorners |= 0x08;
                }

                // bounding box is bigger than screen
                if (inCorners == 0x0F)
                {
                    return true;
                }

                for (uint32_t current = 0; current < 4; ++current)
                {
                    uint32_t next = (current == 3) ? 0 : current + 1;

                    if (linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), Vector2(-1.0f, 1.0f), Vector2(1.0f, 1.0f)) || // top
                        linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), Vector2(-1.0f, -1.0f), Vector2(1.0f, -1.0f)) || // bottom
                        linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), Vector2(1.0f, -1.0f), Vector2(1.0f, 1.0f)) || // right
                        linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), Vector2(-1.0f, -1.0f), Vector2(-1.0f, 1.0f))) // left
                    {
                        return true;
                    }
                }*/

                return true;
            }
        }

        return false;
    }

    void Node::animate(const AnimatorPtr& animator)
    {
        _currentAnimator = animator;

        if (_currentAnimator)
        {
            _currentAnimator->start(std::static_pointer_cast<Node>(shared_from_this()));
        }
    }

    void Node::stopAnimation()
    {
        if (_currentAnimator)
        {
            _currentAnimator->stop();
            _currentAnimator.reset();
        }
    }

    void Node::calculateTransform() const
    {
        Matrix4 translation;
        translation.translate(Vector3(_position.x, _position.y, 0.0f));

        Matrix4 rotation;
        rotation.rotate(Vector3(0.0f, 0.0f, -1.0f), _rotation);

        Vector3 realScale = Vector3(_scale.x * (_flipX ? -1.0f : 1.0f),
                                    _scale.y * (_flipY ? -1.0f : 1.0f),
                                    1.0f);

        Matrix4 scale;
        scale.scale(realScale);

        _transform = _parentTransform * translation * rotation * scale;
        _transformDirty = false;

        for (NodePtr child : _children)
        {
            child->updateTransform(_transform);
        }
    }

    void Node::calculateInverseTransform() const
    {
        if (_transformDirty)
        {
            calculateTransform();
        }

        _inverseTransform = _transform;
        _inverseTransform.invert();
        _inverseTransformDirty = false;
    }

    void Node::markTransformDirty() const
    {
        _transformDirty = true;
        _inverseTransformDirty = true;
    }

    void Node::setParentVisible(bool parentVisible)
    {
        _parentVisible = parentVisible;

        for (const NodePtr& child : _children)
        {
            child->setParentVisible(_visible && _parentVisible);
        }
    }
}
<|endoftext|>


<commit_before>/** @file

  Network message marshalling.

  @section license License

  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */

#include "ink_config.h"
#include "ink_defs.h"
#include "ink_assert.h"
#include "ink_memory.h"
#include "mgmtapi.h"
#include "NetworkMessage.h"

#define MAX_OPERATION_BUFSZ   1024
#define MAX_OPERATION_FIELDS  10

struct NetCmdOperation
{
  unsigned nfields;
  const MgmtMarshallType fields[MAX_OPERATION_FIELDS];
};

// Requests always begin with a OpType, followed by aditional fields.
static const struct NetCmdOperation requests[] = {
  /* FILE_READ                  */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
  /* FILE_WRITE                 */ { 4, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_DATA } },
  /* RECORD_SET                 */ { 3, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING } },
  /* RECORD_GET                 */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* PROXY_STATE_GET            */ { 1, { MGMT_MARSHALL_INT } },
  /* PROXY_STATE_SET            */ { 3, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
  /* RECONFIGURE                */ { 1, { MGMT_MARSHALL_INT } },
  /* RESTART                    */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
  /* BOUNCE                     */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
  /* EVENT_RESOLVE              */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* EVENT_GET_MLT              */ { 1, { MGMT_MARSHALL_INT } },
  /* EVENT_ACTIVE               */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* EVENT_REG_CALLBACK         */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* EVENT_UNREG_CALLBACK       */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* EVENT_NOTIFY               */ { 3, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING} }, // only msg sent from TM to client
  /* SNAPSHOT_TAKE              */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* SNAPSHOT_RESTORE           */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* SNAPSHOT_REMOVE            */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* SNAPSHOT_GET_MLT           */ { 1, { MGMT_MARSHALL_INT } },
  /* DIAGS                      */ { 3, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING} },
  /* STATS_RESET_NODE           */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* STATS_RESET_CLUSTER        */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* STORAGE_DEVICE_CMD_OFFLINE */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* RECORD_MATCH_GET           */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* API_PING                   */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
};

// Responses always begin with a TSMgmtError code, followed by additional fields.
static const struct NetCmdOperation responses[] = {
  /* FILE_READ                  */ { 3, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_DATA } },
  /* FILE_WRITE                 */ { 1, { MGMT_MARSHALL_INT } },
  /* RECORD_SET                 */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
  /* RECORD_GET                 */ { 4, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_DATA } },
  /* PROXY_STATE_GET            */ { 1, { MGMT_MARSHALL_INT } },
  /* PROXY_STATE_SET            */ { 1, { MGMT_MARSHALL_INT } },
  /* RECONFIGURE                */ { 1, { MGMT_MARSHALL_INT } },
  /* RESTART                    */ { 1, { MGMT_MARSHALL_INT } },
  /* BOUNCE                     */ { 1, { MGMT_MARSHALL_INT } },
  /* EVENT_RESOLVE              */ { 1, { MGMT_MARSHALL_INT } },
  /* EVENT_GET_MLT              */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* EVENT_ACTIVE               */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
  /* EVENT_REG_CALLBACK         */ { 0, {} }, // no reply
  /* EVENT_UNREG_CALLBACK       */ { 0, {} }, // no reply
  /* EVENT_NOTIFY               */ { 0, {} }, // no reply
  /* SNAPSHOT_TAKE              */ { 1, { MGMT_MARSHALL_INT } },
  /* SNAPSHOT_RESTORE           */ { 1, { MGMT_MARSHALL_INT } },
  /* SNAPSHOT_REMOVE            */ { 1, { MGMT_MARSHALL_INT } },
  /* SNAPSHOT_GET_MLT           */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* DIAGS                      */ { 0, {} }, // no reply
  /* STATS_RESET_NODE           */ { 1, { MGMT_MARSHALL_INT } },
  /* STATS_RESET_CLUSTER        */ { 1, { MGMT_MARSHALL_INT } },
  /* STORAGE_DEVICE_CMD_OFFLINE */ { 1, { MGMT_MARSHALL_INT } },
  /* RECORD_MATCH_GET           */ { 4, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_DATA } },
  /* API_PING                   */ { 0, {} }, // no reply
};

#define GETCMD(ops, optype, cmd) do { \
  if (optype < 0 || optype >= countof(ops)) { return TS_ERR_PARAMS; } \
  if (ops[optype].nfields == 0) { return TS_ERR_PARAMS; } \
  cmd = &ops[optype]; \
} while (0);

TSMgmtError
send_mgmt_request(const mgmt_message_sender& snd, OpType optype, ...)
{
  va_list ap;
  ats_scoped_mem<char> msgbuf;
  MgmtMarshallInt msglen;
  const MgmtMarshallType lenfield[] = { MGMT_MARSHALL_INT };
  const NetCmdOperation * cmd;

  GETCMD(requests, optype, cmd);

  va_start(ap, optype);
  msglen = mgmt_message_length_v(cmd->fields, cmd->nfields, ap);
  va_end(ap);

  msgbuf = (char *)ats_malloc(msglen + 4);

  // First marshall the total message length.
  mgmt_message_marshall((char *)msgbuf, msglen, lenfield, countof(lenfield), &msglen);

  // Now marshall the message itself.
  va_start(ap, optype);
  if (mgmt_message_marshall_v((char *)msgbuf + 4, msglen, cmd->fields, cmd->nfields, ap) == -1) {
    va_end(ap);
    return TS_ERR_PARAMS;
  }

  va_end(ap);
  return snd.send(msgbuf, msglen + 4);
}

TSMgmtError
send_mgmt_request(int fd, OpType optype, ...)
{
  va_list ap;
  MgmtMarshallInt msglen;
  MgmtMarshallData req = { NULL, 0 };
  const MgmtMarshallType fields[] = { MGMT_MARSHALL_DATA };
  const NetCmdOperation * cmd;

  GETCMD(requests, optype, cmd);

  // Figure out the payload length.
  va_start(ap, optype);
  msglen = mgmt_message_length_v(cmd->fields, cmd->nfields, ap);
  va_end(ap);

  ink_assert(msglen >= 0);

  req.ptr = (char *)ats_malloc(msglen);
  req.len = msglen;

  // Marshall the message itself.
  va_start(ap, optype);
  if (mgmt_message_marshall_v(req.ptr, req.len, cmd->fields, cmd->nfields, ap) == -1) {
    ats_free(req.ptr);
    va_end(ap);
    return TS_ERR_PARAMS;
  }

  va_end(ap);

  // Send the response as the payload of a data object.
  if (mgmt_message_write(fd, fields, countof(fields), &req) == -1) {
    ats_free(req.ptr);
    return TS_ERR_NET_WRITE;
  }

  ats_free(req.ptr);
  return TS_ERR_OKAY;
}

// Send a management message response. We don't need to worry about retransmitting the message if we get
// disconnected, so this is much simpler. We can directly marshall the response as a data object.
TSMgmtError
send_mgmt_response(int fd, OpType optype, ...)
{
  va_list ap;
  MgmtMarshallInt msglen;
  MgmtMarshallData reply = { NULL, 0 };
  const MgmtMarshallType fields[] = { MGMT_MARSHALL_DATA };
  const NetCmdOperation * cmd;

  GETCMD(responses, optype, cmd);

  va_start(ap, optype);
  msglen = mgmt_message_length_v(cmd->fields, cmd->nfields, ap);
  va_end(ap);

  ink_assert(msglen >= 0);

  reply.ptr = (char *)ats_malloc(msglen);
  reply.len = msglen;

  // Marshall the message itself.
  va_start(ap, optype);
  if (mgmt_message_marshall_v(reply.ptr, reply.len, cmd->fields, cmd->nfields, ap) == -1) {
    ats_free(reply.ptr);
    va_end(ap);
    return TS_ERR_PARAMS;
  }

  va_end(ap);

  // Send the response as the payload of a data object.
  if (mgmt_message_write(fd, fields, countof(fields), &reply) == -1) {
    ats_free(reply.ptr);
    return TS_ERR_NET_WRITE;
  }

  ats_free(reply.ptr);
  return TS_ERR_OKAY;
}

template <unsigned N>
static TSMgmtError
recv_x(const struct NetCmdOperation (&ops)[N], void * buf, size_t buflen, OpType optype, va_list ap)
{
  ssize_t msglen;
  const NetCmdOperation * cmd;

  GETCMD(ops, optype, cmd);

  msglen = mgmt_message_parse_v(buf, buflen, cmd->fields, cmd->nfields, ap);
  return (msglen == -1) ? TS_ERR_PARAMS : TS_ERR_OKAY;
}

TSMgmtError
recv_mgmt_request(void * buf, size_t buflen, OpType optype, ...)
{
  TSMgmtError err;
  va_list ap;

  va_start(ap, optype);
  err = recv_x(requests, buf, buflen, optype, ap);
  va_end(ap);

  return err;
}

TSMgmtError
recv_mgmt_response(void * buf, size_t buflen, OpType optype, ...)
{
  TSMgmtError err;
  va_list ap;

  va_start(ap, optype);
  err = recv_x(responses, buf, buflen, optype, ap);
  va_end(ap);

  return err;
}

TSMgmtError
recv_mgmt_message(int fd, MgmtMarshallData& msg)
{
  const MgmtMarshallType fields[] = { MGMT_MARSHALL_DATA };

  if (mgmt_message_read(fd, fields, countof(fields), &msg) == -1) {
    return TS_ERR_NET_READ;
  }

  return TS_ERR_OKAY;
}

OpType
extract_mgmt_request_optype(void * msg, size_t msglen)
{
  const MgmtMarshallType fields[] = { MGMT_MARSHALL_INT };
  MgmtMarshallInt optype;

  if (mgmt_message_parse(msg, msglen, fields, countof(fields), &optype) == -1) {
    return UNDEFINED_OP;
  }

  return (OpType)optype;
}
<commit_msg>TS-3033: Fix build errors.<commit_after>/** @file

  Network message marshalling.

  @section license License

  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */

#include "ink_config.h"
#include "ink_defs.h"
#include "ink_assert.h"
#include "ink_memory.h"
#include "mgmtapi.h"
#include "NetworkMessage.h"

#define MAX_OPERATION_BUFSZ   1024
#define MAX_OPERATION_FIELDS  10

struct NetCmdOperation
{
  unsigned nfields;
  const MgmtMarshallType fields[MAX_OPERATION_FIELDS];
};

// Requests always begin with a OpType, followed by aditional fields.
static const struct NetCmdOperation requests[] = {
  /* FILE_READ                  */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
  /* FILE_WRITE                 */ { 4, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_DATA } },
  /* RECORD_SET                 */ { 3, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING } },
  /* RECORD_GET                 */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* PROXY_STATE_GET            */ { 1, { MGMT_MARSHALL_INT } },
  /* PROXY_STATE_SET            */ { 3, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
  /* RECONFIGURE                */ { 1, { MGMT_MARSHALL_INT } },
  /* RESTART                    */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
  /* BOUNCE                     */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
  /* EVENT_RESOLVE              */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* EVENT_GET_MLT              */ { 1, { MGMT_MARSHALL_INT } },
  /* EVENT_ACTIVE               */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* EVENT_REG_CALLBACK         */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* EVENT_UNREG_CALLBACK       */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* EVENT_NOTIFY               */ { 3, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING} }, // only msg sent from TM to client
  /* SNAPSHOT_TAKE              */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* SNAPSHOT_RESTORE           */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* SNAPSHOT_REMOVE            */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* SNAPSHOT_GET_MLT           */ { 1, { MGMT_MARSHALL_INT } },
  /* DIAGS                      */ { 3, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING} },
  /* STATS_RESET_NODE           */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* STATS_RESET_CLUSTER        */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* STORAGE_DEVICE_CMD_OFFLINE */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* RECORD_MATCH_GET           */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* API_PING                   */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
};

// Responses always begin with a TSMgmtError code, followed by additional fields.
static const struct NetCmdOperation responses[] = {
  /* FILE_READ                  */ { 3, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_DATA } },
  /* FILE_WRITE                 */ { 1, { MGMT_MARSHALL_INT } },
  /* RECORD_SET                 */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
  /* RECORD_GET                 */ { 4, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_DATA } },
  /* PROXY_STATE_GET            */ { 1, { MGMT_MARSHALL_INT } },
  /* PROXY_STATE_SET            */ { 1, { MGMT_MARSHALL_INT } },
  /* RECONFIGURE                */ { 1, { MGMT_MARSHALL_INT } },
  /* RESTART                    */ { 1, { MGMT_MARSHALL_INT } },
  /* BOUNCE                     */ { 1, { MGMT_MARSHALL_INT } },
  /* EVENT_RESOLVE              */ { 1, { MGMT_MARSHALL_INT } },
  /* EVENT_GET_MLT              */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* EVENT_ACTIVE               */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT } },
  /* EVENT_REG_CALLBACK         */ { 0, {} }, // no reply
  /* EVENT_UNREG_CALLBACK       */ { 0, {} }, // no reply
  /* EVENT_NOTIFY               */ { 0, {} }, // no reply
  /* SNAPSHOT_TAKE              */ { 1, { MGMT_MARSHALL_INT } },
  /* SNAPSHOT_RESTORE           */ { 1, { MGMT_MARSHALL_INT } },
  /* SNAPSHOT_REMOVE            */ { 1, { MGMT_MARSHALL_INT } },
  /* SNAPSHOT_GET_MLT           */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING } },
  /* DIAGS                      */ { 0, {} }, // no reply
  /* STATS_RESET_NODE           */ { 1, { MGMT_MARSHALL_INT } },
  /* STATS_RESET_CLUSTER        */ { 1, { MGMT_MARSHALL_INT } },
  /* STORAGE_DEVICE_CMD_OFFLINE */ { 1, { MGMT_MARSHALL_INT } },
  /* RECORD_MATCH_GET           */ { 4, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_DATA } },
  /* API_PING                   */ { 0, {} }, // no reply
};

#define GETCMD(ops, optype, cmd) do { \
  if (optype < 0 || static_cast<unsigned>(optype) >= countof(ops)) { return TS_ERR_PARAMS; } \
  if (ops[optype].nfields == 0) { return TS_ERR_PARAMS; } \
  cmd = &ops[optype]; \
} while (0);

TSMgmtError
send_mgmt_request(const mgmt_message_sender& snd, OpType optype, ...)
{
  va_list ap;
  ats_scoped_mem<char> msgbuf;
  MgmtMarshallInt msglen;
  const MgmtMarshallType lenfield[] = { MGMT_MARSHALL_INT };
  const NetCmdOperation * cmd;

  GETCMD(requests, optype, cmd);

  va_start(ap, optype);
  msglen = mgmt_message_length_v(cmd->fields, cmd->nfields, ap);
  va_end(ap);

  msgbuf = (char *)ats_malloc(msglen + 4);

  // First marshall the total message length.
  mgmt_message_marshall((char *)msgbuf, msglen, lenfield, countof(lenfield), &msglen);

  // Now marshall the message itself.
  va_start(ap, optype);
  if (mgmt_message_marshall_v((char *)msgbuf + 4, msglen, cmd->fields, cmd->nfields, ap) == -1) {
    va_end(ap);
    return TS_ERR_PARAMS;
  }

  va_end(ap);
  return snd.send(msgbuf, msglen + 4);
}

TSMgmtError
send_mgmt_request(int fd, OpType optype, ...)
{
  va_list ap;
  MgmtMarshallInt msglen;
  MgmtMarshallData req = { NULL, 0 };
  const MgmtMarshallType fields[] = { MGMT_MARSHALL_DATA };
  const NetCmdOperation * cmd;

  GETCMD(requests, optype, cmd);

  // Figure out the payload length.
  va_start(ap, optype);
  msglen = mgmt_message_length_v(cmd->fields, cmd->nfields, ap);
  va_end(ap);

  ink_assert(msglen >= 0);

  req.ptr = (char *)ats_malloc(msglen);
  req.len = msglen;

  // Marshall the message itself.
  va_start(ap, optype);
  if (mgmt_message_marshall_v(req.ptr, req.len, cmd->fields, cmd->nfields, ap) == -1) {
    ats_free(req.ptr);
    va_end(ap);
    return TS_ERR_PARAMS;
  }

  va_end(ap);

  // Send the response as the payload of a data object.
  if (mgmt_message_write(fd, fields, countof(fields), &req) == -1) {
    ats_free(req.ptr);
    return TS_ERR_NET_WRITE;
  }

  ats_free(req.ptr);
  return TS_ERR_OKAY;
}

// Send a management message response. We don't need to worry about retransmitting the message if we get
// disconnected, so this is much simpler. We can directly marshall the response as a data object.
TSMgmtError
send_mgmt_response(int fd, OpType optype, ...)
{
  va_list ap;
  MgmtMarshallInt msglen;
  MgmtMarshallData reply = { NULL, 0 };
  const MgmtMarshallType fields[] = { MGMT_MARSHALL_DATA };
  const NetCmdOperation * cmd;

  GETCMD(responses, optype, cmd);

  va_start(ap, optype);
  msglen = mgmt_message_length_v(cmd->fields, cmd->nfields, ap);
  va_end(ap);

  ink_assert(msglen >= 0);

  reply.ptr = (char *)ats_malloc(msglen);
  reply.len = msglen;

  // Marshall the message itself.
  va_start(ap, optype);
  if (mgmt_message_marshall_v(reply.ptr, reply.len, cmd->fields, cmd->nfields, ap) == -1) {
    ats_free(reply.ptr);
    va_end(ap);
    return TS_ERR_PARAMS;
  }

  va_end(ap);

  // Send the response as the payload of a data object.
  if (mgmt_message_write(fd, fields, countof(fields), &reply) == -1) {
    ats_free(reply.ptr);
    return TS_ERR_NET_WRITE;
  }

  ats_free(reply.ptr);
  return TS_ERR_OKAY;
}

template <unsigned N>
static TSMgmtError
recv_x(const struct NetCmdOperation (&ops)[N], void * buf, size_t buflen, OpType optype, va_list ap)
{
  ssize_t msglen;
  const NetCmdOperation * cmd;

  GETCMD(ops, optype, cmd);

  msglen = mgmt_message_parse_v(buf, buflen, cmd->fields, cmd->nfields, ap);
  return (msglen == -1) ? TS_ERR_PARAMS : TS_ERR_OKAY;
}

TSMgmtError
recv_mgmt_request(void * buf, size_t buflen, OpType optype, ...)
{
  TSMgmtError err;
  va_list ap;

  va_start(ap, optype);
  err = recv_x(requests, buf, buflen, optype, ap);
  va_end(ap);

  return err;
}

TSMgmtError
recv_mgmt_response(void * buf, size_t buflen, OpType optype, ...)
{
  TSMgmtError err;
  va_list ap;

  va_start(ap, optype);
  err = recv_x(responses, buf, buflen, optype, ap);
  va_end(ap);

  return err;
}

TSMgmtError
recv_mgmt_message(int fd, MgmtMarshallData& msg)
{
  const MgmtMarshallType fields[] = { MGMT_MARSHALL_DATA };

  if (mgmt_message_read(fd, fields, countof(fields), &msg) == -1) {
    return TS_ERR_NET_READ;
  }

  return TS_ERR_OKAY;
}

OpType
extract_mgmt_request_optype(void * msg, size_t msglen)
{
  const MgmtMarshallType fields[] = { MGMT_MARSHALL_INT };
  MgmtMarshallInt optype;

  if (mgmt_message_parse(msg, msglen, fields, countof(fields), &optype) == -1) {
    return UNDEFINED_OP;
  }

  return (OpType)optype;
}
<|endoftext|>