<commit_before>//   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// 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 <set>
#include <string>
#include <vector>

#include "paddle/fluid/operators/distributed/parameter_prefetch.h"

#include "paddle/fluid/framework/lod_tensor.h"
#include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/framework/selected_rows.h"
#include "paddle/fluid/framework/tensor.h"

#include "paddle/fluid/operators/detail/macros.h"
#include "paddle/fluid/operators/distributed/rpc_client.h"
#include "paddle/fluid/operators/distributed/variable_response.h"
#include "paddle/fluid/operators/distributed_ops/send_recv_util.h"

namespace paddle {
namespace operators {
namespace distributed {

using Tensor = framework::Tensor;
using LoDTensor = framework::LoDTensor;
using SelectedRows = framework::SelectedRows;
using DDim = framework::DDim;

static size_t GetSectionIndex(int64_t id,
                              const std::vector<int64_t>& abs_sections) {
  for (size_t i = 1; i < abs_sections.size(); ++i) {
    if (id < abs_sections[i]) {
      return i - 1;
    }
  }
  return abs_sections.size() - 1;
}

static std::vector<int64_t> ToAbsoluteSection(
    const std::vector<int>& height_sections) {
  std::vector<int64_t> abs_sections;
  abs_sections.resize(height_sections.size());
  abs_sections[0] = 0;
  for (size_t i = 1; i < height_sections.size(); ++i) {
    abs_sections[i] = height_sections[i - 1] + abs_sections[i - 1];
  }
  return abs_sections;
}

static std::vector<std::vector<int64_t>> SplitIds(
    const std::string& id_name, const std::vector<int>& height_section,
    framework::Scope* scope) {
  auto& id_tensor = scope->FindVar(id_name)->Get<framework::LoDTensor>();
  auto* id_data = id_tensor.data<int64_t>();
  std::set<int64_t> all_ids;
  for (size_t i = 0; i < id_tensor.numel(); ++i) {
    all_ids.insert(id_data[i]);
  }
  auto abs_sections = ToAbsoluteSection(height_section);
  std::vector<std::vector<int64_t>> splited_ids;
  splited_ids.resize(height_section.size() + 1);
  for (auto& id : all_ids) {
    auto section_index = GetSectionIndex(id, abs_sections);
    splited_ids[section_index].push_back(id - abs_sections[section_index]);
  }
  return splited_ids;
}

static void SplitIdsIntoMultipleVarsBySection(
    const std::string& id_name, const std::vector<std::string>& in_var_names,
    const std::vector<int>& height_section,
    const std::vector<std::vector<int64_t>>& splited_ids,
    framework::Scope* scope) {
  PADDLE_ENFORCE_EQ(in_var_names.size(), height_section.size(), "");

  auto place = platform::CPUPlace();

  for (size_t i = 0; i < in_var_names.size(); ++i) {
    auto* id_tensor =
        scope->Var(in_var_names[i])->GetMutable<framework::LoDTensor>();
    auto& ids = splited_ids[i];
    if (!ids.empty()) {
      auto* id_tensor_data = id_tensor->mutable_data<int64_t>(
          framework::make_ddim({static_cast<int64_t>(ids.size()), 1}), place);
      memcpy(id_tensor_data, ids.data(), sizeof(int64_t) * ids.size());
    }
  }
}

static void MergeMultipleVarsIntoOneBySection(
    const std::string& id_name, const std::string& out_name,
    const std::vector<std::string>& out_var_names,
    const std::vector<int>& height_section,
    const std::vector<std::vector<int64_t>>& splited_ids,
    const framework::ExecutionContext& context, framework::Scope* scope) {
  PADDLE_ENFORCE_EQ(out_var_names.size(), height_section.size(), "");

  auto cpu_place = platform::CPUPlace();

  auto abs_sections = ToAbsoluteSection(height_section);
  auto& id_tensor = scope->FindVar(id_name)->Get<framework::LoDTensor>();
  auto* id_data = id_tensor.data<int64_t>();
  std::unordered_map<int64_t, std::vector<size_t>> id_to_offset;
  for (size_t i = 0; i < id_tensor.numel(); ++i) {
    id_to_offset[id_data[i]].push_back(i);
  }

  auto* out_tensor =
      scope->FindVar(out_name)->GetMutable<framework::LoDTensor>();
  auto* out_tensor_data = out_tensor->mutable_data<float>(context.GetPlace());

  for (size_t section_idx = 0; section_idx < out_var_names.size();
       ++section_idx) {
    auto& ids_in_this_section = splited_ids[section_idx];
    if (!ids_in_this_section.empty()) {
      auto& prefetch_out_var =
          scope->Var(out_var_names[section_idx])->Get<framework::LoDTensor>();
      const auto* out_var_data = prefetch_out_var.data<float>();
      auto& dims = prefetch_out_var.dims();

      PADDLE_ENFORCE_EQ(dims.size(), 2, "");
      PADDLE_ENFORCE_EQ(ids_in_this_section.size(), dims[0]);

      auto row_numel = dims[1];

      for (size_t i = 0; i < dims[0]; ++i) {
        auto id = ids_in_this_section[i];
        auto origin_id = id + abs_sections[section_idx];
        auto& offsets = id_to_offset[origin_id];
        for (auto& offset : offsets) {
          // should support GPU tensor
          memory::Copy(cpu_place, out_tensor_data + offset * row_numel,
                       cpu_place, out_var_data + i * row_numel,
                       sizeof(float) * row_numel);
        }
      }
    } else {
      VLOG(3) << "ids in this section is empty";
    }
  }
}

void prefetch(const std::string& id_name, const std::string& out_name,
              const std::vector<std::string>& table_names,
              const std::vector<std::string>& epmap,
              const std::vector<int>& height_sections,
              const framework::ExecutionContext& context) {
  auto& local_scope = context.scope().NewScope();

  platform::DeviceContextPool& pool = platform::DeviceContextPool::Instance();
  auto& ctx = *pool.Get(context.GetPlace());

  distributed::RPCClient* rpc_client =
      distributed::RPCClient::GetInstance<RPCCLIENT_T>(
          context.Attr<int>("trainer_id"));

  std::vector<std::string> in_var_names;
  std::vector<std::string> out_var_names;
  for (size_t i = 0; i < epmap.size(); ++i) {
    in_var_names.push_back(id_name + "@" + epmap[i]);
    out_var_names.push_back(out_name + "@" + epmap[i]);
  }

  auto splited_ids = SplitIds(id_name, height_sections, &local_scope);
  SplitIdsIntoMultipleVarsBySection(id_name, in_var_names, height_sections,
                                    splited_ids, &local_scope);

  // create output var in local scope
  for (auto& name : out_var_names) {
    local_scope.Var(name)->GetMutable<framework::LoDTensor>();
  }

  std::vector<distributed::VarHandlePtr> rets;
  for (size_t i = 0; i < in_var_names.size(); i++) {
    if (NeedSend(local_scope, in_var_names[i])) {
      VLOG(3) << "sending " << in_var_names[i] << " to " << epmap[i]
               << " to get " << out_var_names[i] << " back";
      rets.push_back(rpc_client->AsyncPrefetchVar(
          epmap[i], ctx, local_scope, in_var_names[i], out_var_names[i],
          table_names[i]));
    } else {
      VLOG(3) << "don't send no-initialied variable: " << out_var_names[i];
    }
  }

  for (size_t i = 0; i < rets.size(); i++) {
    PADDLE_ENFORCE(rets[i]->Wait(), "internal error in RPCClient");
  }

  MergeMultipleVarsIntoOneBySection(id_name, out_name, out_var_names,
                                    height_sections, splited_ids, context,
                                    &local_scope);

  context.scope().DeleteScope(&local_scope);
}

};  // namespace distributed
};  // namespace operators
};  // namespace paddle
<commit_msg>fix code format test=develop<commit_after>//   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// 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 <set>
#include <string>
#include <vector>

#include "paddle/fluid/operators/distributed/parameter_prefetch.h"

#include "paddle/fluid/framework/lod_tensor.h"
#include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/framework/selected_rows.h"
#include "paddle/fluid/framework/tensor.h"

#include "paddle/fluid/operators/detail/macros.h"
#include "paddle/fluid/operators/distributed/rpc_client.h"
#include "paddle/fluid/operators/distributed/variable_response.h"
#include "paddle/fluid/operators/distributed_ops/send_recv_util.h"

namespace paddle {
namespace operators {
namespace distributed {

using Tensor = framework::Tensor;
using LoDTensor = framework::LoDTensor;
using SelectedRows = framework::SelectedRows;
using DDim = framework::DDim;

static size_t GetSectionIndex(int64_t id,
                              const std::vector<int64_t>& abs_sections) {
  for (size_t i = 1; i < abs_sections.size(); ++i) {
    if (id < abs_sections[i]) {
      return i - 1;
    }
  }
  return abs_sections.size() - 1;
}

static std::vector<int64_t> ToAbsoluteSection(
    const std::vector<int>& height_sections) {
  std::vector<int64_t> abs_sections;
  abs_sections.resize(height_sections.size());
  abs_sections[0] = 0;
  for (size_t i = 1; i < height_sections.size(); ++i) {
    abs_sections[i] = height_sections[i - 1] + abs_sections[i - 1];
  }
  return abs_sections;
}

static std::vector<std::vector<int64_t>> SplitIds(
    const std::string& id_name, const std::vector<int>& height_section,
    framework::Scope* scope) {
  auto& id_tensor = scope->FindVar(id_name)->Get<framework::LoDTensor>();
  auto* id_data = id_tensor.data<int64_t>();
  std::set<int64_t> all_ids;
  for (size_t i = 0; i < id_tensor.numel(); ++i) {
    all_ids.insert(id_data[i]);
  }
  auto abs_sections = ToAbsoluteSection(height_section);
  std::vector<std::vector<int64_t>> splited_ids;
  splited_ids.resize(height_section.size() + 1);
  for (auto& id : all_ids) {
    auto section_index = GetSectionIndex(id, abs_sections);
    splited_ids[section_index].push_back(id - abs_sections[section_index]);
  }
  return splited_ids;
}

static void SplitIdsIntoMultipleVarsBySection(
    const std::string& id_name, const std::vector<std::string>& in_var_names,
    const std::vector<int>& height_section,
    const std::vector<std::vector<int64_t>>& splited_ids,
    framework::Scope* scope) {
  PADDLE_ENFORCE_EQ(in_var_names.size(), height_section.size(), "");

  auto place = platform::CPUPlace();

  for (size_t i = 0; i < in_var_names.size(); ++i) {
    auto* id_tensor =
        scope->Var(in_var_names[i])->GetMutable<framework::LoDTensor>();
    auto& ids = splited_ids[i];
    if (!ids.empty()) {
      auto* id_tensor_data = id_tensor->mutable_data<int64_t>(
          framework::make_ddim({static_cast<int64_t>(ids.size()), 1}), place);
      memcpy(id_tensor_data, ids.data(), sizeof(int64_t) * ids.size());
    }
  }
}

static void MergeMultipleVarsIntoOneBySection(
    const std::string& id_name, const std::string& out_name,
    const std::vector<std::string>& out_var_names,
    const std::vector<int>& height_section,
    const std::vector<std::vector<int64_t>>& splited_ids,
    const framework::ExecutionContext& context, framework::Scope* scope) {
  PADDLE_ENFORCE_EQ(out_var_names.size(), height_section.size(), "");

  auto cpu_place = platform::CPUPlace();

  auto abs_sections = ToAbsoluteSection(height_section);
  auto& id_tensor = scope->FindVar(id_name)->Get<framework::LoDTensor>();
  auto* id_data = id_tensor.data<int64_t>();
  std::unordered_map<int64_t, std::vector<size_t>> id_to_offset;
  for (size_t i = 0; i < id_tensor.numel(); ++i) {
    id_to_offset[id_data[i]].push_back(i);
  }

  auto* out_tensor =
      scope->FindVar(out_name)->GetMutable<framework::LoDTensor>();
  auto* out_tensor_data = out_tensor->mutable_data<float>(context.GetPlace());

  for (size_t section_idx = 0; section_idx < out_var_names.size();
       ++section_idx) {
    auto& ids_in_this_section = splited_ids[section_idx];
    if (!ids_in_this_section.empty()) {
      auto& prefetch_out_var =
          scope->Var(out_var_names[section_idx])->Get<framework::LoDTensor>();
      const auto* out_var_data = prefetch_out_var.data<float>();
      auto& dims = prefetch_out_var.dims();

      PADDLE_ENFORCE_EQ(dims.size(), 2, "");
      PADDLE_ENFORCE_EQ(ids_in_this_section.size(), dims[0]);

      auto row_numel = dims[1];

      for (size_t i = 0; i < dims[0]; ++i) {
        auto id = ids_in_this_section[i];
        auto origin_id = id + abs_sections[section_idx];
        auto& offsets = id_to_offset[origin_id];
        for (auto& offset : offsets) {
          // should support GPU tensor
          memory::Copy(cpu_place, out_tensor_data + offset * row_numel,
                       cpu_place, out_var_data + i * row_numel,
                       sizeof(float) * row_numel);
        }
      }
    } else {
      VLOG(3) << "ids in this section is empty";
    }
  }
}

void prefetch(const std::string& id_name, const std::string& out_name,
              const std::vector<std::string>& table_names,
              const std::vector<std::string>& epmap,
              const std::vector<int>& height_sections,
              const framework::ExecutionContext& context) {
  auto& local_scope = context.scope().NewScope();

  platform::DeviceContextPool& pool = platform::DeviceContextPool::Instance();
  auto& ctx = *pool.Get(context.GetPlace());

  distributed::RPCClient* rpc_client =
      distributed::RPCClient::GetInstance<RPCCLIENT_T>(
          context.Attr<int>("trainer_id"));

  std::vector<std::string> in_var_names;
  std::vector<std::string> out_var_names;
  for (size_t i = 0; i < epmap.size(); ++i) {
    in_var_names.push_back(id_name + "@" + epmap[i]);
    out_var_names.push_back(out_name + "@" + epmap[i]);
  }

  auto splited_ids = SplitIds(id_name, height_sections, &local_scope);
  SplitIdsIntoMultipleVarsBySection(id_name, in_var_names, height_sections,
                                    splited_ids, &local_scope);

  // create output var in local scope
  for (auto& name : out_var_names) {
    local_scope.Var(name)->GetMutable<framework::LoDTensor>();
  }

  std::vector<distributed::VarHandlePtr> rets;
  for (size_t i = 0; i < in_var_names.size(); i++) {
    if (NeedSend(local_scope, in_var_names[i])) {
      VLOG(3) << "sending " << in_var_names[i] << " to " << epmap[i]
              << " to get " << out_var_names[i] << " back";
      rets.push_back(rpc_client->AsyncPrefetchVar(
          epmap[i], ctx, local_scope, in_var_names[i], out_var_names[i],
          table_names[i]));
    } else {
      VLOG(3) << "don't send no-initialied variable: " << out_var_names[i];
    }
  }

  for (size_t i = 0; i < rets.size(); i++) {
    PADDLE_ENFORCE(rets[i]->Wait(), "internal error in RPCClient");
  }

  MergeMultipleVarsIntoOneBySection(id_name, out_name, out_var_names,
                                    height_sections, splited_ids, context,
                                    &local_scope);

  context.scope().DeleteScope(&local_scope);
}

};  // namespace distributed
};  // namespace operators
};  // namespace paddle
<|endoftext|>


