<commit_before>#include "openmc/secondary_thermal.h"

#include "openmc/hdf5_interface.h"
#include "openmc/random_lcg.h"
#include "openmc/search.h"

#include "xtensor/xview.hpp"

#include <cmath> // for log, exp

namespace openmc {

// Helper function to get index on incident energy grid
void
get_energy_index(const std::vector<double>& energies, double E, int& i, double& f)
{
  // Get index and interpolation factor for elastic grid
  i = 0;
  f = 0.0;
  if (E >= energies.front()) {
    i = lower_bound_index(energies.begin(), energies.end(), E);
    f = (E - energies[i]) / (energies[i+1] - energies[i]);
  }
}

//==============================================================================
// CoherentElasticAE implementation
//==============================================================================

CoherentElasticAE::CoherentElasticAE(const CoherentElasticXS& xs)
  : xs_{xs}
{ }

void
CoherentElasticAE::sample(double E_in, double& E_out, double& mu) const
{
  // Get index and interpolation factor for elastic grid
  int i;
  double f;
  const auto& energies {xs_.bragg_edges()};
  get_energy_index(energies, E_in, i, f);

  // Sample a Bragg edge between 1 and i
  const auto& factors = xs_.factors();
  double prob = prn() * factors[i+1];
  int k = 0;
  if (prob >= factors.front()) {
    k = lower_bound_index(factors.begin(), factors.begin() + (i+1), prob);
  }

  // Characteristic scattering cosine for this Bragg edge (ENDF-102, Eq. 7-2)
  mu = 1.0 - 2.0*energies[k] / E_in;

  // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7-1)
  E_out = E_in;
}

//==============================================================================
// IncoherentElasticAE implementation
//==============================================================================

IncoherentElasticAE::IncoherentElasticAE(hid_t group)
{
  read_dataset(group, "debye_waller", debye_waller_);
}

void
IncoherentElasticAE::sample(double E_in, double& E_out, double& mu) const
{
  // Sample angle by inverting the distribution in ENDF-102, Eq. 7.4
  double c = 2 * E_in * debye_waller_;
  mu = std::log(1.0 + prn()*(std::exp(2.0*c) - 1))/c - 1.0;

  // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7.4)
  E_out = E_in;
}

//==============================================================================
// IncoherentElasticAEDiscrete implementation
//==============================================================================

IncoherentElasticAEDiscrete::IncoherentElasticAEDiscrete(hid_t group,
  const std::vector<double>& energy)
  : energy_{energy}
{
  read_dataset(group, "mu_out", mu_out_);
}

void
IncoherentElasticAEDiscrete::sample(double E_in, double& E_out, double& mu) const
{
  // Get index and interpolation factor for elastic grid
  int i;
  double f;
  get_energy_index(energy_, E_in, i, f);

  // Interpolate between two discrete cosines corresponding to neighboring
  // incoming energies.

  // Sample outgoing cosine bin
  int n_mu = mu_out_.shape()[1];
  int k = prn() * n_mu;

  // Rather than use the sampled discrete mu directly, it is smeared over
  // a bin of width 0.5*min(mu[k] - mu[k-1], mu[k+1] - mu[k]) centered on the
  // discrete mu value itself.

  // Interpolate kth mu value between distributions at energies i and i+1
  mu = mu_out_(i, k) + f*(mu_out_(i+1, k) - mu_out_(i, k));

  // Inteprolate (k-1)th mu value between distributions at energies i and i+1.
  // When k==0, pick a value that will smear the cosine out to a minimum of -1.
  double mu_left = (k == 0) ?
    -1.0 - (mu + 1.0) :
    mu_out_(i, k-1) + f*(mu_out_(i+1, k-1) - mu_out_(i, k-1));

  // Inteprolate (k+1)th mu value between distributions at energies i and i+1.
  // When k is the last discrete value, pick a value that will smear the cosine
  // out to a maximum of 1.
  double mu_right = (k == n_mu - 1) ?
    1.0 + (1.0 - mu) :
    mu_out_(i, k+1) + f*(mu_out_(i+1, k+1) - mu_out_(i, k+1));

  // Smear cosine
  mu += std::min(mu - mu_left, mu_right - mu)*(prn() - 0.5);

  // Energy doesn't change in elastic scattering
  E_out = E_in;
}

//==============================================================================
// IncoherentInelasticAEDiscrete implementation
//==============================================================================

IncoherentInelasticAEDiscrete::IncoherentInelasticAEDiscrete(hid_t group,
  const std::vector<double>& energy)
  : energy_{energy}
{
  read_dataset(group, "energy_out", energy_out_);
  read_dataset(group, "mu_out", mu_out_);
  read_dataset(group, "skewed", skewed_);
}

void
IncoherentInelasticAEDiscrete::sample(double E_in, double& E_out, double& mu) const
{
  // Get index and interpolation factor for inelastic grid
  int i;
  double f;
  get_energy_index(energy_, E_in, i, f);

  // Now that we have an incoming energy bin, we need to determine the outgoing
  // energy bin. This will depend on whether the outgoing energy distribution is
  // skewed. If it is skewed, then the first two and last two bins have lower
  // probabilities than the other bins (0.1 for the first and last bins and 0.4
  // for the second and second to last bins, relative to a normal bin
  // probability of 1). Otherwise, each bin is equally probable.

  int j;
  int n = energy_out_.shape()[1];
  if (!skewed_) {
    // All bins equally likely
    j = prn() * n;
  } else {
    // Distribution skewed away from edge points
    double r = prn() * (n - 3);
    if (r > 1.0) {
      // equally likely N-4 middle bins
      j = r + 1;
    } else if (r > 0.6) {
      // second to last bin has relative probability of 0.4
      j = n - 2;
    } else if (r > 0.5) {
      // last bin has relative probability of 0.1
      j = n - 1;
    } else if (r > 0.1) {
      // second bin has relative probability of 0.4
      j = 1;
    } else {
      // first bin has relative probability of 0.1
      j = 0;
    }
  }

  // Determine outgoing energy corresponding to E_in[i] and E_in[i+1]
  double E_ij  = energy_out_(i, j);
  double E_i1j = energy_out_(i+1, j);

  // Outgoing energy
  E_out = (1 - f)*E_ij + f*E_i1j;

  // Sample outgoing cosine bin
  int m = mu_out_.shape()[2];
  int k = prn() * m;

  // Determine outgoing cosine corresponding to E_in[i] and E_in[i+1]
  double mu_ijk  = mu_out_(i, j, k);
  double mu_i1jk = mu_out_(i+1, j, k);

  // Cosine of angle between incoming and outgoing neutron
  mu = (1 - f)*mu_ijk + f*mu_i1jk;
}

//==============================================================================
// IncoherentInelasticAE implementation
//==============================================================================

IncoherentInelasticAE::IncoherentInelasticAE(hid_t group)
{
  // Read correlated angle-energy distribution
  CorrelatedAngleEnergy dist {group};

  // Copy incident energies
  energy_ = dist.energy();

  // Convert to S(a,b) native format
  for (const auto& edist : dist.distribution()) {
    // Create temporary distribution
    DistEnergySab d;

    // Copy outgoing energy distribution
    d.n_e_out = edist.e_out.size();
    d.e_out = edist.e_out;
    d.e_out_pdf = edist.p;
    d.e_out_cdf = edist.c;

    for (int j = 0; j < d.n_e_out; ++j) {
      auto adist = dynamic_cast<Tabular*>(edist.angle[j].get());
      if (adist) {
        // On first pass, allocate space for angles
        if (j == 0) {
          auto n_mu = adist->x().size();
          d.mu = xt::empty<double>({d.n_e_out, n_mu});
        }

        // Copy outgoing angles
        auto mu_j = xt::view(d.mu, j);
        std::copy(adist->x().begin(), adist->x().end(), mu_j.begin());
      }
    }

    distribution_.emplace_back(std::move(d));
  }

}

void
IncoherentInelasticAE::sample(double E_in, double& E_out, double& mu) const
{
  // Get index and interpolation factor for inelastic grid
  int i;
  double f;
  get_energy_index(energy_, E_in, i, f);

  // Sample between ith and [i+1]th bin
  int l = f > prn() ? i + 1 : i;

  // Determine endpoints on grid i
  auto n = distribution_[i].e_out.size();
  double E_i_1 = distribution_[i].e_out[0];
  double E_i_J = distribution_[i].e_out[n - 1];

  // Determine endpoints on grid i + 1
  n = distribution_[i + 1].e_out.size();
  double E_i1_1 = distribution_[i + 1].e_out[0];
  double E_i1_J = distribution_[i + 1].e_out[n - 1];

  double E_1 = E_i_1 + f * (E_i1_1 - E_i_1);
  double E_J = E_i_J + f * (E_i1_J - E_i_J);

  // Determine outgoing energy bin
  // (First reset n_energy_out to the right value)
  n = distribution_[l].n_e_out;
  double r1 = prn();
  double c_j = distribution_[l].e_out_cdf[0];
  double c_j1;
  std::size_t j;
  for (j = 0; j < n - 1; ++j) {
    c_j1 = distribution_[l].e_out_cdf[j + 1];
    if (r1 < c_j1) break;
    c_j = c_j1;
  }

  // check to make sure j is <= n_energy_out - 2
  j = std::min(j, n - 2);

  // Get the data to interpolate between
  double E_l_j = distribution_[l].e_out[j];
  double p_l_j = distribution_[l].e_out_pdf[j];

  // Next part assumes linear-linear interpolation in standard
  double E_l_j1 = distribution_[l].e_out[j + 1];
  double p_l_j1 = distribution_[l].e_out_pdf[j + 1];

  // Find secondary energy (variable E)
  double frac = (p_l_j1 - p_l_j) / (E_l_j1 - E_l_j);
  if (frac == 0.0) {
    E_out = E_l_j + (r1 - c_j) / p_l_j;
  } else {
    E_out = E_l_j + (std::sqrt(std::max(0.0, p_l_j*p_l_j +
          2.0*frac*(r1 - c_j))) - p_l_j) / frac;
  }

  // Now interpolate between incident energy bins i and i + 1
  if (l == i) {
    E_out = E_1 + (E_out - E_i_1) * (E_J - E_1) / (E_i_J - E_i_1);
  } else {
    E_out = E_1 + (E_out - E_i1_1) * (E_J - E_1) / (E_i1_J - E_i1_1);
  }

  // Sample outgoing cosine bin
  int n_mu = distribution_[l].mu.shape()[1];
  std::size_t k = prn() * n_mu;

  // Rather than use the sampled discrete mu directly, it is smeared over
  // a bin of width 0.5*min(mu[k] - mu[k-1], mu[k+1] - mu[k]) centered on the
  // discrete mu value itself.
  const auto& mu_l = distribution_[l].mu;
  f = (r1 - c_j)/(c_j1 - c_j);

  // Interpolate kth mu value between distributions at energies j and j+1
  mu = mu_l(j, k) + f*(mu_l(j+1, k) - mu_l(j, k));

  // Inteprolate (k-1)th mu value between distributions at energies j and j+1.
  // When k==0, pick a value that will smear the cosine out to a minimum of -1.
  double mu_left = (k == 0) ?
    mu_left = -1.0 - (mu + 1.0) :
    mu_left = mu_l(j, k-1) + f*(mu_l(j+1, k-1) - mu_l(j, k-1));

  // Inteprolate (k+1)th mu value between distributions at energies j and j+1.
  // When k is the last discrete value, pick a value that will smear the cosine
  // out to a maximum of 1.
  double mu_right = (k == n_mu - 1) ?
    mu_right = 1.0 + (1.0 - mu) :
    mu_right = mu_l(j, k+1) + f*(mu_l(j+1, k+1) - mu_l(j, k+1));

  // Smear cosine
  mu += std::min(mu - mu_left, mu_right - mu)*(prn() - 0.5);
}

} // namespace openmc
<commit_msg>Change outgoing energy adjustment for incoherent inelastic to match MCNP6<commit_after>#include "openmc/secondary_thermal.h"

#include "openmc/hdf5_interface.h"
#include "openmc/random_lcg.h"
#include "openmc/search.h"

#include "xtensor/xview.hpp"

#include <cmath> // for log, exp

