<commit_before>/* ******************************************************************************
 * Copyright (c) 2017 Nicola Del Gobbo
 * 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
 *
 * THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
 * OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
 * IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
 * MERCHANTABLITY OR NON-INFRINGEMENT.
 *
 * See the Apache Version 2.0 License for specific language governing
 * permissions and limitations under the License.
 *
 * Contributors - initial API implementation:
 * Nicola Del Gobbo <nicoladelgobbo@gmail.com>
 * Mauro Doganieri <mauro.doganieri@gmail.com>
 ******************************************************************************/

#include "ghostscript4js.h"

// Lifeclycle management for the Ghostscript instance
class GhostscriptManager
{
  private:
    void *minst;
    mutex gs;
    static GhostscriptManager *instance;
    GhostscriptManager()
    {
        minst = nullptr;
    }
    void Init();
    void Destroy();
    void Exit();

  public:
    static GhostscriptManager *GetInstance();
    ~GhostscriptManager()
    {
        minst = nullptr;
    };
    void Execute(int gsargc, char *gsargv[]);
};

GhostscriptManager *GhostscriptManager::instance = nullptr;
// Static method that create or simple return the instance of the GhostscriptManager
// following the singleton design pattern
GhostscriptManager *GhostscriptManager::GetInstance()
{
    if (instance == nullptr)
    {
        instance = new GhostscriptManager();
    }
    return instance;
}

// Init is an implementation of gsapi_new_instance.
// It returns a global static instance of Ghostscript.
// i.e. Do not call init more than once, otherwise an error will be returned.
void GhostscriptManager::Init()
{
    int code = 0;
    code = gsapi_new_instance(&minst, NULL);
    if (code < 0)
    {
        throw std::runtime_error("Sorry error happened creating col cazzo Ghostscript instance. Error code: " + to_string(code));
    }
    code = gsapi_set_arg_encoding(minst, GS_ARG_ENCODING_UTF8);
    if (code < 0)
    {
        throw std::runtime_error("Sorry error happened in setting the encoding for Ghostscript interpreter. Error code: " + to_string(code));
    }
}

// Execute is an implementation of gsapi_init_with_args.
// It initialises the Ghostscript interpreter given a set of arguments.
void GhostscriptManager::Execute(int gsargc, char *gsargv[])
{
    lock_guard<mutex> lk(gs);
    Init();
    int code = 0;
    code = gsapi_init_with_args(minst, gsargc, gsargv);
    Exit();
    Destroy();
    if (code < 0 && code != gs_error_Quit)
    {
        throw std::runtime_error("Sorry error happened executing Ghostscript command. Error code: " + to_string(code));
    }
}

// Exit is an implementation of gsapi_exit.
// It exits the Ghostscript interpreter.
// It must be called if init has been called, and just before destroy.
void GhostscriptManager::Exit()
{
    int code = 0;
    code = gsapi_exit(minst);
    if (code < 0 && code != gs_error_Quit)
    {
        throw std::runtime_error("Sorry error happened during the exit from the Ghostscript interpreter. Error code: " + to_string(code));
    }
}

// Destroy is an implementation of gsapi_delete_instance.
// It destroys a global static instance of Ghostscript.
// It should be called only after exit has been called if init has been called.
void GhostscriptManager::Destroy()
{
    gsapi_delete_instance(minst);
    minst = nullptr;
}

class GhostscriptWorker : public Napi::AsyncWorker
{
  public:
    GhostscriptWorker(Napi::Function& callback, vector<string> explodedCmd)
        : Napi::AsyncWorker(callback, "ghostcript4js"), explodedCmd(explodedCmd) {}
    ~GhostscriptWorker() {}

    void Execute()
    {
        int gsargc = static_cast<int>(explodedCmd.size());
        char **gsargv = new char *[gsargc];
        for (int i = 0; i < gsargc; i++)
        {
            gsargv[i] = (char *)explodedCmd[i].c_str();
        }
        try
        {
            GhostscriptManager *gm = GhostscriptManager::GetInstance();
            gm->Execute(gsargc, gsargv);
            delete[] gsargv;
        }
        catch (exception &e)
        {
            delete[] gsargv;
            SetError(e.what());
        }
    }

    void OnOk()
    {
        Napi::HandleScope scope(Env());
        Callback().Call({Env().Null()});
    }

  private:
    vector<string> explodedCmd;
};

Napi::Value Version(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    Napi::Object obj = Napi::Object::New(env);
    gsapi_revision_t r;
    int res = gsapi_revision(&r, sizeof(r));
    if (res == 0) {
        obj["product"] = Napi::String::New(env, r.product);
        obj["copyright"] = Napi::String::New(env, r.copyright);
        obj["revision"] = Napi::Number::New(env, r.revision);
        obj["revisiondate"] = Napi::Number::New(env, r.revisiondate);
    } else {
        std::stringstream msg;
        msg << "Sorry error happened retrieving Ghostscript version info. Error code: " << res;
        throw Napi::Error::New(env, msg.str());
    }
    return obj;
}

vector<string> ConvertArguments(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    if (info.Length() < 1)
    {
        throw Napi::Error::New(env, "Sorry method requires 1 argument that represent the Ghostscript command.");
    }
    if (!info[0].IsString() && !info[0].IsArray())
    {
        throw Napi::Error::New(env, "Sorry method's argument should be a string or an array of strings");
    }
    vector<string> explodedCmd;
    if (info[0].IsString())
    {
        string RAWcmd = info[0].As<Napi::String>().Utf8Value();
        istringstream iss(RAWcmd);
        for (string RAWcmd; iss >> RAWcmd;)
            explodedCmd.push_back(RAWcmd);
    }
    if (info[0].IsArray())
    {
        Napi::Array array = info[0].As<Napi::Array>();
        for (uint32_t i = 0; i < array.Length(); i++)
        {
            Napi::Value value = array[i];
            if (!value.IsString())
            {
                throw Napi::Error::New(env, "Sorry method's argument should be a string or an array of strings");
            }
            string RAWcmd = value.As<Napi::String>().Utf8Value();
            explodedCmd.push_back(RAWcmd);
        }
    }
    return explodedCmd;
}

void Execute(const Napi::CallbackInfo& info) {
    vector<string> explodedCmd = ConvertArguments(info);
    Napi::Function callback = info[1].As<Napi::Function>();
    GhostscriptWorker* gs = new GhostscriptWorker(callback, explodedCmd);
    gs->Queue();
}

void ExecuteSync(const Napi::CallbackInfo& info)
{
    Napi::Env env = info.Env();
    vector<string> explodedCmd = ConvertArguments(info);
    int gsargc = static_cast<int>(explodedCmd.size());
    char **gsargv = new char *[gsargc];
    for (int i = 0; i < gsargc; i++)
    {
        gsargv[i] = (char *)explodedCmd[i].c_str();
    }
    try
    {
        GhostscriptManager *gm = GhostscriptManager::GetInstance();

        gm->Execute(gsargc, gsargv);
        delete[] gsargv;
    }
    catch (exception &e)
    {
        delete[] gsargv;
        throw Napi::Error::New(env, e.what());
    }
}

//////////////////////////// INIT & CONFIG MODULE //////////////////////////////

Napi::Object Init(Napi::Env env, Napi::Object exports) {
    exports.Set(Napi::String::New(env, "version"), Napi::Function::New(env, Version));
    exports.Set(Napi::String::New(env, "execute"), Napi::Function::New(env, Execute));
    exports.Set(Napi::String::New(env, "executeSync"), Napi::Function::New(env, ExecuteSync));
    return exports;
}

NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)

////////////////////////////////////////////////////////////////////////////////
<commit_msg>Ficed typo.<commit_after>/* ******************************************************************************
 * Copyright (c) 2017 Nicola Del Gobbo
 * 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
 *
 * THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
 * OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
 * IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
 * MERCHANTABLITY OR NON-INFRINGEMENT.
 *
 * See the Apache Version 2.0 License for specific language governing
 * permissions and limitations under the License.
 *
 * Contributors - initial API implementation:
 * Nicola Del Gobbo <nicoladelgobbo@gmail.com>
 * Mauro Doganieri <mauro.doganieri@gmail.com>
 ******************************************************************************/

#include "ghostscript4js.h"

// Lifeclycle management for the Ghostscript instance
class GhostscriptManager
{
  private:
    void *minst;
    mutex gs;
    static GhostscriptManager *instance;
    GhostscriptManager()
    {
        minst = nullptr;
    }
    void Init();
    void Destroy();
    void Exit();

  public:
    static GhostscriptManager *GetInstance();
    ~GhostscriptManager()
    {
        minst = nullptr;
    };
    void Execute(int gsargc, char *gsargv[]);
};

GhostscriptManager *GhostscriptManager::instance = nullptr;
// Static method that create or simple return the instance of the GhostscriptManager
// following the singleton design pattern
GhostscriptManager *GhostscriptManager::GetInstance()
{
    if (instance == nullptr)
    {
        instance = new GhostscriptManager();
    }
    return instance;
}

// Init is an implementation of gsapi_new_instance.
// It returns a global static instance of Ghostscript.
// i.e. Do not call init more than once, otherwise an error will be returned.
void GhostscriptManager::Init()
{
    int code = 0;
    code = gsapi_new_instance(&minst, NULL);
    if (code < 0)
    {
        throw std::runtime_error("Sorry error happened creating Ghostscript instance. Error code: " + to_string(code));
    }
    code = gsapi_set_arg_encoding(minst, GS_ARG_ENCODING_UTF8);
    if (code < 0)
    {
        throw std::runtime_error("Sorry error happened in setting the encoding for Ghostscript interpreter. Error code: " + to_string(code));
    }
}

// Execute is an implementation of gsapi_init_with_args.
// It initialises the Ghostscript interpreter given a set of arguments.
void GhostscriptManager::Execute(int gsargc, char *gsargv[])
{
    lock_guard<mutex> lk(gs);
    Init();
    int code = 0;
    code = gsapi_init_with_args(minst, gsargc, gsargv);
    Exit();
    Destroy();
    if (code < 0 && code != gs_error_Quit)
    {
        throw std::runtime_error("Sorry error happened executing Ghostscript command. Error code: " + to_string(code));
    }
}

// Exit is an implementation of gsapi_exit.
// It exits the Ghostscript interpreter.
// It must be called if init has been called, and just before destroy.
void GhostscriptManager::Exit()
{
    int code = 0;
    code = gsapi_exit(minst);
    if (code < 0 && code != gs_error_Quit)
    {
        throw std::runtime_error("Sorry error happened during the exit from the Ghostscript interpreter. Error code: " + to_string(code));
    }
}

// Destroy is an implementation of gsapi_delete_instance.
// It destroys a global static instance of Ghostscript.
// It should be called only after exit has been called if init has been called.
void GhostscriptManager::Destroy()
{
    gsapi_delete_instance(minst);
    minst = nullptr;
}

class GhostscriptWorker : public Napi::AsyncWorker
{
  public:
    GhostscriptWorker(Napi::Function& callback, vector<string> explodedCmd)
        : Napi::AsyncWorker(callback, "ghostcript4js"), explodedCmd(explodedCmd) {}
    ~GhostscriptWorker() {}

    void Execute()
    {
        int gsargc = static_cast<int>(explodedCmd.size());
        char **gsargv = new char *[gsargc];
        for (int i = 0; i < gsargc; i++)
        {
            gsargv[i] = (char *)explodedCmd[i].c_str();
        }
        try
        {
            GhostscriptManager *gm = GhostscriptManager::GetInstance();
            gm->Execute(gsargc, gsargv);
            delete[] gsargv;
        }
        catch (exception &e)
        {
            delete[] gsargv;
            SetError(e.what());
        }
    }

    void OnOk()
    {
        Napi::HandleScope scope(Env());
        Callback().Call({Env().Null()});
    }

  private:
    vector<string> explodedCmd;
};

Napi::Value Version(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    Napi::Object obj = Napi::Object::New(env);
    gsapi_revision_t r;
    int res = gsapi_revision(&r, sizeof(r));
    if (res == 0) {
        obj["product"] = Napi::String::New(env, r.product);
        obj["copyright"] = Napi::String::New(env, r.copyright);
        obj["revision"] = Napi::Number::New(env, r.revision);
        obj["revisiondate"] = Napi::Number::New(env, r.revisiondate);
    } else {
        std::stringstream msg;
        msg << "Sorry error happened retrieving Ghostscript version info. Error code: " << res;
        throw Napi::Error::New(env, msg.str());
    }
    return obj;
}