<commit_before>/*
Copyright (c) 2019,
Dan Bethell, Johannes Saam, Vahan Sosoyan.
All rights reserved. See COPYING.txt for more details.
*/

#include <ai.h>
#include "aton_client.h"

AI_DRIVER_NODE_EXPORT_METHODS(AtonDriverMtd);

inline const int calc_res(int res, int min, int max)
{
    if (min < 0 && max >= res)
        res = max - min + 1;
    else if (min > 0 && max >= res)
        res += max - res + 1;
    else if (min < 0 && max < res)
        res -= min;
    return res;
}

inline const long long calc_rarea(int minx,
                                  int maxx,
                                  int miny,
                                  int maxy)
{
    int w = maxx - minx + 1;
    int h = maxy - miny + 1;
    return w * h;
}

struct ShaderData
{
    Client* client;
    long long session;
    int xres, yres, min_x, min_y, max_x, max_y;
};

enum reconnect
{
    disabled = 0,
    once,
    always,
};

node_parameters
{
    AiParameterStr("host", get_host().c_str());
    AiParameterInt("port", get_port());
    AiParameterStr("output", "");
    AiParameterInt("session", 0);
    AiParameterInt("reconnect", reconnect::disabled);

#ifdef ARNOLD_5
    AiMetaDataSetStr(nentry, NULL, AtString("maya.translator"), AtString("aton"));
    AiMetaDataSetStr(nentry, NULL, AtString("maya.attr_prefix"), AtString(""));
    AiMetaDataSetBool(nentry, NULL, AtString("display_driver"), true);
    AiMetaDataSetBool(nentry, NULL, AtString("single_layer_driver"), false);
#else
    AiMetaDataSetStr(mds, NULL, AtString("maya.translator"), AtString("aton"));
    AiMetaDataSetStr(mds, NULL, AtString("maya.attr_prefix"), AtString(""));
    AiMetaDataSetBool(mds, NULL, AtString("display_driver"), true);
    AiMetaDataSetBool(mds, NULL, AtString("single_layer_driver"), false);
#endif
}

node_initialize
{
    ShaderData* data = (ShaderData*)AiMalloc(sizeof(ShaderData));
    data->client = NULL;

    data->session = AiNodeGetInt(node, AtString("session"));
    if (data->session == 0)
        data->session = get_unique_id();

#ifdef ARNOLD_5
    AiDriverInitialize(node, true);
    AiNodeSetLocalData(node, data);
#else
    AiDriverInitialize(node, true, data);
#endif
}

node_update {}

driver_supports_pixel_type { return true; }

driver_extension { return NULL; }

driver_open
{
#ifdef ARNOLD_5
    ShaderData* data = (ShaderData*)AiNodeGetLocalData(node);
#else
    ShaderData* data = (ShaderData*)AiDriverGetLocalData(node);
#endif

    // Get Options Node
    AtNode* options = AiUniverseGetOptions();

    // Get Resolution
    const int xres = AiNodeGetInt(options, AtString("xres"));
    const int yres = AiNodeGetInt(options, AtString("yres"));

    // Get Regions
    const int min_x = AiNodeGetInt(options, AtString("region_min_x"));
    const int min_y = AiNodeGetInt(options, AtString("region_min_y"));
    const int max_x = AiNodeGetInt(options, AtString("region_max_x"));
    const int max_y = AiNodeGetInt(options, AtString("region_max_y"));

    // Set Resolution
    data->min_x = (min_x == INT_MIN) ? 0 : min_x;
    data->min_y = (min_y == INT_MIN) ? 0 : min_y;
    data->max_x = (max_x == INT_MIN) ? 0 : max_x;
    data->max_y = (max_y == INT_MIN) ? 0 : max_y;

    data->xres = calc_res(xres, data->min_x, data->max_x);
    data->yres = calc_res(yres, data->min_y, data->max_y);

    // Get Region Area
    const long long region_area = calc_rarea(data_window.minx,
                                             data_window.maxx,
                                             data_window.miny,
                                             data_window.maxy);

    // Get Arnold version
    char arch[3], major[3], minor[3], fix[3];
    AiGetVersion(arch, major, minor, fix);
    const int version = pack_4_int(atoi(arch), atoi(major), atoi(minor), atoi(fix));

    // Get Frame
    const float frame = AiNodeGetFlt(options, AtString("frame"));

    // Get Camera Field of view
    AtNode* camera = (AtNode*)AiNodeGetPtr(options, AtString("camera"));
    const float cam_fov = AiNodeGetFlt(camera, AtString("fov"));

    // Get Camera Matrix
#ifdef ARNOLD_5
    const AtMatrix& cMat = AiNodeGetMatrix(camera, AtString("matrix"));
#else
    AtMatrix cMat;
    AiNodeGetMatrix(camera, "matrix", cMat);
#endif

    const float cam_matrix[16] = {cMat[0][0], cMat[1][0], cMat[2][0], cMat[3][0],
                                  cMat[0][1], cMat[1][1], cMat[2][1], cMat[3][1],
                                  cMat[0][2], cMat[1][2], cMat[2][2], cMat[3][2],
                                  cMat[0][3], cMat[1][3], cMat[2][3], cMat[3][3]};

    // Get Samples
    const int& aa_samples = AiNodeGetInt(options, AtString("AA_samples"));
    const int& diffuse_samples = AiNodeGetInt(options, AtString("GI_diffuse_samples"));
    const int& spec_samples = AiNodeGetInt(options, AtString("GI_specular_samples"));
    const int& trans_samples = AiNodeGetInt(options, AtString("GI_transmission_samples"));
    const int& sss_samples = AiNodeGetInt(options, AtString("GI_sss_samples"));
    const int& volume_samples = AiNodeGetInt(options, AtString("GI_volume_samples"));

    // Get Pixel Aspect Ratio
    const float pixel_aspect = AiNodeGetFlt(options, AtString("pixel_aspect_ratio"));

    const int samples[6]  = {aa_samples,
                             diffuse_samples,
                             spec_samples,
                             trans_samples,
                             sss_samples,
                             volume_samples};

    const char* output = AiNodeGetStr(node, AtString("output"));

    // Make image header & send to server
    DataHeader dh(data->session,
                  data->xres,
                  data->yres,
                  pixel_aspect,
                  region_area,
                  version,
                  frame,
                  cam_fov,
                  cam_matrix,
                  samples,
                  output);

    // Get Host and Port
    const char* host = AiNodeGetStr(node, AtString("host"));
    const int port = AiNodeGetInt(node, AtString("port"));

    if (data->client == NULL)
        data->client = new Client(host, port);

    try
    {
        data->client->send_header(dh);
    }
    catch(const std::exception &e)
    {
        const char* err = e.what();
        AiMsgError("ATON | Host %s with Port %i was not found! %s", host, port, err);
    }
}

driver_needs_bucket { return true; }

driver_prepare_bucket {}

driver_process_bucket {}

driver_write_bucket
{

#ifdef ARNOLD_5
    ShaderData* data = (ShaderData*)AiNodeGetLocalData(node);
#else
    ShaderData* data = (ShaderData*)AiDriverGetLocalData(node);
#endif

    int pixel_type, spp = 0;
    const void* bucket_data;
    const char* aov_name;
    const int reconnect_mode = AiNodeGetInt(node, AtString("reconnect"));

    if (data->min_x < 0)
        bucket_xo = bucket_xo - data->min_x;
    if (data->min_y < 0)
        bucket_yo = bucket_yo - data->min_y;

    // Reconnect to server
    if (reconnect_mode)
        data->client->connect();

    while (AiOutputIteratorGetNext(iterator, &aov_name, &pixel_type, &bucket_data))
    {
        const float* ptr = reinterpret_cast<const float*>(bucket_data);
        const long long memory = AiMsgUtilGetUsedMemory();
        const unsigned int time = AiMsgUtilGetElapsedTime();

        switch (pixel_type)
        {
            case(AI_TYPE_INT):
            case(AI_TYPE_UINT):
            case(AI_TYPE_FLOAT):
                spp = 1;
                break;
            case(AI_TYPE_RGBA):
                spp = 4;
                break;
            default:
                spp = 3;
        }

        // Create our DataPixels object
        DataPixels dp(data->session,
                      data->xres,
                      data->yres,
                      bucket_xo,
                      bucket_yo,
                      bucket_size_x,
                      bucket_size_y,
                      spp,
                      memory,
                      time,
                      aov_name,
                      ptr);

        data->client->send_pixels(dp);
    }

    // Disconnect from server
    if (reconnect_mode == reconnect::always)
        data->client->disconnect();
}

driver_close {}

node_finish
{
// Release the driver
#ifdef ARNOLD_5
    ShaderData* data = (ShaderData*)AiNodeGetLocalData(node);
#else
    ShaderData* data = (ShaderData*)AiDriverGetLocalData(node);
#endif

    delete data->client;
    AiFree(data);

#ifndef ARNOLD_5
    AiDriverDestroy(node);
#endif
}

node_loader
{
    sprintf(node->version, AI_VERSION);

    switch (i)
    {
        case 0:
            node->methods = (AtNodeMethods*) AtonDriverMtd;
            node->output_type = AI_TYPE_RGBA;
            node->name = "driver_aton";
            node->node_type = AI_NODE_DRIVER;
            break;
        default:
        return false;
    }
    return true;
}
<commit_msg>Refactores<commit_after>/*
Copyright (c) 2019,
Dan Bethell, Johannes Saam, Vahan Sosoyan.
All rights reserved. See COPYING.txt for more details.
*/

#include <ai.h>
#include "aton_client.h"

AI_DRIVER_NODE_EXPORT_METHODS(AtonDriverMtd);

inline const int calc_res(int res, int min, int max)
{
    if (min < 0 && max >= res)
        res = max - min + 1;
    else if (min > 0 && max >= res)
        res += max - res + 1;
    else if (min < 0 && max < res)
        res -= min;
    return res;
}

inline const long long calc_rarea(int minx,
                                  int maxx,
                                  int miny,
                                  int maxy)
{
    int w = maxx - minx + 1;
    int h = maxy - miny + 1;
    return w * h;
}