namespace openmc {

// Helper function to get index on incident energy grid
void
get_energy_index(const std::vector<double>& energies, double E, int& i, double& f)
{
  // Get index and interpolation factor for elastic grid
  i = 0;
  f = 0.0;
  if (E >= energies.front()) {
    i = lower_bound_index(energies.begin(), energies.end(), E);
    f = (E - energies[i]) / (energies[i+1] - energies[i]);
  }
}

//==============================================================================
// CoherentElasticAE implementation
//==============================================================================

CoherentElasticAE::CoherentElasticAE(const CoherentElasticXS& xs)
  : xs_{xs}
{ }

void
CoherentElasticAE::sample(double E_in, double& E_out, double& mu) const
{
  // Get index and interpolation factor for elastic grid
  int i;
  double f;
  const auto& energies {xs_.bragg_edges()};
  get_energy_index(energies, E_in, i, f);

  // Sample a Bragg edge between 1 and i
  const auto& factors = xs_.factors();
  double prob = prn() * factors[i+1];
  int k = 0;
  if (prob >= factors.front()) {
    k = lower_bound_index(factors.begin(), factors.begin() + (i+1), prob);
  }

  // Characteristic scattering cosine for this Bragg edge (ENDF-102, Eq. 7-2)
  mu = 1.0 - 2.0*energies[k] / E_in;

  // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7-1)
  E_out = E_in;
}

//==============================================================================
// IncoherentElasticAE implementation
//==============================================================================

IncoherentElasticAE::IncoherentElasticAE(hid_t group)
{
  read_dataset(group, "debye_waller", debye_waller_);
}

void
IncoherentElasticAE::sample(double E_in, double& E_out, double& mu) const
{
  // Sample angle by inverting the distribution in ENDF-102, Eq. 7.4
  double c = 2 * E_in * debye_waller_;
  mu = std::log(1.0 + prn()*(std::exp(2.0*c) - 1))/c - 1.0;

  // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7.4)
  E_out = E_in;
}

//==============================================================================
// IncoherentElasticAEDiscrete implementation
//==============================================================================

IncoherentElasticAEDiscrete::IncoherentElasticAEDiscrete(hid_t group,
  const std::vector<double>& energy)
  : energy_{energy}
{
  read_dataset(group, "mu_out", mu_out_);
}

void
IncoherentElasticAEDiscrete::sample(double E_in, double& E_out, double& mu) const
{
  // Get index and interpolation factor for elastic grid
  int i;
  double f;
  get_energy_index(energy_, E_in, i, f);

  // Interpolate between two discrete cosines corresponding to neighboring
  // incoming energies.

  // Sample outgoing cosine bin
  int n_mu = mu_out_.shape()[1];
  int k = prn() * n_mu;

  // Rather than use the sampled discrete mu directly, it is smeared over
  // a bin of width 0.5*min(mu[k] - mu[k-1], mu[k+1] - mu[k]) centered on the
  // discrete mu value itself.

  // Interpolate kth mu value between distributions at energies i and i+1
  mu = mu_out_(i, k) + f*(mu_out_(i+1, k) - mu_out_(i, k));

  // Inteprolate (k-1)th mu value between distributions at energies i and i+1.
  // When k==0, pick a value that will smear the cosine out to a minimum of -1.
  double mu_left = (k == 0) ?
    -1.0 - (mu + 1.0) :
    mu_out_(i, k-1) + f*(mu_out_(i+1, k-1) - mu_out_(i, k-1));

  // Inteprolate (k+1)th mu value between distributions at energies i and i+1.
  // When k is the last discrete value, pick a value that will smear the cosine
  // out to a maximum of 1.
  double mu_right = (k == n_mu - 1) ?
    1.0 + (1.0 - mu) :
    mu_out_(i, k+1) + f*(mu_out_(i+1, k+1) - mu_out_(i, k+1));

  // Smear cosine
  mu += std::min(mu - mu_left, mu_right - mu)*(prn() - 0.5);

  // Energy doesn't change in elastic scattering
  E_out = E_in;
}

//==============================================================================
// IncoherentInelasticAEDiscrete implementation
//==============================================================================

IncoherentInelasticAEDiscrete::IncoherentInelasticAEDiscrete(hid_t group,
  const std::vector<double>& energy)
  : energy_{energy}
{
  read_dataset(group, "energy_out", energy_out_);
  read_dataset(group, "mu_out", mu_out_);
  read_dataset(group, "skewed", skewed_);
}

void
IncoherentInelasticAEDiscrete::sample(double E_in, double& E_out, double& mu) const
{
  // Get index and interpolation factor for inelastic grid
  int i;
  double f;
  get_energy_index(energy_, E_in, i, f);

  // Now that we have an incoming energy bin, we need to determine the outgoing
  // energy bin. This will depend on whether the outgoing energy distribution is
  // skewed. If it is skewed, then the first two and last two bins have lower
  // probabilities than the other bins (0.1 for the first and last bins and 0.4
  // for the second and second to last bins, relative to a normal bin
  // probability of 1). Otherwise, each bin is equally probable.

  int j;
  int n = energy_out_.shape()[1];
  if (!skewed_) {
    // All bins equally likely
    j = prn() * n;
  } else {
    // Distribution skewed away from edge points
    double r = prn() * (n - 3);
    if (r > 1.0) {
      // equally likely N-4 middle bins
      j = r + 1;
    } else if (r > 0.6) {
      // second to last bin has relative probability of 0.4
      j = n - 2;
    } else if (r > 0.5) {
      // last bin has relative probability of 0.1
      j = n - 1;
    } else if (r > 0.1) {
      // second bin has relative probability of 0.4
      j = 1;
    } else {
      // first bin has relative probability of 0.1
      j = 0;
    }
  }

  // Determine outgoing energy corresponding to E_in[i] and E_in[i+1]
  double E_ij  = energy_out_(i, j);
  double E_i1j = energy_out_(i+1, j);

  // Outgoing energy
  E_out = (1 - f)*E_ij + f*E_i1j;

  // Sample outgoing cosine bin
  int m = mu_out_.shape()[2];
  int k = prn() * m;

  // Determine outgoing cosine corresponding to E_in[i] and E_in[i+1]
  double mu_ijk  = mu_out_(i, j, k);
  double mu_i1jk = mu_out_(i+1, j, k);

  // Cosine of angle between incoming and outgoing neutron
  mu = (1 - f)*mu_ijk + f*mu_i1jk;
}

//==============================================================================
// IncoherentInelasticAE implementation
//==============================================================================

IncoherentInelasticAE::IncoherentInelasticAE(hid_t group)
{
  // Read correlated angle-energy distribution
  CorrelatedAngleEnergy dist {group};

  // Copy incident energies
  energy_ = dist.energy();

  // Convert to S(a,b) native format
  for (const auto& edist : dist.distribution()) {
    // Create temporary distribution
    DistEnergySab d;

    // Copy outgoing energy distribution
    d.n_e_out = edist.e_out.size();
    d.e_out = edist.e_out;
    d.e_out_pdf = edist.p;
    d.e_out_cdf = edist.c;

    for (int j = 0; j < d.n_e_out; ++j) {
      auto adist = dynamic_cast<Tabular*>(edist.angle[j].get());
      if (adist) {
        // On first pass, allocate space for angles
        if (j == 0) {
          auto n_mu = adist->x().size();
          d.mu = xt::empty<double>({d.n_e_out, n_mu});
        }

        // Copy outgoing angles
        auto mu_j = xt::view(d.mu, j);
        std::copy(adist->x().begin(), adist->x().end(), mu_j.begin());
      }
    }

    distribution_.emplace_back(std::move(d));
  }

}

void
IncoherentInelasticAE::sample(double E_in, double& E_out, double& mu) const
{
  // Get index and interpolation factor for inelastic grid
  int i;
  double f;
  get_energy_index(energy_, E_in, i, f);

  // Pick closer energy based on interpolation factor
  int l = f > 0.5 ? i + 1 : i;

  // Determine endpoints on grid i
  auto n = distribution_[i].e_out.size();
  double E_i_1 = distribution_[i].e_out[0];
  double E_i_J = distribution_[i].e_out[n - 1];

  // Determine endpoints on grid i + 1
  n = distribution_[i + 1].e_out.size();
  double E_i1_1 = distribution_[i + 1].e_out[0];
  double E_i1_J = distribution_[i + 1].e_out[n - 1];

  double E_1 = E_i_1 + f * (E_i1_1 - E_i_1);
  double E_J = E_i_J + f * (E_i1_J - E_i_J);

  // Determine outgoing energy bin
  // (First reset n_energy_out to the right value)
  n = distribution_[l].n_e_out;
  double r1 = prn();
  double c_j = distribution_[l].e_out_cdf[0];
  double c_j1;
  std::size_t j;
  for (j = 0; j < n - 1; ++j) {
    c_j1 = distribution_[l].e_out_cdf[j + 1];
    if (r1 < c_j1) break;
    c_j = c_j1;
  }

  // check to make sure j is <= n_energy_out - 2
  j = std::min(j, n - 2);

  // Get the data to interpolate between
  double E_l_j = distribution_[l].e_out[j];
  double p_l_j = distribution_[l].e_out_pdf[j];

  // Next part assumes linear-linear interpolation in standard
  double E_l_j1 = distribution_[l].e_out[j + 1];
  double p_l_j1 = distribution_[l].e_out_pdf[j + 1];

  // Find secondary energy (variable E)
  double frac = (p_l_j1 - p_l_j) / (E_l_j1 - E_l_j);
  if (frac == 0.0) {
    E_out = E_l_j + (r1 - c_j) / p_l_j;
  } else {
    E_out = E_l_j + (std::sqrt(std::max(0.0, p_l_j*p_l_j +
          2.0*frac*(r1 - c_j))) - p_l_j) / frac;
  }

  // Adjustment of outgoing energy
  double E_l = energy_[l];
  if (E_out < 0.5*E_l) {
    E_out *= 2.0*E_in/E_l - 1.0;
  } else {
    E_out += E_in - E_l;
  }

  // Sample outgoing cosine bin
  int n_mu = distribution_[l].mu.shape()[1];
  std::size_t k = prn() * n_mu;

  // Rather than use the sampled discrete mu directly, it is smeared over
  // a bin of width 0.5*min(mu[k] - mu[k-1], mu[k+1] - mu[k]) centered on the
  // discrete mu value itself.
  const auto& mu_l = distribution_[l].mu;
  f = (r1 - c_j)/(c_j1 - c_j);

  // Interpolate kth mu value between distributions at energies j and j+1
  mu = mu_l(j, k) + f*(mu_l(j+1, k) - mu_l(j, k));

  // Inteprolate (k-1)th mu value between distributions at energies j and j+1.
  // When k==0, pick a value that will smear the cosine out to a minimum of -1.
  double mu_left = (k == 0) ?
    mu_left = -1.0 - (mu + 1.0) :
    mu_left = mu_l(j, k-1) + f*(mu_l(j+1, k-1) - mu_l(j, k-1));

  // Inteprolate (k+1)th mu value between distributions at energies j and j+1.
  // When k is the last discrete value, pick a value that will smear the cosine
  // out to a maximum of 1.
  double mu_right = (k == n_mu - 1) ?
    mu_right = 1.0 + (1.0 - mu) :
    mu_right = mu_l(j, k+1) + f*(mu_l(j+1, k+1) - mu_l(j, k+1));

  // Smear cosine
  mu += std::min(mu - mu_left, mu_right - mu)*(prn() - 0.5);
}

} // namespace openmc
<|endoftext|>