vector<string> ConvertArguments(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    if (info.Length() < 1)
    {
        throw Napi::Error::New(env, "Sorry method requires 1 argument that represent the Ghostscript command.");
    }
    if (!info[0].IsString() && !info[0].IsArray())
    {
        throw Napi::Error::New(env, "Sorry method's argument should be a string or an array of strings");
    }
    vector<string> explodedCmd;
    if (info[0].IsString())
    {
        string RAWcmd = info[0].As<Napi::String>().Utf8Value();
        istringstream iss(RAWcmd);
        for (string RAWcmd; iss >> RAWcmd;)
            explodedCmd.push_back(RAWcmd);
    }
    if (info[0].IsArray())
    {
        Napi::Array array = info[0].As<Napi::Array>();
        for (uint32_t i = 0; i < array.Length(); i++)
        {
            Napi::Value value = array[i];
            if (!value.IsString())
            {
                throw Napi::Error::New(env, "Sorry method's argument should be a string or an array of strings");
            }
            string RAWcmd = value.As<Napi::String>().Utf8Value();
            explodedCmd.push_back(RAWcmd);
        }
    }
    return explodedCmd;
}

void Execute(const Napi::CallbackInfo& info) {
    vector<string> explodedCmd = ConvertArguments(info);
    Napi::Function callback = info[1].As<Napi::Function>();
    GhostscriptWorker* gs = new GhostscriptWorker(callback, explodedCmd);
    gs->Queue();
}

void ExecuteSync(const Napi::CallbackInfo& info)
{
    Napi::Env env = info.Env();
    vector<string> explodedCmd = ConvertArguments(info);
    int gsargc = static_cast<int>(explodedCmd.size());
    char **gsargv = new char *[gsargc];
    for (int i = 0; i < gsargc; i++)
    {
        gsargv[i] = (char *)explodedCmd[i].c_str();
    }
    try
    {
        GhostscriptManager *gm = GhostscriptManager::GetInstance();

        gm->Execute(gsargc, gsargv);
        delete[] gsargv;
    }
    catch (exception &e)
    {
        delete[] gsargv;
        throw Napi::Error::New(env, e.what());
    }
}

//////////////////////////// INIT & CONFIG MODULE //////////////////////////////

Napi::Object Init(Napi::Env env, Napi::Object exports) {
    exports.Set(Napi::String::New(env, "version"), Napi::Function::New(env, Version));
    exports.Set(Napi::String::New(env, "execute"), Napi::Function::New(env, Execute));
    exports.Set(Napi::String::New(env, "executeSync"), Napi::Function::New(env, ExecuteSync));
    return exports;
}

NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)

////////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>#include "gibbs_sampling.h"
#include "single_node_sampler.h"
#include "binary_format.h"
#include "common.h"
#include "timer.h"
#include <fstream>
#include <memory>
#include <unistd.h>

namespace dd {

GibbsSampling::GibbsSampling(std::unique_ptr<CompiledFactorGraph> p_cfg,
                             const Weight weights[], const CmdParser &opts)
    : weights(weights), opts(opts) {
  // the highest node number available
  n_numa_nodes = numa_max_node();

  // if n_datacopy is valid, use it, otherwise, use numa_max_node
  if (opts.n_datacopy >= 1 && opts.n_datacopy <= n_numa_nodes + 1) {
    n_numa_nodes = opts.n_datacopy - 1;
  }

  // max possible threads per NUMA node
  n_thread_per_numa = (sysconf(_SC_NPROCESSORS_CONF)) / (n_numa_nodes + 1);
  // n_thread_per_numa = 1;

  // copy factor graphs
  for (int i = 0; i <= n_numa_nodes; ++i) {
    numa_run_on_node(i);
    numa_set_localalloc();

    std::cout << "CREATE CFG ON NODE ..." << i << std::endl;
    sampler.push_back(SingleNodeSampler(
        std::unique_ptr<CompiledFactorGraph>(
            i == 0 ? p_cfg.release() : new CompiledFactorGraph(*p_cfg)),
        weights, n_thread_per_numa /* TODO fold this into opts */, i, opts));
  }
};

void GibbsSampling::inference(const int &n_epoch, const bool is_quiet) {
  Timer t_total;

  Timer t;
  int nvar = sampler[0].fg.size.num_variables;
  int nnode = n_numa_nodes + 1;

  for (auto &s : sampler)
      s.infrs.clear_variabletally();

  // inference epochs
  for (int i_epoch = 0; i_epoch < n_epoch; i_epoch++) {
    if (!is_quiet) {
      std::cout << std::setprecision(2) << "INFERENCE EPOCH " << i_epoch * nnode
                << "~" << ((i_epoch + 1) * nnode) << "...." << std::flush;
    }

    // restart timer
    t.restart();

    // sample
    for (int i = 0; i < nnode; i++) {
      sampler[i].sample(i_epoch);
    }

    // wait for samplers to finish
    for (int i = 0; i < nnode; i++) {
      sampler[i].wait();
    }

    double elapsed = t.elapsed();
    if (!is_quiet) {
      std::cout << "" << elapsed << " sec.";
      std::cout << "," << (nvar * nnode) / elapsed << " vars/sec" << std::endl;
    }
  }

  double elapsed = t_total.elapsed();
  std::cout << "TOTAL INFERENCE TIME: " << elapsed << " sec." << std::endl;
}

void GibbsSampling::learn(const int &n_epoch, const int &n_sample_per_epoch,
                          const double &stepsize, const double &decay,
                          const double reg_param, const bool is_quiet,
                          const regularization reg) {
  Timer t_total;

  double current_stepsize = stepsize;
  std::cerr << sampler[0].fg.size << std::endl;

  Timer t;
  int nvar = sampler[0].fg.size.num_variables;
  int nnode = n_numa_nodes + 1;
  int nweight = sampler[0].fg.size.num_weights;

  std::cerr << sampler[0].fg.size << std::endl;

  InferenceResult &infrs = sampler[0].infrs;

  std::unique_ptr<double[]> ori_weights(new double[nweight]);
  memcpy(ori_weights.get(), infrs.weight_values.get(),
         sizeof(*ori_weights.get()) * nweight);
  std::cerr << sampler[0].fg.size << std::endl;

  // learning epochs
  for (int i_epoch = 0; i_epoch < n_epoch; i_epoch++) {
    if (!is_quiet) {
      std::cout << std::setprecision(2) << "LEARNING EPOCH " << i_epoch * nnode
                << "~" << ((i_epoch + 1) * nnode) << "...." << std::flush;
    }

    t.restart();

    // performs stochastic gradient descent with sampling
    for (int i = 0; i < nnode; i++) {
      sampler[i].sample_sgd(current_stepsize);
    }

    // wait the samplers to finish
    for (int i = 0; i < nnode; i++) {
      sampler[i].wait_sgd();
    }

    // sum the weights and store in the first factor graph
    // the average weights will be calculated and assigned to all factor graphs
    for (int i = 1; i <= n_numa_nodes; i++) {
      auto &sampler_other = sampler[i];
      for (int j = 0; j < nweight; j++) {
        infrs.weight_values[j] +=
            sampler_other.infrs.weight_values[j];
      }
    }

    // calculate average weights and regularize weights
    for (int j = 0; j < nweight; j++) {
      infrs.weight_values[j] /= nnode;
      if (!infrs.weights_isfixed[j]) {
        if (reg == REG_L2)
          infrs.weight_values[j] *=
              (1.0 / (1.0 + reg_param * current_stepsize));
        else  // l1
          infrs.weight_values[j] +=
              reg_param * (infrs.weight_values[j] < 0);
      }
    }

    // set weights for other factor graph to be the same as the first factor
    // graph
    for (int i = 1; i <= n_numa_nodes; i++) {
      auto &sampler_other = sampler[i];
      for (int j = 0; j < nweight; j++) {
        if (!infrs.weights_isfixed[j]) {
          sampler_other.infrs.weight_values[j] =
              infrs.weight_values[j];
        }
      }
    }

    // calculate the norms of the difference of weights from the current epoch
    // and last epoch
    double lmax = -1000000;
    double l2 = 0.0;
    for (int i = 0; i < nweight; i++) {
      double diff = fabs(ori_weights[i] - infrs.weight_values[i]);
      ori_weights[i] = infrs.weight_values[i];
      l2 += diff * diff;
      if (lmax < diff) {
        lmax = diff;
      }
    }
    lmax = lmax / current_stepsize;

    double elapsed = t.elapsed();
    if (!is_quiet) {
      std::cout << "" << elapsed << " sec.";
      std::cout << "," << (nvar * nnode) / elapsed << " vars/sec."
                << ",stepsize=" << current_stepsize << ",lmax=" << lmax
                << ",l2=" << sqrt(l2) / current_stepsize << std::endl;
    }

    current_stepsize = current_stepsize * decay;
  }

  double elapsed = t_total.elapsed();
  std::cout << "TOTAL LEARNING TIME: " << elapsed << " sec." << std::endl;
}

void GibbsSampling::dump_weights(const bool is_quiet) {
  // learning weights snippets
  InferenceResult &infrs = sampler[0].infrs;

  if (!is_quiet) {
    std::cout << "LEARNING SNIPPETS (QUERY WEIGHTS):" << std::endl;
    int ct = 0;
    for (long i = 0; i < infrs.nweights; i++) {
      ct++;
      std::cout << "   " << i << " " << infrs.weight_values[i]
                << std::endl;
      if (ct % 10 == 0) {
        break;
      }
    }
    std::cout << "   ..." << std::endl;
  }

  // dump learned weights
  std::string filename_text;
  filename_text = opts.output_folder + "/inference_result.out.weights.text";

  std::cout << "DUMPING... TEXT    : " << filename_text << std::endl;

  std::ofstream fout_text(filename_text.c_str());
  for (long i = 0; i < infrs.nweights; i++) {
    fout_text << i << " " << infrs.weight_values[i] << std::endl;
  }
  fout_text.close();
}

void GibbsSampling::aggregate_results_and_dump(const bool is_quiet) {
InferenceResult &infrs = sampler[0].infrs;
  // sum of variable assignments
  std::unique_ptr<double[]> agg_means(
      new double[infrs.nvars]);
  // number of samples
  std::unique_ptr<double[]> agg_nsamples(
      new double[infrs.nvars]);
  std::unique_ptr<int[]> multinomial_tallies(
      new int[infrs.ntallies]);

  for (long i = 0; i < infrs.nvars; i++) {
    agg_means[i] = 0;
    agg_nsamples[i] = 0;
  }

  for (long i = 0; i < infrs.ntallies; i++) {
    multinomial_tallies[i] = 0;
  }

  // sum variable assignments over all NUMA nodes
  for (int i = 0; i <= n_numa_nodes; i++) {
    const InferenceResult &infrs_i = sampler[i].infrs;
    for (long j = 0; j < infrs_i.nvars; ++j) {
      const Variable &variable = sampler[i].fg.variables[j];
      agg_means[variable.id] += infrs_i.agg_means[variable.id];
      agg_nsamples[variable.id] += infrs_i.agg_nsamples[variable.id];
    }
    for (long j = 0; j < infrs_i.ntallies; ++j) {
      multinomial_tallies[j] += infrs_i.multinomial_tallies[j];
    }
  }

  // inference snippets
  if (!is_quiet) {
    std::cout << "INFERENCE SNIPPETS (QUERY VARIABLES):" << std::endl;
    int ct = 0;
    for (long i = 0; i < infrs.nvars; i++) {
      const Variable &variable = sampler[0].fg.variables[i];
      if (!variable.is_evid || opts.should_sample_evidence) {
        ct++;
        std::cout << "   " << variable.id
                  << "  NSAMPLE=" << agg_nsamples[variable.id] << std::endl;
        switch (variable.domain_type) {
          case DTYPE_BOOLEAN:
            std::cout << "        @ 1 -> EXP="
                      << agg_means[variable.id] / agg_nsamples[variable.id]
                      << std::endl;
            break;

          case DTYPE_MULTINOMIAL: {
            const auto &print_snippet = [&multinomial_tallies, &agg_nsamples,
                                         variable](int domain_value,
                                                   int domain_index) {
              std::cout << "        @ " << domain_value << " -> EXP="
                        << 1.0 * multinomial_tallies[variable.n_start_i_tally +
                                                     domain_index] /
                               agg_nsamples[variable.id]
                        << std::endl;
            };
            if (variable.domain_map) {  // sparse
              for (const auto &entry : *variable.domain_map)
                print_snippet(entry.first, entry.second);
            } else {  // dense case
              for (size_t j = 0; j < variable.cardinality; j++)
                print_snippet(j, j);
            }
            break;
          }

          default:
            abort();
        }

        if (ct % 10 == 0) {
          break;
        }
      }
    }
    std::cout << "   ..." << std::endl;
  }

  // dump inference results
  std::string filename_text;
  filename_text = opts.output_folder + "/inference_result.out.text";

  std::cout << "DUMPING... TEXT    : " << filename_text << std::endl;
  std::ofstream fout_text(filename_text.c_str());
  for (long i = 0; i < sampler[0].fg.size.num_variables; i++) {
    const Variable &variable = sampler[0].fg.variables[i];
    if (variable.is_evid && !opts.should_sample_evidence) {
      continue;
    }

    switch (variable.domain_type) {
      case DTYPE_BOOLEAN: {
        fout_text << variable.id << " " << 1 << " "
                  << (agg_means[variable.id] / agg_nsamples[variable.id])
                  << std::endl;
        break;
      }

      case DTYPE_MULTINOMIAL: {
        const auto &print_result = [&fout_text, &multinomial_tallies,
                                    &agg_nsamples, variable](int domain_value,
                                                             int domain_index) {
          fout_text
              << variable.id << " " << domain_value << " "
              << (1.0 *
                  multinomial_tallies[variable.n_start_i_tally + domain_index] /
                  agg_nsamples[variable.id])
              << std::endl;
        };
        if (variable.domain_map) {  // sparse
          for (const auto &entry : *variable.domain_map)
            print_result(entry.first, entry.second);
        } else {  // dense
          for (size_t j = 0; j < variable.cardinality; j++) print_result(j, j);
        }
        break;
      }

      default:
        abort();
    }
  }
  fout_text.close();

  if (!is_quiet) {
    // show a histogram of inference results
    std::cout << "INFERENCE CALIBRATION (QUERY BINS):" << std::endl;
    std::vector<int> abc;
    for (int i = 0; i <= 10; i++) {
      abc.push_back(0);
    }
    int bad = 0;
    for (long i = 0; i < sampler[0].fg.size.num_variables; i++) {
      const Variable &variable = sampler[0].fg.variables[i];
      if (variable.is_evid && !opts.should_sample_evidence) {
        continue;
      }
      int bin = (int)(agg_means[variable.id] / agg_nsamples[variable.id] * 10);
      if (bin >= 0 && bin <= 10) {
        abc[bin]++;
      } else {
        bad++;
      }
    }
    abc[9] += abc[10];
    for (int i = 0; i < 10; i++) {
      std::cout << "PROB BIN 0." << i << "~0." << (i + 1) << "  -->  # "
                << abc[i] << std::endl;
    }
  }
}

}  // namespace dd
<commit_msg>Cleans up for loops, favors prefix increments<commit_after>#include "gibbs_sampling.h"
#include "single_node_sampler.h"
#include "binary_format.h"
#include "common.h"
#include "timer.h"
#include <fstream>
#include <memory>
#include <unistd.h>