struct ShaderData
{
    Client* client;
    long long session;
    int xres, yres, min_x, min_y, max_x, max_y;
};

enum reconnect
{
    disabled = 0,
    always,
    next_bucket,
};

node_parameters
{
    AiParameterStr("host", get_host().c_str());
    AiParameterInt("port", get_port());
    AiParameterStr("output", "");
    AiParameterInt("session", 0);
    AiParameterInt("reconnect", reconnect::disabled);

#ifdef ARNOLD_5
    AiMetaDataSetStr(nentry, NULL, AtString("maya.translator"), AtString("aton"));
    AiMetaDataSetStr(nentry, NULL, AtString("maya.attr_prefix"), AtString(""));
    AiMetaDataSetBool(nentry, NULL, AtString("display_driver"), true);
    AiMetaDataSetBool(nentry, NULL, AtString("single_layer_driver"), false);
#else
    AiMetaDataSetStr(mds, NULL, AtString("maya.translator"), AtString("aton"));
    AiMetaDataSetStr(mds, NULL, AtString("maya.attr_prefix"), AtString(""));
    AiMetaDataSetBool(mds, NULL, AtString("display_driver"), true);
    AiMetaDataSetBool(mds, NULL, AtString("single_layer_driver"), false);
#endif
}

node_initialize
{
    ShaderData* data = (ShaderData*)AiMalloc(sizeof(ShaderData));
    data->client = NULL;

    data->session = AiNodeGetInt(node, AtString("session"));
    if (data->session == 0)
        data->session = get_unique_id();

#ifdef ARNOLD_5
    AiDriverInitialize(node, true);
    AiNodeSetLocalData(node, data);
#else
    AiDriverInitialize(node, true, data);
#endif
}

node_update {}

driver_supports_pixel_type { return true; }

driver_extension { return NULL; }

driver_open
{
#ifdef ARNOLD_5
    ShaderData* data = (ShaderData*)AiNodeGetLocalData(node);
#else
    ShaderData* data = (ShaderData*)AiDriverGetLocalData(node);
#endif

    // Get Options Node
    AtNode* options = AiUniverseGetOptions();

    // Get Resolution
    const int xres = AiNodeGetInt(options, AtString("xres"));
    const int yres = AiNodeGetInt(options, AtString("yres"));

    // Get Regions
    const int min_x = AiNodeGetInt(options, AtString("region_min_x"));
    const int min_y = AiNodeGetInt(options, AtString("region_min_y"));
    const int max_x = AiNodeGetInt(options, AtString("region_max_x"));
    const int max_y = AiNodeGetInt(options, AtString("region_max_y"));

    // Set Resolution
    data->min_x = (min_x == INT_MIN) ? 0 : min_x;
    data->min_y = (min_y == INT_MIN) ? 0 : min_y;
    data->max_x = (max_x == INT_MIN) ? 0 : max_x;
    data->max_y = (max_y == INT_MIN) ? 0 : max_y;

    data->xres = calc_res(xres, data->min_x, data->max_x);
    data->yres = calc_res(yres, data->min_y, data->max_y);

    // Get Region Area
    const long long region_area = calc_rarea(data_window.minx,
                                             data_window.maxx,
                                             data_window.miny,
                                             data_window.maxy);

    // Get Arnold version
    char arch[3], major[3], minor[3], fix[3];
    AiGetVersion(arch, major, minor, fix);
    const int version = pack_4_int(atoi(arch), atoi(major), atoi(minor), atoi(fix));

    // Get Frame
    const float frame = AiNodeGetFlt(options, AtString("frame"));

    // Get Camera Field of view
    AtNode* camera = (AtNode*)AiNodeGetPtr(options, AtString("camera"));
    const float cam_fov = AiNodeGetFlt(camera, AtString("fov"));

    // Get Camera Matrix
#ifdef ARNOLD_5
    const AtMatrix& cMat = AiNodeGetMatrix(camera, AtString("matrix"));
#else
    AtMatrix cMat;
    AiNodeGetMatrix(camera, "matrix", cMat);
#endif

    const float cam_matrix[16] = {cMat[0][0], cMat[1][0], cMat[2][0], cMat[3][0],
                                  cMat[0][1], cMat[1][1], cMat[2][1], cMat[3][1],
                                  cMat[0][2], cMat[1][2], cMat[2][2], cMat[3][2],
                                  cMat[0][3], cMat[1][3], cMat[2][3], cMat[3][3]};

    // Get Samples
    const int& aa_samples = AiNodeGetInt(options, AtString("AA_samples"));
    const int& diffuse_samples = AiNodeGetInt(options, AtString("GI_diffuse_samples"));
    const int& spec_samples = AiNodeGetInt(options, AtString("GI_specular_samples"));
    const int& trans_samples = AiNodeGetInt(options, AtString("GI_transmission_samples"));
    const int& sss_samples = AiNodeGetInt(options, AtString("GI_sss_samples"));
    const int& volume_samples = AiNodeGetInt(options, AtString("GI_volume_samples"));

    // Get Pixel Aspect Ratio
    const float pixel_aspect = AiNodeGetFlt(options, AtString("pixel_aspect_ratio"));

    const int samples[6]  = {aa_samples,
                             diffuse_samples,
                             spec_samples,
                             trans_samples,
                             sss_samples,
                             volume_samples};

    const char* output = AiNodeGetStr(node, AtString("output"));

    // Make image header & send to server
    DataHeader dh(data->session,
                  data->xres,
                  data->yres,
                  pixel_aspect,
                  region_area,
                  version,
                  frame,
                  cam_fov,
                  cam_matrix,
                  samples,
                  output);

    // Get Host and Port
    const char* host = AiNodeGetStr(node, AtString("host"));
    const int port = AiNodeGetInt(node, AtString("port"));

    if (data->client == NULL)
        data->client = new Client(host, port);

    try
    {
        data->client->send_header(dh);
    }
    catch(const std::exception &e)
    {
        const char* err = e.what();
        AiMsgError("ATON | Host %s with Port %i was not found! %s", host, port, err);
    }
}

driver_needs_bucket { return true; }

driver_prepare_bucket {}

driver_process_bucket {}

driver_write_bucket
{

#ifdef ARNOLD_5
    ShaderData* data = (ShaderData*)AiNodeGetLocalData(node);
#else
    ShaderData* data = (ShaderData*)AiDriverGetLocalData(node);
#endif

    int pixel_type, spp = 0;
    const void* bucket_data;
    const char* aov_name;
    const int reconnect_mode = AiNodeGetInt(node, AtString("reconnect"));

    if (data->min_x < 0)
        bucket_xo = bucket_xo - data->min_x;
    if (data->min_y < 0)
        bucket_yo = bucket_yo - data->min_y;

    // Reconnect to server
    if (reconnect_mode)
        data->client->connect();

    while (AiOutputIteratorGetNext(iterator, &aov_name, &pixel_type, &bucket_data))
    {
        const float* ptr = reinterpret_cast<const float*>(bucket_data);
        const long long memory = AiMsgUtilGetUsedMemory();
        const unsigned int time = AiMsgUtilGetElapsedTime();

        switch (pixel_type)
        {
            case(AI_TYPE_INT):
            case(AI_TYPE_UINT):
            case(AI_TYPE_FLOAT):
                spp = 1;
                break;
            case(AI_TYPE_RGBA):
                spp = 4;
                break;
            default:
                spp = 3;
        }

        // Create our DataPixels object
        DataPixels dp(data->session,
                      data->xres,
                      data->yres,
                      bucket_xo,
                      bucket_yo,
                      bucket_size_x,
                      bucket_size_y,
                      spp,
                      memory,
                      time,
                      aov_name,
                      ptr);

        data->client->send_pixels(dp);
    }

    // Disconnect from server
    if (reconnect_mode == reconnect::always)
        data->client->disconnect();
}

driver_close {}

node_finish
{
// Release the driver
#ifdef ARNOLD_5
    ShaderData* data = (ShaderData*)AiNodeGetLocalData(node);
#else
    ShaderData* data = (ShaderData*)AiDriverGetLocalData(node);
#endif

    delete data->client;
    AiFree(data);

#ifndef ARNOLD_5
    AiDriverDestroy(node);
#endif
}

node_loader
{
    sprintf(node->version, AI_VERSION);

    switch (i)
    {
        case 0:
            node->methods = (AtNodeMethods*) AtonDriverMtd;
            node->output_type = AI_TYPE_RGBA;
            node->name = "driver_aton";
            node->node_type = AI_NODE_DRIVER;
            break;
        default:
        return false;
    }
    return true;
}
<|endoftext|>


<commit_before>// Copyright: 2014, Ableton AG, Berlin. All rights reserved.

#pragma once

#include <atria/estd/type_traits.hpp>
#include <atria/estd/utility.hpp>
#include <algorithm>
#include <numeric>
#include <tuple>

//!
// When defined to 1, std::accumulate will be used as an
// implementation for reduce() over one single collection.  In that
// case, halting reducers (e.g. take) might not work.
//
#ifndef ABL_REDUCE_WITH_ACCUMULATE
#define ABL_REDUCE_WITH_ACCUMULATE 0
#endif

//!
// When defined to 1, reduce will be defined separately for the
// non-variadic version.
//
#ifndef ABL_REDUCE_NON_VARIADIC
#define ABL_REDUCE_NON_VARIADIC 1
#endif

namespace atria {
namespace xform {

//!
// Box for a value that is `finished` reducing. It might be returned
// by reducers that might know that they are done before processing
// all the input, for example, when the state reaches an idempotent
// value.
//
// @see take
//
template <typename T>
struct reduced_t
{
  T value;
};

//!
// Constructs a reduced value for `value`
//
template <typename T>
auto reduced(T&& value) -> reduced_t<estd::decay_t<T> >
{
  return { std::forward<T>(value) };
}

//!
// Unwraps the value in a potentially *reduced* box.
// @see reduced_t
// @see maybe_reduced
//
template <typename T>
auto from_reduced(T& v) -> T& { return v; }
template <typename T>
auto from_reduced(const T& v) -> const T& { return v; }
template <typename T>
auto from_reduced(reduced_t<T>& v) -> T& { return v.value; }
template <typename T>
auto from_reduced(const reduced_t<T>& v) -> const T& { return v.value; }

//!
// Returns whether all values of a type are finished reducing.
// @see reduced_t
//
template <typename T>
struct type_is_reduced : std::false_type {};
template <typename T>
struct type_is_reduced<reduced_t<T>> : std::true_type {};

//!
// Returns whether a value `v` is a finished reduction.
// @see reduced_t
//
template <typename T>
auto is_reduced(const T&)
  -> estd::enable_if_t<!type_is_reduced<T>::value, bool>
{
  return false;
}

template <typename T>
auto is_reduced(const T&)
  -> estd::enable_if_t<type_is_reduced<T>::value, bool>
{
  return true;
}

//!
// Holds a value that may or may not be a finished reduction.  It
// is convertible from any other type that may be reduced.
// @see reduced_t
//
template <typename T>
struct maybe_reduced
{
  bool reduced;
  T value;