<commit_before>/* libs/graphics/images/SkImageDecoder.cpp
**
** Copyright 2006, The Android Open Source Project
**
** Licensed 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 "SkImageDecoder.h"
#include "SkBitmap.h"
#include "SkPixelRef.h"
#include "SkStream.h"
#include "SkTemplates.h"

static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config;

SkBitmap::Config SkImageDecoder::GetDeviceConfig()
{
    return gDeviceConfig;
}

void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config)
{
    gDeviceConfig = config;
}

///////////////////////////////////////////////////////////////////////////////

SkImageDecoder::SkImageDecoder()
    : fPeeker(NULL), fChooser(NULL), fAllocator(NULL), fSampleSize(1),
      fDefaultPref(SkBitmap::kNo_Config), fDitherImage(true),
      fUsePrefTable(false) {
          SkDebugf("--------- sizeof(fPrefTable) = %d\n", sizeof(fPrefTable));
}

SkImageDecoder::~SkImageDecoder() {
    fPeeker->safeUnref();
    fChooser->safeUnref();
    fAllocator->safeUnref();
}

SkImageDecoder::Format SkImageDecoder::getFormat() const {
    return kUnknown_Format;
}

SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
    SkRefCnt_SafeAssign(fPeeker, peeker);
    return peeker;
}

SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
    SkRefCnt_SafeAssign(fChooser, chooser);
    return chooser;
}

SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
    SkRefCnt_SafeAssign(fAllocator, alloc);
    return alloc;
}

void SkImageDecoder::setSampleSize(int size) {
    if (size < 1) {
        size = 1;
    }
    fSampleSize = size;
}

bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width,
                                         int height) const {
    Chooser* chooser = fChooser;

    if (NULL == chooser) {    // no chooser, we just say YES to decoding :)
        return true;
    }
    chooser->begin(1);
    chooser->inspect(0, config, width, height);
    return chooser->choose() == 0;
}

bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
                                   SkColorTable* ctable) const {
    return bitmap->allocPixels(fAllocator, ctable);
}

///////////////////////////////////////////////////////////////////////////////

void SkImageDecoder::setPrefConfigTable(const SkBitmap::Config pref[6]) {
    if (NULL == pref) {
        fUsePrefTable = false;
    } else {
        fUsePrefTable = true;
        memcpy(fPrefTable, pref, sizeof(fPrefTable));
    }
}

SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
                                               bool srcHasAlpha) const {
    SkBitmap::Config config;

    if (fUsePrefTable) {
        int index = 0;
        switch (srcDepth) {
            case kIndex_SrcDepth:
                index = 0;
                break;
            case k16Bit_SrcDepth:
                index = 2;
                break;
            case k32Bit_SrcDepth:
                index = 4;
                break;
        }
        if (srcHasAlpha) {
            index += 1;
        }
        config = fPrefTable[index];
    } else {
        config = fDefaultPref;
    }

    if (SkBitmap::kNo_Config == config) {
        config = SkImageDecoder::GetDeviceConfig();
    }
    return config;
}

bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
                            SkBitmap::Config pref, Mode mode) {
    // pass a temporary bitmap, so that if we return false, we are assured of
    // leaving the caller's bitmap untouched.
    SkBitmap    tmp;

    // we reset this to false before calling onDecode
    fShouldCancelDecode = false;
    // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
    fDefaultPref = pref;

    if (!this->onDecode(stream, &tmp, mode)) {
        return false;
    }
    bm->swap(tmp);
    return true;
}

///////////////////////////////////////////////////////////////////////////////

bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
                            SkBitmap::Config pref,  Mode mode, Format* format) {
    SkASSERT(file);
    SkASSERT(bm);

    SkFILEStream    stream(file);
    if (stream.isValid()) {
        if (SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format)) {
            bm->pixelRef()->setURI(file);
        }
        return true;
    }
    return false;
}

bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
                          SkBitmap::Config pref, Mode mode, Format* format) {
    if (0 == size) {
        return false;
    }
    SkASSERT(buffer);

    SkMemoryStream  stream(buffer, size);
    return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
}

bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
                          SkBitmap::Config pref, Mode mode, Format* format) {
    SkASSERT(stream);
    SkASSERT(bm);

    bool success = false;
    SkImageDecoder* codec = SkImageDecoder::Factory(stream);

    if (NULL != codec) {
        success = codec->decode(stream, bm, pref, mode);
        if (success && format) {
            *format = codec->getFormat();
        }
        delete codec;
    }
    return success;
}

<commit_msg>remove debugging printf<commit_after>/* libs/graphics/images/SkImageDecoder.cpp
**
** Copyright 2006, The Android Open Source Project
**
** Licensed 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 "SkImageDecoder.h"
#include "SkBitmap.h"
#include "SkPixelRef.h"
#include "SkStream.h"
#include "SkTemplates.h"

static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config;

SkBitmap::Config SkImageDecoder::GetDeviceConfig()
{
    return gDeviceConfig;
}

void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config)
{
    gDeviceConfig = config;
}

///////////////////////////////////////////////////////////////////////////////

SkImageDecoder::SkImageDecoder()
    : fPeeker(NULL), fChooser(NULL), fAllocator(NULL), fSampleSize(1),
      fDefaultPref(SkBitmap::kNo_Config), fDitherImage(true),
      fUsePrefTable(false) {
}

SkImageDecoder::~SkImageDecoder() {
    fPeeker->safeUnref();
    fChooser->safeUnref();
    fAllocator->safeUnref();
}

SkImageDecoder::Format SkImageDecoder::getFormat() const {
    return kUnknown_Format;
}

SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
    SkRefCnt_SafeAssign(fPeeker, peeker);
    return peeker;
}

SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
    SkRefCnt_SafeAssign(fChooser, chooser);
    return chooser;
}

SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
    SkRefCnt_SafeAssign(fAllocator, alloc);
    return alloc;
}

void SkImageDecoder::setSampleSize(int size) {
    if (size < 1) {
        size = 1;
    }
    fSampleSize = size;
}

bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width,
                                         int height) const {
    Chooser* chooser = fChooser;

    if (NULL == chooser) {    // no chooser, we just say YES to decoding :)
        return true;
    }
    chooser->begin(1);
    chooser->inspect(0, config, width, height);
    return chooser->choose() == 0;
}

bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
                                   SkColorTable* ctable) const {
    return bitmap->allocPixels(fAllocator, ctable);
}

///////////////////////////////////////////////////////////////////////////////

void SkImageDecoder::setPrefConfigTable(const SkBitmap::Config pref[6]) {
    if (NULL == pref) {
        fUsePrefTable = false;
    } else {
        fUsePrefTable = true;
        memcpy(fPrefTable, pref, sizeof(fPrefTable));
    }
}

SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
                                               bool srcHasAlpha) const {
    SkBitmap::Config config;

    if (fUsePrefTable) {
        int index = 0;
        switch (srcDepth) {
            case kIndex_SrcDepth:
                index = 0;
                break;
            case k16Bit_SrcDepth:
                index = 2;
                break;
            case k32Bit_SrcDepth:
                index = 4;
                break;
        }
        if (srcHasAlpha) {
            index += 1;
        }
        config = fPrefTable[index];
    } else {
        config = fDefaultPref;
    }

    if (SkBitmap::kNo_Config == config) {
        config = SkImageDecoder::GetDeviceConfig();
    }
    return config;
}

bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
                            SkBitmap::Config pref, Mode mode) {
    // pass a temporary bitmap, so that if we return false, we are assured of
    // leaving the caller's bitmap untouched.
    SkBitmap    tmp;

    // we reset this to false before calling onDecode
    fShouldCancelDecode = false;
    // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
    fDefaultPref = pref;

    if (!this->onDecode(stream, &tmp, mode)) {
        return false;
    }
    bm->swap(tmp);
    return true;
}

///////////////////////////////////////////////////////////////////////////////

bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
                            SkBitmap::Config pref,  Mode mode, Format* format) {
    SkASSERT(file);
    SkASSERT(bm);

    SkFILEStream    stream(file);
    if (stream.isValid()) {
        if (SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format)) {
            bm->pixelRef()->setURI(file);
        }
        return true;
    }
    return false;
}

bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
                          SkBitmap::Config pref, Mode mode, Format* format) {
    if (0 == size) {
        return false;
    }
    SkASSERT(buffer);

    SkMemoryStream  stream(buffer, size);
    return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
}

bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
                          SkBitmap::Config pref, Mode mode, Format* format) {
    SkASSERT(stream);
    SkASSERT(bm);

    bool success = false;
    SkImageDecoder* codec = SkImageDecoder::Factory(stream);

    if (NULL != codec) {
        success = codec->decode(stream, bm, pref, mode);
        if (success && format) {
            *format = codec->getFormat();
        }
        delete codec;
    }
    return success;
}

<|endoftext|>


<commit_before><commit_msg>typo: equantion -> equation<commit_after><|endoftext|>


<commit_before>/* indivudal.cpp - CS 472 Project #2: Genetic Programming
 * Copyright 2014 Andrew Schwartzmeyer
 *
 * Source file for Individual
 */

#include <algorithm>
#include <cassert>
#include <cmath>
#include <iostream>
#include <memory>
#include <tuple>
#include <vector>

#include "individual.hpp"
#include "../problem/problem.hpp"
#include "../random_generator/random_generator.hpp"

namespace individual {
  using std::vector;
  using std::string;
  using problem::Problem;
  using namespace random_generator;

  // vectors of same-arity function enums
  vector<Function> terminals {constant, input};
  vector<Function> unaries {sqrt, sin, cos, log, exp};
  vector<Function> binaries {add, subtract, multiply, divide, pow};
  vector<Function> quadnaries {lesser, greater};
  vector<Function> internals {add, subtract, multiply, divide, lesser, greater};

  template<typename I, typename S> bool contains(const I & item, const S & set) {
    return std::find(set.begin(), set.end(), item) != set.end();
  }

  Node::Node(const Problem & problem, const int & depth) {
    if (depth < problem.max_depth) {
      // assign random internal function
      int_dist dist(0, internals.size() - 1); // closed interval
      function = Function(internals[dist(rg.engine)]);
      assert(contains(function, internals));
      // determine node's arity
      if (contains(function, unaries)) arity = 1;
      else if (contains(function, binaries)) arity = 2;
      else if (contains(function, quadnaries)) arity = 4;
      assert(arity != 0);
      // recursively create subtrees
      for (int i = 0; i < arity; i++)
	children.emplace_back(Node(problem, depth + 1));
    } else {
      // reached max depth, assign random terminal function
      int_dist dist(0, terminals.size() - 1); // closed interval
      function = Function(terminals[dist(rg.engine)]);
      assert(arity == 0);
      // setup constant function; input is provided on evaluation
      if (function == constant) {
	// choose a random value between the problem's min and max
	real_dist dist(problem.constant_min, problem.constant_max);
	k = dist(rg.engine);
      }
    }
    assert(function != null); // do not create null types
  }

  string Node::represent() const {
    switch(function) {
    case null:
      assert(false); // never represent empty node
    case constant:
      return std::to_string(k);
    case input:
      return "x";
    case sqrt:
      return " sqrt";
    case sin:
      return " sin";
    case cos:
      return " cos";
    case log:
      return " log";
    case exp:
      return " exp";
    case add:
      return " +";
    case subtract:
      return " -";
    case multiply:
      return " *";
    case divide:
      return " /";
    case pow:
      return " ^";
    case lesser:
      return " a < b ? c : d";
    case greater:
      return " a > b ? c : d";
    }
  }

  string Node::print() const {
    // Post-order traversal print of expression in RPN/postfix notation
    string formula = "(";
    for (auto child : children)
      formula += child.print();
    return formula + represent() + ")";
  }

  double Node::evaluate(const double & x) const {
    // depth-first post-order recursive evaluation tree
    double a, b, c, d;
    if (arity == 1)
      a = children[0].evaluate(x);
    else if (arity == 2) {
      a = children[0].evaluate(x);
      b = children[1].evaluate(x);
    }
    else if (arity == 4) {
      a = children[0].evaluate(x);
      b = children[1].evaluate(x);
      c = children[2].evaluate(x);
      d = children[3].evaluate(x);
    }
    // calculate the result
    switch(function) {
    case null:
      assert(false); // never calculate empty node
    case constant:
      return k;
    case input:
      return x;
    case sqrt:
      return std::sqrt(std::abs(a)); // protected
    case sin:
      return std::sin(a);
    case cos:
      return std::cos(a);
    case log:
      return (a == 0) ? 0 : std::log(std::abs(a)); // protected
    case exp:
      return std::exp(a);
    case add:
      return a + b;
    case subtract:
      return a - b;
    case multiply:
      return a * b;
    case divide:
      return (b == 0) ? 1 : a / b; // protected
    case pow:
      return std::pow(std::abs(a), std::abs(b)); // protected
    case lesser:
      return (a < b) ? c : d;
    case greater:
      return (a > b) ? c : d;
    }
  }

  const Size Node::size() const {
    // recursively count children via post-order traversal
    // keep track of internals and leafs via Size struct
    Size size;
    for (const Node & child : children) {
      Size temp = child.size(); // is this micro-optimizing?
      size.internals += temp.internals;
      size.leafs += temp.leafs;
    }
    if (children.size() == 0) ++size.leafs;
    else ++size.internals;
    return size;
  }

  Node empty;

  Node & Node::visit(const Size & i, Size & visiting) {
    // depth-first search for taget node, either internal or leaf
    for (Node & child : children) {
      // increase relevant count
      if (child.children.size() == 0) ++visiting.leafs;
      else ++visiting.internals;
      // return node reference if found
      if (visiting.internals == i.internals or visiting.leafs == i.leafs)
	return child;
      Node & temp = child.visit(i, visiting); // mark each node
      if (temp.function != null) return temp; // return found node
    }
    return empty; // need to indicate "not-found"
  }