namespace dd {

GibbsSampling::GibbsSampling(std::unique_ptr<CompiledFactorGraph> p_cfg,
                             const Weight weights[], const CmdParser &opts)
    : weights(weights), opts(opts) {
  // the highest node number available
  n_numa_nodes = numa_max_node();

  // if n_datacopy is valid, use it, otherwise, use numa_max_node
  if (opts.n_datacopy >= 1 && opts.n_datacopy <= n_numa_nodes + 1) {
    n_numa_nodes = opts.n_datacopy - 1;
  }

  // max possible threads per NUMA node
  n_thread_per_numa = (sysconf(_SC_NPROCESSORS_CONF)) / (n_numa_nodes + 1);
  // n_thread_per_numa = 1;

  // copy factor graphs
  for (int i = 0; i <= n_numa_nodes; ++i) {
    numa_run_on_node(i);
    numa_set_localalloc();

    std::cout << "CREATE CFG ON NODE ..." << i << std::endl;
    sampler.push_back(SingleNodeSampler(
        std::unique_ptr<CompiledFactorGraph>(
            i == 0 ? p_cfg.release() : new CompiledFactorGraph(*p_cfg)),
        weights, n_thread_per_numa /* TODO fold this into opts */, i, opts));
  }
};

void GibbsSampling::inference(const int &n_epoch, const bool is_quiet) {
  Timer t_total;

  Timer t;
  int nvar = sampler[0].fg.size.num_variables;
  int nnode = n_numa_nodes + 1;

  for (auto &s : sampler) s.infrs.clear_variabletally();

  // inference epochs
  for (int i_epoch = 0; i_epoch < n_epoch; ++i_epoch) {
    if (!is_quiet) {
      std::cout << std::setprecision(2) << "INFERENCE EPOCH " << i_epoch * nnode
                << "~" << ((i_epoch + 1) * nnode) << "...." << std::flush;
    }

    // restart timer
    t.restart();

    // sample
    for (auto &s: sampler) s.sample(i_epoch);

    // wait for samplers to finish
    for (auto &s: sampler) s.wait();

    double elapsed = t.elapsed();
    if (!is_quiet) {
      std::cout << "" << elapsed << " sec.";
      std::cout << "," << (nvar * nnode) / elapsed << " vars/sec" << std::endl;
    }
  }

  double elapsed = t_total.elapsed();
  std::cout << "TOTAL INFERENCE TIME: " << elapsed << " sec." << std::endl;
}

void GibbsSampling::learn(const int &n_epoch, const int &n_sample_per_epoch,
                          const double &stepsize, const double &decay,
                          const double reg_param, const bool is_quiet,
                          const regularization reg) {
  Timer t_total;

  double current_stepsize = stepsize;
  std::cerr << sampler[0].fg.size << std::endl;

  Timer t;
  int nvar = sampler[0].fg.size.num_variables;
  int nnode = n_numa_nodes + 1;
  int nweight = sampler[0].fg.size.num_weights;

  std::cerr << sampler[0].fg.size << std::endl;

  InferenceResult &infrs = sampler[0].infrs;

  std::unique_ptr<double[]> ori_weights(new double[nweight]);
  memcpy(ori_weights.get(), infrs.weight_values.get(),
         sizeof(*ori_weights.get()) * nweight);
  std::cerr << sampler[0].fg.size << std::endl;

  // learning epochs
  for (int i_epoch = 0; i_epoch < n_epoch; ++i_epoch) {
    if (!is_quiet) {
      std::cout << std::setprecision(2) << "LEARNING EPOCH " << i_epoch * nnode
                << "~" << ((i_epoch + 1) * nnode) << "...." << std::flush;
    }

    t.restart();

    // performs stochastic gradient descent with sampling
    for (auto &s : sampler) s.sample_sgd(current_stepsize);

    // wait the samplers to finish
    for (auto &s : sampler) s.wait_sgd();

    // sum the weights and store in the first factor graph
    // the average weights will be calculated and assigned to all factor graphs
    for (int i = 1; i <= n_numa_nodes; ++i) {
      auto &sampler_other = sampler[i];
      for (int j = 0; j < nweight; ++j) {
        infrs.weight_values[j] += sampler_other.infrs.weight_values[j];
      }
    }

    // calculate average weights and regularize weights
    for (int j = 0; j < nweight; ++j) {
      infrs.weight_values[j] /= nnode;
      if (!infrs.weights_isfixed[j]) {
        if (reg == REG_L2)
          infrs.weight_values[j] *=
              (1.0 / (1.0 + reg_param * current_stepsize));
        else  // l1
          infrs.weight_values[j] += reg_param * (infrs.weight_values[j] < 0);
      }
    }

    // set weights for other factor graph to be the same as the first factor
    // graph
    for (int i = 1; i <= n_numa_nodes; ++i) {
      auto &sampler_other = sampler[i];
      for (int j = 0; j < nweight; ++j) {
        if (!infrs.weights_isfixed[j]) {
          sampler_other.infrs.weight_values[j] = infrs.weight_values[j];
        }
      }
    }

    // calculate the norms of the difference of weights from the current epoch
    // and last epoch
    double lmax = -1000000;
    double l2 = 0.0;
    for (int j = 0; j < nweight; ++j) {
      double diff = fabs(ori_weights[j] - infrs.weight_values[j]);
      ori_weights[j] = infrs.weight_values[j];
      l2 += diff * diff;
      if (lmax < diff) {
        lmax = diff;
      }
    }
    lmax = lmax / current_stepsize;

    double elapsed = t.elapsed();
    if (!is_quiet) {
      std::cout << "" << elapsed << " sec.";
      std::cout << "," << (nvar * nnode) / elapsed << " vars/sec."
                << ",stepsize=" << current_stepsize << ",lmax=" << lmax
                << ",l2=" << sqrt(l2) / current_stepsize << std::endl;
    }

    current_stepsize = current_stepsize * decay;
  }

  double elapsed = t_total.elapsed();
  std::cout << "TOTAL LEARNING TIME: " << elapsed << " sec." << std::endl;
}

void GibbsSampling::dump_weights(const bool is_quiet) {
  // learning weights snippets
  InferenceResult &infrs = sampler[0].infrs;

  if (!is_quiet) {
    std::cout << "LEARNING SNIPPETS (QUERY WEIGHTS):" << std::endl;
    int ct = 0;
    for (long j = 0; j < infrs.nweights; ++j) {
      ++ct;
      std::cout << "   " << j << " " << infrs.weight_values[j] << std::endl;
      if (ct % 10 == 0) {
        break;
      }
    }
    std::cout << "   ..." << std::endl;
  }

  // dump learned weights
  std::string filename_text;
  filename_text = opts.output_folder + "/inference_result.out.weights.text";

  std::cout << "DUMPING... TEXT    : " << filename_text << std::endl;

  std::ofstream fout_text(filename_text.c_str());
  for (long j = 0; j < infrs.nweights; ++j) {
    fout_text << j << " " << infrs.weight_values[j] << std::endl;
  }
  fout_text.close();
}

void GibbsSampling::aggregate_results_and_dump(const bool is_quiet) {
  InferenceResult &infrs = sampler[0].infrs;
  // sum of variable assignments
  std::unique_ptr<double[]> agg_means(new double[infrs.nvars]);
  // number of samples
  std::unique_ptr<double[]> agg_nsamples(new double[infrs.nvars]);
  std::unique_ptr<int[]> multinomial_tallies(new int[infrs.ntallies]);

  for (long j = 0; j < infrs.nvars; ++j) {
    agg_means[j] = 0;
    agg_nsamples[j] = 0;
  }

  for (long j = 0; j < infrs.ntallies; ++j) {
    multinomial_tallies[j] = 0;
  }

  // sum variable assignments over all NUMA nodes
  for (int i = 0; i <= n_numa_nodes; ++i) {
    const InferenceResult &infrs_i = sampler[i].infrs;
    for (long j = 0; j < infrs_i.nvars; ++j) {
      const Variable &variable = sampler[i].fg.variables[j];
      agg_means[variable.id] += infrs_i.agg_means[variable.id];
      agg_nsamples[variable.id] += infrs_i.agg_nsamples[variable.id];
    }
    for (long j = 0; j < infrs_i.ntallies; ++j) {
      multinomial_tallies[j] += infrs_i.multinomial_tallies[j];
    }
  }

  // inference snippets
  if (!is_quiet) {
    std::cout << "INFERENCE SNIPPETS (QUERY VARIABLES):" << std::endl;
    int ct = 0;
    for (long j = 0; j < sampler[0].fg.size.num_variables; ++j) {
      const Variable &variable = sampler[0].fg.variables[j];
      if (!variable.is_evid || opts.should_sample_evidence) {
        ++ct;
        std::cout << "   " << variable.id
                  << "  NSAMPLE=" << agg_nsamples[variable.id] << std::endl;
        switch (variable.domain_type) {
          case DTYPE_BOOLEAN:
            std::cout << "        @ 1 -> EXP="
                      << agg_means[variable.id] / agg_nsamples[variable.id]
                      << std::endl;
            break;

          case DTYPE_MULTINOMIAL: {
            const auto &print_snippet = [&multinomial_tallies, &agg_nsamples,
                                         variable](int domain_value,
                                                   int domain_index) {
              std::cout << "        @ " << domain_value << " -> EXP="
                        << 1.0 * multinomial_tallies[variable.n_start_i_tally +
                                                     domain_index] /
                               agg_nsamples[variable.id]
                        << std::endl;
            };
            if (variable.domain_map) {  // sparse
              for (const auto &entry : *variable.domain_map)
                print_snippet(entry.first, entry.second);
            } else {  // dense case
              for (size_t j = 0; j < variable.cardinality; ++j)
                print_snippet(j, j);
            }
            break;
          }

          default:
            abort();
        }

        if (ct % 10 == 0) {
          break;
        }
      }
    }
    std::cout << "   ..." << std::endl;
  }

  // dump inference results
  std::string filename_text;
  filename_text = opts.output_folder + "/inference_result.out.text";

  std::cout << "DUMPING... TEXT    : " << filename_text << std::endl;
  std::ofstream fout_text(filename_text.c_str());
  for (long j = 0; j < sampler[0].fg.size.num_variables; ++j) {
    const Variable &variable = sampler[0].fg.variables[j];
    if (variable.is_evid && !opts.should_sample_evidence) {
      continue;
    }

    switch (variable.domain_type) {
      case DTYPE_BOOLEAN: {
        fout_text << variable.id << " " << 1 << " "
                  << (agg_means[variable.id] / agg_nsamples[variable.id])
                  << std::endl;
        break;
      }

      case DTYPE_MULTINOMIAL: {
        const auto &print_result = [&fout_text, &multinomial_tallies,
                                    &agg_nsamples, variable](int domain_value,
                                                             int domain_index) {
          fout_text
              << variable.id << " " << domain_value << " "
              << (1.0 *
                  multinomial_tallies[variable.n_start_i_tally + domain_index] /
                  agg_nsamples[variable.id])
              << std::endl;
        };
        if (variable.domain_map) {  // sparse
          for (const auto &entry : *variable.domain_map)
            print_result(entry.first, entry.second);
        } else {  // dense
          for (size_t k = 0; k < variable.cardinality; ++k) print_result(k, k);
        }
        break;
      }

      default:
        abort();
    }
  }
  fout_text.close();

  if (!is_quiet) {
    // show a histogram of inference results
    std::cout << "INFERENCE CALIBRATION (QUERY BINS):" << std::endl;
    std::vector<int> abc;
    for (int i = 0; i <= 10; ++i) {
      abc.push_back(0);
    }
    int bad = 0;
    for (long j = 0; j < sampler[0].fg.size.num_variables; ++j) {
      const Variable &variable = sampler[0].fg.variables[j];
      if (variable.is_evid && !opts.should_sample_evidence) {
        continue;
      }
      int bin = (int)(agg_means[variable.id] / agg_nsamples[variable.id] * 10);
      if (bin >= 0 && bin <= 10) {
        ++abc[bin];
      } else {
        ++bad;
      }
    }
    abc[9] += abc[10];
    for (int i = 0; i < 10; ++i) {
      std::cout << "PROB BIN 0." << i << "~0." << (i + 1) << "  -->  # "
                << abc[i] << std::endl;
    }
  }
}

}  // namespace dd
<|endoftext|>