  template <typename U>
  maybe_reduced(U x)
    : reduced(is_reduced(x))
    , value(std::move(from_reduced(x)))
  {}
};

template <typename T>
auto is_reduced(const maybe_reduced<T>& v) -> bool { return v.reduced; }
template <typename T>
auto from_reduced(maybe_reduced<T>& v) -> T& { return v.value; }
template <typename T>
auto from_reduced(const maybe_reduced<T>& v) -> const T& { return v.value; }

namespace detail {

template <typename ReducerT,
          typename StateT,
          typename InputRangeT>
auto reduce_nested_accumulate(ReducerT&& reducer, StateT&& state, InputRangeT&& range)
  -> estd::decay_t<StateT>
{
  return std::accumulate(
    std::begin(range),
    std::end(range),
    std::forward<StateT>(state),
    std::forward<ReducerT>(reducer));
}

template <typename ReducerT,
          typename StateT,
          typename InputRangeT>
auto reduce_nested_non_variadic(ReducerT&& reducer, StateT&& initial, InputRangeT&& range)
  -> estd::decay_t<decltype(reducer(initial, *range.begin()))>
{
  using final_state_t = estd::decay_t<decltype(
    reducer(initial, *range.begin()))>;

  auto state = final_state_t(std::forward<StateT>(initial));
  for (auto first = std::begin(range),
            last  = std::end(range);
       !is_reduced(state) && first != last;
       ++first)
  {
    state = reducer(from_reduced(state), *first);
  }

  return state;
}

template <typename ReducerT,
          typename StateT,
          std::size_t ...Indices,
          typename ...InputRangeTs>
auto reduce_nested_variadic_impl(ReducerT&& reducer,
                                 StateT&& initial,
                                 estd::index_sequence<Indices...>,
                                 InputRangeTs&& ...ranges)
  -> estd::decay_t<decltype(reducer(initial, *ranges.begin()...))>
{
  using final_state_t = estd::decay_t<decltype(
    reducer(initial, *ranges.begin()...))>;

  auto state = final_state_t(std::forward<StateT>(initial));

  for (auto firsts = std::make_tuple(std::begin(ranges)...),
            lasts  = std::make_tuple(std::end(ranges)...);
       !is_reduced(state) &&
         std::min({ std::get<Indices>(firsts) !=
                    std::get<Indices>(lasts)... });
       meta::noop(++std::get<Indices>(firsts)...))
  {
    state = reducer(from_reduced(state),
                    *std::get<Indices>(firsts)...);
  }

  return state;
}

template <typename ReducerT,
          typename StateT,
          typename ...InputRangeTs>
auto reduce_nested_variadic(ReducerT&& reducer, StateT&& state, InputRangeTs&& ...ranges)
  -> estd::decay_t<StateT>
{
  return detail::reduce_nested_variadic_impl(
    std::forward<ReducerT>(reducer),
    std::forward<StateT>(state),
    estd::make_index_sequence<sizeof...(InputRangeTs)> {},
    std::forward<InputRangeTs>(ranges)...);
}

} // namespace detail

#if ABL_REDUCE_WITH_ACCUMULATE
#  define ABL_REDUCE_NESTED_NON_VARIADIC_IMPL \
  ::atria::xform::detail::reduce_nested_accumulate
#elif ABL_REDUCE_NON_VARIADIC
#  define ABL_REDUCE_NESTED_NON_VARIADIC_IMPL \
  ::atria::xform::detail::reduce_nested_non_variadic
#else
#  define ABL_REDUCE_NESTED_NON_VARIADIC_IMPL \
  ::atria::xform::detail::reduce_nested_variadic
#endif

//!
// Similar to @a reduce, but does not unwrap `reduced` values.  This is
// useful when calling reduce recursively inside a transducer.
// @see take
//
template <typename ReducerT,
          typename StateT,
          typename InputRangeT>
auto reduce_nested(ReducerT&& reducer, StateT&& state, InputRangeT&& range)
  -> decltype(ABL_REDUCE_NESTED_NON_VARIADIC_IMPL(
                std::forward<ReducerT>(reducer),
                std::forward<StateT>(state),
                std::forward<InputRangeT>(range)))
{
  return ABL_REDUCE_NESTED_NON_VARIADIC_IMPL(
    std::forward<ReducerT>(reducer),
    std::forward<StateT>(state),
    std::forward<InputRangeT>(range));
}

template <typename ReducerT,
          typename StateT,
          typename InputRangeT,
          typename ...InputRangeTs>
auto reduce_nested(ReducerT&& reducer, StateT&& state, InputRangeT&& range,
                   InputRangeTs&& ...ranges)
  -> decltype(detail::reduce_nested_variadic(
                std::forward<ReducerT>(reducer),
                std::forward<StateT>(state),
                std::forward<InputRangeT>(range),
                std::forward<InputRangeTs>(ranges)...))
{
  return detail::reduce_nested_variadic(
    std::forward<ReducerT>(reducer),
    std::forward<StateT>(state),
    std::forward<InputRangeT>(range),
    std::forward<InputRangeTs>(ranges)...);
}

//!
// Similar to clojure.core/reduce.  Unlike `std::accumulate`, this
// reduces over a range (doesn't take to distinct iterators) and can
// reduce over several ranges at the same time.  It also supports
// early termination for transducers.
//
template <typename ReducerT,
          typename StateT,
          typename ...InputRangeTs>
auto reduce(ReducerT&& reducer, StateT&& state, InputRangeTs&& ...ranges)
  -> estd::decay_t<StateT>
{
  return from_reduced(
    reduce_nested(
      std::forward<ReducerT>(reducer),
      std::forward<StateT>(state),
      std::forward<InputRangeTs>(ranges)...));
}

} // namespace xform
} // namespace atria
<commit_msg>xform: simplify implementation of `from_reduced` overloads<commit_after>// Copyright: 2014, Ableton AG, Berlin. All rights reserved.

#pragma once

#include <atria/estd/type_traits.hpp>
#include <atria/estd/utility.hpp>
#include <atria/xform/functional.hpp>

#include <algorithm>
#include <numeric>
#include <tuple>

//!
// When defined to 1, std::accumulate will be used as an
// implementation for reduce() over one single collection.  In that
// case, halting reducers (e.g. take) might not work.
//
#ifndef ABL_REDUCE_WITH_ACCUMULATE
#define ABL_REDUCE_WITH_ACCUMULATE 0
#endif

//!
// When defined to 1, reduce will be defined separately for the
// non-variadic version.
//
#ifndef ABL_REDUCE_NON_VARIADIC
#define ABL_REDUCE_NON_VARIADIC 1
#endif

namespace atria {
namespace xform {

//!
// Box for a value that is `finished` reducing. It might be returned
// by reducers that might know that they are done before processing
// all the input, for example, when the state reaches an idempotent
// value.
//
// @see take
//
template <typename T>
struct reduced_t
{
  T value;
};

//!
// Constructs a reduced value for `value`
//
template <typename T>
auto reduced(T&& value) -> reduced_t<estd::decay_t<T> >
{
  return { std::forward<T>(value) };
}

template <typename T>
struct from_reduced_impl : identity_t {};

template <typename T>
struct from_reduced_impl<reduced_t<T>>
{
  template <typename ArgT>
    auto operator() (ArgT&& x) -> decltype(x.value) { return x.value; }
};

//!
// Unwraps the value in a potentially *reduced* box.
// @see reduced_t
// @see maybe_reduced
//
template <typename T>
auto from_reduced(T&& t) ->
  decltype(from_reduced_impl<estd::decay_t<T>>{}(std::forward<T>(t)))
{
  return from_reduced_impl<estd::decay_t<T>>{}(std::forward<T>(t));
}

//!
// Returns whether all values of a type are finished reducing.
// @see reduced_t
//
template <typename T>
struct type_is_reduced : std::false_type {};
template <typename T>
struct type_is_reduced<reduced_t<T>> : std::true_type {};

//!
// Returns whether a value `v` is a finished reduction.
// @see reduced_t
//
template <typename T>
auto is_reduced(const T&)
  -> estd::enable_if_t<!type_is_reduced<T>::value, bool>
{
  return false;
}

template <typename T>
auto is_reduced(const T&)
  -> estd::enable_if_t<type_is_reduced<T>::value, bool>
{
  return true;
}

//!
// Holds a value that may or may not be a finished reduction.  It
// is convertible from any other type that may be reduced.
// @see reduced_t
//
template <typename T>
struct maybe_reduced
{
  bool reduced;
  T value;

  template <typename U>
  maybe_reduced(U x)
    : reduced(is_reduced(x))
    , value(std::move(from_reduced(x)))
  {}
};

template <typename T>
auto is_reduced(const maybe_reduced<T>& v) -> bool { return v.reduced; }

template <typename T>
struct from_reduced_impl<maybe_reduced<T>>
  : from_reduced_impl<reduced_t<T>> {};

namespace detail {

template <typename ReducerT,
          typename StateT,
          typename InputRangeT>
auto reduce_nested_accumulate(ReducerT&& reducer, StateT&& state, InputRangeT&& range)
  -> estd::decay_t<StateT>
{
  return std::accumulate(
    std::begin(range),
    std::end(range),
    std::forward<StateT>(state),
    std::forward<ReducerT>(reducer));
}

template <typename ReducerT,
          typename StateT,
          typename InputRangeT>
auto reduce_nested_non_variadic(ReducerT&& reducer, StateT&& initial, InputRangeT&& range)
  -> estd::decay_t<decltype(reducer(initial, *range.begin()))>
{
  using final_state_t = estd::decay_t<decltype(
    reducer(initial, *range.begin()))>;

  auto state = final_state_t(std::forward<StateT>(initial));
  for (auto first = std::begin(range),
            last  = std::end(range);
       !is_reduced(state) && first != last;
       ++first)
  {
    state = reducer(from_reduced(state), *first);
  }

  return state;
}

template <typename ReducerT,
          typename StateT,
          std::size_t ...Indices,
          typename ...InputRangeTs>
auto reduce_nested_variadic_impl(ReducerT&& reducer,
                                 StateT&& initial,
                                 estd::index_sequence<Indices...>,
                                 InputRangeTs&& ...ranges)
  -> estd::decay_t<decltype(reducer(initial, *ranges.begin()...))>
{
  using final_state_t = estd::decay_t<decltype(
    reducer(initial, *ranges.begin()...))>;

  auto state = final_state_t(std::forward<StateT>(initial));

  for (auto firsts = std::make_tuple(std::begin(ranges)...),
            lasts  = std::make_tuple(std::end(ranges)...);
       !is_reduced(state) &&
         std::min({ std::get<Indices>(firsts) !=
                    std::get<Indices>(lasts)... });
       meta::noop(++std::get<Indices>(firsts)...))
  {
    state = reducer(from_reduced(state),
                    *std::get<Indices>(firsts)...);
  }

  return state;
}

template <typename ReducerT,
          typename StateT,
          typename ...InputRangeTs>
auto reduce_nested_variadic(ReducerT&& reducer, StateT&& state, InputRangeTs&& ...ranges)
  -> estd::decay_t<StateT>
{
  return detail::reduce_nested_variadic_impl(
    std::forward<ReducerT>(reducer),
    std::forward<StateT>(state),
    estd::make_index_sequence<sizeof...(InputRangeTs)> {},
    std::forward<InputRangeTs>(ranges)...);
}

} // namespace detail

#if ABL_REDUCE_WITH_ACCUMULATE
#  define ABL_REDUCE_NESTED_NON_VARIADIC_IMPL \
  ::atria::xform::detail::reduce_nested_accumulate
#elif ABL_REDUCE_NON_VARIADIC
#  define ABL_REDUCE_NESTED_NON_VARIADIC_IMPL \
  ::atria::xform::detail::reduce_nested_non_variadic
#else
#  define ABL_REDUCE_NESTED_NON_VARIADIC_IMPL \
  ::atria::xform::detail::reduce_nested_variadic
#endif

//!
// Similar to @a reduce, but does not unwrap `reduced` values.  This is
// useful when calling reduce recursively inside a transducer.
// @see take
//
template <typename ReducerT,
          typename StateT,
          typename InputRangeT>
auto reduce_nested(ReducerT&& reducer, StateT&& state, InputRangeT&& range)
  -> decltype(ABL_REDUCE_NESTED_NON_VARIADIC_IMPL(
                std::forward<ReducerT>(reducer),
                std::forward<StateT>(state),
                std::forward<InputRangeT>(range)))
{
  return ABL_REDUCE_NESTED_NON_VARIADIC_IMPL(
    std::forward<ReducerT>(reducer),
    std::forward<StateT>(state),
    std::forward<InputRangeT>(range));
}

template <typename ReducerT,
          typename StateT,
          typename InputRangeT,
          typename ...InputRangeTs>
auto reduce_nested(ReducerT&& reducer, StateT&& state, InputRangeT&& range,
                   InputRangeTs&& ...ranges)
  -> decltype(detail::reduce_nested_variadic(
                std::forward<ReducerT>(reducer),
                std::forward<StateT>(state),
                std::forward<InputRangeT>(range),
                std::forward<InputRangeTs>(ranges)...))
{
  return detail::reduce_nested_variadic(
    std::forward<ReducerT>(reducer),
    std::forward<StateT>(state),
    std::forward<InputRangeT>(range),
    std::forward<InputRangeTs>(ranges)...);
}

//!
// Similar to clojure.core/reduce.  Unlike `std::accumulate`, this
// reduces over a range (doesn't take to distinct iterators) and can
// reduce over several ranges at the same time.  It also supports
// early termination for transducers.
//
template <typename ReducerT,
          typename StateT,
          typename ...InputRangeTs>
auto reduce(ReducerT&& reducer, StateT&& state, InputRangeTs&& ...ranges)
  -> estd::decay_t<StateT>
{
  return from_reduced(
    reduce_nested(
      std::forward<ReducerT>(reducer),
      std::forward<StateT>(state),
      std::forward<InputRangeTs>(ranges)...));
}

} // namespace xform
} // namespace atria
<|endoftext|>


<commit_before>#include "ExampleGame/Components/GameScripts/Units/Thief.h"
#include "ExampleGame/Components/Grid/GameGrid.h"
#include "ExampleGame/Components/Grid/GridCell.h"
#include "ExampleGame/Components/Grid/GridConstants.h"
#include "ExampleGame/Components/Grid/GridManager.h"
#include "ExampleGame/Components/Grid/GridNavigator.h"
#include "ExampleGame/GameConstants/GameConstants.h"
#include "ExampleGame/GameSingletons/GameSingletons.h"
#include "ExampleGame/Messages/Declarations.h"