  void Node::mutate_self(const double & min, const double & max) {
    // single node mutation to different function of same arity
    if (arity == 0) {
      // mutate constant to a value in its neighborhood, don't switch functions
      if (function == constant) {
	normal_dist dist{0, 1};
	k *= 1 + dist(rg.engine);
      }
    }
    else if (arity == 1) {
      int_dist dist{0, int(unaries.size()) - 1};
      Function prior = function;
      // ensure we're using a specified available function
      while (function == prior or not contains(function, internals))
	function = Function(unaries[dist(rg.engine)]);
    }
    else if (arity == 2) {
      int_dist dist{0, int(binaries.size()) - 1};
      Function prior = function;
      while (function == prior or not contains(function, internals))
	function = Function(binaries[dist(rg.engine)]);
    }
    else if (arity == 4) {
      int_dist dist{0, int(quadnaries.size()) - 1};
      Function prior = function;
      while (function == prior or not contains(function, internals))
	function = Function(quadnaries[dist(rg.engine)]);
    }
    assert(function != null);
  }

  void Node::mutate_tree(const double & chance, const double & min, const double & max) {
    // recursively mutate nodes with problem.mutate_chance probability
    real_dist dist{0, 1};
    for (Node & child : children) {
      child.mutate_tree(chance, min, max);
      if (dist(rg.engine) < chance) child.mutate_self();
    }
  }

  Individual::Individual(const Problem & problem): root{Node{problem}} {
    update_size();
    evaluate(problem.values);
  }

  string Individual::print_formula() const {
    using std::to_string;
    string formula = "Expression tree of size " + to_string(get_total())
      + " with " + to_string(get_internals()) + " internals"
      + " and " + to_string(get_leafs()) + " leafs"
      + " has the following formula: " + "\n"
      + root.print() + "\n";
    return formula;
  }

  string Individual::print_calculation(const problem::pairs & values) const {
    using std::to_string;
    using std::get;
    double fitness = 0;
    string calculation;
    for (auto pair : values) {
      double output = root.evaluate(get<0>(pair));
      double error = std::pow(output - get<1>(pair), 2);
      fitness += error;
      calculation += + "f(" + to_string(get<0>(pair))
	+ ") = " + to_string(output)
	+ ", expected " + to_string(get<1>(pair))
	+ ", error = " + to_string(error) + "\n";
    }
    calculation += "Total fitness: " + to_string(std::sqrt(fitness)) + ".\n";
    return calculation;
  }

  void Individual::update_size() {
    size = root.size();
  }

  void Individual::evaluate(const problem::pairs & values) {
    double error = 0;
    for (auto pair : values) {
      double output = root.evaluate(std::get<0>(pair));
      assert(not std::isnan(output) and not std::isinf(output));
      error += std::pow(output - std::get<1>(pair), 2);
    }
    fitness = error;
    // update size on evaluation because it's incredibly convenient
    update_size();
  }

  void Individual::mutate(const double & chance, const double & min, const double & max) {
    // mutate each node with a problem.mutate_chance probability
    root.mutate_tree(chance, min, max);
  }

  Node & Individual::operator[](const Size & i) {
    assert(i.internals <= get_internals());
    assert(i.leafs <= get_leafs());
    Size visiting;
    return root.visit(i, visiting);
  }

  void crossover(const double & chance, Individual & a, Individual & b) {
    real_dist probability{0, 1};
    Size target_a, target_b;
    if (probability(rg.engine) < chance) {
      // choose an internal node
      int_dist dist{0, a.get_internals() - 1};
      target_a.internals = dist(rg.engine);
    } else {
      // otherwise we choose a leaf node
      int_dist dist{0, a.get_leafs() - 1};
      target_a.leafs = dist(rg.engine);
    }
    // do the same thing for the second individual
    if (probability(rg.engine) < chance) {
      int_dist dist{0, b.get_internals() - 1};
      target_b.internals = dist(rg.engine);
    } else {
      int_dist dist{0, b.get_leafs() - 1};
      target_b.leafs = dist(rg.engine);
    }
    // replace nodes
    std::swap(a[target_a], b[target_b]);
  }
}
<commit_msg>Refactoring to add get_function methods<commit_after>/* indivudal.cpp - CS 472 Project #2: Genetic Programming
 * Copyright 2014 Andrew Schwartzmeyer
 *
 * Source file for Individual
 */

#include <algorithm>
#include <cassert>
#include <cmath>
#include <iostream>
#include <memory>
#include <tuple>
#include <vector>

#include "individual.hpp"
#include "../problem/problem.hpp"
#include "../random_generator/random_generator.hpp"

namespace individual {
  using std::vector;
  using std::string;
  using problem::Problem;
  using namespace random_generator;

  // vectors of same-arity function enums
  vector<Function> nullaries{constant, input};
  vector<Function> unaries{sqrt, sin, cos, log, exp};
  vector<Function> binaries{add, subtract, multiply, divide, pow};
  vector<Function> quadnaries{lesser, greater};
  vector<Function> internals{add, subtract, multiply, divide, lesser, greater};

  template<typename I, typename S> bool contains(const I & item, const S & set) {
    return std::find(set.begin(), set.end(), item) != set.end();
  }

  Function get_internal() {
    int_dist dist{0, int(internals.size()) - 1}; // closed interval
    return Function(internals[dist(rg.engine)]);
  }

  Function get_leaf() {
    int_dist dist{0, int(nullaries.size()) - 1}; // closed interval
    return Function(nullaries[dist(rg.engine)]);
  }

  double get_constant(const double & min, const double & max) {
    real_dist dist{min, max};
    return dist(rg.engine);
  }

  int get_arity(const Function & function) {
    if (contains(function, nullaries)) return 0;
    else if (contains(function, unaries)) return 1;
    else if (contains(function, binaries)) return 2;
    else if (contains(function, quadnaries)) return 4;
    assert(false);
  }

  Node::Node(const Problem & problem, const int & depth) {
    if (depth < problem.max_depth) {
      // assign random internal function
      int_dist dist(0, internals.size() - 1); // closed interval
      function = Function(internals[dist(rg.engine)]);
      assert(contains(function, internals));
      // determine node's arity
      if (contains(function, unaries)) arity = 1;
      else if (contains(function, binaries)) arity = 2;
      else if (contains(function, quadnaries)) arity = 4;
      assert(arity != 0);
      // recursively create subtrees
      for (int i = 0; i < arity; i++)
	children.emplace_back(Node(problem, depth + 1));
    } else {
      // reached max depth, assign random terminal function
      int_dist dist(0, terminals.size() - 1); // closed interval
      function = Function(terminals[dist(rg.engine)]);
      assert(arity == 0);
      // setup constant function; input is provided on evaluation
      if (function == constant) {
	// choose a random value between the problem's min and max
	real_dist dist(problem.constant_min, problem.constant_max);
	k = dist(rg.engine);
      }
    }
    assert(function != null); // do not create null types
  }

  string Node::represent() const {
    switch(function) {
    case null:
      assert(false); // never represent empty node
    case constant:
      return std::to_string(k);
    case input:
      return "x";
    case sqrt:
      return " sqrt";
    case sin:
      return " sin";
    case cos:
      return " cos";
    case log:
      return " log";
    case exp:
      return " exp";
    case add:
      return " +";
    case subtract:
      return " -";
    case multiply:
      return " *";
    case divide:
      return " /";
    case pow:
      return " ^";
    case lesser:
      return " a < b ? c : d";
    case greater:
      return " a > b ? c : d";
    }
  }

  string Node::print() const {
    // Post-order traversal print of expression in RPN/postfix notation
    string formula = "(";
    for (auto child : children)
      formula += child.print();
    return formula + represent() + ")";
  }

  double Node::evaluate(const double & x) const {
    // depth-first post-order recursive evaluation tree
    double a, b, c, d;
    if (arity == 1)
      a = children[0].evaluate(x);
    else if (arity == 2) {
      a = children[0].evaluate(x);
      b = children[1].evaluate(x);
    }
    else if (arity == 4) {
      a = children[0].evaluate(x);
      b = children[1].evaluate(x);
      c = children[2].evaluate(x);
      d = children[3].evaluate(x);
    }
    // calculate the result
    switch(function) {
    case null:
      assert(false); // never calculate empty node
    case constant:
      return k;
    case input:
      return x;
    case sqrt:
      return std::sqrt(std::abs(a)); // protected
    case sin:
      return std::sin(a);
    case cos:
      return std::cos(a);
    case log:
      return (a == 0) ? 0 : std::log(std::abs(a)); // protected
    case exp:
      return std::exp(a);
    case add:
      return a + b;
    case subtract:
      return a - b;
    case multiply:
      return a * b;
    case divide:
      return (b == 0) ? 1 : a / b; // protected
    case pow:
      return std::pow(std::abs(a), std::abs(b)); // protected
    case lesser:
      return (a < b) ? c : d;
    case greater:
      return (a > b) ? c : d;
    }
  }

  const Size Node::size() const {
    // recursively count children via post-order traversal
    // keep track of internals and leafs via Size struct
    Size size;
    for (const Node & child : children) {
      Size temp = child.size(); // is this micro-optimizing?
      size.internals += temp.internals;
      size.leafs += temp.leafs;
    }
    if (children.size() == 0) ++size.leafs;
    else ++size.internals;
    return size;
  }

  Node empty;

  Node & Node::visit(const Size & i, Size & visiting) {
    // depth-first search for taget node, either internal or leaf
    for (Node & child : children) {
      // increase relevant count
      if (child.children.size() == 0) ++visiting.leafs;
      else ++visiting.internals;
      // return node reference if found
      if (visiting.internals == i.internals or visiting.leafs == i.leafs)
	return child;
      Node & temp = child.visit(i, visiting); // mark each node
      if (temp.function != null) return temp; // return found node
    }
    return empty; // need to indicate "not-found"
  }

  void Node::mutate_self(const double & min, const double & max) {
    // single node mutation to different function of same arity
    if (arity == 0) {
      // mutate constant to a value in its neighborhood, don't switch functions
      if (function == constant) {
	normal_dist dist{0, 1};
	k *= 1 + dist(rg.engine);
      }
    }
    else if (arity == 1) {
      int_dist dist{0, int(unaries.size()) - 1};
      Function prior = function;
      // ensure we're using a specified available function
      while (function == prior or not contains(function, internals))
	function = Function(unaries[dist(rg.engine)]);
    }
    else if (arity == 2) {
      int_dist dist{0, int(binaries.size()) - 1};
      Function prior = function;
      while (function == prior or not contains(function, internals))
	function = Function(binaries[dist(rg.engine)]);
    }
    else if (arity == 4) {
      int_dist dist{0, int(quadnaries.size()) - 1};
      Function prior = function;
      while (function == prior or not contains(function, internals))
	function = Function(quadnaries[dist(rg.engine)]);
    }
    assert(function != null);
  }

  void Node::mutate_tree(const double & chance, const double & min, const double & max) {
    // recursively mutate nodes with problem.mutate_chance probability
    real_dist dist{0, 1};
    for (Node & child : children) {
      child.mutate_tree(chance, min, max);
      if (dist(rg.engine) < chance) child.mutate_self();
    }
  }

  Individual::Individual(const Problem & problem): root{Node{problem}} {
    update_size();
    evaluate(problem.values);
  }

  string Individual::print_formula() const {
    using std::to_string;
    string formula = "Expression tree of size " + to_string(get_total())
      + " with " + to_string(get_internals()) + " internals"
      + " and " + to_string(get_leafs()) + " leafs"
      + " has the following formula: " + "\n"
      + root.print() + "\n";
    return formula;
  }

  string Individual::print_calculation(const problem::pairs & values) const {
    using std::to_string;
    using std::get;
    double fitness = 0;
    string calculation;
    for (auto pair : values) {
      double output = root.evaluate(get<0>(pair));
      double error = std::pow(output - get<1>(pair), 2);
      fitness += error;
      calculation += + "f(" + to_string(get<0>(pair))
	+ ") = " + to_string(output)
	+ ", expected " + to_string(get<1>(pair))
	+ ", error = " + to_string(error) + "\n";
    }
    calculation += "Total fitness: " + to_string(std::sqrt(fitness)) + ".\n";
    return calculation;
  }

  void Individual::update_size() {
    size = root.size();
  }

  void Individual::evaluate(const problem::pairs & values) {
    double error = 0;
    for (auto pair : values) {
      double output = root.evaluate(std::get<0>(pair));
      assert(not std::isnan(output) and not std::isinf(output));
      error += std::pow(output - std::get<1>(pair), 2);
    }
    fitness = error;
    // update size on evaluation because it's incredibly convenient
    update_size();
  }

  void Individual::mutate(const double & chance, const double & min, const double & max) {
    // mutate each node with a problem.mutate_chance probability
    root.mutate_tree(chance, min, max);
  }