<commit_before>
/*----------------------------------------------------------------------
This work derived from alex joni's halui.cc
Copyright: 2013,2014
Author:    Dewey Garrett <dgarrett@panix.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
----------------------------------------------------------------------*/
#include "rcs_print.hh"
#include "emc.hh"
#include <stdio.h>
#include "hal.h"
#include "rtapi.h"
#include "inihal.hh"

static int debug=1;
static int comp_id;
extern value_inihal_data old_inihal_data;

static ptr_inihal_data *the_inihal_data;

#define PREFIX "ini."
#define EPSILON .00001
#define CLOSE(a,b,eps) ((a)-(b) < +(eps) && (a)-(b) > -(eps))

#define NEW(NAME) new_inihal_data.NAME

#define CHANGED(NAME) \
    ((old_inihal_data.NAME) - (new_inihal_data.NAME) > +(EPSILON)) \
    || \
    ((old_inihal_data.NAME) - (new_inihal_data.NAME) < -(EPSILON))

#define CHANGED_IDX(NAME,IDX) \
    ((old_inihal_data.NAME[IDX]) - (new_inihal_data.NAME[IDX]) > +(EPSILON)) \
    || \
    ((old_inihal_data.NAME[IDX]) - (new_inihal_data.NAME[IDX]) < -(EPSILON))

#define UPDATE(NAME) old_inihal_data.NAME = new_inihal_data.NAME

#define UPDATE_IDX(NAME,IDX) old_inihal_data.NAME[IDX] = new_inihal_data.NAME[IDX]

#define SHOW_CHANGE(NAME) \
    fprintf(stderr,"Changed: "#NAME" %f-->%f\n",old_inihal_data.NAME, \
                                                new_inihal_data.NAME);
#define SHOW_CHANGE_ARC_BLEND() \
    fprintf(stderr,"Changed: blend_enable:          %d-->%d\n"\
                   "         blend_fallback_enable: %d-->%d\n"\
                   "         optimization_depth:    %d-->%d\n"\
                   "         gap_cycles:            %f-->%f\n"\
                   "         ramp_freq:             %f-->%f\n"\
           ,old_inihal_data.traj_arc_blend_enable \
           ,new_inihal_data.traj_arc_blend_enable \
           ,old_inihal_data.traj_arc_blend_fallback_enable \
           ,new_inihal_data.traj_arc_blend_fallback_enable \
           ,old_inihal_data.traj_arc_blend_optimization_depth \
           ,new_inihal_data.traj_arc_blend_optimization_depth \
           ,old_inihal_data.traj_arc_blend_gap_cycles \
           ,new_inihal_data.traj_arc_blend_gap_cycles \
           ,old_inihal_data.traj_arc_blend_ramp_freq \
           ,new_inihal_data.traj_arc_blend_ramp_freq \
          );

#define SHOW_CHANGE_IDX(NAME,IDX) \
    fprintf(stderr,"Changed: "#NAME"[%d] %f-->%f\n",IDX,old_inihal_data.NAME[IDX], \
                                                        new_inihal_data.NAME[IDX]);