#include "Vajra/Engine/Components/DerivedComponents/Renderer/SpriteRenderer.h"
#include "Vajra/Engine/Components/DerivedComponents/Transform/Transform.h"
#include "Vajra/Engine/Core/Engine.h"
#include "Vajra/Engine/Input/Input.h"
#include "Vajra/Engine/MessageHub/MessageHub.h"
#include "Vajra/Engine/SceneGraph/SceneGraph3D.h"
#include "Vajra/Engine/Tween/Tween.h"
#include "Vajra/Framework/DeviceUtils/FileSystemUtils/FileSystemUtils.h"

// Tween callbacks
void thiefTweenCallback(ObjectIdType gameObjectId , std::string /* tweenClipName */) {
	GameObject* go = ENGINE->GetSceneGraph3D()->GetGameObjectById(gameObjectId);
	ASSERT(go != nullptr, "Game object id passed into playerUnitNuumberTweenCallback is not valid");
	if(go != nullptr) {
		Thief* pUnit = go->GetComponent<Thief>();
		ASSERT(pUnit != nullptr, "Game object passed into playerUnitNuumberTweenCallback doesn't have a player unit");
		if(pUnit != nullptr) {
			pUnit->onSpecialEnd();
		}
	}

}

void thiefNumberTweenCallback(float /* fromNumber */, float /* toNumber */, float /*currentNumber*/, std::string /*tweenClipName*/, MessageData1S1I1F* /*userParams*/) {
	/*GameObject* go = ENGINE->GetSceneGraph3D()->GetGameObjectById(userParams->i);
	ASSERT(go != nullptr, "Game object id passed into playerUnitNuumberTweenCallback is not valid");
	if(go != nullptr) {
		Thief* thief = go->GetComponent<Thief>();
		ASSERT(thief != nullptr, "Game object passed into playerUnitNuumberTweenCallback doesn't have a player unit");
		if(thief != nullptr) {
			if(tweenClipName == "targetsIn") {
				for(GameObject* go : thief->targetIndicatorsRef ) {

				}

			}
		}
	}*/
}

Thief::Thief() : PlayerUnit() {
	this->init();
}

Thief::Thief(Object* object_) : PlayerUnit(object_) {
	this->init();
}

Thief::~Thief() {
	this->destroy();
}

void Thief::init() {
	this->unitType = UnitType::UNIT_TYPE_THIEF;
}

void Thief::destroy() {
	this->deleteTargets();
}

bool Thief::isSpecialTouch(int touchId) {
	if(this->getTouchNearUnit()) {
		Touch touch = ENGINE->GetInput()->GetTouch(touchId);
		if(touch.timeDown >= GetFloatGameConstant(GAME_CONSTANT_long_press_length_in_seconds) && glm::distance(touch.pos, this->touchStartPos) <= GetFloatGameConstant(GAME_CONSTANT_allowed_finger_movement_in_press)) {
			this->targetedCell = nullptr;
			this->SetTouchIndicatorVisible(false);
			this->gridNavRef->HaltMovement();
			this->updateLegalTagets();
			return true;
		}
	}
	return false;
}

void Thief::onSpecialTouch(int touchId) {
	Touch touch = ENGINE->GetInput()->GetTouch(touchId);
	if(touch.phase == TouchPhase::Ended) {
		if(std::find(this->legalTargets.begin(), this->legalTargets.end(), this->targetedCell) != this->legalTargets.end()) {
			this->startSpecial();
		} else {
			this->targetedCell = this->gridNavRef->GetCurrentCell();
			this->cancelSpecial();
		}
		this->tweenOutTargets();
	} else if(touch.phase == TouchPhase::Moved) {
		this->aimSpecial(touchId);
	}
}

void Thief::startSpecial() {
	PlayerUnit::startSpecial();
	this->gridNavRef->SetGridPosition(nullptr);

	// Remove the indicator at the selected position
	GameObject* selectedTargetIndicator = this->targetIndicatorsRef[this->targetedCell];
	this->targetIndicatorsRef.erase(this->targetedCell);
	delete selectedTargetIndicator;

	this->SetTouchIndicatorCell(this->targetedCell);
	this->startTouchIndicatorPulse();
	this->SetTouchIndicatorSprite(THIEF_SPECIAL);
	this->SetTouchIndicatorVisible(true);

	float jumpDist = glm::distance(this->targetedCell->center, this->gameObjectRef->GetTransform()->GetPosition());
	float jumpTweenTime = jumpDist / GetFloatGameConstant(GAME_CONSTANT_jump_speed_in_units_per_second);
	ENGINE->GetTween()->TweenPosition(this->gameObjectRef->GetId(),
									  this->gameObjectRef->GetTransform()->GetPosition(),
									  this->targetedCell->center,
									  jumpTweenTime,
									  false,
									  TWEEN_TRANSLATION_CURVE_TYPE_PARABOLA,
									  false,
									  thiefTweenCallback);

}

void Thief::onSpecialEnd() {
	PlayerUnit::onSpecialEnd();
	this->gridNavRef->SetGridPosition(this->targetedCell);

	// Broadcast an attack message
	MessageChunk attackMessage = ENGINE->GetMessageHub()->GetOneFreeMessage();
	attackMessage->SetMessageType(MESSAGE_TYPE_UNIT_SPECIAL_HIT);
	attackMessage->messageData.iv1.x = this->targetedCell->x;
	attackMessage->messageData.iv1.y = this->targetedCell->y;
	attackMessage->messageData.iv1.z = this->targetedCell->z;
	attackMessage->messageData.fv1 = this->specialStartPos;
	ENGINE->GetMessageHub()->SendMulticastMessage(attackMessage, this->GetObject()->GetId());
}

void Thief::cancelSpecial() {
	PlayerUnit::cancelSpecial();
	this->tweenOutTargets();

}
void Thief::aimSpecial(int touchId) {
	GridCell* prevTargetCell = this->targetedCell;
	this->targetedCell = this->GetCurrentTouchedCell();

	if(this->targetIndicatorsRef[prevTargetCell] && this->targetIndicatorsRef[this->targetedCell]) {
		if(this->targetedCell != prevTargetCell) {
			this->scaleUpIndicator(this->targetedCell);
			this->scaleDownIndicator(prevTargetCell);
		}
	} else if(this->targetIndicatorsRef[prevTargetCell]) {
		GridCell* nearCell = this->getNearCellTowardsUnit(touchId);
		if(nearCell != nullptr) {
			if(this->targetIndicatorsRef[nearCell]) {
				if(nearCell != prevTargetCell) {
					this->scaleUpIndicator(nearCell);
					this->scaleDownIndicator(prevTargetCell);
				}
			} else {
				this->scaleDownIndicator(prevTargetCell);
			}
			this->targetedCell = nearCell;
		} else {
			this->scaleDownIndicator(prevTargetCell);
		}
	} else if(this->targetIndicatorsRef[this->targetedCell]) {
		this->scaleUpIndicator(this->targetedCell);
	} else {
		GridCell* nearCell = this->getNearCellTowardsUnit(touchId);
		if(nearCell != nullptr) {
			if(this->targetIndicatorsRef[nearCell]) {
				this->scaleUpIndicator(nearCell);
			}
			this->targetedCell = nearCell;
		}
	}

	if(std::find(this->legalTargets.begin(), this->legalTargets.end(), this->GetCurrentTouchedCell()) != this->legalTargets.end()) {
		this->SetTouchIndicatorSprite(GOOD_TOUCH);
	} else {
		this->SetTouchIndicatorSprite(BAD_TOUCH);
	}
}

void Thief::updateLegalTagets() {
	this->legalTargets.clear();
	this->deleteTargets();
	GridCell* currentCell = this->gridNavRef->GetCurrentCell();
	int elevation = SINGLETONS->GetGridManager()->GetGrid()->GetElevationFromWorldY(currentCell->center.y);

	std::list<GridCell*> cellsInRange;
	SINGLETONS->GetGridManager()->GetGrid()->GetNeighborCells(cellsInRange, this->gridNavRef->GetCurrentCell(),
															  GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units)
															  + elevation * GetFloatGameConstant(GAME_CONSTANT_jump_elevation_multiplier));

	for ( GridCell* c : cellsInRange) {
		int cellElevation = 0;
		for(int i = 0; i < NUM_ELEVATIONS; ++i) {
			if(SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, i)) {
				cellElevation = i;
				break;
			}

		}

		// The thief can jump to a maximum of one elevation level above her
		if (cellElevation <= (elevation + 1)) {
			if ((cellElevation > elevation) || (SINGLETONS->GetGridManager()->GetGrid()->HasLineOfSight(currentCell, c, elevation))) {
				if (SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, cellElevation)) {
					int elevationDiff = elevation - cellElevation;
					if (elevationDiff < 0) {
						elevationDiff = 0;
					}

					float maxRange = fmax(GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units) + elevationDiff * GetFloatGameConstant(GAME_CONSTANT_jump_elevation_multiplier), 1.0f);
					float dist = SINGLETONS->GetGridManager()->GetGrid()->GetGroundDistanceBetweenCells(currentCell, c);
					if (dist <= maxRange) {
						this->legalTargets.push_back(c);
					}
				}
			}
		}
		/*
		if(cellElevation == elevation) { // is the cell on the same height
			if(this->gridNavRef->CanReachDestination(c, GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units)) && SINGLETONS->GetGridManager()->GetGrid()->HasLineOfSight(currentCell, c, elevation) ) {
				this->legalTargets.push_back(c);
			}
		}
		else if(cellElevation <= elevation + 1) { // is the cell below it
			if(SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, cellElevation)) {
				float dist = glm::distance(glm::vec2(c->x, c->z), glm::vec2(currentCell->x, currentCell->z));
				if(dist <= GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units) + cellElevation * GetFloatGameConstant(GAME_CONSTANT_jump_elevation_multiplier)) {
					this->legalTargets.push_back(c);
				}
			}
		}
		*/
	}
	this->createTargets();
	this->tweenInTargets();
}

void Thief::tweenInTargets() {
	glm::vec3 pos;
	for(auto contents : this->targetIndicatorsRef ) {
		if(contents.second != nullptr) {
			pos = contents.second->GetTransform()->GetPosition();
			ENGINE->GetTween()->TweenPosition(contents.second->GetId(), pos, pos + glm::vec3(0.0f, GetFloatGameConstant(GAME_CONSTANT_target_indicator_offset), 0.0f), GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
		}
	}
}

void Thief::tweenOutTargets() {
	glm::vec3 pos;
	for(auto contents : this->targetIndicatorsRef ) {
		if(contents.second != nullptr) {
			pos = contents.second->GetTransform()->GetPosition();
			ENGINE->GetTween()->TweenScale(contents.second->GetId(), glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)), glm::vec3(0), GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
		}
	}
}

void Thief::createTargets() {
	for( GridCell* c : this->legalTargets ) {
		GameObject* indicator = new GameObject(ENGINE->GetSceneGraph3D());
		SpriteRenderer* spriteRenderer = indicator->AddComponent<SpriteRenderer>();
		std::vector<std::string> pathsToTextures;
		pathsToTextures.push_back(FRAMEWORK->GetFileSystemUtils()->GetDevicePictureResourcesFolderName() + "SD_UIEffect_Thief_Jump_cyan.png");
		spriteRenderer->initPlane(1.0f, 1.0f, "sptshdr", pathsToTextures, PlaneOrigin::Center);
		indicator->GetTransform()->SetPosition(c->center);
		indicator->GetTransform()->SetScale( glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)));
		indicator->GetTransform()->Rotate(90.0f inRadians, XAXIS);
		this->targetIndicatorsRef[c] = indicator;
	}
}

void Thief::deleteTargets() {
	this->targetIndicatorsRef.clear();
}

void Thief::scaleUpIndicator(GridCell* cell) {
	if(cell != nullptr && this->targetIndicatorsRef[cell] != nullptr) {
		ENGINE->GetTween()->TweenScale(this->targetIndicatorsRef[cell]->GetId(),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale_hover)),
									   GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
	}
}

void Thief::scaleDownIndicator(GridCell* cell) {
	if(cell != nullptr && this->targetIndicatorsRef[cell] != nullptr) {
		ENGINE->GetTween()->TweenScale(this->targetIndicatorsRef[cell]->GetId(),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale_hover)),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)),
									   GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
	}
}