  Node & Individual::operator[](const Size & i) {
    assert(i.internals <= get_internals());
    assert(i.leafs <= get_leafs());
    Size visiting;
    return root.visit(i, visiting);
  }

  void crossover(const double & chance, Individual & a, Individual & b) {
    real_dist probability{0, 1};
    Size target_a, target_b;
    if (probability(rg.engine) < chance) {
      // choose an internal node
      int_dist dist{0, a.get_internals() - 1};
      target_a.internals = dist(rg.engine);
    } else {
      // otherwise we choose a leaf node
      int_dist dist{0, a.get_leafs() - 1};
      target_a.leafs = dist(rg.engine);
    }
    // do the same thing for the second individual
    if (probability(rg.engine) < chance) {
      int_dist dist{0, b.get_internals() - 1};
      target_b.internals = dist(rg.engine);
    } else {
      int_dist dist{0, b.get_leafs() - 1};
      target_b.leafs = dist(rg.engine);
    }
    // replace nodes
    std::swap(a[target_a], b[target_b]);
  }
}
<|endoftext|>


<commit_before>#if _WIN32
#pragma warning(disable : 4267 4996)
#endif

#include "./labelling_coordinator.h"
#include <QtOpenGLExtensions>
#include <QLoggingCategory>
#include <map>
#include <vector>
#include <memory>
#include "./labelling/clustering.h"
#include "./labelling/labels.h"
#include "./placement/occlusion_calculator.h"
#include "./placement/constraint_updater.h"
#include "./placement/persistent_constraint_updater.h"
#include "./placement/cuda_texture_mapper.h"
#include "./placement/integral_costs_calculator.h"
#include "./placement/saliency.h"
#include "./placement/labels_arranger.h"
#include "./placement/insertion_order_labels_arranger.h"
#include "./placement/randomized_labels_arranger.h"
#include "./placement/apollonius_labels_arranger.h"
#include "./placement/anchor_constraint_drawer.h"
#include "./placement/shadow_constraint_drawer.h"
#include "./graphics/buffer_drawer.h"
#include "./nodes.h"
#include "./math/eigen.h"
#include "./label_node.h"
#include "./texture_mapper_manager.h"
#include "./utils/profiler.h"
#include "./utils/profiling_statistics.h"
#include "./graphics/managers.h"

QLoggingCategory lcChan("LabellingCoordinator");

LabellingCoordinator::LabellingCoordinator(
    int layerCount, std::shared_ptr<Forces::Labeller> forcesLabeller,
    std::shared_ptr<Labels> labels, std::shared_ptr<Nodes> nodes)
  : layerCount(layerCount), forcesLabeller(forcesLabeller), labels(labels),
    nodes(nodes), clustering(labels, layerCount - 1),
    profilingStatistics("LabellingCoordinator", lcChan)
{
  occlusionCalculator =
      std::make_shared<Placement::OcclusionCalculator>(layerCount);
}

void LabellingCoordinator::initialize(
    Graphics::Gl *gl, int bufferSize,
    std::shared_ptr<Graphics::Managers> managers,
    std::shared_ptr<TextureMapperManager> textureMapperManager, int width,
    int height)
{
  qCInfo(lcChan) << "Initialize";
  this->bufferSize = Eigen::Vector2f(bufferSize, bufferSize);
  saliency = std::make_shared<Placement::Saliency>(
      textureMapperManager->getAccumulatedLayersTextureMapper(),
      textureMapperManager->getSaliencyTextureMapper());

  occlusionCalculator->initialize(textureMapperManager);
  integralCostsCalculator =
      std::make_shared<Placement::IntegralCostsCalculator>(
          textureMapperManager->getOcclusionTextureMapper(),
          textureMapperManager->getSaliencyTextureMapper(),
          textureMapperManager->getIntegralCostsTextureMapper());

  auto shaderManager = managers->getShaderManager();
  auto anchorConstraintDrawer =
      std::make_shared<AnchorConstraintDrawer>(bufferSize, bufferSize);
  anchorConstraintDrawer->initialize(gl, shaderManager);

  auto connectorShadowDrawer =
      std::make_shared<ShadowConstraintDrawer>(bufferSize, bufferSize);
  connectorShadowDrawer->initialize(gl, shaderManager);

  auto shadowConstraintDrawer =
      std::make_shared<ShadowConstraintDrawer>(bufferSize, bufferSize);
  shadowConstraintDrawer->initialize(gl, shaderManager);

  float scaleFactor = bufferSize / width;
  auto constraintUpdater = std::make_shared<ConstraintUpdater>(
      bufferSize, bufferSize, anchorConstraintDrawer, connectorShadowDrawer,
      shadowConstraintDrawer, scaleFactor);
  persistentConstraintUpdater =
      std::make_shared<PersistentConstraintUpdater>(constraintUpdater);

  insertionOrderLabelsArranger =
      std::make_shared<Placement::InsertionOrderLabelsArranger>();
  randomizedLabelsArranger =
      std::make_shared<Placement::RandomizedLabelsArranger>();

  for (int layerIndex = 0; layerIndex < layerCount; ++layerIndex)
  {
    auto labelsContainer = std::make_shared<LabelsContainer>();
    labelsInLayer.push_back(labelsContainer);
    auto labeller = std::make_shared<Placement::Labeller>(labelsContainer);
    labeller->resize(width, height);
    labeller->initialize(textureMapperManager->getIntegralCostsTextureMapper(),
                         textureMapperManager->getConstraintTextureMapper(),
                         persistentConstraintUpdater);

    auto apolloniusLabelsArranger =
        std::make_shared<Placement::ApolloniusLabelsArranger>();
    apolloniusLabelsArranger->initialize(
        textureMapperManager->getDistanceTransformTextureMapper(layerIndex),
        textureMapperManager->getOcclusionTextureMapper(),
        textureMapperManager->getApolloniusTextureMapper(layerIndex));
    apolloniusLabelsArrangers.push_back(apolloniusLabelsArranger);

    labeller->setLabelsArranger(useApollonius ? apolloniusLabelsArranger
                                              : insertionOrderLabelsArranger);

    placementLabellers.push_back(labeller);
  }
}

void LabellingCoordinator::cleanup()
{
  occlusionCalculator.reset();
  saliency.reset();
  integralCostsCalculator.reset();

  for (auto apolloniusLabelsArranger : apolloniusLabelsArrangers)
    apolloniusLabelsArranger->cleanup();

  for (auto placementLabeller : placementLabellers)
    placementLabeller->cleanup();
}

void LabellingCoordinator::setEnabled(bool enabled)
{
  labellingEnabled = enabled;
  for (auto &labelNode : nodes->getLabelNodes())
  {
    labelNode->setIsVisible(!enabled);
  }
}

bool LabellingCoordinator::update(double frameTime, bool isIdle,
                                  Eigen::Matrix4f projection,
                                  Eigen::Matrix4f view, int activeLayerNumber)
{
  Profiler profiler("update", lcChan, &profilingStatistics);

  if (!labellingEnabled)
    return false;

  this->isIdle = isIdle;
  labellerFrameData = LabellerFrameData(frameTime, projection, view);

  if (internalLabellingEnabled)
  {
    LabelPositions labelPositions;
    for (auto label : labels->getLabels())
    {
      auto ndc = labellerFrameData.project(label.anchorPosition);
      labelPositions.update(label.id, ndc, label.anchorPosition);
    }

    updateLabelPositionsInLabelNodes(labelPositions);
    return false;
  }

  saliency->runKernel();

  auto positionsNDC2d = getPlacementPositions(activeLayerNumber);
  auto positionsNDC = addDepthValueNDC(positionsNDC2d);
  LabelPositions labelPositions(positionsNDC, ndcPositionsTo3d(positionsNDC));

  if (forcesEnabled)
    labelPositions = getForcesPositions(labelPositions);

  distributeLabelsToLayers();

  updateLabelPositionsInLabelNodes(labelPositions);

  return hasChanges;
}

void LabellingCoordinator::updatePlacement()
{
  Profiler profiler("updatePlacement", lcChan, &profilingStatistics);

  if (!labellingEnabled)
    return;

  bool optimize = isIdle && optimizeOnIdle;
  bool ignoreOldPosition = !isIdle;

  float newSumOfCosts = 0.0f;
  persistentConstraintUpdater->clear();
  if (saveConstraintsInNextFrame)
  {
    persistentConstraintUpdater->save();
    saveConstraintsInNextFrame = false;
  }

  std::vector<Eigen::Vector2f> anchorPositions(labels->count());
  auto labelVector = labels->getLabels();
  for (int index = 0; index < labels->count(); index++)
  {
    auto anchor2D =
        labellerFrameData.project(labelVector[index].anchorPosition);
    anchorPositions[index] = toPixel(anchor2D.head<2>(), bufferSize);
  }

  persistentConstraintUpdater->setAnchorPositions(anchorPositions);

  for (int layerIndex = 0; layerIndex < layerCount; ++layerIndex)
  {
    occlusionCalculator->calculateFor(layerIndex);
    integralCostsCalculator->runKernel();

    auto labeller = placementLabellers[layerIndex];
    auto defaultArranger = useApollonius ? apolloniusLabelsArrangers[layerIndex]
                                         : insertionOrderLabelsArranger;
    labeller->setLabelsArranger(optimize ? randomizedLabelsArranger
                                         : defaultArranger);
    labeller->update(labellerFrameData, ignoreOldPosition);
    newSumOfCosts += labeller->getLastSumOfCosts();
  }

  if (optimize)
  {
    std::cout << "Old costs: " << sumOfCosts << "\tnew costs:" << newSumOfCosts
              << std::endl;
  }

  if (optimize && newSumOfCosts > sumOfCosts)
  {
    preserveLastResult = true;
  }
  else
  {
    preserveLastResult = false;
    sumOfCosts = newSumOfCosts;
  }
}

std::vector<float> LabellingCoordinator::updateClusters()
{
  if (!labellingEnabled)
    return std::vector<float>{ 1.0f };

  clustering.update(labellerFrameData.viewProjection);
  return clustering.getMedianClusterMembers();
}

bool LabellingCoordinator::haveLabelPositionsChanged()
{
  return labellingEnabled && hasChanges;
}

void LabellingCoordinator::resize(int width, int height)
{
  for (auto placementLabeller : placementLabellers)
    placementLabeller->resize(width, height);

  forcesLabeller->resize(width, height);
}

void LabellingCoordinator::toggleAnchorVisibility()
{
  for (auto node : nodes->getLabelNodes())
    node->isAnchorVisible = !node->isAnchorVisible;
}

void LabellingCoordinator::saveOcclusion()
{
  occlusionCalculator->saveOcclusion();
}

void LabellingCoordinator::saveConstraints()
{
  saveConstraintsInNextFrame = true;
}

void LabellingCoordinator::setCostFunctionWeights(
    Placement::CostFunctionWeights weights)
{
  for (auto placementLabeller : placementLabellers)
    placementLabeller->setCostFunctionWeights(weights);
}

std::map<int, Eigen::Vector2f>
LabellingCoordinator::getPlacementPositions(int activeLayerNumber)
{
  if (preserveLastResult)
    return lastPlacementResult;

  std::map<int, Eigen::Vector2f> placementPositions;
  int layerIndex = 0;
  for (auto placementLabeller : placementLabellers)
  {
    if (activeLayerNumber == 0 || activeLayerNumber - 1 == layerIndex)
    {
      auto newPositionsForLayer = placementLabeller->getLastPlacementResult();
      placementPositions.insert(newPositionsForLayer.begin(),
                                newPositionsForLayer.end());
    }

    layerIndex++;
  }

  lastPlacementResult = placementPositions;

  return placementPositions;
}

LabelPositions
LabellingCoordinator::getForcesPositions(LabelPositions placementPositions)
{
  if (placementPositions.size() == 0)
    return LabelPositions();

  if (firstFramesWithoutPlacement && placementPositions.size())
  {
    firstFramesWithoutPlacement = false;
    forcesLabeller->overallForceFactor = isIdle ? 6.0f : 3.0f;
    forcesLabeller->setPositions(labellerFrameData, placementPositions);
  }

  return forcesLabeller->update(labellerFrameData, placementPositions);
}