#define MAKE_BIT_PIN(NAME,DIR) \
do { \
     retval = hal_pin_bit_newf(DIR,&(the_inihal_data->NAME),comp_id,PREFIX#NAME); \
     if (retval < 0) return retval; \
   } while (0)

#define MAKE_S32_PIN(NAME,DIR) \
do { \
     retval = hal_pin_s32_newf(DIR,&(the_inihal_data->NAME),comp_id,PREFIX#NAME); \
     if (retval < 0) return retval; \
   } while (0)

#define MAKE_FLOAT_PIN(NAME,DIR) \
do { \
     retval = hal_pin_float_newf(DIR,&(the_inihal_data->NAME),comp_id,PREFIX#NAME); \
     if (retval < 0) return retval; \
   } while (0)

#define MAKE_FLOAT_PIN_IDX(NAME,DIR,IDX) \
do {                        \
     retval = hal_pin_float_newf(DIR,&(the_inihal_data->NAME[IDX]),\
                                 comp_id,PREFIX"%d."#NAME,IDX); \
     if (retval < 0) return retval; \
   } while (0)

#define INIT_PIN(NAME) *(the_inihal_data->NAME) = old_inihal_data.NAME;

int ini_hal_init(void)
{
    int retval;

    comp_id = hal_init("inihal");
    if (comp_id < 0) {
    rtapi_print_msg(RTAPI_MSG_ERR,
            "ini_hal_init: ERROR: hal_init() failed\n");
    return -1;
    }

    the_inihal_data = (ptr_inihal_data *) hal_malloc(sizeof(ptr_inihal_data));
    if (the_inihal_data == 0) {
        rtapi_print_msg(RTAPI_MSG_ERR,
                       "ini_hal_init: ERROR: hal_malloc() failed\n");
        hal_exit(comp_id);
        return -1;
    }

    for (int idx = 0; idx < EMCMOT_MAX_JOINTS; idx++) {
        MAKE_FLOAT_PIN_IDX(backlash,HAL_IN,idx);
        MAKE_FLOAT_PIN_IDX(min_limit,HAL_IN,idx);
        MAKE_FLOAT_PIN_IDX(max_limit,HAL_IN,idx);
        MAKE_FLOAT_PIN_IDX(max_velocity,HAL_IN,idx);
        MAKE_FLOAT_PIN_IDX(max_acceleration,HAL_IN,idx);
        MAKE_FLOAT_PIN_IDX(ferror,HAL_IN,idx);
        MAKE_FLOAT_PIN_IDX(min_ferror,HAL_IN,idx);
    }

    MAKE_FLOAT_PIN(traj_default_velocity,HAL_IN);
    MAKE_FLOAT_PIN(traj_max_velocity,HAL_IN);
    MAKE_FLOAT_PIN(traj_default_acceleration,HAL_IN);
    MAKE_FLOAT_PIN(traj_max_acceleration,HAL_IN);

    MAKE_BIT_PIN(traj_arc_blend_enable,HAL_IN);
    MAKE_BIT_PIN(traj_arc_blend_fallback_enable,HAL_IN);
    MAKE_S32_PIN(traj_arc_blend_optimization_depth,HAL_IN);
    MAKE_FLOAT_PIN(traj_arc_blend_gap_cycles,HAL_IN);
    MAKE_FLOAT_PIN(traj_arc_blend_ramp_freq,HAL_IN);

    hal_ready(comp_id);
    return 0;
} // ini_hal_init()

int ini_hal_init_pins()
{
    INIT_PIN(traj_default_velocity);
    INIT_PIN(traj_max_velocity);
    INIT_PIN(traj_default_acceleration);
    INIT_PIN(traj_max_acceleration);

    INIT_PIN(traj_arc_blend_enable);
    INIT_PIN(traj_arc_blend_fallback_enable);
    INIT_PIN(traj_arc_blend_optimization_depth);
    INIT_PIN(traj_arc_blend_gap_cycles);
    INIT_PIN(traj_arc_blend_ramp_freq);

    for (int idx = 0; idx < EMCMOT_MAX_JOINTS; idx++) {
        INIT_PIN(backlash[idx]);
        INIT_PIN(min_limit[idx]);
        INIT_PIN(max_limit[idx]);
        INIT_PIN(max_velocity[idx]);
        INIT_PIN(max_acceleration[idx]);
        INIT_PIN(ferror[idx]);
        INIT_PIN(min_ferror[idx]);
    }

    return 0;
} // ini_hal_init_pins()

static void copy_hal_data(const ptr_inihal_data &i, value_inihal_data &j)
{
    int x;
#define FIELD(t,f) j.f = (i.f)?*i.f:0;
#define ARRAY(t,f,n) do { for (x = 0; x < n; x++) j.f[x] = (i.f[x])?*i.f[x]:0; } while (0);
    HAL_FIELDS
#undef FIELD
#undef ARRAY
} // copy_hal_data()

int check_ini_hal_items()
{
    value_inihal_data new_inihal_data_mutable;
    copy_hal_data(*the_inihal_data, new_inihal_data_mutable);
    const value_inihal_data &new_inihal_data = new_inihal_data_mutable;

    if (CHANGED(traj_default_velocity)) {
        if (debug) SHOW_CHANGE(traj_default_velocity)
        UPDATE(traj_default_velocity);
        if (0 != emcTrajSetVelocity(0, NEW(traj_default_velocity))) {
            rcs_print("check_ini_hal_items:bad return value from emcTrajSetVelocity\n");
        }
    }
    if (CHANGED(traj_max_velocity)) {
        if (debug) SHOW_CHANGE(traj_max_velocity)
        UPDATE(traj_max_velocity);
        if (0 != emcTrajSetMaxVelocity(NEW(traj_max_velocity))) {
            if (emc_debug & EMC_DEBUG_CONFIG) {
                rcs_print("check_ini_hal_items:bad return value from emcTrajSetMaxVelocity\n");
            }
        }
    }
    if (CHANGED(traj_default_acceleration)) {
        if (debug) SHOW_CHANGE(traj_default_acceleration)
        UPDATE(traj_default_acceleration);
        if (0 != emcTrajSetAcceleration(NEW(traj_default_acceleration))) {
            if (emc_debug & EMC_DEBUG_CONFIG) {
                rcs_print("check_ini_hal_items:bad return value from emcTrajSetAcceleration\n");
            }
        }
    }
    if (CHANGED(traj_max_acceleration)) {
        if (debug) SHOW_CHANGE(traj_max_acceleration)
        UPDATE(traj_max_acceleration);
        if (0 != emcTrajSetMaxAcceleration(NEW(traj_max_acceleration))) {
            if (emc_debug & EMC_DEBUG_CONFIG) {
                rcs_print("check_ini_hal_items:bad return value from emcTrajSetMaxAcceleration\n");
            }
        }
    }

    if (   CHANGED(traj_arc_blend_enable)
        || CHANGED(traj_arc_blend_fallback_enable)
        || CHANGED(traj_arc_blend_optimization_depth)
        || CHANGED(traj_arc_blend_gap_cycles)
        || CHANGED(traj_arc_blend_ramp_freq)
       ) {
        if (debug) SHOW_CHANGE_ARC_BLEND()
        UPDATE(traj_arc_blend_enable);
        UPDATE(traj_arc_blend_fallback_enable);
        UPDATE(traj_arc_blend_optimization_depth);
        UPDATE(traj_arc_blend_gap_cycles);
        UPDATE(traj_arc_blend_ramp_freq);
        if (0 != emcSetupArcBlends(old_inihal_data.traj_arc_blend_enable
                                  ,old_inihal_data.traj_arc_blend_fallback_enable
                                  ,old_inihal_data.traj_arc_blend_optimization_depth
                                  ,old_inihal_data.traj_arc_blend_gap_cycles
                                  ,old_inihal_data.traj_arc_blend_ramp_freq
                                  )) {
            if (emc_debug & EMC_DEBUG_CONFIG) {
                rcs_print("bad return value from emcSetupArcBlends\n");
            }
            return -1;
        }
    }

    for (int idx = 0; idx < EMCMOT_MAX_JOINTS; idx++) {
        if (CHANGED_IDX(backlash,idx) ) {
            if (debug) SHOW_CHANGE_IDX(backlash,idx);
            UPDATE_IDX(backlash,idx);
            if (0 != emcTrajSetMaxAcceleration(NEW(backlash[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print("check_ini_hal_items:bad return value from emcTrajSetMaxAcceleration\n");
                }
        }
        }
        if (CHANGED_IDX(min_limit,idx) ) {
            if (debug) SHOW_CHANGE_IDX(min_limit,idx);
            UPDATE_IDX(min_limit,idx);
            if (0 != emcAxisSetMinPositionLimit(idx,NEW(min_limit[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print_error("check_ini_hal_items:bad return from emcAxisSetMinPositionLimit\n");
                }
            }
        }
        if (CHANGED_IDX(max_limit,idx) ) {
            if (debug) SHOW_CHANGE_IDX(max_limit,idx);
            UPDATE_IDX(max_limit,idx);
            if (0 != emcAxisSetMaxPositionLimit(idx,NEW(max_limit[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print_error("check_ini_hal_items:bad return from emcAxisSetMaxPositionLimit\n");
                }
            }
        }
        if (CHANGED_IDX(max_velocity,idx) ) {
            if (debug) SHOW_CHANGE_IDX(max_velocity,idx);
            UPDATE_IDX(max_velocity,idx);
            if (0 != emcAxisSetMaxVelocity(idx, NEW(max_velocity[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print_error("check_ini_hal_items:bad return from emcAxisSetMaxVelocity\n");
                }
            }
        }
        if (CHANGED_IDX(max_acceleration,idx) ) {
            if (debug) SHOW_CHANGE_IDX(max_acceleration,idx);
            UPDATE_IDX(max_acceleration,idx);
            if (0 != emcAxisSetMaxAcceleration(idx, NEW(max_acceleration[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print_error("check_ini_hal_items:bad return from emcAxisSetMaxAcceleration\n");
                }
            }
        }
        if (CHANGED_IDX(ferror,idx) ) {
            if (debug) SHOW_CHANGE_IDX(ferror,idx);
            UPDATE_IDX(ferror,idx);
            if (0 != emcAxisSetFerror(idx,NEW(ferror[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print_error("check_ini_hal_items:bad return from emcAxisSetFerror\n");
                }
            }
        }
        if (CHANGED_IDX(min_ferror,idx) ) {
            if (debug) SHOW_CHANGE_IDX(min_ferror,idx);
            UPDATE_IDX(min_ferror,idx);
            if (0 != emcAxisSetMinFerror(idx,NEW(min_ferror[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print_error("check_ini_hal_items:bad return from emcAxisSetMinFerror\n");
                }
        }
        }
    }

    return 0;
} // check_ini_hal_items

// vim: sts=4 sw=4 et
<commit_msg>inihal.cc: use %g for reals in Changed: prints<commit_after>
/*----------------------------------------------------------------------
This work derived from alex joni's halui.cc
Copyright: 2013,2014
Author:    Dewey Garrett <dgarrett@panix.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
----------------------------------------------------------------------*/
#include "rcs_print.hh"
#include "emc.hh"
#include <stdio.h>
#include "hal.h"
#include "rtapi.h"
#include "inihal.hh"

static int debug=1;
static int comp_id;
extern value_inihal_data old_inihal_data;

static ptr_inihal_data *the_inihal_data;

#define PREFIX "ini."
#define EPSILON .00001
#define CLOSE(a,b,eps) ((a)-(b) < +(eps) && (a)-(b) > -(eps))

#define NEW(NAME) new_inihal_data.NAME

#define CHANGED(NAME) \
    ((old_inihal_data.NAME) - (new_inihal_data.NAME) > +(EPSILON)) \
    || \
    ((old_inihal_data.NAME) - (new_inihal_data.NAME) < -(EPSILON))

#define CHANGED_IDX(NAME,IDX) \
    ((old_inihal_data.NAME[IDX]) - (new_inihal_data.NAME[IDX]) > +(EPSILON)) \
    || \
    ((old_inihal_data.NAME[IDX]) - (new_inihal_data.NAME[IDX]) < -(EPSILON))

#define UPDATE(NAME) old_inihal_data.NAME = new_inihal_data.NAME

#define UPDATE_IDX(NAME,IDX) old_inihal_data.NAME[IDX] = new_inihal_data.NAME[IDX]

#define SHOW_CHANGE(NAME) \
    fprintf(stderr,"Changed: "#NAME" %g-->%g\n",old_inihal_data.NAME, \
                                                new_inihal_data.NAME);
#define SHOW_CHANGE_ARC_BLEND() \
    fprintf(stderr,"Changed: blend_enable:          %d-->%d\n"\
                   "         blend_fallback_enable: %d-->%d\n"\
                   "         optimization_depth:    %d-->%d\n"\
                   "         gap_cycles:            %f-->%f\n"\
                   "         ramp_freq:             %f-->%f\n"\
           ,old_inihal_data.traj_arc_blend_enable \
           ,new_inihal_data.traj_arc_blend_enable \
           ,old_inihal_data.traj_arc_blend_fallback_enable \
           ,new_inihal_data.traj_arc_blend_fallback_enable \
           ,old_inihal_data.traj_arc_blend_optimization_depth \
           ,new_inihal_data.traj_arc_blend_optimization_depth \
           ,old_inihal_data.traj_arc_blend_gap_cycles \
           ,new_inihal_data.traj_arc_blend_gap_cycles \
           ,old_inihal_data.traj_arc_blend_ramp_freq \
           ,new_inihal_data.traj_arc_blend_ramp_freq \
          );

#define SHOW_CHANGE_IDX(NAME,IDX) \
    fprintf(stderr,"Changed: "#NAME"[%d] %g-->%g\n",IDX,old_inihal_data.NAME[IDX], \
                                                        new_inihal_data.NAME[IDX]);
#define MAKE_BIT_PIN(NAME,DIR) \
do { \
     retval = hal_pin_bit_newf(DIR,&(the_inihal_data->NAME),comp_id,PREFIX#NAME); \
     if (retval < 0) return retval; \
   } while (0)

#define MAKE_S32_PIN(NAME,DIR) \
do { \
     retval = hal_pin_s32_newf(DIR,&(the_inihal_data->NAME),comp_id,PREFIX#NAME); \
     if (retval < 0) return retval; \
   } while (0)

#define MAKE_FLOAT_PIN(NAME,DIR) \
do { \
     retval = hal_pin_float_newf(DIR,&(the_inihal_data->NAME),comp_id,PREFIX#NAME); \
     if (retval < 0) return retval; \
   } while (0)

#define MAKE_FLOAT_PIN_IDX(NAME,DIR,IDX) \
do {                        \
     retval = hal_pin_float_newf(DIR,&(the_inihal_data->NAME[IDX]),\
                                 comp_id,PREFIX"%d."#NAME,IDX); \
     if (retval < 0) return retval; \
   } while (0)

#define INIT_PIN(NAME) *(the_inihal_data->NAME) = old_inihal_data.NAME;

int ini_hal_init(void)
{
    int retval;

    comp_id = hal_init("inihal");
    if (comp_id < 0) {
    rtapi_print_msg(RTAPI_MSG_ERR,
            "ini_hal_init: ERROR: hal_init() failed\n");
    return -1;
    }

    the_inihal_data = (ptr_inihal_data *) hal_malloc(sizeof(ptr_inihal_data));
    if (the_inihal_data == 0) {
        rtapi_print_msg(RTAPI_MSG_ERR,
                       "ini_hal_init: ERROR: hal_malloc() failed\n");
        hal_exit(comp_id);
        return -1;
    }

    for (int idx = 0; idx < EMCMOT_MAX_JOINTS; idx++) {
        MAKE_FLOAT_PIN_IDX(backlash,HAL_IN,idx);
        MAKE_FLOAT_PIN_IDX(min_limit,HAL_IN,idx);
        MAKE_FLOAT_PIN_IDX(max_limit,HAL_IN,idx);
        MAKE_FLOAT_PIN_IDX(max_velocity,HAL_IN,idx);
        MAKE_FLOAT_PIN_IDX(max_acceleration,HAL_IN,idx);
        MAKE_FLOAT_PIN_IDX(ferror,HAL_IN,idx);
        MAKE_FLOAT_PIN_IDX(min_ferror,HAL_IN,idx);
    }

    MAKE_FLOAT_PIN(traj_default_velocity,HAL_IN);
    MAKE_FLOAT_PIN(traj_max_velocity,HAL_IN);
    MAKE_FLOAT_PIN(traj_default_acceleration,HAL_IN);
    MAKE_FLOAT_PIN(traj_max_acceleration,HAL_IN);

    MAKE_BIT_PIN(traj_arc_blend_enable,HAL_IN);
    MAKE_BIT_PIN(traj_arc_blend_fallback_enable,HAL_IN);
    MAKE_S32_PIN(traj_arc_blend_optimization_depth,HAL_IN);
    MAKE_FLOAT_PIN(traj_arc_blend_gap_cycles,HAL_IN);
    MAKE_FLOAT_PIN(traj_arc_blend_ramp_freq,HAL_IN);

    hal_ready(comp_id);
    return 0;
} // ini_hal_init()

int ini_hal_init_pins()
{
    INIT_PIN(traj_default_velocity);
    INIT_PIN(traj_max_velocity);
    INIT_PIN(traj_default_acceleration);
    INIT_PIN(traj_max_acceleration);

    INIT_PIN(traj_arc_blend_enable);
    INIT_PIN(traj_arc_blend_fallback_enable);
    INIT_PIN(traj_arc_blend_optimization_depth);
    INIT_PIN(traj_arc_blend_gap_cycles);
    INIT_PIN(traj_arc_blend_ramp_freq);

    for (int idx = 0; idx < EMCMOT_MAX_JOINTS; idx++) {
        INIT_PIN(backlash[idx]);
        INIT_PIN(min_limit[idx]);
        INIT_PIN(max_limit[idx]);
        INIT_PIN(max_velocity[idx]);
        INIT_PIN(max_acceleration[idx]);
        INIT_PIN(ferror[idx]);
        INIT_PIN(min_ferror[idx]);
    }

    return 0;
} // ini_hal_init_pins()

static void copy_hal_data(const ptr_inihal_data &i, value_inihal_data &j)
{
    int x;
#define FIELD(t,f) j.f = (i.f)?*i.f:0;
#define ARRAY(t,f,n) do { for (x = 0; x < n; x++) j.f[x] = (i.f[x])?*i.f[x]:0; } while (0);
    HAL_FIELDS
#undef FIELD
#undef ARRAY
} // copy_hal_data()

int check_ini_hal_items()
{
    value_inihal_data new_inihal_data_mutable;
    copy_hal_data(*the_inihal_data, new_inihal_data_mutable);
    const value_inihal_data &new_inihal_data = new_inihal_data_mutable;

    if (CHANGED(traj_default_velocity)) {
        if (debug) SHOW_CHANGE(traj_default_velocity)
        UPDATE(traj_default_velocity);
        if (0 != emcTrajSetVelocity(0, NEW(traj_default_velocity))) {
            rcs_print("check_ini_hal_items:bad return value from emcTrajSetVelocity\n");
        }
    }
    if (CHANGED(traj_max_velocity)) {
        if (debug) SHOW_CHANGE(traj_max_velocity)
        UPDATE(traj_max_velocity);
        if (0 != emcTrajSetMaxVelocity(NEW(traj_max_velocity))) {
            if (emc_debug & EMC_DEBUG_CONFIG) {
                rcs_print("check_ini_hal_items:bad return value from emcTrajSetMaxVelocity\n");
            }
        }
    }
    if (CHANGED(traj_default_acceleration)) {
        if (debug) SHOW_CHANGE(traj_default_acceleration)
        UPDATE(traj_default_acceleration);
        if (0 != emcTrajSetAcceleration(NEW(traj_default_acceleration))) {
            if (emc_debug & EMC_DEBUG_CONFIG) {
                rcs_print("check_ini_hal_items:bad return value from emcTrajSetAcceleration\n");
            }
        }
    }
    if (CHANGED(traj_max_acceleration)) {
        if (debug) SHOW_CHANGE(traj_max_acceleration)
        UPDATE(traj_max_acceleration);
        if (0 != emcTrajSetMaxAcceleration(NEW(traj_max_acceleration))) {
            if (emc_debug & EMC_DEBUG_CONFIG) {
                rcs_print("check_ini_hal_items:bad return value from emcTrajSetMaxAcceleration\n");
            }
        }
    }

    if (   CHANGED(traj_arc_blend_enable)
        || CHANGED(traj_arc_blend_fallback_enable)
        || CHANGED(traj_arc_blend_optimization_depth)
        || CHANGED(traj_arc_blend_gap_cycles)
        || CHANGED(traj_arc_blend_ramp_freq)
       ) {
        if (debug) SHOW_CHANGE_ARC_BLEND()
        UPDATE(traj_arc_blend_enable);
        UPDATE(traj_arc_blend_fallback_enable);
        UPDATE(traj_arc_blend_optimization_depth);
        UPDATE(traj_arc_blend_gap_cycles);
        UPDATE(traj_arc_blend_ramp_freq);
        if (0 != emcSetupArcBlends(old_inihal_data.traj_arc_blend_enable
                                  ,old_inihal_data.traj_arc_blend_fallback_enable
                                  ,old_inihal_data.traj_arc_blend_optimization_depth
                                  ,old_inihal_data.traj_arc_blend_gap_cycles
                                  ,old_inihal_data.traj_arc_blend_ramp_freq
                                  )) {
            if (emc_debug & EMC_DEBUG_CONFIG) {
                rcs_print("bad return value from emcSetupArcBlends\n");
            }
            return -1;
        }
    }

    for (int idx = 0; idx < EMCMOT_MAX_JOINTS; idx++) {
        if (CHANGED_IDX(backlash,idx) ) {
            if (debug) SHOW_CHANGE_IDX(backlash,idx);
            UPDATE_IDX(backlash,idx);
            if (0 != emcTrajSetMaxAcceleration(NEW(backlash[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print("check_ini_hal_items:bad return value from emcTrajSetMaxAcceleration\n");
                }
        }
        }
        if (CHANGED_IDX(min_limit,idx) ) {
            if (debug) SHOW_CHANGE_IDX(min_limit,idx);
            UPDATE_IDX(min_limit,idx);
            if (0 != emcAxisSetMinPositionLimit(idx,NEW(min_limit[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print_error("check_ini_hal_items:bad return from emcAxisSetMinPositionLimit\n");
                }
            }
        }
        if (CHANGED_IDX(max_limit,idx) ) {
            if (debug) SHOW_CHANGE_IDX(max_limit,idx);
            UPDATE_IDX(max_limit,idx);
            if (0 != emcAxisSetMaxPositionLimit(idx,NEW(max_limit[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print_error("check_ini_hal_items:bad return from emcAxisSetMaxPositionLimit\n");
                }
            }
        }
        if (CHANGED_IDX(max_velocity,idx) ) {
            if (debug) SHOW_CHANGE_IDX(max_velocity,idx);
            UPDATE_IDX(max_velocity,idx);
            if (0 != emcAxisSetMaxVelocity(idx, NEW(max_velocity[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print_error("check_ini_hal_items:bad return from emcAxisSetMaxVelocity\n");
                }
            }
        }
        if (CHANGED_IDX(max_acceleration,idx) ) {
            if (debug) SHOW_CHANGE_IDX(max_acceleration,idx);
            UPDATE_IDX(max_acceleration,idx);
            if (0 != emcAxisSetMaxAcceleration(idx, NEW(max_acceleration[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print_error("check_ini_hal_items:bad return from emcAxisSetMaxAcceleration\n");
                }
            }
        }
        if (CHANGED_IDX(ferror,idx) ) {
            if (debug) SHOW_CHANGE_IDX(ferror,idx);
            UPDATE_IDX(ferror,idx);
            if (0 != emcAxisSetFerror(idx,NEW(ferror[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print_error("check_ini_hal_items:bad return from emcAxisSetFerror\n");
                }
            }
        }
        if (CHANGED_IDX(min_ferror,idx) ) {
            if (debug) SHOW_CHANGE_IDX(min_ferror,idx);
            UPDATE_IDX(min_ferror,idx);
            if (0 != emcAxisSetMinFerror(idx,NEW(min_ferror[idx]))) {
                if (emc_debug & EMC_DEBUG_CONFIG) {
                    rcs_print_error("check_ini_hal_items:bad return from emcAxisSetMinFerror\n");
                }
        }
        }
    }

    return 0;
} // check_ini_hal_items

// vim: sts=4 sw=4 et
<|endoftext|>


<commit_before>/*
 * Copyright 2010, 2011 Esrille Inc.
 *
 * 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 "Box.h"

#include <GL/gl.h>
#include <GL/glu.h>

#include <stdio.h>

#include "CSSStyleDeclarationImp.h"
#include "ViewCSSImp.h"

#include "font/FontManager.h"
#include "font/FontManagerBackEndGL.h"

namespace org { namespace w3c { namespace dom { namespace bootstrap {

namespace {

enum
{
    TOP,
    RIGHT,
    BOTTOM,
    LEFT
};

std::map<uint8_t*, GLuint> texnames;

GLuint addImage(uint8_t* image, unsigned width, unsigned heigth, unsigned repeat, GLenum format)
{
    GLuint texname;

    glGenTextures(1, &texname);
    glBindTexture(GL_TEXTURE_2D, texname);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (repeat & BoxImage::RepeatS) ? GL_REPEAT : (repeat & BoxImage::Clamp) ? GL_CLAMP_TO_EDGE : GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (repeat & BoxImage::RepeatT) ? GL_REPEAT : (repeat & BoxImage::Clamp) ? GL_CLAMP_TO_EDGE : GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, heigth, 0, format, GL_UNSIGNED_BYTE, image);
    texnames.insert(std::pair<uint8_t*, GLuint>(image, texname));
    return texname;
}

GLuint getTexname(uint8_t* image, unsigned width, unsigned height, unsigned repeat, GLenum format)
{
    std::map<uint8_t*, GLuint>::iterator it = texnames.find(image);
    if (it == texnames.end())
        return addImage(image, width, height, repeat, format);
    return it->second;
}

void deleteImage(uint8_t* image)
{
    std::map<uint8_t*, GLuint>::iterator it = texnames.find(image);
    if (it != texnames.end()) {
        glDeleteTextures(1, &it->second);
        texnames.erase(it);
    }
}

void getOriginScreenPosition(float& x, float& y)
{
    GLfloat m[16];
    glGetFloatv(GL_MODELVIEW_MATRIX, m);
    x = m[12];
    y = glutGet(GLUT_WINDOW_HEIGHT) - m[13];
}

}  // namespace

BoxImage::~BoxImage()
{
    if (state == CompletelyAvailable)
        deleteImage(pixels);
    delete pixels;
}

void BoxImage::render(ViewCSSImp* view, float x, float y, float width, float height, float left, float top)
{
    if (state != CompletelyAvailable)
        return;

    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();
    if (repeat & BoxImage::Clamp)
        glScalef(1.0f / width, 1.0f / height, 0.0f);
    else
        glScalef(1.0f / naturalWidth, 1.0f / naturalHeight, 0.0f);
    glMatrixMode(GL_MODELVIEW);

    GLuint texname = getTexname(pixels, naturalWidth, naturalHeight, repeat, format);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texname);
    glColor3ub(255, 255, 255);
    glBegin(GL_QUADS);
            glTexCoord2f(x - left, y - top);
            glVertex2f(x, y);
            glTexCoord2f(x - left + width, y - top);
            glVertex2f(x + width, y);
            glTexCoord2f(x - left + width, y - top + height);
            glVertex2f(x + width, y + height);
            glTexCoord2f(x - left, y - top + height);
            glVertex2f(x, y + height);
    glEnd();
    glDisable(GL_TEXTURE_2D);
}

void Box::renderBorderEdge(ViewCSSImp* view, int edge, unsigned borderStyle, unsigned color,
                           float a, float b, float c, float d,
                           float e, float f, float g, float h)
{
    if (borderStyle == CSSBorderStyleValueImp::None ||
        borderStyle == CSSBorderStyleValueImp::Hidden)
        return;

    GLubyte red = color >> 16;
    GLubyte green = color >> 8;
    GLubyte blue = color;
    GLubyte alpha = color >> 24;

    if (borderStyle == CSSBorderStyleValueImp::Double) {
        float offset;
        offset = (g - a) / 3;
        float i = a + offset;
        float m = g - offset;
        offset = (h - b) / 3;
        float j = b + offset;
        float n = h - offset;
        offset = (c - e) / 3;
        float k = c - offset;
        float o = e + offset;
        offset = (f - d) / 3;
        float l = d + offset;
        float p = f - offset;
        glColor4ub(red, green, blue, alpha);
        glBegin(GL_QUADS);
            glVertex2f(a, b);
            glVertex2f(c, d);
            glVertex2f(k, l);
            glVertex2f(i, j);
        glEnd();
        glBegin(GL_QUADS);
            glVertex2f(m, n);
            glVertex2f(o, p);
            glVertex2f(e, f);
            glVertex2f(g, h);
        glEnd();
        return;
    }

    if (borderStyle == CSSBorderStyleValueImp::Dotted ||
        borderStyle == CSSBorderStyleValueImp::Dashed) {
        glEnable(GL_LINE_STIPPLE);
        glLineWidth(fabsf(g - a));
        glLineStipple(fabsf(g - a), (borderStyle == CSSBorderStyleValueImp::Dotted) ? 0xaaaa : 0xcccc);
        glBegin(GL_LINES);
            glColor4ub(red, green, blue, alpha);
            glVertex2f((a + g) / 2, (b + h) / 2);
            glVertex2f((c + e) / 2, (d + f) / 2);
        glEnd();
        glDisable(GL_LINE_STIPPLE);
        return;
    }

    if (borderStyle == CSSBorderStyleValueImp::Groove ||
        borderStyle == CSSBorderStyleValueImp::Ridge) {
        GLubyte redDark = std::max(red - 128, 0);
        GLubyte greenDark = std::max(green - 128, 0);
        GLubyte blueDark = std::max(blue - 128, 0);
        GLubyte redBright = std::min(red + 128, 255);
        GLubyte greenBright = std::min(green + 128, 255);
        GLubyte blueBright = std::min(blue + 128, 255);
        float offset;
        offset = (g - a) / 2;
        float i = a + offset;
        offset = (h - b) / 2;
        float j = b + offset;
        offset = (c - e) / 2;
        float k = c - offset;
        offset = (f - d) / 2;
        float l = d + offset;
        if (borderStyle == CSSBorderStyleValueImp::Groove)
            glColor4ub(redDark, greenDark, blueDark, alpha);
        else
            glColor4ub(redBright, greenBright, blueBright, alpha);
        glBegin(GL_QUADS);
            glVertex2f(a, b);
            glVertex2f(c, d);
            glVertex2f(k, l);
            glVertex2f(i, j);
        glEnd();
        if (borderStyle == CSSBorderStyleValueImp::Groove)
            glColor4ub(redBright, greenBright, blueBright, alpha);
        else
            glColor4ub(redDark, greenDark, blueDark, alpha);
        glBegin(GL_QUADS);
            glVertex2f(i, j);
            glVertex2f(k, l);
            glVertex2f(e, f);
            glVertex2f(g, h);
        glEnd();
        return;
    }

    if (borderStyle == CSSBorderStyleValueImp::Inset ||
        borderStyle == CSSBorderStyleValueImp::Outset) {
        bool dark = false;
        bool bright = false;
        if (borderStyle == CSSBorderStyleValueImp::Inset) {
            switch (edge) {
            case TOP:
            case LEFT:
                dark = true;
                break;;
            case RIGHT:
            case BOTTOM:
                bright = true;
                break;;
            }
        }
        if (borderStyle == CSSBorderStyleValueImp::Outset) {
            switch (edge) {
            case TOP:
            case LEFT:
                bright = true;
                break;;
            case RIGHT:
            case BOTTOM:
                dark = true;
                break;;
            }
        }
        if (dark) {
            red = std::max(red - 128, 0);
            green = std::max(green - 128, 0);
            blue = std::max(blue - 128, 0);
        } else if (bright) {
            red = std::min(red + 128, 255);
            green = std::min(green + 128, 255);
            blue = std::min(blue + 128, 255);
        }
    }

    glColor4ub(red, green, blue, alpha);
    glBegin(GL_QUADS);
        glVertex2f(a, b);
        glVertex2f(c, d);
        glVertex2f(e, f);
        glVertex2f(g, h);
    glEnd();
}

void Box::renderBorder(ViewCSSImp* view)
{
    glDisable(GL_TEXTURE_2D);

    float ll = marginLeft;
    float lr = ll + borderLeft;
    float rl = lr + paddingLeft + width + paddingRight;
    float rr = rl + borderRight;
    float tt = marginTop;
    float tb = tt + borderTop;
    float bt = tb + paddingTop + height + paddingBottom;
    float bb = bt + borderBottom;

    if (backgroundColor != 0x00000000) {
        glColor4ub(backgroundColor >> 16, backgroundColor >> 8, backgroundColor, backgroundColor >> 24);
        glBegin(GL_QUADS);
            glVertex2f(lr, tb);
            glVertex2f(rl, tb);
            glVertex2f(rl, bt);
            glVertex2f(lr, bt);
        glEnd();
    }

    if (backgroundImage) {
        GLfloat border[] = { ((backgroundColor >> 16) & 0xff) / 255.0f,
                             ((backgroundColor >> 8) & 0xff) / 255.0f,
                             ((backgroundColor) & 0xff) / 255.0f,
                             ((backgroundColor >> 24) & 0xff) / 255.0f };
        glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
        glPushMatrix();
        if (getParentBox()) {
            // TODO: check padding
            glTranslatef(lr, tb, 0.0f);
            backgroundImage->render(view, 0, 0, rl - lr, bt - tb, backgroundLeft, backgroundTop);
        } else {
            const ContainingBlock* containingBlock = getContainingBlock(view);
            glTranslatef(ll, tt, 0.0f);
            backgroundImage->render(view, -ll, -tt, containingBlock->width, containingBlock->height, backgroundLeft, backgroundTop);
        }
        glPopMatrix();
    }

    if (borderTop)
        renderBorderEdge(view, TOP,
                         style->borderTopStyle.getValue(),
                         style->borderTopColor.getARGB(),
                         ll, tt, rr, tt, rl, tb, lr, tb);
    if (borderRight)
        renderBorderEdge(view, RIGHT,
                         style->borderRightStyle.getValue(),
                         style->borderRightColor.getARGB(),
                         rl, bt, rl, tb, rr, tt, rr, bb);
    if (borderBottom)
        renderBorderEdge(view, BOTTOM,
                         style->borderBottomStyle.getValue(),
                         style->borderBottomColor.getARGB(),
                         lr, bt, rl, bt, rr, bb, ll, bb);
    if (borderLeft)
        renderBorderEdge(view, LEFT,
                         style->borderLeftStyle.getValue(),
                         style->borderLeftColor.getARGB(),
                         ll, bb, ll, tt, lr, tb, lr, bt);
    glEnable(GL_TEXTURE_2D);
}

void BlockLevelBox::render(ViewCSSImp* view)
{
    glPushMatrix();
    glTranslatef(offsetH, offsetV, 0.0f);
    getOriginScreenPosition(x, y);
    renderBorder(view);
    glTranslatef(getBlankLeft(), getBlankTop(), 0.0f);
    if (shadow)
        shadow->render();
    else {
        for (auto child = getFirstChild(); child; child = child->getNextSibling()) {
            child->render(view);
            glTranslatef(0.0f, child->getTotalHeight(), 0.0f);
        }
    }
    glPopMatrix();
}

void LineBox::render(ViewCSSImp* view)
{
    glPushMatrix();
    glTranslatef(offsetH, offsetV, 0.0f);
    getOriginScreenPosition(x, y);
    for (auto child = getFirstChild(); child; child = child->getNextSibling()) {
        child->render(view);
        if (!child->isAbsolutelyPositioned()) {
            glTranslatef(child->getTotalWidth(), 0.0f, 0.0f);
        }
    }
    glPopMatrix();
}

void InlineLevelBox::render(ViewCSSImp* view)
{
    glPushMatrix();
    glTranslatef(offsetH, offsetV, 0.0f);
    renderBorder(view);
    glTranslatef(getBlankLeft(), getBlankTop(), 0.0f);
    getOriginScreenPosition(x, y);
    if (shadow)
        shadow->render();
    else if (getFirstChild())  // for inline-block
        getFirstChild()->render(view);
    else if (0 < data.length()) {
        glTranslatef(0.0f, baseline, 0.0f);
        glScalef(point / font->getPoint(), point / font->getPoint(), 1.0);
        unsigned color = getStyle()->color.getARGB();
        glColor4ub(color >> 16, color >> 8, color, color >> 24);
        font->renderText(data.c_str(), data.length());
    }
    glPopMatrix();
}

}}}}  // org::w3c::dom::bootstrap
<commit_msg>(Box::renderBorder) : Paint the background of the render tree root box correctly.<commit_after>/*
 * Copyright 2010, 2011 Esrille Inc.
 *
 * 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 "Box.h"

#include <GL/gl.h>
#include <GL/glu.h>

#include <stdio.h>

#include "CSSStyleDeclarationImp.h"
#include "ViewCSSImp.h"

#include "font/FontManager.h"
#include "font/FontManagerBackEndGL.h"

namespace org { namespace w3c { namespace dom { namespace bootstrap {

namespace {

enum
{
    TOP,
    RIGHT,
    BOTTOM,
    LEFT
};

std::map<uint8_t*, GLuint> texnames;

GLuint addImage(uint8_t* image, unsigned width, unsigned heigth, unsigned repeat, GLenum format)
{
    GLuint texname;

    glGenTextures(1, &texname);
    glBindTexture(GL_TEXTURE_2D, texname);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (repeat & BoxImage::RepeatS) ? GL_REPEAT : (repeat & BoxImage::Clamp) ? GL_CLAMP_TO_EDGE : GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (repeat & BoxImage::RepeatT) ? GL_REPEAT : (repeat & BoxImage::Clamp) ? GL_CLAMP_TO_EDGE : GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, heigth, 0, format, GL_UNSIGNED_BYTE, image);
    texnames.insert(std::pair<uint8_t*, GLuint>(image, texname));
    return texname;
}

GLuint getTexname(uint8_t* image, unsigned width, unsigned height, unsigned repeat, GLenum format)
{
    std::map<uint8_t*, GLuint>::iterator it = texnames.find(image);
    if (it == texnames.end())
        return addImage(image, width, height, repeat, format);
    return it->second;
}

void deleteImage(uint8_t* image)
{
    std::map<uint8_t*, GLuint>::iterator it = texnames.find(image);
    if (it != texnames.end()) {
        glDeleteTextures(1, &it->second);
        texnames.erase(it);
    }
}

void getOriginScreenPosition(float& x, float& y)
{
    GLfloat m[16];
    glGetFloatv(GL_MODELVIEW_MATRIX, m);
    x = m[12];
    y = glutGet(GLUT_WINDOW_HEIGHT) - m[13];
}

}  // namespace

BoxImage::~BoxImage()
{
    if (state == CompletelyAvailable)
        deleteImage(pixels);
    delete pixels;
}

void BoxImage::render(ViewCSSImp* view, float x, float y, float width, float height, float left, float top)
{
    if (state != CompletelyAvailable)
        return;

    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();
    if (repeat & BoxImage::Clamp)
        glScalef(1.0f / width, 1.0f / height, 0.0f);
    else
        glScalef(1.0f / naturalWidth, 1.0f / naturalHeight, 0.0f);
    glMatrixMode(GL_MODELVIEW);

    GLuint texname = getTexname(pixels, naturalWidth, naturalHeight, repeat, format);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texname);
    glColor3ub(255, 255, 255);
    glBegin(GL_QUADS);
            glTexCoord2f(x - left, y - top);
            glVertex2f(x, y);
            glTexCoord2f(x - left + width, y - top);
            glVertex2f(x + width, y);
            glTexCoord2f(x - left + width, y - top + height);
            glVertex2f(x + width, y + height);
            glTexCoord2f(x - left, y - top + height);
            glVertex2f(x, y + height);
    glEnd();
    glDisable(GL_TEXTURE_2D);
}

void Box::renderBorderEdge(ViewCSSImp* view, int edge, unsigned borderStyle, unsigned color,
                           float a, float b, float c, float d,
                           float e, float f, float g, float h)
{
    if (borderStyle == CSSBorderStyleValueImp::None ||
        borderStyle == CSSBorderStyleValueImp::Hidden)
        return;

    GLubyte red = color >> 16;
    GLubyte green = color >> 8;
    GLubyte blue = color;
    GLubyte alpha = color >> 24;

    if (borderStyle == CSSBorderStyleValueImp::Double) {
        float offset;
        offset = (g - a) / 3;
        float i = a + offset;
        float m = g - offset;
        offset = (h - b) / 3;
        float j = b + offset;
        float n = h - offset;
        offset = (c - e) / 3;
        float k = c - offset;
        float o = e + offset;
        offset = (f - d) / 3;
        float l = d + offset;
        float p = f - offset;
        glColor4ub(red, green, blue, alpha);
        glBegin(GL_QUADS);
            glVertex2f(a, b);
            glVertex2f(c, d);
            glVertex2f(k, l);
            glVertex2f(i, j);
        glEnd();
        glBegin(GL_QUADS);
            glVertex2f(m, n);
            glVertex2f(o, p);
            glVertex2f(e, f);
            glVertex2f(g, h);
        glEnd();
        return;
    }

    if (borderStyle == CSSBorderStyleValueImp::Dotted ||
        borderStyle == CSSBorderStyleValueImp::Dashed) {
        glEnable(GL_LINE_STIPPLE);
        glLineWidth(fabsf(g - a));
        glLineStipple(fabsf(g - a), (borderStyle == CSSBorderStyleValueImp::Dotted) ? 0xaaaa : 0xcccc);
        glBegin(GL_LINES);
            glColor4ub(red, green, blue, alpha);
            glVertex2f((a + g) / 2, (b + h) / 2);
            glVertex2f((c + e) / 2, (d + f) / 2);
        glEnd();
        glDisable(GL_LINE_STIPPLE);
        return;
    }

    if (borderStyle == CSSBorderStyleValueImp::Groove ||
        borderStyle == CSSBorderStyleValueImp::Ridge) {
        GLubyte redDark = std::max(red - 128, 0);
        GLubyte greenDark = std::max(green - 128, 0);
        GLubyte blueDark = std::max(blue - 128, 0);
        GLubyte redBright = std::min(red + 128, 255);
        GLubyte greenBright = std::min(green + 128, 255);
        GLubyte blueBright = std::min(blue + 128, 255);
        float offset;
        offset = (g - a) / 2;
        float i = a + offset;
        offset = (h - b) / 2;
        float j = b + offset;
        offset = (c - e) / 2;
        float k = c - offset;
        offset = (f - d) / 2;
        float l = d + offset;
        if (borderStyle == CSSBorderStyleValueImp::Groove)
            glColor4ub(redDark, greenDark, blueDark, alpha);
        else
            glColor4ub(redBright, greenBright, blueBright, alpha);
        glBegin(GL_QUADS);
            glVertex2f(a, b);
            glVertex2f(c, d);
            glVertex2f(k, l);
            glVertex2f(i, j);
        glEnd();
        if (borderStyle == CSSBorderStyleValueImp::Groove)
            glColor4ub(redBright, greenBright, blueBright, alpha);
        else
            glColor4ub(redDark, greenDark, blueDark, alpha);
        glBegin(GL_QUADS);
            glVertex2f(i, j);
            glVertex2f(k, l);
            glVertex2f(e, f);
            glVertex2f(g, h);
        glEnd();
        return;
    }

    if (borderStyle == CSSBorderStyleValueImp::Inset ||
        borderStyle == CSSBorderStyleValueImp::Outset) {
        bool dark = false;
        bool bright = false;
        if (borderStyle == CSSBorderStyleValueImp::Inset) {
            switch (edge) {
            case TOP:
            case LEFT:
                dark = true;
                break;;
            case RIGHT:
            case BOTTOM:
                bright = true;
                break;;
            }
        }
        if (borderStyle == CSSBorderStyleValueImp::Outset) {
            switch (edge) {
            case TOP:
            case LEFT:
                bright = true;
                break;;
            case RIGHT:
            case BOTTOM:
                dark = true;
                break;;
            }
        }
        if (dark) {
            red = std::max(red - 128, 0);
            green = std::max(green - 128, 0);
            blue = std::max(blue - 128, 0);
        } else if (bright) {
            red = std::min(red + 128, 255);
            green = std::min(green + 128, 255);
            blue = std::min(blue + 128, 255);
        }
    }

    glColor4ub(red, green, blue, alpha);
    glBegin(GL_QUADS);
        glVertex2f(a, b);
        glVertex2f(c, d);
        glVertex2f(e, f);
        glVertex2f(g, h);
    glEnd();
}

void Box::renderBorder(ViewCSSImp* view)
{
    glDisable(GL_TEXTURE_2D);

    float ll = marginLeft;
    float lr = ll + borderLeft;
    float rl = lr + paddingLeft + width + paddingRight;
    float rr = rl + borderRight;
    float tt = marginTop;
    float tb = tt + borderTop;
    float bt = tb + paddingTop + height + paddingBottom;
    float bb = bt + borderBottom;

    if (backgroundColor != 0x00000000) {
        glColor4ub(backgroundColor >> 16, backgroundColor >> 8, backgroundColor, backgroundColor >> 24);
        glBegin(GL_QUADS);
            if (getParentBox()) {
                glVertex2f(lr, tb);
                glVertex2f(rl, tb);
                glVertex2f(rl, bt);
                glVertex2f(lr, bt);
            } else {
                const ContainingBlock* containingBlock = getContainingBlock(view);
                glVertex2f(0, 0);
                glVertex2f(containingBlock->width, 0);
                glVertex2f(containingBlock->width, containingBlock->height);
                glVertex2f(0, containingBlock->height);
            }
        glEnd();
    }

    if (backgroundImage) {
        GLfloat border[] = { ((backgroundColor >> 16) & 0xff) / 255.0f,
                             ((backgroundColor >> 8) & 0xff) / 255.0f,
                             ((backgroundColor) & 0xff) / 255.0f,
                             ((backgroundColor >> 24) & 0xff) / 255.0f };
        glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
        glPushMatrix();
        if (getParentBox()) {
            // TODO: check padding
            glTranslatef(lr, tb, 0.0f);
            backgroundImage->render(view, 0, 0, rl - lr, bt - tb, backgroundLeft, backgroundTop);
        } else {
            const ContainingBlock* containingBlock = getContainingBlock(view);
            glTranslatef(ll, tt, 0.0f);
            backgroundImage->render(view, -ll, -tt, containingBlock->width, containingBlock->height, backgroundLeft, backgroundTop);
        }
        glPopMatrix();
    }

    if (borderTop)
        renderBorderEdge(view, TOP,
                         style->borderTopStyle.getValue(),
                         style->borderTopColor.getARGB(),
                         ll, tt, rr, tt, rl, tb, lr, tb);
    if (borderRight)
        renderBorderEdge(view, RIGHT,
                         style->borderRightStyle.getValue(),
                         style->borderRightColor.getARGB(),
                         rl, bt, rl, tb, rr, tt, rr, bb);
    if (borderBottom)
        renderBorderEdge(view, BOTTOM,
                         style->borderBottomStyle.getValue(),
                         style->borderBottomColor.getARGB(),
                         lr, bt, rl, bt, rr, bb, ll, bb);
    if (borderLeft)
        renderBorderEdge(view, LEFT,
                         style->borderLeftStyle.getValue(),
                         style->borderLeftColor.getARGB(),
                         ll, bb, ll, tt, lr, tb, lr, bt);
    glEnable(GL_TEXTURE_2D);
}

void BlockLevelBox::render(ViewCSSImp* view)
{
    glPushMatrix();
    glTranslatef(offsetH, offsetV, 0.0f);
    getOriginScreenPosition(x, y);
    renderBorder(view);
    glTranslatef(getBlankLeft(), getBlankTop(), 0.0f);
    if (shadow)
        shadow->render();
    else {
        for (auto child = getFirstChild(); child; child = child->getNextSibling()) {
            child->render(view);
            glTranslatef(0.0f, child->getTotalHeight(), 0.0f);
        }
    }
    glPopMatrix();
}

void LineBox::render(ViewCSSImp* view)
{
    glPushMatrix();
    glTranslatef(offsetH, offsetV, 0.0f);
    getOriginScreenPosition(x, y);
    for (auto child = getFirstChild(); child; child = child->getNextSibling()) {
        child->render(view);
        if (!child->isAbsolutelyPositioned()) {
            glTranslatef(child->getTotalWidth(), 0.0f, 0.0f);
        }
    }
    glPopMatrix();
}

void InlineLevelBox::render(ViewCSSImp* view)
{
    glPushMatrix();
    glTranslatef(offsetH, offsetV, 0.0f);
    renderBorder(view);
    glTranslatef(getBlankLeft(), getBlankTop(), 0.0f);
    getOriginScreenPosition(x, y);
    if (shadow)
        shadow->render();
    else if (getFirstChild())  // for inline-block
        getFirstChild()->render(view);
    else if (0 < data.length()) {
        glTranslatef(0.0f, baseline, 0.0f);
        glScalef(point / font->getPoint(), point / font->getPoint(), 1.0);
        unsigned color = getStyle()->color.getARGB();
        glColor4ub(color >> 16, color >> 8, color, color >> 24);
        font->renderText(data.c_str(), data.length());
    }
    glPopMatrix();
}

}}}}  // org::w3c::dom::bootstrap
<|endoftext|>


<commit_before>//
//  Config.hpp
//  GL_Template
//
//  Created by Simon Rodriguez on 30/12/2017.
//  Copyright © 2017 Simon Rodriguez. All rights reserved.
//

#ifndef Config_hpp
#define Config_hpp
#include "Common.hpp"
#include <map>

class Config {
public:

	Config(int argc, char** argv);

public:

	/// Configuration properties (loaded from command-line or config file).
	const size_t version = 1;

	bool vsync = true;

	bool fullscreen = false;

	bool logVerbose = false;

	unsigned int initialWidth = 800;

	unsigned int initialHeight = 600;

	float internalVerticalResolution = 720.0f;

	/// Computed properties.
	glm::vec2 screenResolution = glm::vec2(800.0,600.0);

	float screenDensity = 1.0f;

	std::string logPath = "";

public:

	static void parseFromFile(const char* filePath, std::map<std::string, std::vector<std::string>>& arguments);

	static void parseFromArgs(const int argc, char** argv, std::map<std::string, std::vector<std::string>> & arguments);

	void processArguments();

protected:

	std::map<std::string, std::vector<std::string>> _rawArguments;
};
#endif /* Config_hpp */
<commit_msg>Config: documentation.<commit_after>//
//  Config.hpp
//  GL_Template
//
//  Created by Simon Rodriguez on 30/12/2017.
//  Copyright © 2017 Simon Rodriguez. All rights reserved.
//

#ifndef Config_hpp
#define Config_hpp
#include "Common.hpp"
#include <map>

/**
 \class Config
 \brief Contains configurable elements as attributes, populated from the command line, a configuration file or default values.
 */
class Config {
public:

	/** Initialize a new config object, parsing the input arguments and filling the attributes with their values.
	 	\param argc the number of input arguments.
	 	\param argv a pointer to the raw input arguments.
	 */
	Config(int argc, char** argv);

public:

	// Configuration properties (loaded from command-line or config file).

	/// The configuration version number (unused).
	const size_t version = 1;

	/// Toggle V-Sync.
	bool vsync = true;

	/// Toggle fullscreen window.
	bool fullscreen = false;

	/// Toggle log verbose level.
	bool logVerbose = false;

	/// Initial width of the window in relative pixels.
	unsigned int initialWidth = 800;

	/// Initial height of the window in relative pixels.
	unsigned int initialHeight = 600;

	/// \brief Internal vertical rendering resolution.
	/// \note The width should be computed based on the window aspect ratio.
	/// \warning The Wwidth should be computed based on the window aspect ratio.
	/// \todo The TDwidth should be computed based on the window aspect ratio.
	/// \test The Twidth should be computed based on the window aspect ratio.
	/// \bug The Bwidth should be computed based on the window aspect ratio.
	/// \deprecated The Dwidth should be computed based on the window aspect ratio.
	/// \pre The Prewidth should be computed based on the window aspect ratio.
	/// \post The Postwidth should be computed based on the window aspect ratio.
	/// \invariant babdainv
	float internalVerticalResolution = 720.0f;

	/// Size of the window in raw pixels, updated at launch based on screen density.
	glm::vec2 screenResolution = glm::vec2(800.0,600.0);

	/// Screen density, udpated at launch.
	float screenDensity = 1.0f;

	/// Output log file path.
	std::string logPath = "";

public:

	/**
	 Helper to extract (key, [values]) from a configuration file on disk.
	 \param filePath the path to the configuration file.
	 \param arguments a dictionary that will be populated with (key, [values]).
	 */
	static void parseFromFile(const char* filePath, std::map<std::string, std::vector<std::string>>& arguments);

	/**
	 Helper to extract (key, [values]) from the given command-line raw C-style arguments.
	 \param argc the number of input arguments.
	 \param argv a pointer to the raw input arguments.
	 \param arguments a dictionary that will be populated with (key, [values]).
	 */
	static void parseFromArgs(const int argc, char** argv, std::map<std::string, std::vector<std::string>> & arguments);

protected:

	/**
	 Read the internal (key, [values]) populated dictionary, and transfer their values to the object attributes.
	 */
	void processArguments();

	/// Store the internal parsed (keys, [values]) extracted from a file or the command-line.
	std::map<std::string, std::vector<std::string>> _rawArguments;
};
#endif /* Config_hpp */
<|endoftext|>