GridCell* Thief::getNearCellTowardsUnit(int touchId) {
	glm::vec3 unitPos = this->gridNavRef->GetCurrentCell()->center;
	glm::vec3 touchPos;
	if(this->GetCurrentTouchedCell()) {
		touchPos = SINGLETONS->GetGridManager()->TouchPositionToGridPositionAtElevation(ENGINE->GetInput()->GetTouch(touchId).pos, this->GetCurrentTouchedCell()->y);
	}  else {
		touchPos = SINGLETONS->GetGridManager()->TouchPositionToGridPositionAtElevation(ENGINE->GetInput()->GetTouch(touchId).pos, this->gridNavRef->GetCurrentCell()->y);
	}
	glm::vec3 dirTowardsThief = glm::normalize(unitPos - touchPos);
	dirTowardsThief *= .55f;
	glm::vec3 cellLocation = touchPos + dirTowardsThief;
	return SINGLETONS->GetGridManager()->GetGrid()->GetCell(cellLocation);
}
<commit_msg>thief now looks at her targets again<commit_after>#include "ExampleGame/Components/GameScripts/Units/Thief.h"
#include "ExampleGame/Components/Grid/GameGrid.h"
#include "ExampleGame/Components/Grid/GridCell.h"
#include "ExampleGame/Components/Grid/GridConstants.h"
#include "ExampleGame/Components/Grid/GridManager.h"
#include "ExampleGame/Components/Grid/GridNavigator.h"
#include "ExampleGame/GameConstants/GameConstants.h"
#include "ExampleGame/GameSingletons/GameSingletons.h"
#include "ExampleGame/Messages/Declarations.h"

#include "Vajra/Engine/Components/DerivedComponents/Renderer/SpriteRenderer.h"
#include "Vajra/Engine/Components/DerivedComponents/Transform/Transform.h"
#include "Vajra/Engine/Core/Engine.h"
#include "Vajra/Engine/Input/Input.h"
#include "Vajra/Engine/MessageHub/MessageHub.h"
#include "Vajra/Engine/SceneGraph/SceneGraph3D.h"
#include "Vajra/Engine/Tween/Tween.h"
#include "Vajra/Framework/DeviceUtils/FileSystemUtils/FileSystemUtils.h"

// Tween callbacks
void thiefTweenCallback(ObjectIdType gameObjectId , std::string /* tweenClipName */) {
	GameObject* go = ENGINE->GetSceneGraph3D()->GetGameObjectById(gameObjectId);
	ASSERT(go != nullptr, "Game object id passed into playerUnitNuumberTweenCallback is not valid");
	if(go != nullptr) {
		Thief* pUnit = go->GetComponent<Thief>();
		ASSERT(pUnit != nullptr, "Game object passed into playerUnitNuumberTweenCallback doesn't have a player unit");
		if(pUnit != nullptr) {
			pUnit->onSpecialEnd();
		}
	}

}

void thiefNumberTweenCallback(float /* fromNumber */, float /* toNumber */, float /*currentNumber*/, std::string /*tweenClipName*/, MessageData1S1I1F* /*userParams*/) {
	/*GameObject* go = ENGINE->GetSceneGraph3D()->GetGameObjectById(userParams->i);
	ASSERT(go != nullptr, "Game object id passed into playerUnitNuumberTweenCallback is not valid");
	if(go != nullptr) {
		Thief* thief = go->GetComponent<Thief>();
		ASSERT(thief != nullptr, "Game object passed into playerUnitNuumberTweenCallback doesn't have a player unit");
		if(thief != nullptr) {
			if(tweenClipName == "targetsIn") {
				for(GameObject* go : thief->targetIndicatorsRef ) {

				}

			}
		}
	}*/
}

Thief::Thief() : PlayerUnit() {
	this->init();
}

Thief::Thief(Object* object_) : PlayerUnit(object_) {
	this->init();
}

Thief::~Thief() {
	this->destroy();
}

void Thief::init() {
	this->unitType = UnitType::UNIT_TYPE_THIEF;
}

void Thief::destroy() {
	this->deleteTargets();
}

bool Thief::isSpecialTouch(int touchId) {
	if(this->getTouchNearUnit()) {
		Touch touch = ENGINE->GetInput()->GetTouch(touchId);
		if(touch.timeDown >= GetFloatGameConstant(GAME_CONSTANT_long_press_length_in_seconds) && glm::distance(touch.pos, this->touchStartPos) <= GetFloatGameConstant(GAME_CONSTANT_allowed_finger_movement_in_press)) {
			this->targetedCell = nullptr;
			this->SetTouchIndicatorVisible(false);
			this->gridNavRef->HaltMovement();
			this->updateLegalTagets();
			return true;
		}
	}
	return false;
}

void Thief::onSpecialTouch(int touchId) {
	Touch touch = ENGINE->GetInput()->GetTouch(touchId);
	if(touch.phase == TouchPhase::Ended) {
		if(std::find(this->legalTargets.begin(), this->legalTargets.end(), this->targetedCell) != this->legalTargets.end()) {
			this->startSpecial();
		} else {
			this->targetedCell = this->gridNavRef->GetCurrentCell();
			this->cancelSpecial();
		}
		this->tweenOutTargets();
	} else if(touch.phase == TouchPhase::Moved) {
		this->aimSpecial(touchId);
	}
}

void Thief::startSpecial() {
	PlayerUnit::startSpecial();
	this->gridNavRef->SetGridPosition(nullptr);

	// Remove the indicator at the selected position
	GameObject* selectedTargetIndicator = this->targetIndicatorsRef[this->targetedCell];
	this->targetIndicatorsRef.erase(this->targetedCell);
	delete selectedTargetIndicator;

	this->SetTouchIndicatorCell(this->targetedCell);
	this->startTouchIndicatorPulse();
	this->SetTouchIndicatorSprite(THIEF_SPECIAL);
	this->SetTouchIndicatorVisible(true);

	float jumpDist = glm::distance(this->targetedCell->center, this->gameObjectRef->GetTransform()->GetPosition());
	float jumpTweenTime = jumpDist / GetFloatGameConstant(GAME_CONSTANT_jump_speed_in_units_per_second);
	ENGINE->GetTween()->TweenPosition(this->gameObjectRef->GetId(),
									  this->gameObjectRef->GetTransform()->GetPosition(),
									  this->targetedCell->center,
									  jumpTweenTime,
									  false,
									  TWEEN_TRANSLATION_CURVE_TYPE_PARABOLA,
									  false,
									  thiefTweenCallback);

}

void Thief::onSpecialEnd() {
	PlayerUnit::onSpecialEnd();
	this->gridNavRef->SetGridPosition(this->targetedCell);

	// Broadcast an attack message
	MessageChunk attackMessage = ENGINE->GetMessageHub()->GetOneFreeMessage();
	attackMessage->SetMessageType(MESSAGE_TYPE_UNIT_SPECIAL_HIT);
	attackMessage->messageData.iv1.x = this->targetedCell->x;
	attackMessage->messageData.iv1.y = this->targetedCell->y;
	attackMessage->messageData.iv1.z = this->targetedCell->z;
	attackMessage->messageData.fv1 = this->specialStartPos;
	ENGINE->GetMessageHub()->SendMulticastMessage(attackMessage, this->GetObject()->GetId());
}

void Thief::cancelSpecial() {
	PlayerUnit::cancelSpecial();
	this->tweenOutTargets();

}
void Thief::aimSpecial(int touchId) {
	GridCell* prevTargetCell = this->targetedCell;
	this->targetedCell = this->GetCurrentTouchedCell();

	if(this->targetIndicatorsRef[prevTargetCell] && this->targetIndicatorsRef[this->targetedCell]) {
		if(this->targetedCell != prevTargetCell) {
			this->scaleUpIndicator(this->targetedCell);
			this->scaleDownIndicator(prevTargetCell);
		}
	} else if(this->targetIndicatorsRef[prevTargetCell]) {
		GridCell* nearCell = this->getNearCellTowardsUnit(touchId);
		if(nearCell != nullptr) {
			if(this->targetIndicatorsRef[nearCell]) {
				if(nearCell != prevTargetCell) {
					this->scaleUpIndicator(nearCell);
					this->scaleDownIndicator(prevTargetCell);
				}
			} else {
				this->scaleDownIndicator(prevTargetCell);
			}
			this->targetedCell = nearCell;
		} else {
			this->scaleDownIndicator(prevTargetCell);
		}
	} else if(this->targetIndicatorsRef[this->targetedCell]) {
		this->scaleUpIndicator(this->targetedCell);
	} else {
		GridCell* nearCell = this->getNearCellTowardsUnit(touchId);
		if(nearCell != nullptr) {
			if(this->targetIndicatorsRef[nearCell]) {
				this->scaleUpIndicator(nearCell);
			}
			this->targetedCell = nearCell;
		}
	}

	// Looka the target
	this->gridNavRef->SetLookTarget(this->targetedCell->center);
}

void Thief::updateLegalTagets() {
	this->legalTargets.clear();
	this->deleteTargets();
	GridCell* currentCell = this->gridNavRef->GetCurrentCell();
	int elevation = SINGLETONS->GetGridManager()->GetGrid()->GetElevationFromWorldY(currentCell->center.y);

	std::list<GridCell*> cellsInRange;
	SINGLETONS->GetGridManager()->GetGrid()->GetNeighborCells(cellsInRange, this->gridNavRef->GetCurrentCell(),
															  GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units)
															  + elevation * GetFloatGameConstant(GAME_CONSTANT_jump_elevation_multiplier));

	for ( GridCell* c : cellsInRange) {
		int cellElevation = 0;
		for(int i = 0; i < NUM_ELEVATIONS; ++i) {
			if(SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, i)) {
				cellElevation = i;
				break;
			}

		}

		// The thief can jump to a maximum of one elevation level above her
		if (cellElevation <= (elevation + 1)) {
			if ((cellElevation > elevation) || (SINGLETONS->GetGridManager()->GetGrid()->HasLineOfSight(currentCell, c, elevation))) {
				if (SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, cellElevation)) {
					int elevationDiff = elevation - cellElevation;
					if (elevationDiff < 0) {
						elevationDiff = 0;
					}

					float maxRange = fmax(GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units) + elevationDiff * GetFloatGameConstant(GAME_CONSTANT_jump_elevation_multiplier), 1.0f);
					float dist = SINGLETONS->GetGridManager()->GetGrid()->GetGroundDistanceBetweenCells(currentCell, c);
					if (dist <= maxRange) {
						this->legalTargets.push_back(c);
					}
				}
			}
		}
		/*
		if(cellElevation == elevation) { // is the cell on the same height
			if(this->gridNavRef->CanReachDestination(c, GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units)) && SINGLETONS->GetGridManager()->GetGrid()->HasLineOfSight(currentCell, c, elevation) ) {
				this->legalTargets.push_back(c);
			}
		}
		else if(cellElevation <= elevation + 1) { // is the cell below it
			if(SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, cellElevation)) {
				float dist = glm::distance(glm::vec2(c->x, c->z), glm::vec2(currentCell->x, currentCell->z));
				if(dist <= GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units) + cellElevation * GetFloatGameConstant(GAME_CONSTANT_jump_elevation_multiplier)) {
					this->legalTargets.push_back(c);
				}
			}
		}
		*/
	}
	this->createTargets();
	this->tweenInTargets();
}

void Thief::tweenInTargets() {
	glm::vec3 pos;
	for(auto contents : this->targetIndicatorsRef ) {
		if(contents.second != nullptr) {
			pos = contents.second->GetTransform()->GetPosition();
			ENGINE->GetTween()->TweenPosition(contents.second->GetId(), pos, pos + glm::vec3(0.0f, GetFloatGameConstant(GAME_CONSTANT_target_indicator_offset), 0.0f), GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
		}
	}
}

void Thief::tweenOutTargets() {
	glm::vec3 pos;
	for(auto contents : this->targetIndicatorsRef ) {
		if(contents.second != nullptr) {
			pos = contents.second->GetTransform()->GetPosition();
			ENGINE->GetTween()->TweenScale(contents.second->GetId(), glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)), glm::vec3(0), GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
		}
	}
}

void Thief::createTargets() {
	for( GridCell* c : this->legalTargets ) {
		GameObject* indicator = new GameObject(ENGINE->GetSceneGraph3D());
		SpriteRenderer* spriteRenderer = indicator->AddComponent<SpriteRenderer>();
		std::vector<std::string> pathsToTextures;
		pathsToTextures.push_back(FRAMEWORK->GetFileSystemUtils()->GetDevicePictureResourcesFolderName() + "SD_UIEffect_Thief_Jump_cyan.png");
		spriteRenderer->initPlane(1.0f, 1.0f, "sptshdr", pathsToTextures, PlaneOrigin::Center);
		indicator->GetTransform()->SetPosition(c->center);
		indicator->GetTransform()->SetScale( glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)));
		indicator->GetTransform()->Rotate(90.0f inRadians, XAXIS);
		this->targetIndicatorsRef[c] = indicator;
	}
}