void LabellingCoordinator::distributeLabelsToLayers()
{
  auto centerWithLabelIds = clustering.getMedianClusterMembersWithLabelIds();
  labelIdToLayerIndex.clear();
  labelIdToZValue.clear();

  for (auto& layerLabels : labelsInLayer)
    layerLabels->clear();

  int layerIndex = 0;
  for (auto &pair : centerWithLabelIds)
  {
    auto &container = labelsInLayer[layerIndex];

    for (int labelId : pair.second)
    {
      container->add(labels->getById(labelId));
      labelIdToLayerIndex[labelId] = layerIndex;
      labelIdToZValue[labelId] = pair.first;
    }

    layerIndex++;
  }

  if (lcChan.isDebugEnabled())
  {
    std::stringstream output;
    int layerIndex = 0;
    for (auto layerLabels : labelsInLayer)
    {
      output << std::endl << "Layer " << layerIndex++ << "\t";
      for (auto &label : layerLabels->getLabels())
        output << "\"" << label.text << "\" (" << label.id << "), ";
    }
    qCDebug(lcChan) << "distributeLabelsToLayers: " << output.str().c_str();
  }
}

void LabellingCoordinator::updateLabelPositionsInLabelNodes(
    LabelPositions labelPositions)
{
  hasChanges = false;
  for (auto &labelNode : nodes->getLabelNodes())
  {
    int labelId = labelNode->label.id;
    if (labelPositions.count(labelId))
    {
      auto newPosition = labelPositions.get3dFor(labelId);
      if (!hasChanges && (labelNode->labelPosition - newPosition).norm() > 1e-7)
      {
        hasChanges = true;
      }

      labelNode->setIsVisible(true);
      labelNode->labelPosition = labelPositions.get3dFor(labelId);
      labelNode->labelPositionNDC = labelPositions.getNDCFor(labelId);
      labelNode->layerIndex = labelIdToLayerIndex[labelId];
    }
    else
    {
      labelNode->setIsVisible(false);
    }
  }
}

std::map<int, Eigen::Vector3f> LabellingCoordinator::addDepthValueNDC(
    std::map<int, Eigen::Vector2f> positionsNDC)
{
  std::map<int, Eigen::Vector3f> positions;
  for (auto positionNDCPair : positionsNDC)
  {
    int labelId = positionNDCPair.first;
    auto position2d = positionNDCPair.second;
    positions[labelId] = Eigen::Vector3f(position2d.x(), position2d.y(),
                                         labelIdToZValue[labelId]);
  }

  return positions;
}

std::map<int, Eigen::Vector3f> LabellingCoordinator::ndcPositionsTo3d(
    std::map<int, Eigen::Vector3f> positionsNDC)
{
  Eigen::Matrix4f inverseViewProjection =
      labellerFrameData.viewProjection.inverse();

  std::map<int, Eigen::Vector3f> positions;
  for (auto positionNDCPair : positionsNDC)
  {
    int labelId = positionNDCPair.first;
    positions[labelId] = project(inverseViewProjection, positionNDCPair.second);
  }

  return positions;
}

<commit_msg>Ensure that all labels are in the first layer for internal labelling mode.<commit_after>#if _WIN32
#pragma warning(disable : 4267 4996)
#endif

#include "./labelling_coordinator.h"
#include <QtOpenGLExtensions>
#include <QLoggingCategory>
#include <map>
#include <vector>
#include <memory>
#include "./labelling/clustering.h"
#include "./labelling/labels.h"
#include "./placement/occlusion_calculator.h"
#include "./placement/constraint_updater.h"
#include "./placement/persistent_constraint_updater.h"
#include "./placement/cuda_texture_mapper.h"
#include "./placement/integral_costs_calculator.h"
#include "./placement/saliency.h"
#include "./placement/labels_arranger.h"
#include "./placement/insertion_order_labels_arranger.h"
#include "./placement/randomized_labels_arranger.h"
#include "./placement/apollonius_labels_arranger.h"
#include "./placement/anchor_constraint_drawer.h"
#include "./placement/shadow_constraint_drawer.h"
#include "./graphics/buffer_drawer.h"
#include "./nodes.h"
#include "./math/eigen.h"
#include "./label_node.h"
#include "./texture_mapper_manager.h"
#include "./utils/profiler.h"
#include "./utils/profiling_statistics.h"
#include "./graphics/managers.h"

QLoggingCategory lcChan("LabellingCoordinator");

LabellingCoordinator::LabellingCoordinator(
    int layerCount, std::shared_ptr<Forces::Labeller> forcesLabeller,
    std::shared_ptr<Labels> labels, std::shared_ptr<Nodes> nodes)
  : layerCount(layerCount), forcesLabeller(forcesLabeller), labels(labels),
    nodes(nodes), clustering(labels, layerCount - 1),
    profilingStatistics("LabellingCoordinator", lcChan)
{
  occlusionCalculator =
      std::make_shared<Placement::OcclusionCalculator>(layerCount);
}

void LabellingCoordinator::initialize(
    Graphics::Gl *gl, int bufferSize,
    std::shared_ptr<Graphics::Managers> managers,
    std::shared_ptr<TextureMapperManager> textureMapperManager, int width,
    int height)
{
  qCInfo(lcChan) << "Initialize";
  this->bufferSize = Eigen::Vector2f(bufferSize, bufferSize);
  saliency = std::make_shared<Placement::Saliency>(
      textureMapperManager->getAccumulatedLayersTextureMapper(),
      textureMapperManager->getSaliencyTextureMapper());

  occlusionCalculator->initialize(textureMapperManager);
  integralCostsCalculator =
      std::make_shared<Placement::IntegralCostsCalculator>(
          textureMapperManager->getOcclusionTextureMapper(),
          textureMapperManager->getSaliencyTextureMapper(),
          textureMapperManager->getIntegralCostsTextureMapper());

  auto shaderManager = managers->getShaderManager();
  auto anchorConstraintDrawer =
      std::make_shared<AnchorConstraintDrawer>(bufferSize, bufferSize);
  anchorConstraintDrawer->initialize(gl, shaderManager);

  auto connectorShadowDrawer =
      std::make_shared<ShadowConstraintDrawer>(bufferSize, bufferSize);
  connectorShadowDrawer->initialize(gl, shaderManager);

  auto shadowConstraintDrawer =
      std::make_shared<ShadowConstraintDrawer>(bufferSize, bufferSize);
  shadowConstraintDrawer->initialize(gl, shaderManager);

  float scaleFactor = bufferSize / width;
  auto constraintUpdater = std::make_shared<ConstraintUpdater>(
      bufferSize, bufferSize, anchorConstraintDrawer, connectorShadowDrawer,
      shadowConstraintDrawer, scaleFactor);
  persistentConstraintUpdater =
      std::make_shared<PersistentConstraintUpdater>(constraintUpdater);

  insertionOrderLabelsArranger =
      std::make_shared<Placement::InsertionOrderLabelsArranger>();
  randomizedLabelsArranger =
      std::make_shared<Placement::RandomizedLabelsArranger>();

  for (int layerIndex = 0; layerIndex < layerCount; ++layerIndex)
  {
    auto labelsContainer = std::make_shared<LabelsContainer>();
    labelsInLayer.push_back(labelsContainer);
    auto labeller = std::make_shared<Placement::Labeller>(labelsContainer);
    labeller->resize(width, height);
    labeller->initialize(textureMapperManager->getIntegralCostsTextureMapper(),
                         textureMapperManager->getConstraintTextureMapper(),
                         persistentConstraintUpdater);

    auto apolloniusLabelsArranger =
        std::make_shared<Placement::ApolloniusLabelsArranger>();
    apolloniusLabelsArranger->initialize(
        textureMapperManager->getDistanceTransformTextureMapper(layerIndex),
        textureMapperManager->getOcclusionTextureMapper(),
        textureMapperManager->getApolloniusTextureMapper(layerIndex));
    apolloniusLabelsArrangers.push_back(apolloniusLabelsArranger);

    labeller->setLabelsArranger(useApollonius ? apolloniusLabelsArranger
                                              : insertionOrderLabelsArranger);

    placementLabellers.push_back(labeller);
  }
}

void LabellingCoordinator::cleanup()
{
  occlusionCalculator.reset();
  saliency.reset();
  integralCostsCalculator.reset();

  for (auto apolloniusLabelsArranger : apolloniusLabelsArrangers)
    apolloniusLabelsArranger->cleanup();

  for (auto placementLabeller : placementLabellers)
    placementLabeller->cleanup();
}

void LabellingCoordinator::setEnabled(bool enabled)
{
  labellingEnabled = enabled;
  for (auto &labelNode : nodes->getLabelNodes())
  {
    labelNode->setIsVisible(!enabled);
  }
}

bool LabellingCoordinator::update(double frameTime, bool isIdle,
                                  Eigen::Matrix4f projection,
                                  Eigen::Matrix4f view, int activeLayerNumber)
{
  Profiler profiler("update", lcChan, &profilingStatistics);

  if (!labellingEnabled)
    return false;

  this->isIdle = isIdle;
  labellerFrameData = LabellerFrameData(frameTime, projection, view);

  if (internalLabellingEnabled)
  {
    for (int layerIndex = 0; layerIndex < layerCount; layerIndex++)
      labelsInLayer[layerIndex]->clear();

    LabelPositions labelPositions;
    auto container = labelsInLayer[0];
    for (auto label : labels->getLabels())
    {
      auto ndc = labellerFrameData.project(label.anchorPosition);
      labelPositions.update(label.id, ndc, label.anchorPosition);

      container->add(label);
      labelIdToLayerIndex[label.id] = 0;
      labelIdToZValue[label.id] = 1.0f;
    }

    updateLabelPositionsInLabelNodes(labelPositions);
    return false;
  }

  saliency->runKernel();

  auto positionsNDC2d = getPlacementPositions(activeLayerNumber);
  auto positionsNDC = addDepthValueNDC(positionsNDC2d);
  LabelPositions labelPositions(positionsNDC, ndcPositionsTo3d(positionsNDC));

  if (forcesEnabled)
    labelPositions = getForcesPositions(labelPositions);

  distributeLabelsToLayers();

  updateLabelPositionsInLabelNodes(labelPositions);

  return hasChanges;
}

void LabellingCoordinator::updatePlacement()
{
  Profiler profiler("updatePlacement", lcChan, &profilingStatistics);

  if (!labellingEnabled)
    return;

  bool optimize = isIdle && optimizeOnIdle;
  bool ignoreOldPosition = !isIdle;

  float newSumOfCosts = 0.0f;
  persistentConstraintUpdater->clear();
  if (saveConstraintsInNextFrame)
  {
    persistentConstraintUpdater->save();
    saveConstraintsInNextFrame = false;
  }

  std::vector<Eigen::Vector2f> anchorPositions(labels->count());
  auto labelVector = labels->getLabels();
  for (int index = 0; index < labels->count(); index++)
  {
    auto anchor2D =
        labellerFrameData.project(labelVector[index].anchorPosition);
    anchorPositions[index] = toPixel(anchor2D.head<2>(), bufferSize);
  }

  persistentConstraintUpdater->setAnchorPositions(anchorPositions);

  for (int layerIndex = 0; layerIndex < layerCount; ++layerIndex)
  {
    occlusionCalculator->calculateFor(layerIndex);
    integralCostsCalculator->runKernel();

    auto labeller = placementLabellers[layerIndex];
    auto defaultArranger = useApollonius ? apolloniusLabelsArrangers[layerIndex]
                                         : insertionOrderLabelsArranger;
    labeller->setLabelsArranger(optimize ? randomizedLabelsArranger
                                         : defaultArranger);
    labeller->update(labellerFrameData, ignoreOldPosition);
    newSumOfCosts += labeller->getLastSumOfCosts();
  }

  if (optimize)
  {
    std::cout << "Old costs: " << sumOfCosts << "\tnew costs:" << newSumOfCosts
              << std::endl;
  }

  if (optimize && newSumOfCosts > sumOfCosts)
  {
    preserveLastResult = true;
  }
  else
  {
    preserveLastResult = false;
    sumOfCosts = newSumOfCosts;
  }
}

std::vector<float> LabellingCoordinator::updateClusters()
{
  if (!labellingEnabled || internalLabellingEnabled)
    return std::vector<float>{ 1.0f };

  clustering.update(labellerFrameData.viewProjection);
  return clustering.getMedianClusterMembers();
}

bool LabellingCoordinator::haveLabelPositionsChanged()
{
  return labellingEnabled && hasChanges;
}

void LabellingCoordinator::resize(int width, int height)
{
  for (auto placementLabeller : placementLabellers)
    placementLabeller->resize(width, height);

  forcesLabeller->resize(width, height);
}

void LabellingCoordinator::toggleAnchorVisibility()
{
  for (auto node : nodes->getLabelNodes())
    node->isAnchorVisible = !node->isAnchorVisible;
}

void LabellingCoordinator::saveOcclusion()
{
  occlusionCalculator->saveOcclusion();
}

void LabellingCoordinator::saveConstraints()
{
  saveConstraintsInNextFrame = true;
}