void Thief::deleteTargets() {
	this->targetIndicatorsRef.clear();
}

void Thief::scaleUpIndicator(GridCell* cell) {
	if(cell != nullptr && this->targetIndicatorsRef[cell] != nullptr) {
		ENGINE->GetTween()->TweenScale(this->targetIndicatorsRef[cell]->GetId(),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale_hover)),
									   GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
	}
}

void Thief::scaleDownIndicator(GridCell* cell) {
	if(cell != nullptr && this->targetIndicatorsRef[cell] != nullptr) {
		ENGINE->GetTween()->TweenScale(this->targetIndicatorsRef[cell]->GetId(),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale_hover)),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)),
									   GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
	}
}

GridCell* Thief::getNearCellTowardsUnit(int touchId) {
	glm::vec3 unitPos = this->gridNavRef->GetCurrentCell()->center;
	glm::vec3 touchPos;
	if(this->GetCurrentTouchedCell()) {
		touchPos = SINGLETONS->GetGridManager()->TouchPositionToGridPositionAtElevation(ENGINE->GetInput()->GetTouch(touchId).pos, this->GetCurrentTouchedCell()->y);
	}  else {
		touchPos = SINGLETONS->GetGridManager()->TouchPositionToGridPositionAtElevation(ENGINE->GetInput()->GetTouch(touchId).pos, this->gridNavRef->GetCurrentCell()->y);
	}
	glm::vec3 dirTowardsThief = glm::normalize(unitPos - touchPos);
	dirTowardsThief *= .55f;
	glm::vec3 cellLocation = touchPos + dirTowardsThief;
	return SINGLETONS->GetGridManager()->GetGrid()->GetCell(cellLocation);
}
<|endoftext|>


<commit_before>/*
 * Part of Appstream, a library for accessing AppStream on-disk database
 * Copyright (C) 2014  Sune Vuorela <sune@vuorela.dk>
 * Copyright (C) 2015  Matthias Klumpp <matthias@tenstral.net>
 *
 * Based upon database-read.hpp
 * Copyright (C) 2012-2014 Matthias Klumpp <matthias@tenstral.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#define QT_NO_KEYWORDS
#include "database.h"

#include <appstream.h>
#include <QStringList>
#include <QUrl>
#include <QMultiHash>
#include <QLoggingCategory>
#include "as-component-private.h"

#include "image.h"
#include "screenshot.h"
#include "release.h"

Q_LOGGING_CATEGORY(APPSTREAMQT_DB, "appstreamqt.database")

using namespace Appstream;

class Appstream::DatabasePrivate {
    public:
        DatabasePrivate(const QString& cachePath)
            : m_cachePath(cachePath)
            , m_errorString()
            , m_dpool(nullptr)
        {
        }

        QString m_cachePath;
        QString m_errorString;
        AsPool *m_dpool;

        bool open() {
            g_autoptr(GError) error = NULL;

            m_dpool = as_pool_new ();

            if (m_cachePath.isEmpty())
                as_pool_load (m_dpool, NULL, &error);
            else
                as_pool_load_cache_file (m_dpool, qPrintable(m_cachePath), &error);
            if (error != NULL) {
                m_errorString = QString::fromUtf8 (error->message);
                return false;
            }

            return true;
        }

        ~DatabasePrivate() {
            g_object_unref (m_dpool);
        }
};

Database::Database(const QString& cachePath) : d(new DatabasePrivate(cachePath)) {

}

bool Database::open() {
    return d->open();
}

QString Database::errorString() const {
    return d->m_errorString;
}

Database::~Database() {
    // empty. needed for the scoped pointer for the private pointer
}

QString value(const gchar *cstr) {
    return QString::fromUtf8(cstr);
}

QStringList value(gchar **strv) {
    QStringList res;
    if (strv == NULL)
        return res;
    for (uint i = 0; strv[i] != NULL; i++) {
        res.append (QString::fromUtf8(strv[i]));
    }
    return res;
}

QStringList value(GPtrArray *array) {
    QStringList res;
    res.reserve(array->len);
    for (uint i = 0; i < array->len; i++) {
        auto strval = (const gchar*) g_ptr_array_index (array, i);
	res.append (QString::fromUtf8(strval));
    }
    return res;
}

Component convertAsComponent(AsComponent *cpt) {
    Component component;

    // kind
    QString kindString = value (as_component_kind_to_string (as_component_get_kind (cpt)));
    component.setKind(Component::stringToKind(kindString));

    // Identifier
    QString id = value(as_component_get_id (cpt));
    component.setId(id);

    // Component name
    QString name = value(as_component_get_name (cpt));
    component.setName(name);

    // Summary
    QString summary = value(as_component_get_summary (cpt));
    component.setSummary(summary);

    // Package name
    QStringList packageNames = value(as_component_get_pkgnames (cpt));
    component.setPackageNames(packageNames);

    // Bundles
    auto bundles_array = as_component_get_bundles (cpt);
    if (bundles_array->len > 0) {
        QHash<Component::BundleKind, QString> bundles;

        for (uint i = 0; i < bundles_array->len; i++) {
            auto bundle = AS_BUNDLE (g_ptr_array_index (bundles_array, i));

            auto bkind = (Component::BundleKind) as_bundle_get_kind (bundle);
            auto bval = QString::fromUtf8(as_bundle_get_id (bundle));
            bundles.insertMulti(bkind, bval);
        }
        component.setBundles(bundles);
    }

    // URLs
    auto urls_table = as_component_get_urls_table (cpt);
    if (g_hash_table_size (urls_table) > 0) {
        GHashTableIter iter;
        gpointer key, cstrUrl;

        QMultiHash<Component::UrlKind, QUrl> urls;
        g_hash_table_iter_init (&iter, urls_table);
        while (g_hash_table_iter_next (&iter, &key, &cstrUrl)) {
            auto ukind = (Component::UrlKind) GPOINTER_TO_INT (key);
            auto url = QUrl::fromUserInput(QString::fromUtf8((const gchar*) cstrUrl));

            if (ukind != Component::UrlKindUnknown) {
                urls.insertMulti(ukind, url);
            } else {
                qCWarning(APPSTREAMQT_DB, "URL of unknown type found for '%s': %s", qPrintable(id), qPrintable(url.toString()));
            }
        }
        component.setUrls(urls);
    }

    // Provided items
    QList<Provides> provideslist;
    for (uint i = 0; i < AS_PROVIDED_KIND_LAST; i++) {
        AsProvidedKind kind = (AsProvidedKind) i;

        AsProvided *prov = as_component_get_provided_for_kind (cpt, kind);
        if (prov == NULL)
            continue;

        auto pkind = (Provides::Kind) kind;
        auto items = as_provided_get_items (prov);
	for (uint j = 0; j < items->len; j++) {
            const gchar *value = (const gchar*) g_ptr_array_index (items, j);
            Provides provides;
            provides.setKind(pkind);
            provides.setValue(QString::fromUtf8(value));
            provideslist << provides;
        }
    }
    component.setProvides(provideslist);

    // Icons
    auto icons = as_component_get_icons (cpt);
    for (uint i = 0; i < icons->len; i++) {
        auto icon = AS_ICON (g_ptr_array_index (icons, i));
        if (as_icon_get_kind (icon) == AS_ICON_KIND_STOCK) {
            component.setIcon(QString::fromUtf8(as_icon_get_name (icon)));
        } else {
            // TODO: Support more icon types properly
            auto size = QSize(as_icon_get_width (icon),
                              as_icon_get_height (icon));
            QUrl url = QUrl::fromUserInput(QString::fromUtf8(as_icon_get_filename (icon)));
            component.addIconUrl(url, size);
        }
    }

    // Long description
    QString description = value(as_component_get_description (cpt));
    component.setDescription(description);

    // Categories
    QStringList categories = value(as_component_get_categories (cpt));
    component.setCategories(categories);

    // Extends
    const QStringList extends = value(as_component_get_extends (cpt));
    component.setExtends(extends);

    // Extensions
    auto addons_array = as_component_get_addons (cpt);
    QStringList addons;
    addons.reserve(addons_array->len);
    for (uint i = 0; i < addons_array->len; i++) {
        auto addon = AS_COMPONENT (g_ptr_array_index (addons_array, i));
	addons.append (QString::fromUtf8(as_component_get_id (addon)));
    }
    component.setExtensions(addons);

    // Screenshots
    QList<Appstream::Screenshot> screenshots;
    auto scrs = as_component_get_screenshots (cpt);
    for (uint i = 0; i < scrs->len; i++) {
        auto cscr = AS_SCREENSHOT (g_ptr_array_index (scrs, i));
	Appstream::Screenshot scr;

        if (as_screenshot_get_kind (cscr) == AS_SCREENSHOT_KIND_DEFAULT)
            scr.setDefault(true);

        if (as_screenshot_get_caption (cscr) != NULL)
            scr.setCaption(QString::fromUtf8(as_screenshot_get_caption (cscr)));

        QList<Appstream::Image> images;
        auto images_array = as_screenshot_get_images (cscr);
        for (uint j = 0; j < images_array->len; j++) {
            auto cimg = AS_IMAGE (g_ptr_array_index (images_array, j));

            Appstream::Image image;

            image.setUrl(QUrl(QString::fromUtf8(as_image_get_url (cimg))));
            image.setWidth(as_image_get_width (cimg));
            image.setHeight(as_image_get_height (cimg));

            if (as_image_get_kind (cimg) == AS_IMAGE_KIND_THUMBNAIL)
                image.setKind(Appstream::Image::Kind::Thumbnail);
            else
                image.setKind(Appstream::Image::Kind::Plain);
            images.append(image);
        }
        scr.setImages(images);

        screenshots.append(scr);
    }
    component.setScreenshots(screenshots);

    // Compulsory-for-desktop information
    QStringList compulsory = value(as_component_get_compulsory_for_desktops (cpt));
    component.setCompulsoryForDesktops(compulsory);

    // License
    QString license = value(as_component_get_project_license (cpt));
    component.setProjectLicense(license);

    // Project group
    QString projectGroup = value(as_component_get_project_group (cpt));
    component.setProjectGroup(projectGroup);

    // Developer name
    QString developerName = value(as_component_get_developer_name (cpt));
    component.setDeveloperName(developerName);

    // Releases
    QList<Release> releases;
    auto releaseArray = as_component_get_releases(cpt);
    for (uint i = 0; i < releaseArray->len; i++) {
        auto release = AS_RELEASE (g_ptr_array_index (releaseArray, i));
        releases += Release(release);
    }
    component.setReleases(releases);

    return component;
}

QList< Component > Database::allComponents() const
{
    g_autoptr(GPtrArray) array = NULL;
    QList<Component> components;

    // get a pointer array of all components we have
    array = as_pool_get_components (d->m_dpool);
    components.reserve(array->len);

    // create QList of AppStream::Component out of the AsComponents
    for (uint i = 0; i < array->len; i++) {
        auto cpt = AS_COMPONENT (g_ptr_array_index (array, i));
        components << convertAsComponent(cpt);
    }

    return components;
}

Component Database::componentById(const QString& id) const
{
    g_autoptr(GPtrArray) result = NULL;

    result = as_pool_get_components_by_id (d->m_dpool, qPrintable(id));
    if (result->len == 0)
        return Component();

    /* TODO: Return all metadata sets describing the component with this ID. Requires API break. */

    return convertAsComponent(AS_COMPONENT (g_ptr_array_index (result, 0)));
}

QList< Component > Database::componentsByKind(Component::Kind kind) const
{
    g_autoptr(GPtrArray) array = NULL;
    g_autoptr(GError) error = NULL;
    QList<Component> result;
    AsComponentKind ckind;

    ckind = (AsComponentKind) kind;

    if ((ckind > AS_COMPONENT_KIND_LAST) || (ckind > AS_COMPONENT_KIND_UNKNOWN)) {
        qCCritical(APPSTREAMQT_DB, "Unable to get components by kind: Component kind is invalid.");
        return result;
    }

    array = as_pool_get_components_by_kind (d->m_dpool, ckind);
    result.reserve(array->len);
    for (uint i = 0; i < array->len; i++) {
        auto cpt = AS_COMPONENT (g_ptr_array_index (array, i));
        result << convertAsComponent(cpt);
    }

    return result;
}

QList< Component > Database::findComponentsByString(const QString& searchTerm, const QStringList& categories)
{
    Q_UNUSED(categories); // FIXME

    g_autoptr(GPtrArray) array = NULL;
    array = as_pool_search (d->m_dpool, qPrintable(searchTerm));
    QList<Component> result;
    result.reserve(array->len);

    for (uint i = 0; i < array->len; i++) {
        auto cpt = AS_COMPONENT (g_ptr_array_index (array, i));
        result << convertAsComponent(cpt);
    }

    return result;
}

QList<Component> Database::findComponentsByPackageName(const QString& packageName) const
{
    const gchar *pkgname = qPrintable(packageName);
    QList<Component> result;

    g_autoptr(GPtrArray) cpts = as_pool_get_components (d->m_dpool);
    for (uint i = 0; i < cpts->len; i++) {
        auto cpt = AS_COMPONENT (g_ptr_array_index (cpts, i));
	auto pkgnames = as_component_get_pkgnames (cpt);
	if (pkgnames == NULL)
            continue;

        for (uint j = 0; pkgnames[j] != NULL; j++) {
            if (g_strcmp0 (pkgnames[j], pkgname) == 0)
                result << convertAsComponent(cpt);
	}
    }

    return result;
}

Database::Database() : d(new DatabasePrivate(QString())) {

}
<commit_msg>qt: nullptr access on unopened databases resulting in a crash<commit_after>/*
 * Part of Appstream, a library for accessing AppStream on-disk database
 * Copyright (C) 2014  Sune Vuorela <sune@vuorela.dk>
 * Copyright (C) 2015  Matthias Klumpp <matthias@tenstral.net>
 *
 * Based upon database-read.hpp
 * Copyright (C) 2012-2014 Matthias Klumpp <matthias@tenstral.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#define QT_NO_KEYWORDS
#include "database.h"

#include <appstream.h>
#include <QStringList>
#include <QUrl>
#include <QMultiHash>
#include <QLoggingCategory>
#include "as-component-private.h"

#include "image.h"
#include "screenshot.h"
#include "release.h"

Q_LOGGING_CATEGORY(APPSTREAMQT_DB, "appstreamqt.database")

using namespace Appstream;

class Appstream::DatabasePrivate {
    public:
        DatabasePrivate(const QString& cachePath)
            : m_cachePath(cachePath)
            , m_errorString()
            , m_dpool(nullptr)
        {
        }

        QString m_cachePath;
        QString m_errorString;
        AsPool *m_dpool;

        bool open() {
            g_autoptr(GError) error = NULL;

            m_dpool = as_pool_new ();

            if (m_cachePath.isEmpty())
                as_pool_load (m_dpool, NULL, &error);
            else
                as_pool_load_cache_file (m_dpool, qPrintable(m_cachePath), &error);
            if (error != NULL) {
                m_errorString = QString::fromUtf8 (error->message);
                return false;
            }

            return true;
        }

        ~DatabasePrivate() {
            if (m_dpool)
                g_object_unref (m_dpool);
        }
};

Database::Database(const QString& cachePath) : d(new DatabasePrivate(cachePath)) {

}

bool Database::open() {
    return d->open();
}

QString Database::errorString() const {
    return d->m_errorString;
}

Database::~Database() {
    // empty. needed for the scoped pointer for the private pointer
}

QString value(const gchar *cstr) {
    return QString::fromUtf8(cstr);
}

QStringList value(gchar **strv) {
    QStringList res;
    if (strv == NULL)
        return res;
    for (uint i = 0; strv[i] != NULL; i++) {
        res.append (QString::fromUtf8(strv[i]));
    }
    return res;
}

QStringList value(GPtrArray *array) {
    QStringList res;
    res.reserve(array->len);
    for (uint i = 0; i < array->len; i++) {
        auto strval = (const gchar*) g_ptr_array_index (array, i);
	res.append (QString::fromUtf8(strval));
    }
    return res;
}

Component convertAsComponent(AsComponent *cpt) {
    Component component;

    // kind
    QString kindString = value (as_component_kind_to_string (as_component_get_kind (cpt)));
    component.setKind(Component::stringToKind(kindString));

    // Identifier
    QString id = value(as_component_get_id (cpt));
    component.setId(id);

    // Component name
    QString name = value(as_component_get_name (cpt));
    component.setName(name);

    // Summary
    QString summary = value(as_component_get_summary (cpt));
    component.setSummary(summary);

    // Package name
    QStringList packageNames = value(as_component_get_pkgnames (cpt));
    component.setPackageNames(packageNames);

    // Bundles
    auto bundles_array = as_component_get_bundles (cpt);
    if (bundles_array->len > 0) {
        QHash<Component::BundleKind, QString> bundles;

        for (uint i = 0; i < bundles_array->len; i++) {
            auto bundle = AS_BUNDLE (g_ptr_array_index (bundles_array, i));

            auto bkind = (Component::BundleKind) as_bundle_get_kind (bundle);
            auto bval = QString::fromUtf8(as_bundle_get_id (bundle));
            bundles.insertMulti(bkind, bval);
        }
        component.setBundles(bundles);
    }

    // URLs
    auto urls_table = as_component_get_urls_table (cpt);
    if (g_hash_table_size (urls_table) > 0) {
        GHashTableIter iter;
        gpointer key, cstrUrl;

        QMultiHash<Component::UrlKind, QUrl> urls;
        g_hash_table_iter_init (&iter, urls_table);
        while (g_hash_table_iter_next (&iter, &key, &cstrUrl)) {
            auto ukind = (Component::UrlKind) GPOINTER_TO_INT (key);
            auto url = QUrl::fromUserInput(QString::fromUtf8((const gchar*) cstrUrl));

            if (ukind != Component::UrlKindUnknown) {
                urls.insertMulti(ukind, url);
            } else {
                qCWarning(APPSTREAMQT_DB, "URL of unknown type found for '%s': %s", qPrintable(id), qPrintable(url.toString()));
            }
        }
        component.setUrls(urls);
    }

    // Provided items
    QList<Provides> provideslist;
    for (uint i = 0; i < AS_PROVIDED_KIND_LAST; i++) {
        AsProvidedKind kind = (AsProvidedKind) i;

        AsProvided *prov = as_component_get_provided_for_kind (cpt, kind);
        if (prov == NULL)
            continue;

        auto pkind = (Provides::Kind) kind;
        auto items = as_provided_get_items (prov);
	for (uint j = 0; j < items->len; j++) {
            const gchar *value = (const gchar*) g_ptr_array_index (items, j);
            Provides provides;
            provides.setKind(pkind);
            provides.setValue(QString::fromUtf8(value));
            provideslist << provides;
        }
    }
    component.setProvides(provideslist);

    // Icons
    auto icons = as_component_get_icons (cpt);
    for (uint i = 0; i < icons->len; i++) {
        auto icon = AS_ICON (g_ptr_array_index (icons, i));
        if (as_icon_get_kind (icon) == AS_ICON_KIND_STOCK) {
            component.setIcon(QString::fromUtf8(as_icon_get_name (icon)));
        } else {
            // TODO: Support more icon types properly
            auto size = QSize(as_icon_get_width (icon),
                              as_icon_get_height (icon));
            QUrl url = QUrl::fromUserInput(QString::fromUtf8(as_icon_get_filename (icon)));
            component.addIconUrl(url, size);
        }
    }

    // Long description
    QString description = value(as_component_get_description (cpt));
    component.setDescription(description);

    // Categories
    QStringList categories = value(as_component_get_categories (cpt));
    component.setCategories(categories);

    // Extends
    const QStringList extends = value(as_component_get_extends (cpt));
    component.setExtends(extends);

    // Extensions
    auto addons_array = as_component_get_addons (cpt);
    QStringList addons;
    addons.reserve(addons_array->len);
    for (uint i = 0; i < addons_array->len; i++) {
        auto addon = AS_COMPONENT (g_ptr_array_index (addons_array, i));
	addons.append (QString::fromUtf8(as_component_get_id (addon)));
    }
    component.setExtensions(addons);

    // Screenshots
    QList<Appstream::Screenshot> screenshots;
    auto scrs = as_component_get_screenshots (cpt);
    for (uint i = 0; i < scrs->len; i++) {
        auto cscr = AS_SCREENSHOT (g_ptr_array_index (scrs, i));
	Appstream::Screenshot scr;

        if (as_screenshot_get_kind (cscr) == AS_SCREENSHOT_KIND_DEFAULT)
            scr.setDefault(true);

        if (as_screenshot_get_caption (cscr) != NULL)
            scr.setCaption(QString::fromUtf8(as_screenshot_get_caption (cscr)));

        QList<Appstream::Image> images;
        auto images_array = as_screenshot_get_images (cscr);
        for (uint j = 0; j < images_array->len; j++) {
            auto cimg = AS_IMAGE (g_ptr_array_index (images_array, j));

            Appstream::Image image;

            image.setUrl(QUrl(QString::fromUtf8(as_image_get_url (cimg))));
            image.setWidth(as_image_get_width (cimg));
            image.setHeight(as_image_get_height (cimg));

            if (as_image_get_kind (cimg) == AS_IMAGE_KIND_THUMBNAIL)
                image.setKind(Appstream::Image::Kind::Thumbnail);
            else
                image.setKind(Appstream::Image::Kind::Plain);
            images.append(image);
        }
        scr.setImages(images);

        screenshots.append(scr);
    }
    component.setScreenshots(screenshots);

    // Compulsory-for-desktop information
    QStringList compulsory = value(as_component_get_compulsory_for_desktops (cpt));
    component.setCompulsoryForDesktops(compulsory);

    // License
    QString license = value(as_component_get_project_license (cpt));
    component.setProjectLicense(license);

    // Project group
    QString projectGroup = value(as_component_get_project_group (cpt));
    component.setProjectGroup(projectGroup);

    // Developer name
    QString developerName = value(as_component_get_developer_name (cpt));
    component.setDeveloperName(developerName);

    // Releases
    QList<Release> releases;
    auto releaseArray = as_component_get_releases(cpt);
    for (uint i = 0; i < releaseArray->len; i++) {
        auto release = AS_RELEASE (g_ptr_array_index (releaseArray, i));
        releases += Release(release);
    }
    component.setReleases(releases);

    return component;
}

QList< Component > Database::allComponents() const
{
    g_autoptr(GPtrArray) array = NULL;
    QList<Component> components;

    // get a pointer array of all components we have
    array = as_pool_get_components (d->m_dpool);
    components.reserve(array->len);

    // create QList of AppStream::Component out of the AsComponents
    for (uint i = 0; i < array->len; i++) {
        auto cpt = AS_COMPONENT (g_ptr_array_index (array, i));
        components << convertAsComponent(cpt);
    }

    return components;
}

Component Database::componentById(const QString& id) const
{
    g_autoptr(GPtrArray) result = NULL;

    result = as_pool_get_components_by_id (d->m_dpool, qPrintable(id));
    if (result->len == 0)
        return Component();

    /* TODO: Return all metadata sets describing the component with this ID. Requires API break. */

    return convertAsComponent(AS_COMPONENT (g_ptr_array_index (result, 0)));
}

QList< Component > Database::componentsByKind(Component::Kind kind) const
{
    g_autoptr(GPtrArray) array = NULL;
    g_autoptr(GError) error = NULL;
    QList<Component> result;
    AsComponentKind ckind;

    ckind = (AsComponentKind) kind;

    if ((ckind > AS_COMPONENT_KIND_LAST) || (ckind > AS_COMPONENT_KIND_UNKNOWN)) {
        qCCritical(APPSTREAMQT_DB, "Unable to get components by kind: Component kind is invalid.");
        return result;
    }

    array = as_pool_get_components_by_kind (d->m_dpool, ckind);
    result.reserve(array->len);
    for (uint i = 0; i < array->len; i++) {
        auto cpt = AS_COMPONENT (g_ptr_array_index (array, i));
        result << convertAsComponent(cpt);
    }

    return result;
}

QList< Component > Database::findComponentsByString(const QString& searchTerm, const QStringList& categories)
{
    Q_UNUSED(categories); // FIXME

    g_autoptr(GPtrArray) array = NULL;
    array = as_pool_search (d->m_dpool, qPrintable(searchTerm));
    QList<Component> result;
    result.reserve(array->len);

    for (uint i = 0; i < array->len; i++) {
        auto cpt = AS_COMPONENT (g_ptr_array_index (array, i));
        result << convertAsComponent(cpt);
    }

    return result;
}

QList<Component> Database::findComponentsByPackageName(const QString& packageName) const
{
    const gchar *pkgname = qPrintable(packageName);
    QList<Component> result;

    g_autoptr(GPtrArray) cpts = as_pool_get_components (d->m_dpool);
    for (uint i = 0; i < cpts->len; i++) {
        auto cpt = AS_COMPONENT (g_ptr_array_index (cpts, i));
	auto pkgnames = as_component_get_pkgnames (cpt);
	if (pkgnames == NULL)
            continue;

        for (uint j = 0; pkgnames[j] != NULL; j++) {
            if (g_strcmp0 (pkgnames[j], pkgname) == 0)
                result << convertAsComponent(cpt);
	}
    }

    return result;
}

Database::Database() : d(new DatabasePrivate(QString())) {

}
<|endoftext|>