void LabellingCoordinator::setCostFunctionWeights(
    Placement::CostFunctionWeights weights)
{
  for (auto placementLabeller : placementLabellers)
    placementLabeller->setCostFunctionWeights(weights);
}

std::map<int, Eigen::Vector2f>
LabellingCoordinator::getPlacementPositions(int activeLayerNumber)
{
  if (preserveLastResult)
    return lastPlacementResult;

  std::map<int, Eigen::Vector2f> placementPositions;
  int layerIndex = 0;
  for (auto placementLabeller : placementLabellers)
  {
    if (activeLayerNumber == 0 || activeLayerNumber - 1 == layerIndex)
    {
      auto newPositionsForLayer = placementLabeller->getLastPlacementResult();
      placementPositions.insert(newPositionsForLayer.begin(),
                                newPositionsForLayer.end());
    }

    layerIndex++;
  }

  lastPlacementResult = placementPositions;

  return placementPositions;
}

LabelPositions
LabellingCoordinator::getForcesPositions(LabelPositions placementPositions)
{
  if (placementPositions.size() == 0)
    return LabelPositions();

  if (firstFramesWithoutPlacement && placementPositions.size())
  {
    firstFramesWithoutPlacement = false;
    forcesLabeller->overallForceFactor = isIdle ? 6.0f : 3.0f;
    forcesLabeller->setPositions(labellerFrameData, placementPositions);
  }

  return forcesLabeller->update(labellerFrameData, placementPositions);
}

void LabellingCoordinator::distributeLabelsToLayers()
{
  auto centerWithLabelIds = clustering.getMedianClusterMembersWithLabelIds();
  labelIdToLayerIndex.clear();
  labelIdToZValue.clear();

  for (auto& layerLabels : labelsInLayer)
    layerLabels->clear();

  int layerIndex = 0;
  for (auto &pair : centerWithLabelIds)
  {
    auto &container = labelsInLayer[layerIndex];

    for (int labelId : pair.second)
    {
      container->add(labels->getById(labelId));
      labelIdToLayerIndex[labelId] = layerIndex;
      labelIdToZValue[labelId] = pair.first;
    }

    layerIndex++;
  }

  if (lcChan.isDebugEnabled())
  {
    std::stringstream output;
    int layerIndex = 0;
    for (auto layerLabels : labelsInLayer)
    {
      output << std::endl << "Layer " << layerIndex++ << "\t";
      for (auto &label : layerLabels->getLabels())
        output << "\"" << label.text << "\" (" << label.id << "), ";
    }
    qCDebug(lcChan) << "distributeLabelsToLayers: " << output.str().c_str();
  }
}

void LabellingCoordinator::updateLabelPositionsInLabelNodes(
    LabelPositions labelPositions)
{
  hasChanges = false;
  for (auto &labelNode : nodes->getLabelNodes())
  {
    int labelId = labelNode->label.id;
    if (labelPositions.count(labelId))
    {
      auto newPosition = labelPositions.get3dFor(labelId);
      if (!hasChanges && (labelNode->labelPosition - newPosition).norm() > 1e-7)
      {
        hasChanges = true;
      }

      labelNode->setIsVisible(true);
      labelNode->labelPosition = labelPositions.get3dFor(labelId);
      labelNode->labelPositionNDC = labelPositions.getNDCFor(labelId);
      labelNode->layerIndex = labelIdToLayerIndex[labelId];
    }
    else
    {
      labelNode->setIsVisible(false);
    }
  }
}

std::map<int, Eigen::Vector3f> LabellingCoordinator::addDepthValueNDC(
    std::map<int, Eigen::Vector2f> positionsNDC)
{
  std::map<int, Eigen::Vector3f> positions;
  for (auto positionNDCPair : positionsNDC)
  {
    int labelId = positionNDCPair.first;
    auto position2d = positionNDCPair.second;
    positions[labelId] = Eigen::Vector3f(position2d.x(), position2d.y(),
                                         labelIdToZValue[labelId]);
  }

  return positions;
}

std::map<int, Eigen::Vector3f> LabellingCoordinator::ndcPositionsTo3d(
    std::map<int, Eigen::Vector3f> positionsNDC)
{
  Eigen::Matrix4f inverseViewProjection =
      labellerFrameData.viewProjection.inverse();

  std::map<int, Eigen::Vector3f> positions;
  for (auto positionNDCPair : positionsNDC)
  {
    int labelId = positionNDCPair.first;
    positions[labelId] = project(inverseViewProjection, positionNDCPair.second);
  }

  return positions;
}

<|endoftext|>


<commit_before>#include "model_ops.h"
#include <set>
#include <deque>
#include <vector>

void makeVerticallySymmetric(std::shared_ptr<figures::Curve> curve) {
    for (Point &p : curve->points) {
        std::swap(p.x, p.y);
    }
    makeHorizontallySymmetric(curve);
    for (Point &p : curve->points) {
        std::swap(p.x, p.y);
    }
}

bool getVerticalIntersection(Point a, Point b, double x, Point &result) {
    if (fabs(b.x - a.x) < 1e-8) {
        return false;
    }
    result = Point(x, a.y + (b.y - a.y) * (x - a.x) / (b.x - a.x));
    return true;
}

void makeHorizontallySymmetric(std::shared_ptr<figures::Curve> curve) {
    BoundingBox box = curve->getBoundingBox();
    auto &points = curve->points;
    double midX = box.center().x;
    if (points.at(0).x > midX) {
        for (Point &p : points) {
            p.x = -p.x;
        }
        makeHorizontallySymmetric(curve);
        for (Point &p : points) {
            p.x = -p.x;
        }
        return;
    }
    size_t cnt = 0;
    while (cnt < points.size() && points[cnt].x <= midX) {
        cnt++;
    }
    assert(cnt >= 1);
    assert(cnt < points.size());

    Point midPoint;
    bool hasMidPoint = getVerticalIntersection(points.at(cnt - 1), points.at(cnt), midX, midPoint);
    points.erase(points.begin() + cnt, points.end());
    bool midArrowBegin = curve->arrowBegin.at(cnt - 1);
    bool midArrowEnd = curve->arrowEnd.at(cnt - 1);

    curve->arrowBegin.erase(curve->arrowBegin.begin() + cnt - 1, curve->arrowBegin.end());
    curve->arrowEnd.erase(curve->arrowEnd.begin() + cnt - 1, curve->arrowEnd.end());

    curve->arrowBegin.push_back(midArrowBegin);
    if (hasMidPoint) {
        curve->arrowEnd.push_back(midArrowEnd);
        points.push_back(midPoint);
        curve->arrowBegin.push_back(midArrowEnd);
    }
    curve->arrowEnd.push_back(midArrowBegin);
    for (int i = cnt - 1; i >= 0; i--) {
        points.push_back(Point(2 * midX - points[i].x, points[i].y));
        if (i > 0) {
            curve->arrowBegin.push_back(curve->arrowEnd[i - 1]);
            curve->arrowEnd.push_back(curve->arrowBegin[i - 1]);
        }
    }
    curve->selfCheck();
}

void makeTopBottomTree(Model &model, figures::PBoundedFigure root) {
    typedef figures::PBoundedFigure Node;
    std::map<Node, std::vector<Node>> edges;
    // building graph
    for (PFigure figure : model) {
        auto connection = std::dynamic_pointer_cast<figures::SegmentConnection>(figure);
        if (connection) {
            bool dirAB = connection->getArrowedB();
            bool dirBA = connection->getArrowedA();
            if (!dirAB && !dirBA) {
                dirAB = dirBA = true;
            }
            if (dirAB) {
                edges[connection->getFigureA()].push_back(connection->getFigureB());
            }
            if (dirBA) {
                edges[connection->getFigureB()].push_back(connection->getFigureA());
            }
        }
    }

    const double NODES_GAP_K = 0.5; // 0.5 of node's height

    std::map<Node, std::vector<Node>> children;
    std::vector<Node> order;

    // breadth-first-search, building tree itself
    {
        std::set<Node> visited;
        std::deque<Node> q;

        q.push_back(root);
        visited.insert(root);
        while (!q.empty()) {
            Node v = q.front();
            q.pop_front();
            order.push_back(v);
            for (Node child : edges[v]) {
                if (visited.count(child)) { continue; }
                visited.insert(child);
                children[v].push_back(child);
                q.push_back(child);
            }
        }
    }

    std::map<Node, BoundingBox> boxes;
    // traversing nodes from the bottom to the top
    for (int i = order.size() - 1; i >= 0; i--) {
        Node v = order[i];
        BoundingBox &box = boxes[v] = v->getBoundingBox();
        const double NODES_GAP = box.height() * NODES_GAP_K;

        if (!children[v].empty()) {
            double sumChildrenWidth = 0;
            double maxChildHeight = 0;
            for (Node child : children[v]) {
                sumChildrenWidth += boxes[child].width();
                maxChildHeight = std::max(maxChildHeight, boxes[child].height());
            }
            if (children[v].size() >= 2) {
                sumChildrenWidth += (children[v].size()  - 1) * NODES_GAP;
            }
            box.rightDown.x = std::max(box.rightDown.x, box.leftUp.x + sumChildrenWidth);
            box.rightDown.y += NODES_GAP + maxChildHeight;
        }
    }

    // returns offset by which node should be translated to be 'root node' with corresponding sumBox
    const auto getNodeOffset = [](const BoundingBox &vBox, const BoundingBox &sumBox) {
        Point offset;
        offset.x = sumBox.center().x - vBox.center().x;
        offset.y = sumBox.leftUp.y - vBox.leftUp.y;
        return offset;
    };

    // traversing tree from the top to the bottom and arranging nodes
    // invariant: when visiting node X, its bounding box is correct

    { // first, we need to keep the root in its original place
        Node v = order[0];
        BoundingBox vBox = v->getBoundingBox();
        BoundingBox &sumBox = boxes[v];
        sumBox.translate(Point() - getNodeOffset(vBox, sumBox));
    }
    for (size_t i = 0; i < order.size(); i++) {
        Node v = order[i];
        BoundingBox vBox = v->getBoundingBox();
        const double NODES_GAP = vBox.height() * NODES_GAP_K;

        BoundingBox sumBox = boxes[v];
        v->translate(getNodeOffset(vBox, sumBox));
        Point currentCorner = sumBox.leftUp;
        currentCorner.y += vBox.height() + NODES_GAP;
        for (Node child : children[v]) {
            BoundingBox &childBox = boxes[child];
            childBox.translate(currentCorner - childBox.leftUp);
            currentCorner.x += childBox.width() + NODES_GAP;
        }
    }
    model.recalculate();
}
<commit_msg>model_ops: makeSymmetric now processes curve->isStop correctly<commit_after>#include "model_ops.h"
#include <set>
#include <deque>
#include <vector>

void makeVerticallySymmetric(std::shared_ptr<figures::Curve> curve) {
    for (Point &p : curve->points) {
        std::swap(p.x, p.y);
    }
    makeHorizontallySymmetric(curve);
    for (Point &p : curve->points) {
        std::swap(p.x, p.y);
    }
}

bool getVerticalIntersection(Point a, Point b, double x, Point &result) {
    if (fabs(b.x - a.x) < 1e-8) {
        return false;
    }
    result = Point(x, a.y + (b.y - a.y) * (x - a.x) / (b.x - a.x));
    return true;
}

void makeHorizontallySymmetric(std::shared_ptr<figures::Curve> curve) {
    BoundingBox box = curve->getBoundingBox();
    auto &points = curve->points;
    double midX = box.center().x;
    if (points.at(0).x > midX) {
        for (Point &p : points) {
            p.x = -p.x;
        }
        makeHorizontallySymmetric(curve);
        for (Point &p : points) {
            p.x = -p.x;
        }
        return;
    }
    size_t cnt = 0;
    while (cnt < points.size() && points[cnt].x <= midX) {
        cnt++;
    }
    assert(cnt >= 1);
    assert(cnt < points.size());

    Point midPoint;
    bool hasMidPoint = getVerticalIntersection(points.at(cnt - 1), points.at(cnt), midX, midPoint);
    points.erase(points.begin() + cnt, points.end());
    curve->isStop.erase(curve->isStop.begin() + cnt, curve->isStop.end());

    bool midArrowBegin = curve->arrowBegin.at(cnt - 1);
    bool midArrowEnd = curve->arrowEnd.at(cnt - 1);

    curve->arrowBegin.erase(curve->arrowBegin.begin() + cnt - 1, curve->arrowBegin.end());
    curve->arrowEnd.erase(curve->arrowEnd.begin() + cnt - 1, curve->arrowEnd.end());

    curve->arrowBegin.push_back(midArrowBegin);
    if (hasMidPoint) {
        curve->arrowEnd.push_back(midArrowEnd);
        points.push_back(midPoint);
        curve->isStop.push_back(false);
        curve->arrowBegin.push_back(midArrowEnd);
    }
    curve->arrowEnd.push_back(midArrowBegin);
    for (int i = cnt - 1; i >= 0; i--) {
        points.push_back(Point(2 * midX - points[i].x, points[i].y));
        curve->isStop.push_back(curve->isStop[i]);
        if (i > 0) {
            curve->arrowBegin.push_back(curve->arrowEnd[i - 1]);
            curve->arrowEnd.push_back(curve->arrowBegin[i - 1]);
        }
    }
    curve->selfCheck();
}

void makeTopBottomTree(Model &model, figures::PBoundedFigure root) {
    typedef figures::PBoundedFigure Node;
    std::map<Node, std::vector<Node>> edges;
    // building graph
    for (PFigure figure : model) {
        auto connection = std::dynamic_pointer_cast<figures::SegmentConnection>(figure);
        if (connection) {
            bool dirAB = connection->getArrowedB();
            bool dirBA = connection->getArrowedA();
            if (!dirAB && !dirBA) {
                dirAB = dirBA = true;
            }
            if (dirAB) {
                edges[connection->getFigureA()].push_back(connection->getFigureB());
            }
            if (dirBA) {
                edges[connection->getFigureB()].push_back(connection->getFigureA());
            }
        }
    }

    const double NODES_GAP_K = 0.5; // 0.5 of node's height

    std::map<Node, std::vector<Node>> children;
    std::vector<Node> order;

    // breadth-first-search, building tree itself
    {
        std::set<Node> visited;
        std::deque<Node> q;

        q.push_back(root);
        visited.insert(root);
        while (!q.empty()) {
            Node v = q.front();
            q.pop_front();
            order.push_back(v);
            for (Node child : edges[v]) {
                if (visited.count(child)) { continue; }
                visited.insert(child);
                children[v].push_back(child);
                q.push_back(child);
            }
        }
    }

    std::map<Node, BoundingBox> boxes;
    // traversing nodes from the bottom to the top
    for (int i = order.size() - 1; i >= 0; i--) {
        Node v = order[i];
        BoundingBox &box = boxes[v] = v->getBoundingBox();
        const double NODES_GAP = box.height() * NODES_GAP_K;

        if (!children[v].empty()) {
            double sumChildrenWidth = 0;
            double maxChildHeight = 0;
            for (Node child : children[v]) {
                sumChildrenWidth += boxes[child].width();
                maxChildHeight = std::max(maxChildHeight, boxes[child].height());
            }
            if (children[v].size() >= 2) {
                sumChildrenWidth += (children[v].size()  - 1) * NODES_GAP;
            }
            box.rightDown.x = std::max(box.rightDown.x, box.leftUp.x + sumChildrenWidth);
            box.rightDown.y += NODES_GAP + maxChildHeight;
        }
    }

    // returns offset by which node should be translated to be 'root node' with corresponding sumBox
    const auto getNodeOffset = [](const BoundingBox &vBox, const BoundingBox &sumBox) {
        Point offset;
        offset.x = sumBox.center().x - vBox.center().x;
        offset.y = sumBox.leftUp.y - vBox.leftUp.y;
        return offset;
    };

    // traversing tree from the top to the bottom and arranging nodes
    // invariant: when visiting node X, its bounding box is correct

    { // first, we need to keep the root in its original place
        Node v = order[0];
        BoundingBox vBox = v->getBoundingBox();
        BoundingBox &sumBox = boxes[v];
        sumBox.translate(Point() - getNodeOffset(vBox, sumBox));
    }
    for (size_t i = 0; i < order.size(); i++) {
        Node v = order[i];
        BoundingBox vBox = v->getBoundingBox();
        const double NODES_GAP = vBox.height() * NODES_GAP_K;

        BoundingBox sumBox = boxes[v];
        v->translate(getNodeOffset(vBox, sumBox));
        Point currentCorner = sumBox.leftUp;
        currentCorner.y += vBox.height() + NODES_GAP;
        for (Node child : children[v]) {
            BoundingBox &childBox = boxes[child];
            childBox.translate(currentCorner - childBox.leftUp);
            currentCorner.x += childBox.width() + NODES_GAP;
        }
    }
    model.recalculate();
}
<|endoftext|>


<commit_before>//
// Copyright 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// signal_utils:
//   Implements the Observer pattern for sending state change notifications
//   from Subject objects to dependent Observer objects.
//
//   See design document:
//   https://docs.google.com/document/d/15Edfotqg6_l1skTEL8ADQudF_oIdNa7i8Po43k6jMd4/

#include "libANGLE/signal_utils.h"

#include "common/debug.h"

namespace angle
{
// Observer implementation.
ObserverInterface::~ObserverInterface() = default;

// Subject implementation.
Subject::Subject()
{
}

Subject::~Subject()
{
    resetObservers();
}

bool Subject::hasObservers() const
{
    return !mObservers.empty();
}

void Subject::addObserver(ObserverBinding *observer)
{
    ASSERT(std::find(mObservers.begin(), mObservers.end(), observer) == mObservers.end());
    mObservers.push_back(observer);
}

void Subject::removeObserver(ObserverBinding *observer)
{
    auto iter = std::find(mObservers.begin(), mObservers.end(), observer);
    ASSERT(iter != mObservers.end());
    mObservers.erase(iter);
}

void Subject::onStateChange(const gl::Context *context, SubjectMessage message) const
{
    if (mObservers.empty())
        return;

    for (const angle::ObserverBinding *receiver : mObservers)
    {
        receiver->onStateChange(context, message);
    }
}

void Subject::resetObservers()
{
    for (angle::ObserverBinding *observer : mObservers)
    {
        observer->onSubjectReset();
    }
    mObservers.clear();
}

// ObserverBinding implementation.
ObserverBinding::ObserverBinding(ObserverInterface *observer, SubjectIndex index)
    : mSubject(nullptr), mObserver(observer), mIndex(index)
{
    ASSERT(observer);
}

ObserverBinding::~ObserverBinding()
{
    reset();
}

ObserverBinding::ObserverBinding(const ObserverBinding &other) = default;

ObserverBinding &ObserverBinding::operator=(const ObserverBinding &other) = default;

void ObserverBinding::bind(Subject *subject)
{
    ASSERT(mObserver);
    if (mSubject)
    {
        mSubject->removeObserver(this);
    }

    mSubject = subject;

    if (mSubject)
    {
        mSubject->addObserver(this);
    }
}

void ObserverBinding::reset()
{
    bind(nullptr);
}

void ObserverBinding::onStateChange(const gl::Context *context, SubjectMessage message) const
{
    mObserver->onSubjectStateChange(context, mIndex, message);
}

void ObserverBinding::onSubjectReset()
{
    mSubject = nullptr;
}
}  // namespace angle
<commit_msg>Add missing #include.<commit_after>//
// Copyright 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// signal_utils:
//   Implements the Observer pattern for sending state change notifications
//   from Subject objects to dependent Observer objects.
//
//   See design document:
//   https://docs.google.com/document/d/15Edfotqg6_l1skTEL8ADQudF_oIdNa7i8Po43k6jMd4/

#include "libANGLE/signal_utils.h"

#include <algorithm>

#include "common/debug.h"

namespace angle
{
// Observer implementation.
ObserverInterface::~ObserverInterface() = default;

// Subject implementation.
Subject::Subject()
{
}

Subject::~Subject()
{
    resetObservers();
}

bool Subject::hasObservers() const
{
    return !mObservers.empty();
}

void Subject::addObserver(ObserverBinding *observer)
{
    ASSERT(std::find(mObservers.begin(), mObservers.end(), observer) == mObservers.end());
    mObservers.push_back(observer);
}

void Subject::removeObserver(ObserverBinding *observer)
{
    auto iter = std::find(mObservers.begin(), mObservers.end(), observer);
    ASSERT(iter != mObservers.end());
    mObservers.erase(iter);
}

void Subject::onStateChange(const gl::Context *context, SubjectMessage message) const
{
    if (mObservers.empty())
        return;

    for (const angle::ObserverBinding *receiver : mObservers)
    {
        receiver->onStateChange(context, message);
    }
}

void Subject::resetObservers()
{
    for (angle::ObserverBinding *observer : mObservers)
    {
        observer->onSubjectReset();
    }
    mObservers.clear();
}

// ObserverBinding implementation.
ObserverBinding::ObserverBinding(ObserverInterface *observer, SubjectIndex index)
    : mSubject(nullptr), mObserver(observer), mIndex(index)
{
    ASSERT(observer);
}

ObserverBinding::~ObserverBinding()
{
    reset();
}

ObserverBinding::ObserverBinding(const ObserverBinding &other) = default;

ObserverBinding &ObserverBinding::operator=(const ObserverBinding &other) = default;

void ObserverBinding::bind(Subject *subject)
{
    ASSERT(mObserver);
    if (mSubject)
    {
        mSubject->removeObserver(this);
    }

    mSubject = subject;

    if (mSubject)
    {
        mSubject->addObserver(this);
    }
}

void ObserverBinding::reset()
{
    bind(nullptr);
}

void ObserverBinding::onStateChange(const gl::Context *context, SubjectMessage message) const
{
    mObserver->onSubjectStateChange(context, mIndex, message);
}

void ObserverBinding::onSubjectReset()
{
    mSubject = nullptr;
}
}  // namespace angle
<|endoftext|>


<commit_before>#include <string.h>
#include "renderer/film.h"

namespace aten
{
    Film::Film(int w, int h)
    {
        init(w, h);
    }

    void Film::init(int w, int h)
    {
        m_width = w;
        m_height = h;
        m_image.resize(m_width * m_height);
    }

    void Film::clear()
    {
        memset(&m_image[0], 0, m_image.size() * sizeof(vec4));
    }

    void Film::put(int x, int y, const vec3& v)
    {
        put(x, y, vec4(v, 1));
    }

    void Film::put(int i, const vec3& v)
    {
        put(i, vec4(v, 1));
    }

    void Film::put(int x, int y, const vec4& v)
    {
        x = aten::clamp<int>(x, 0, m_width - 1);
        y = aten::clamp<int>(y, 0, m_height - 1);

        auto pos = y * m_width + x;
        put(pos, v);
    }

    void Film::put(int i, const vec4& v)
    {
        m_image[i] = v;
    }

    void Film::add(int i, const vec4& v)
    {
        m_image[i] += v;
    }

    const vec4& Film::at(int x, int y) const
    {
        auto pos = y * m_width + x;
        return m_image[pos];
    }

    // NOTE
    // http://www.flint.jp/blog/?entry=86

    void FilmProgressive::put(int i, const vec4& v)
    {
        auto& curValue = m_image[i];

        // First curValue.w is 1, so -1.
        int n = curValue.w;
        int attn = n - 1;

        curValue = attn * curValue + v;
        curValue /= (attn + 1);

        curValue.w = n + 1;
    }
}
<commit_msg>Fix progressive accumulation bug<commit_after>#include <string.h>
#include "renderer/film.h"

namespace aten
{
    Film::Film(int w, int h)
    {
        init(w, h);
    }

    void Film::init(int w, int h)
    {
        m_width = w;
        m_height = h;
        m_image.resize(m_width * m_height);
    }

    void Film::clear()
    {
        memset(&m_image[0], 0, m_image.size() * sizeof(vec4));
    }

    void Film::put(int x, int y, const vec3& v)
    {
        put(x, y, vec4(v, 1));
    }

    void Film::put(int i, const vec3& v)
    {
        put(i, vec4(v, 1));
    }

    void Film::put(int x, int y, const vec4& v)
    {
        x = aten::clamp<int>(x, 0, m_width - 1);
        y = aten::clamp<int>(y, 0, m_height - 1);

        auto pos = y * m_width + x;
        put(pos, v);
    }

    void Film::put(int i, const vec4& v)
    {
        m_image[i] = v;
    }

    void Film::add(int i, const vec4& v)
    {
        m_image[i] += v;
    }

    const vec4& Film::at(int x, int y) const
    {
        auto pos = y * m_width + x;
        return m_image[pos];
    }

    // NOTE
    // http://www.flint.jp/blog/?entry=86

    void FilmProgressive::put(int i, const vec4& v)
    {
        auto& curValue = m_image[i];

        int n = curValue.w;

        curValue = n * curValue + v;
        curValue /= (n + 1);

        curValue.w = n + 1;
    }
}
<|endoftext|>