<commit_before>//needed for testing
#define CATCH_CONFIG_RUNNER
#include <catch.hpp>

//other stuff needed
#include <glm/vec3.hpp>
#include <cmath>
#include <iostream>

//include classes for testing
#include "shape.hpp"
#include "sphere.hpp"
#include "box.hpp"
#include "material.hpp"
#include "color.hpp"

// ----------------------------------
// SHAPE TESTS
// ----------------------------------

TEST_CASE("getter shape","[shape]")
{
	Sphere s {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f},1.0f};
	REQUIRE(s.get_name() == "name");

	Material c{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f};
	REQUIRE(s.get_material() == c); //added operator == in color.hpp
}

TEST_CASE("operator<< and print shape","[shape]")
{
	Sphere es {"empty_sphere"};
	std::cout << es;

	Sphere s {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f},1.0f};
	std::cout << s;
}

// ----------------------------------
// SPHERE TESTS
// ----------------------------------

TEST_CASE("constructors of sphere","[sphere]")
{
	Sphere s1 {"name"};
	Sphere s2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};

	REQUIRE(s1.get_center() == s2.get_center());
	REQUIRE(s1.get_radius() == s2.get_radius());
}

TEST_CASE("get_center and get_radius","[sphere]")
{
	Sphere s {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};

	REQUIRE(s.get_radius() == 1.0f);
	REQUIRE(s.get_center() == glm::vec3{0.0f});
}

TEST_CASE("area","[sphere]")
{
	Sphere s {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};
	REQUIRE(12.566f == Approx(s.area()).epsilon(0.001));
}

TEST_CASE("volume","[sphere]")
{
	Sphere s {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};
	REQUIRE(4.189 == Approx(s.volume()).epsilon(0.001));
}

TEST_CASE("print sphere","[sphere]")
{
	Sphere es {"empty_sphere"};
	std::cout << es;

	Sphere s {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f},1.0f};
	std::cout << s;
}

// ----------------------------------
// BOX TESTS
// ----------------------------------

TEST_CASE("constructors of box","[box]")
{
	Box b1 {"name"};
	Box b2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, glm::vec3{1.0f}};

	REQUIRE(b1.get_min() == b2.get_min());
	REQUIRE(b1.get_max() == b2.get_max());
}

TEST_CASE("get_min and get_max","[box]")
{
	Box b2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, glm::vec3{1.0f}};

	REQUIRE(b2.get_min() == glm::vec3{0.0f});
	REQUIRE(b2.get_max() == glm::vec3{1.0f});
}

TEST_CASE("area of box","[box]")
{
	Box b2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, glm::vec3{1.0f}};
	REQUIRE(6.0f == Approx(b2.area()).epsilon(0.001));
}

TEST_CASE("volume of box","[box]")
{
	Box b2 {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f}, glm::vec3{0.0f}, glm::vec3{1.0f}};

	REQUIRE(1.0f == Approx(b2.volume()).epsilon(0.001));
}

TEST_CASE("print box","[box]")
{
	Box eb {"empty_box"};
	std::cout << eb;

	Box b {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f}, glm::vec3{0.0f}, glm::vec3{1.0f}};
	std::cout << b;
}

// ----------------------------------
// AUFGABE 5.6 TEST
// ----------------------------------

TEST_CASE("intersectRaySphere", "[intersect]")
{
	//Ray
	glm::vec3 ray_origin{0.0f, 0.0f, 0.0f};
	//ray direction has to be normalized !
	//you can use:
		//v = glm::normalize(some_vector);
	glm::vec3 ray_direction{0.0f, 0.0f, 1.0f};

	//Sphere
	glm::vec3 sphere_center{0.0f, 0.0f, 5.0f};
	float sphere_radius{1.0f};

	float distance{0.0f};
	auto result = glm::intersectRaySphere(
		ray_origin, ray_direction,
		sphere_center,
		sphere_radius * sphere_radius,
		distance);

	REQUIRE(distance == Approx(4.0f));

	Sphere s {"some_sphere", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},
		sphere_center, sphere_radius};

	Ray r {ray_origin,ray_direction};

	s.intersect(r,distance);

	REQUIRE(distance == Approx(4.0f));
}

// ----------------------------------
// AUFGABE 5.8 TEST
// ----------------------------------

TEST_CASE("virtual", "[Destructors]")
{

	std::cout << "\ntesting virtual and non-virtual construction and destruction\n";

	Color c_red {255.0f, 0.0f, 0.0f};
	Material red {"", c_red, c_red, c_red, 0.0f};

	glm::vec3 position {0.0f,0.0f,0.0f};

	std::cout << "Create s1\n";
	Sphere* s1 = new Sphere("sphere0",red,position,1.2f);
	std::cout << "Create s2\n";
	Shape* s2 = new Sphere("sphere1",red,position,1.2f);

	std::cout << "Printing objects: \n";
	std::cout << *s1;
	std::cout << *s2 << "\n";

	std::cout << "Delete s1\n";
	delete s1;
	std::cout << "Delete s2\n";
	delete s2;
}

// ----------------------------------
// MATERIAL Tests
// ----------------------------------

TEST_CASE("material in place of color struct", "Material")
{
	Color c{0.0f,0.0f,0.0f};
	Material mate1 {};
	std::cout << mate1;

	Material mate2 {"some material",c,c,c,0.0f};
	std::cout << mate2;
}

int main(int argc, char *argv[])
{
  return Catch::Session().run(argc, argv);
}
<commit_msg>:heart:<commit_after>//needed for testing
#define CATCH_CONFIG_RUNNER
#include <catch.hpp>

//other stuff needed
#include <glm/vec3.hpp>
#include <cmath>
#include <iostream>

//include classes for testing
#include "shape.hpp"
#include "sphere.hpp"
#include "box.hpp"
#include "material.hpp"
#include "color.hpp"

// ----------------------------------
// SHAPE TESTS
// ----------------------------------
//hallo ich heiße lissy tschüß
TEST_CASE("getter shape","[shape]")
{
	Sphere s {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f},1.0f};
	REQUIRE(s.get_name() == "name");

	Material c{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f};
	REQUIRE(s.get_material() == c); //added operator == in color.hpp
}

TEST_CASE("operator<< and print shape","[shape]")
{
	Sphere es {"empty_sphere"};
	std::cout << es;

	Sphere s {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f},1.0f};
	std::cout << s;
}

// ----------------------------------
// SPHERE TESTS
// ----------------------------------

TEST_CASE("constructors of sphere","[sphere]")
{
	Sphere s1 {"name"};
	Sphere s2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};

	REQUIRE(s1.get_center() == s2.get_center());
	REQUIRE(s1.get_radius() == s2.get_radius());
}

TEST_CASE("get_center and get_radius","[sphere]")
{
	Sphere s {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};

	REQUIRE(s.get_radius() == 1.0f);
	REQUIRE(s.get_center() == glm::vec3{0.0f});
}

TEST_CASE("area","[sphere]")
{
	Sphere s {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};
	REQUIRE(12.566f == Approx(s.area()).epsilon(0.001));
}

TEST_CASE("volume","[sphere]")
{
	Sphere s {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};
	REQUIRE(4.189 == Approx(s.volume()).epsilon(0.001));
}

TEST_CASE("print sphere","[sphere]")
{
	Sphere es {"empty_sphere"};
	std::cout << es;

	Sphere s {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f},1.0f};
	std::cout << s;
}

// ----------------------------------
// BOX TESTS
// ----------------------------------

TEST_CASE("constructors of box","[box]")
{
	Box b1 {"name"};
	Box b2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, glm::vec3{1.0f}};

	REQUIRE(b1.get_min() == b2.get_min());
	REQUIRE(b1.get_max() == b2.get_max());
}

TEST_CASE("get_min and get_max","[box]")
{
	Box b2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, glm::vec3{1.0f}};

	REQUIRE(b2.get_min() == glm::vec3{0.0f});
	REQUIRE(b2.get_max() == glm::vec3{1.0f});
}

TEST_CASE("area of box","[box]")
{
	Box b2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, glm::vec3{1.0f}};
	REQUIRE(6.0f == Approx(b2.area()).epsilon(0.001));
}

TEST_CASE("volume of box","[box]")
{
	Box b2 {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f}, glm::vec3{0.0f}, glm::vec3{1.0f}};

	REQUIRE(1.0f == Approx(b2.volume()).epsilon(0.001));
}

TEST_CASE("print box","[box]")
{
	Box eb {"empty_box"};
	std::cout << eb;

	Box b {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f}, glm::vec3{0.0f}, glm::vec3{1.0f}};
	std::cout << b;
}

// ----------------------------------
// AUFGABE 5.6 TEST
// ----------------------------------

TEST_CASE("intersectRaySphere", "[intersect]")
{
	//Ray
	glm::vec3 ray_origin{0.0f, 0.0f, 0.0f};
	//ray direction has to be normalized !
	//you can use:
		//v = glm::normalize(some_vector);
	glm::vec3 ray_direction{0.0f, 0.0f, 1.0f};

	//Sphere
	glm::vec3 sphere_center{0.0f, 0.0f, 5.0f};
	float sphere_radius{1.0f};

	float distance{0.0f};
	auto result = glm::intersectRaySphere(
		ray_origin, ray_direction,
		sphere_center,
		sphere_radius * sphere_radius,
		distance);

	REQUIRE(distance == Approx(4.0f));

	Sphere s {"some_sphere", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},
		sphere_center, sphere_radius};

	Ray r {ray_origin,ray_direction};

	s.intersect(r,distance);

	REQUIRE(distance == Approx(4.0f));
}

// ----------------------------------
// AUFGABE 5.8 TEST
// ----------------------------------

TEST_CASE("virtual", "[Destructors]")
{

	std::cout << "\ntesting virtual and non-virtual construction and destruction\n";

	Color c_red {255.0f, 0.0f, 0.0f};
	Material red {"", c_red, c_red, c_red, 0.0f};

	glm::vec3 position {0.0f,0.0f,0.0f};

	std::cout << "Create s1\n";
	Sphere* s1 = new Sphere("sphere0",red,position,1.2f);
	std::cout << "Create s2\n";
	Shape* s2 = new Sphere("sphere1",red,position,1.2f);

	std::cout << "Printing objects: \n";
	std::cout << *s1;
	std::cout << *s2 << "\n";

	std::cout << "Delete s1\n";
	delete s1;
	std::cout << "Delete s2\n";
	delete s2;
}

// ----------------------------------
// MATERIAL Tests
// ----------------------------------

TEST_CASE("material in place of color struct", "Material")
{
	Color c{0.0f,0.0f,0.0f};
	Material mate1 {};
	std::cout << mate1;

	Material mate2 {"some material",c,c,c,0.0f};
	std::cout << mate2;
}

int main(int argc, char *argv[])
{
  return Catch::Session().run(argc, argv);
}
<|endoftext|>


<commit_before>#include <GLFW/glfw3.h>
#include <iostream>
#include <map>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "gl/Primitives.hpp"
#include "gl/Camera.hpp"
#include "gl/Element.hpp"
#include <GL/OOGL.hpp>

using namespace std;

void windowRefresh(GLFWwindow* window);
void init();
void render();

GLFWwindow *window;

int width = 800, height = 600;
GL::Camera* camera;
glm::mat4 projection;

typedef pair<const char*, Element::object*> object;
map<const char*, Element::object*> objects;

Element::object* cube = NULL;
Element::object* overlay = NULL;
Element::color overlayColor(50, 200, 50, 0.65);

int main() {

  if (!glfwInit())
    exit(EXIT_FAILURE);

  glfwWindowHint(GLFW_SAMPLES, 4);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  window = glfwCreateWindow(width, height, "DukVis", NULL, NULL);
  if (!window) {
    glfwTerminate();
    exit(EXIT_FAILURE);
  }

  glfwSetWindowRefreshCallback(window, windowRefresh);

  glfwMakeContextCurrent(window);

  init();

  while (!glfwWindowShouldClose(window)) {
    glfwMakeContextCurrent(window);

    render();

    glfwSwapBuffers(window);
    glfwPollEvents();
  }

  glfwDestroyWindow(window);

  glfwTerminate();
  exit(EXIT_SUCCESS);
}

void windowRefresh(GLFWwindow* window) {
  glfwGetFramebufferSize(window, &width, &height);
  camera->Resize(width, height);
  projection = glm::ortho(0.0f, (float)width, (float)height, 0.0f, -1.0f, 1.0f);

  if (overlay != NULL && overlay->shader != NULL) {
    glfwMakeContextCurrent(window);
    glUseProgram(*overlay->shader);
    glUniformMatrix4fv(overlay->uniforms.at("mvp"), 1, false, glm::value_ptr(
      glm::scale(glm::mat4(1.0f), glm::vec3((float)width, (float)height, 1.0))
    ));
    glUniformMatrix4fv(overlay->uniforms.at("projection"), 1, false, glm::value_ptr(projection));
  }

  render();
  glfwSwapBuffers(window);
}

void init() {
  camera = new GL::Camera(width, height);
  camera->MoveTo(glm::vec3(5,3,2));

  projection = glm::ortho(0.0f, (float)width, (float)height, 0.0f, -1.0f, 1.0f);

  cube = Element::create("cube", Primitives::cube());
  cube->bindUniform("mvp");
  cube->bindUniform("color");

  float verticesOverlay[] {
    // Pos      // Tex
    0.0f, 1.0f, 0.0f, 1.0f,
    1.0f, 0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 0.0f,

    0.0f, 1.0f, 0.0f, 1.0f,
    1.0f, 1.0f, 1.0f, 1.0f,
    1.0f, 0.0f, 1.0f, 0.0f
  };
  uint count = ARRAY_SIZE(verticesOverlay);
  float* overlayGeom = new float[count];
  std::copy(verticesOverlay, verticesOverlay + count, overlayGeom);
  Primitives::geometry* vertsO = new Primitives::geometry(overlayGeom, count);
  overlay = new Element::object("overlay");
  overlay = Element::create(vertsO, overlay, 4, true, false);
  overlay->vao->BindAttribute(overlay->shader->GetAttribute("vertex"),
    *(overlay->vbos[0]), GL::Type::Float, 4, 4 * sizeof(float), 0);
  overlay->bindUniform("mvp");
  overlay->bindUniform("projection");
  overlay->bindUniform("image");
  overlay->bindUniform("penColor");
  glUniformMatrix4fv(overlay->uniforms.at("mvp"), 1, false, glm::value_ptr(
    glm::scale(glm::mat4(1.0f), glm::vec3((float)width, (float)height, 1.0))
  ));
  glUniformMatrix4fv(overlay->uniforms.at("projection"), 1, false, glm::value_ptr(projection));

  overlay->textures.insert(Textures::namedTexture(
    "overlay", Textures::empty(width, height)
  ));
  Textures::texture* overlayBuff = overlay->textures.at("overlay");
  overlayBuff->penLine(floor(height / 2.0), overlayColor);

  objects.insert(object("cube", cube));
  objects.insert(object("overlay", overlay));
}

double oldTime = 0.0;
double rTime = 0.0;

void render() {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  double time = glfwGetTime() / 4.0;
  rTime += ((time - oldTime) * 255);
  if (rTime > 255) rTime = 0;

  glUseProgram(*cube->shader);

  glm::mat4 model = glm::mat4(1.0f);
  model = glm::rotate(model, glm::radians((float)((rTime / 255) * 360.0)), glm::vec3(0,1,0));
  glUniformMatrix4fv(cube->uniforms.at("mvp"), 1, false, glm::value_ptr(camera->Project(model)));

  Element::color color(1.0f, 0.0f, 0.0f, 1.0f);
  glUniform4fv(cube->uniforms.at("color"), 1, color);

  oldTime = time;

  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LESS);

  Element::draw(cube->vao);

  glDisable(GL_DEPTH_TEST);

  // Render overlay 2D graphics surface

  //TODO: Make this configurable with script? ;)
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glUseProgram(*overlay->shader);

  glActiveTexture(GL_TEXTURE0);
  overlay->textures.at("overlay")->bind(0);

  glUniform4fv(overlay->uniforms.at("penColor"), 1, overlayColor);

  Element::draw(overlay->vao);

  glDisable(GL_BLEND);
}
<commit_msg>Add note to remember to release OpenGL stuff<commit_after>#include <GLFW/glfw3.h>
#include <iostream>
#include <map>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "gl/Primitives.hpp"
#include "gl/Camera.hpp"
#include "gl/Element.hpp"
#include <GL/OOGL.hpp>

using namespace std;

void windowRefresh(GLFWwindow* window);
void init();
void render();

GLFWwindow *window;

int width = 800, height = 600;
GL::Camera* camera;
glm::mat4 projection;

typedef pair<const char*, Element::object*> object;
map<const char*, Element::object*> objects;

Element::object* cube = NULL;
Element::object* overlay = NULL;
Element::color overlayColor(50, 200, 50, 0.65);

int main() {

  if (!glfwInit())
    exit(EXIT_FAILURE);

  glfwWindowHint(GLFW_SAMPLES, 4);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  window = glfwCreateWindow(width, height, "DukVis", NULL, NULL);
  if (!window) {
    glfwTerminate();
    exit(EXIT_FAILURE);
  }

  glfwSetWindowRefreshCallback(window, windowRefresh);

  glfwMakeContextCurrent(window);

  init();

  while (!glfwWindowShouldClose(window)) {
    glfwMakeContextCurrent(window);

    render();

    glfwSwapBuffers(window);
    glfwPollEvents();
  }

  glfwDestroyWindow(window);

  //TODO: Remember to release all OpenGL held resources (DukVis' are "released" on their own)

  glfwTerminate();
  exit(EXIT_SUCCESS);
}

void windowRefresh(GLFWwindow* window) {
  glfwGetFramebufferSize(window, &width, &height);
  camera->Resize(width, height);
  projection = glm::ortho(0.0f, (float)width, (float)height, 0.0f, -1.0f, 1.0f);

  if (overlay != NULL && overlay->shader != NULL) {
    glfwMakeContextCurrent(window);
    glUseProgram(*overlay->shader);
    glUniformMatrix4fv(overlay->uniforms.at("mvp"), 1, false, glm::value_ptr(
      glm::scale(glm::mat4(1.0f), glm::vec3((float)width, (float)height, 1.0))
    ));
    glUniformMatrix4fv(overlay->uniforms.at("projection"), 1, false, glm::value_ptr(projection));
  }

  render();
  glfwSwapBuffers(window);
}

void init() {
  camera = new GL::Camera(width, height);
  camera->MoveTo(glm::vec3(5,3,2));

  projection = glm::ortho(0.0f, (float)width, (float)height, 0.0f, -1.0f, 1.0f);

  cube = Element::create("cube", Primitives::cube());
  cube->bindUniform("mvp");
  cube->bindUniform("color");

  float verticesOverlay[] {
    // Pos      // Tex
    0.0f, 1.0f, 0.0f, 1.0f,
    1.0f, 0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 0.0f,

    0.0f, 1.0f, 0.0f, 1.0f,
    1.0f, 1.0f, 1.0f, 1.0f,
    1.0f, 0.0f, 1.0f, 0.0f
  };
  uint count = ARRAY_SIZE(verticesOverlay);
  float* overlayGeom = new float[count];
  std::copy(verticesOverlay, verticesOverlay + count, overlayGeom);
  Primitives::geometry* vertsO = new Primitives::geometry(overlayGeom, count);
  overlay = new Element::object("overlay");
  overlay = Element::create(vertsO, overlay, 4, true, false);
  overlay->vao->BindAttribute(overlay->shader->GetAttribute("vertex"),
    *(overlay->vbos[0]), GL::Type::Float, 4, 4 * sizeof(float), 0);
  overlay->bindUniform("mvp");
  overlay->bindUniform("projection");
  overlay->bindUniform("image");
  overlay->bindUniform("penColor");
  glUniformMatrix4fv(overlay->uniforms.at("mvp"), 1, false, glm::value_ptr(
    glm::scale(glm::mat4(1.0f), glm::vec3((float)width, (float)height, 1.0))
  ));
  glUniformMatrix4fv(overlay->uniforms.at("projection"), 1, false, glm::value_ptr(projection));

  overlay->textures.insert(Textures::namedTexture(
    "overlay", Textures::empty(width, height)
  ));
  Textures::texture* overlayBuff = overlay->textures.at("overlay");
  overlayBuff->penLine(floor(height / 2.0), overlayColor);

  objects.insert(object("cube", cube));
  objects.insert(object("overlay", overlay));
}

double oldTime = 0.0;
double rTime = 0.0;

void render() {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  double time = glfwGetTime() / 4.0;
  rTime += ((time - oldTime) * 255);
  if (rTime > 255) rTime = 0;

  glUseProgram(*cube->shader);

  glm::mat4 model = glm::mat4(1.0f);
  model = glm::rotate(model, glm::radians((float)((rTime / 255) * 360.0)), glm::vec3(0,1,0));
  glUniformMatrix4fv(cube->uniforms.at("mvp"), 1, false, glm::value_ptr(camera->Project(model)));

  Element::color color(1.0f, 0.0f, 0.0f, 1.0f);
  glUniform4fv(cube->uniforms.at("color"), 1, color);

  oldTime = time;

  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LESS);

  Element::draw(cube->vao);

  glDisable(GL_DEPTH_TEST);

  // Render overlay 2D graphics surface

  //TODO: Make this configurable with script? ;)
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glUseProgram(*overlay->shader);

  glActiveTexture(GL_TEXTURE0);
  overlay->textures.at("overlay")->bind(0);

  glUniform4fv(overlay->uniforms.at("penColor"), 1, overlayColor);

  Element::draw(overlay->vao);

  glDisable(GL_BLEND);
}
<|endoftext|>


<commit_before>#include "basicfilelauncher.h"
#include "fileinfojob.h"
#include "mountoperation.h"

#include <gio/gdesktopappinfo.h>
#include <glib/gi18n.h>

#include <unordered_map>
#include <string>

#include <QObject>
#include <QEventLoop>
#include <QDebug>

#include "legacy/fm-app-info.h"

namespace Fm {

BasicFileLauncher::BasicFileLauncher():
    quickExec_{false} {
}

BasicFileLauncher::~BasicFileLauncher() {
}

bool BasicFileLauncher::launchFiles(const FileInfoList& fileInfos, GAppLaunchContext* ctx) {
    std::unordered_map<std::string, FileInfoList> mimeTypeToFiles;
    FileInfoList folderInfos;
    FilePathList pathsToLaunch;
    // classify files according to different mimetypes
    for(auto& fileInfo : fileInfos) {
        // qDebug("path: %s, target: %s", fileInfo->path().toString().get(), fileInfo->target().c_str());
        if(fileInfo->isDir()) {
            folderInfos.emplace_back(fileInfo);
        }
        else if(fileInfo->isMountable()) {
            if(fileInfo->target().empty()) {
                // the mountable is not yet mounted so we have no target URI.
                GErrorPtr err{G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED,
                            QObject::tr("The path is not mounted.")};
                if(!showError(ctx, err, fileInfo->path(), fileInfo)) {
                    // the user fail to handle the error, skip this file.
                    continue;
                }

                // we do not have the target path in the FileInfo object.
                // try to launch our path again to query the new file info later so we can get the mounted target URI.
                pathsToLaunch.emplace_back(fileInfo->path());
            }
            else {
                // we have the target path, launch it later
                pathsToLaunch.emplace_back(FilePath::fromPathStr(fileInfo->target().c_str()));
            }
        }
        else if(fileInfo->isDesktopEntry()) {
            // launch the desktop entry
            launchDesktopEntry(fileInfo, FilePathList{}, ctx);
        }
        else if(fileInfo->isExecutableType()) {
            // directly execute the file
            launchExecutable(fileInfo, ctx);
        }
        else if(fileInfo->isShortcut()) {
            // for shortcuts, launch their targets instead
            auto path = handleShortcut(fileInfo, ctx);
            if(path.isValid()) {
                pathsToLaunch.emplace_back(path);
            }
        }
        else {
            auto& mimeType = fileInfo->mimeType();
            mimeTypeToFiles[mimeType->name()].emplace_back(fileInfo);
        }
    }

    // open folders
    if(!folderInfos.empty()) {
        GErrorPtr err;
        openFolder(ctx, folderInfos, err);
    }

    // open files of different mime-types with their default app
    for(auto& typeFiles : mimeTypeToFiles) {
        auto& mimeType = typeFiles.first;
        auto& files = typeFiles.second;
        GErrorPtr err;
        GAppInfoPtr app{g_app_info_get_default_for_type(mimeType.c_str(), false), false};
        if(!app) {
            app = chooseApp(files, mimeType.c_str(), err);
        }
        if(app) {
            launchWithApp(app.get(), files.paths(), ctx);
        }
    }

    if(!pathsToLaunch.empty()) {
        launchPaths(pathsToLaunch, ctx);
    }

    return true;
}

bool BasicFileLauncher::launchPaths(FilePathList paths, GAppLaunchContext* ctx) {
    // FIXME: blocking with an event loop is not a good design :-(
    QEventLoop eventLoop;

    auto job = new FileInfoJob{paths};
    job->setAutoDelete(false);  // do not automatically delete the job since we want its results later.

    GObjectPtr<GAppLaunchContext> ctxPtr{ctx};
    QObject::connect(job, &FileInfoJob::finished,
            [&eventLoop]() {
        // exit the event loop when the job is done
        eventLoop.exit();
    });
    // run the job in another thread to not block the UI
    job->runAsync();

    // blocking until the job is done with a event loop
    eventLoop.exec();

    // launch the file info
    launchFiles(job->files(), ctx);

    delete job;
    return false;
}

GAppInfoPtr BasicFileLauncher::chooseApp(const FileInfoList& /* fileInfos */, const char* /*mimeType*/, GErrorPtr& /* err */) {
    return GAppInfoPtr{};
}

bool BasicFileLauncher::openFolder(GAppLaunchContext* ctx, const FileInfoList& folderInfos, GErrorPtr& err) {
    auto app = chooseApp(folderInfos, "inode/directory", err);
    if(app) {
        launchWithApp(app.get(), folderInfos.paths(), ctx);
    }
    else {
        showError(ctx, err);
    }
    return false;
}

BasicFileLauncher::ExecAction BasicFileLauncher::askExecFile(const FileInfoPtr & /* file */) {
    return ExecAction::DIRECT_EXEC;
}

bool BasicFileLauncher::showError(GAppLaunchContext* /* ctx */, GErrorPtr& /* err */, const FilePath& /* path */, const FileInfoPtr& /* info */) {
    return false;
}

int BasicFileLauncher::ask(const char* /* msg */, char* const* /* btn_labels */, int default_btn) {
    return default_btn;
}

bool BasicFileLauncher::launchWithApp(GAppInfo* app, const FilePathList& paths, GAppLaunchContext* ctx) {
    GList* uris = nullptr;
    for(auto& path : paths) {
        auto uri = path.uri();
        uris = g_list_prepend(uris, uri.release());
    }
    GErrorPtr err;
    bool ret = bool(g_app_info_launch_uris(app, uris, ctx, &err));
    g_list_foreach(uris, reinterpret_cast<GFunc>(g_free), nullptr);
    g_list_free(uris);
    if(!ret) {
        // FIXME: show error for all files
        showError(ctx, err, paths[0]);
    }
    return ret;
}

bool BasicFileLauncher::launchDesktopEntry(const FileInfoPtr &fileInfo, const FilePathList &paths, GAppLaunchContext* ctx) {
    /* treat desktop entries as executables */
    auto target = fileInfo->target();
    CStrPtr filename;
    const char* desktopEntryName = nullptr;
    FilePathList shortcutTargetPaths;
    if(fileInfo->isExecutableType()) {
        auto act = quickExec_ ? ExecAction::DIRECT_EXEC : askExecFile(fileInfo);
        switch(act) {
        case ExecAction::EXEC_IN_TERMINAL:
        case ExecAction::DIRECT_EXEC: {
            if(fileInfo->isShortcut()) {
                auto path = handleShortcut(fileInfo, ctx);
                if(path.isValid()) {
                    shortcutTargetPaths.emplace_back(path);
                }
            }
            else {
                if(target.empty()) {
                    filename = fileInfo->path().localPath();
                }
                desktopEntryName = !target.empty() ? target.c_str() : filename.get();
            }
            break;
        }
        case ExecAction::OPEN_WITH_DEFAULT_APP:
            return launchWithDefaultApp(fileInfo, ctx);
        case ExecAction::CANCEL:
            return false;
        default:
            return false;
        }
    }
    /* make exception for desktop entries under menu */
    else if(fileInfo->isNative() /* an exception */ ||
            fileInfo->path().hasUriScheme("menu")) {
        if(target.empty()) {
            filename = fileInfo->path().localPath();
        }
        desktopEntryName = !target.empty() ? target.c_str() : filename.get();
    }

    if(desktopEntryName) {
        return launchDesktopEntry(desktopEntryName, paths, ctx);
    }
    if(!shortcutTargetPaths.empty()) {
        launchPaths(shortcutTargetPaths, ctx);
    }
    return false;
}

bool BasicFileLauncher::launchDesktopEntry(const char *desktopEntryName, const FilePathList &paths, GAppLaunchContext *ctx) {
    bool ret = false;
    GAppInfo* app;

    /* Let GDesktopAppInfo try first. */
    if(g_path_is_absolute(desktopEntryName)) {
        app = G_APP_INFO(g_desktop_app_info_new_from_filename(desktopEntryName));
    }
    else {
        app = G_APP_INFO(g_desktop_app_info_new(desktopEntryName));
    }
    /* we handle Type=Link in FmFileInfo so if GIO failed then
       it cannot be launched in fact */

    if(app) {
        return launchWithApp(app, paths, ctx);
    }
    else {
        QString msg = QObject::tr("Invalid desktop entry file: '%1'").arg(desktopEntryName);
        GErrorPtr err{G_IO_ERROR, G_IO_ERROR_FAILED, msg};
        showError(ctx, err);
    }
    return ret;
}

FilePath BasicFileLauncher::handleShortcut(const FileInfoPtr& fileInfo, GAppLaunchContext* ctx) {
    auto target = fileInfo->target();
    auto scheme = CStrPtr{g_uri_parse_scheme(target.c_str())};
    if(scheme) {
        // collect the uri schemes we support
        if(strcmp(scheme.get(), "file") == 0
                || strcmp(scheme.get(), "trash") == 0
                || strcmp(scheme.get(), "network") == 0
                || strcmp(scheme.get(), "computer") == 0) {
            return FilePath::fromUri(fileInfo->target().c_str());
        }
        else {
            // ask gio to launch the default handler for the uri scheme
            GAppInfoPtr app{g_app_info_get_default_for_uri_scheme(scheme.get()), false};
            FilePathList uris{FilePath::fromUri(fileInfo->target().c_str())};
            launchWithApp(app.get(), uris, ctx);
        }
    }
    else {
        // see it as a local path
        return FilePath::fromLocalPath(fileInfo->target().c_str());
    }
    return FilePath();
}

bool BasicFileLauncher::launchExecutable(const FileInfoPtr &fileInfo, GAppLaunchContext* ctx) {
    /* if it's an executable file, directly execute it. */
    auto filename = fileInfo->path().localPath();
    /* FIXME: we need to use eaccess/euidaccess here. */
    if(g_file_test(filename.get(), G_FILE_TEST_IS_EXECUTABLE)) {
        auto act = quickExec_ ? ExecAction::DIRECT_EXEC : askExecFile(fileInfo);
        int flags = G_APP_INFO_CREATE_NONE;
        switch(act) {
        case ExecAction::EXEC_IN_TERMINAL:
            flags |= G_APP_INFO_CREATE_NEEDS_TERMINAL;
        /* Falls through. */
        case ExecAction::DIRECT_EXEC: {
            /* filename may contain spaces. Fix #3143296 */
            CStrPtr quoted{g_shell_quote(filename.get())};
            // FIXME: remove libfm dependency
            GAppInfoPtr app{fm_app_info_create_from_commandline(quoted.get(), nullptr, GAppInfoCreateFlags(flags), nullptr)};
            if(app) {
                CStrPtr run_path{g_path_get_dirname(filename.get())};
                CStrPtr cwd;
                /* bug #3589641: scripts are ran from $HOME.
                   since GIO launcher is kinda ugly - it has
                   no means to set running directory so we
                   do workaround - change directory to it */
                if(run_path && strcmp(run_path.get(), ".")) {
                    cwd = CStrPtr{g_get_current_dir()};
                    if(chdir(run_path.get()) != 0) {
                        cwd.reset();
                        // show errors
                        QString msg = QObject::tr("Cannot set working directory to '%1': %2").arg(run_path.get()).arg(g_strerror(errno));
                        GErrorPtr err{G_IO_ERROR, g_io_error_from_errno(errno), msg};
                        showError(ctx, err);
                    }
                }

                // FIXME: remove libfm dependency
                GErrorPtr err;
                if(!fm_app_info_launch(app.get(), nullptr, ctx, &err)) {
                    showError(ctx, err);
                }
                if(cwd) { /* return back */
                    if(chdir(cwd.get()) != 0) {
                        g_warning("fm_launch_files(): chdir() failed");
                    }
                }
                return true;
            }
            break;
        }
        case ExecAction::OPEN_WITH_DEFAULT_APP:
            return launchWithDefaultApp(fileInfo, ctx);
        case ExecAction::CANCEL:
        default:
            break;
        }
    }
    return false;
}

bool BasicFileLauncher::launchWithDefaultApp(const FileInfoPtr &fileInfo, GAppLaunchContext* ctx) {
    FileInfoList files;
    files.emplace_back(fileInfo);
    GErrorPtr err;
    GAppInfoPtr app{g_app_info_get_default_for_type(fileInfo->mimeType()->name(), false), false};
    if(app) {
        return launchWithApp(app.get(), files.paths(), ctx);
    }
    else {
        showError(ctx, err, fileInfo->path());
    }
    return false;
}

} // namespace Fm
<commit_msg>Check if the opening app exists before using it<commit_after>#include "basicfilelauncher.h"
#include "fileinfojob.h"
#include "mountoperation.h"

#include <gio/gdesktopappinfo.h>
#include <glib/gi18n.h>

#include <unordered_map>
#include <string>

#include <QObject>
#include <QEventLoop>
#include <QDebug>

#include "legacy/fm-app-info.h"

namespace Fm {

BasicFileLauncher::BasicFileLauncher():
    quickExec_{false} {
}

BasicFileLauncher::~BasicFileLauncher() {
}

bool BasicFileLauncher::launchFiles(const FileInfoList& fileInfos, GAppLaunchContext* ctx) {
    std::unordered_map<std::string, FileInfoList> mimeTypeToFiles;
    FileInfoList folderInfos;
    FilePathList pathsToLaunch;
    // classify files according to different mimetypes
    for(auto& fileInfo : fileInfos) {
        // qDebug("path: %s, target: %s", fileInfo->path().toString().get(), fileInfo->target().c_str());
        if(fileInfo->isDir()) {
            folderInfos.emplace_back(fileInfo);
        }
        else if(fileInfo->isMountable()) {
            if(fileInfo->target().empty()) {
                // the mountable is not yet mounted so we have no target URI.
                GErrorPtr err{G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED,
                            QObject::tr("The path is not mounted.")};
                if(!showError(ctx, err, fileInfo->path(), fileInfo)) {
                    // the user fail to handle the error, skip this file.
                    continue;
                }

                // we do not have the target path in the FileInfo object.
                // try to launch our path again to query the new file info later so we can get the mounted target URI.
                pathsToLaunch.emplace_back(fileInfo->path());
            }
            else {
                // we have the target path, launch it later
                pathsToLaunch.emplace_back(FilePath::fromPathStr(fileInfo->target().c_str()));
            }
        }
        else if(fileInfo->isDesktopEntry()) {
            // launch the desktop entry
            launchDesktopEntry(fileInfo, FilePathList{}, ctx);
        }
        else if(fileInfo->isExecutableType()) {
            // directly execute the file
            launchExecutable(fileInfo, ctx);
        }
        else if(fileInfo->isShortcut()) {
            // for shortcuts, launch their targets instead
            auto path = handleShortcut(fileInfo, ctx);
            if(path.isValid()) {
                pathsToLaunch.emplace_back(path);
            }
        }
        else {
            auto& mimeType = fileInfo->mimeType();
            mimeTypeToFiles[mimeType->name()].emplace_back(fileInfo);
        }
    }

    // open folders
    if(!folderInfos.empty()) {
        GErrorPtr err;
        openFolder(ctx, folderInfos, err);
    }

    // open files of different mime-types with their default app
    for(auto& typeFiles : mimeTypeToFiles) {
        auto& mimeType = typeFiles.first;
        auto& files = typeFiles.second;
        GErrorPtr err;
        GAppInfoPtr app{g_app_info_get_default_for_type(mimeType.c_str(), false), false};
        if(!app) {
            app = chooseApp(files, mimeType.c_str(), err);
        }
        if(app) {
            launchWithApp(app.get(), files.paths(), ctx);
        }
    }

    if(!pathsToLaunch.empty()) {
        launchPaths(pathsToLaunch, ctx);
    }

    return true;
}

bool BasicFileLauncher::launchPaths(FilePathList paths, GAppLaunchContext* ctx) {
    // FIXME: blocking with an event loop is not a good design :-(
    QEventLoop eventLoop;

    auto job = new FileInfoJob{paths};
    job->setAutoDelete(false);  // do not automatically delete the job since we want its results later.

    GObjectPtr<GAppLaunchContext> ctxPtr{ctx};
    QObject::connect(job, &FileInfoJob::finished,
            [&eventLoop]() {
        // exit the event loop when the job is done
        eventLoop.exit();
    });
    // run the job in another thread to not block the UI
    job->runAsync();

    // blocking until the job is done with a event loop
    eventLoop.exec();

    // launch the file info
    launchFiles(job->files(), ctx);

    delete job;
    return false;
}

GAppInfoPtr BasicFileLauncher::chooseApp(const FileInfoList& /* fileInfos */, const char* /*mimeType*/, GErrorPtr& /* err */) {
    return GAppInfoPtr{};
}

bool BasicFileLauncher::openFolder(GAppLaunchContext* ctx, const FileInfoList& folderInfos, GErrorPtr& err) {
    auto app = chooseApp(folderInfos, "inode/directory", err);
    if(app) {
        launchWithApp(app.get(), folderInfos.paths(), ctx);
    }
    else {
        showError(ctx, err);
    }
    return false;
}

BasicFileLauncher::ExecAction BasicFileLauncher::askExecFile(const FileInfoPtr & /* file */) {
    return ExecAction::DIRECT_EXEC;
}

bool BasicFileLauncher::showError(GAppLaunchContext* /* ctx */, GErrorPtr& /* err */, const FilePath& /* path */, const FileInfoPtr& /* info */) {
    return false;
}

int BasicFileLauncher::ask(const char* /* msg */, char* const* /* btn_labels */, int default_btn) {
    return default_btn;
}

bool BasicFileLauncher::launchWithApp(GAppInfo* app, const FilePathList& paths, GAppLaunchContext* ctx) {
    GList* uris = nullptr;
    for(auto& path : paths) {
        auto uri = path.uri();
        uris = g_list_prepend(uris, uri.release());
    }
    GErrorPtr err;
    bool ret = bool(g_app_info_launch_uris(app, uris, ctx, &err));
    g_list_foreach(uris, reinterpret_cast<GFunc>(g_free), nullptr);
    g_list_free(uris);
    if(!ret) {
        // FIXME: show error for all files
        showError(ctx, err, paths[0]);
    }
    return ret;
}

bool BasicFileLauncher::launchDesktopEntry(const FileInfoPtr &fileInfo, const FilePathList &paths, GAppLaunchContext* ctx) {
    /* treat desktop entries as executables */
    auto target = fileInfo->target();
    CStrPtr filename;
    const char* desktopEntryName = nullptr;
    FilePathList shortcutTargetPaths;
    if(fileInfo->isExecutableType()) {
        auto act = quickExec_ ? ExecAction::DIRECT_EXEC : askExecFile(fileInfo);
        switch(act) {
        case ExecAction::EXEC_IN_TERMINAL:
        case ExecAction::DIRECT_EXEC: {
            if(fileInfo->isShortcut()) {
                auto path = handleShortcut(fileInfo, ctx);
                if(path.isValid()) {
                    shortcutTargetPaths.emplace_back(path);
                }
            }
            else {
                if(target.empty()) {
                    filename = fileInfo->path().localPath();
                }
                desktopEntryName = !target.empty() ? target.c_str() : filename.get();
            }
            break;
        }
        case ExecAction::OPEN_WITH_DEFAULT_APP:
            return launchWithDefaultApp(fileInfo, ctx);
        case ExecAction::CANCEL:
            return false;
        default:
            return false;
        }
    }
    /* make exception for desktop entries under menu */
    else if(fileInfo->isNative() /* an exception */ ||
            fileInfo->path().hasUriScheme("menu")) {
        if(target.empty()) {
            filename = fileInfo->path().localPath();
        }
        desktopEntryName = !target.empty() ? target.c_str() : filename.get();
    }

    if(desktopEntryName) {
        return launchDesktopEntry(desktopEntryName, paths, ctx);
    }
    if(!shortcutTargetPaths.empty()) {
        launchPaths(shortcutTargetPaths, ctx);
    }
    return false;
}

bool BasicFileLauncher::launchDesktopEntry(const char *desktopEntryName, const FilePathList &paths, GAppLaunchContext *ctx) {
    bool ret = false;
    GAppInfo* app;

    /* Let GDesktopAppInfo try first. */
    if(g_path_is_absolute(desktopEntryName)) {
        app = G_APP_INFO(g_desktop_app_info_new_from_filename(desktopEntryName));
    }
    else {
        app = G_APP_INFO(g_desktop_app_info_new(desktopEntryName));
    }
    /* we handle Type=Link in FmFileInfo so if GIO failed then
       it cannot be launched in fact */

    if(app) {
        return launchWithApp(app, paths, ctx);
    }
    else {
        QString msg = QObject::tr("Invalid desktop entry file: '%1'").arg(desktopEntryName);
        GErrorPtr err{G_IO_ERROR, G_IO_ERROR_FAILED, msg};
        showError(ctx, err);
    }
    return ret;
}

FilePath BasicFileLauncher::handleShortcut(const FileInfoPtr& fileInfo, GAppLaunchContext* ctx) {
    auto target = fileInfo->target();
    auto scheme = CStrPtr{g_uri_parse_scheme(target.c_str())};
    if(scheme) {
        // collect the uri schemes we support
        if(strcmp(scheme.get(), "file") == 0
                || strcmp(scheme.get(), "trash") == 0
                || strcmp(scheme.get(), "network") == 0
                || strcmp(scheme.get(), "computer") == 0) {
            return FilePath::fromUri(target.c_str());
        }
        else {
            // ask gio to launch the default handler for the uri scheme
            if(GAppInfoPtr app{g_app_info_get_default_for_uri_scheme(scheme.get()), false}) {
                FilePathList uris{FilePath::fromUri(target.c_str())};
                launchWithApp(app.get(), uris, ctx);
            }
            else {
                GErrorPtr err{G_IO_ERROR, G_IO_ERROR_FAILED,
                              QObject::tr("No default application is set to launch '%1'")
                              .arg(target.c_str())};
                showError(nullptr, err);
            }
        }
    }
    else {
        // see it as a local path
        return FilePath::fromLocalPath(target.c_str());
    }
    return FilePath();
}

bool BasicFileLauncher::launchExecutable(const FileInfoPtr &fileInfo, GAppLaunchContext* ctx) {
    /* if it's an executable file, directly execute it. */
    auto filename = fileInfo->path().localPath();
    /* FIXME: we need to use eaccess/euidaccess here. */
    if(g_file_test(filename.get(), G_FILE_TEST_IS_EXECUTABLE)) {
        auto act = quickExec_ ? ExecAction::DIRECT_EXEC : askExecFile(fileInfo);
        int flags = G_APP_INFO_CREATE_NONE;
        switch(act) {
        case ExecAction::EXEC_IN_TERMINAL:
            flags |= G_APP_INFO_CREATE_NEEDS_TERMINAL;
        /* Falls through. */
        case ExecAction::DIRECT_EXEC: {
            /* filename may contain spaces. Fix #3143296 */
            CStrPtr quoted{g_shell_quote(filename.get())};
            // FIXME: remove libfm dependency
            GAppInfoPtr app{fm_app_info_create_from_commandline(quoted.get(), nullptr, GAppInfoCreateFlags(flags), nullptr)};
            if(app) {
                CStrPtr run_path{g_path_get_dirname(filename.get())};
                CStrPtr cwd;
                /* bug #3589641: scripts are ran from $HOME.
                   since GIO launcher is kinda ugly - it has
                   no means to set running directory so we
                   do workaround - change directory to it */
                if(run_path && strcmp(run_path.get(), ".")) {
                    cwd = CStrPtr{g_get_current_dir()};
                    if(chdir(run_path.get()) != 0) {
                        cwd.reset();
                        // show errors
                        QString msg = QObject::tr("Cannot set working directory to '%1': %2").arg(run_path.get()).arg(g_strerror(errno));
                        GErrorPtr err{G_IO_ERROR, g_io_error_from_errno(errno), msg};
                        showError(ctx, err);
                    }
                }

                // FIXME: remove libfm dependency
                GErrorPtr err;
                if(!fm_app_info_launch(app.get(), nullptr, ctx, &err)) {
                    showError(ctx, err);
                }
                if(cwd) { /* return back */
                    if(chdir(cwd.get()) != 0) {
                        g_warning("fm_launch_files(): chdir() failed");
                    }
                }
                return true;
            }
            break;
        }
        case ExecAction::OPEN_WITH_DEFAULT_APP:
            return launchWithDefaultApp(fileInfo, ctx);
        case ExecAction::CANCEL:
        default:
            break;
        }
    }
    return false;
}

bool BasicFileLauncher::launchWithDefaultApp(const FileInfoPtr &fileInfo, GAppLaunchContext* ctx) {
    FileInfoList files;
    files.emplace_back(fileInfo);
    GErrorPtr err;
    GAppInfoPtr app{g_app_info_get_default_for_type(fileInfo->mimeType()->name(), false), false};
    if(app) {
        return launchWithApp(app.get(), files.paths(), ctx);
    }
    else {
        showError(ctx, err, fileInfo->path());
    }
    return false;
}

} // namespace Fm
<|endoftext|>


<commit_before>/**
 *  \file RMF/Category.h
 *  \brief Handle read/write of Model data from/to files.
 *
 *  Copyright 2007-2013 IMP Inventors. All rights reserved.
 *
 */

#include "RMF/decorator/alternatives.h"
#include "RMF/decorator/physics.h"
#include <numeric>

RMF_ENABLE_WARNINGS

namespace RMF {
namespace decorator {

namespace {
double get_resolution_metric(double a, double b) {
  if (a < b) std::swap(a, b);
  return a / b - 1;
}

std::pair<double, double> get_resolution_impl(NodeConstHandle root,
                                              IntermediateParticleFactory ipcf,
                                              GaussianParticleFactory gpf) {
  std::pair<double, double> ret(0.0, 0.0);
  RMF_FOREACH(NodeConstHandle ch, root.get_children()) {
    std::pair<double, double> cur = get_resolution_impl(ch, ipcf, gpf);
    ret.first += cur.first;
    ret.second += cur.second;
  }
  if (ret.second == 0) {
    if (ipcf.get_is(root)) {
      ret.first = ipcf.get(root).get_radius();
      ret.second = 1.0;
    } else if (gpf.get_is(root)) {
      Vector3 sdfs = gpf.get(root).get_variances();
      ret.first = std::accumulate(sdfs.begin(), sdfs.end(), 0.0) / 3.0;
      ret.second = 1.0;
    }
  }
  return ret;
}
}

AlternativesFactory::AlternativesFactory(FileHandle fh)
    : cat_(fh.get_category("alternatives")),
      types_key_(fh.get_key<IntsTag>(cat_, "types")),
      roots_key_(fh.get_key<IntsTag>(cat_, "roots")) {}

AlternativesFactory::AlternativesFactory(FileConstHandle fh)
    : cat_(fh.get_category("alternatives")),
      types_key_(fh.get_key<IntsTag>(cat_, "types")),
      roots_key_(fh.get_key<IntsTag>(cat_, "roots")) {}

NodeID AlternativesConst::get_alternative_impl(RepresentationType type,
                                               float resolution) const {
  if (!get_node().get_has_value(types_key_)) return get_node().get_id();

  double closest_resolution = get_resolution(get_node());
  int closest_index = -1;
  Nullable<Ints> types = get_node().get_value(types_key_);
  if (!types.get_is_null()) {
    Ints roots = get_node().get_value(roots_key_);
    for (unsigned int i = 0; i < types.get().size(); ++i) {
      if (types.get()[i] != type) continue;
      double cur_resolution =
          get_resolution(get_node().get_file().get_node(NodeID(roots[i])));
      if (get_resolution_metric(resolution, cur_resolution) <
          get_resolution_metric(resolution, closest_resolution)) {
        closest_index = i;
        closest_resolution = cur_resolution;
      }
    }
  }
  if (closest_index == -1) {
    return get_node().get_id();
  } else {
    return NodeID(get_node().get_value(roots_key_).get()[closest_index]);
  }
}

NodeIDs AlternativesConst::get_alternatives_impl(RepresentationType type)
    const {
  NodeIDs ret;
  if (type == PARTICLE) ret.push_back(get_node().get_id());

  if (get_node().get_has_value(roots_key_)) {
    Ints roots = get_node().get_value(roots_key_).get();
    Ints types = get_node().get_value(types_key_).get();

    for (unsigned int i = 0; i < roots.size(); ++i) {
      RMF_INTERNAL_CHECK(roots[i] != 0, "The root can't be an alternative rep");
      if (RepresentationType(types[i]) == type) ret.push_back(NodeID(roots[i]));
    }
  }
  return ret;
}

double get_resolution(NodeConstHandle root) {
  IntermediateParticleFactory ipcf(root.get_file());
  GaussianParticleFactory gpf(root.get_file());
  std::pair<double, double> total = get_resolution_impl(root, ipcf, gpf);
  RMF_USAGE_CHECK(total.first != 0,
                  std::string("No particles were found at ") + root.get_name());
  return total.second / total.first;
}

Alternatives::Alternatives(NodeHandle nh, IntsKey types_key, IntsKey roots_key)
    : AlternativesConst(nh, types_key, roots_key) {}

void Alternatives::add_alternative(NodeHandle root, RepresentationType type) {
  RMF_USAGE_CHECK(root.get_id() != NodeID(0),
                  "The root can't be an alternative");
  get_node()
      .get_shared_data()
      ->access_static_value(get_node().get_id(), types_key_)
      .push_back(type);
  get_node()
      .get_shared_data()
      ->access_static_value(get_node().get_id(), roots_key_)
      .push_back(root.get_id().get_index());

  RMF_INTERNAL_CHECK(get_alternatives(type).size() >= 1, "None found");
}

NodeConstHandle AlternativesConst::get_alternative(RepresentationType type,
                                                   double resolution) const {
  return get_node().get_file().get_node(get_alternative_impl(type, resolution));
}

NodeConstHandles AlternativesConst::get_alternatives(RepresentationType type)
    const {
  NodeConstHandles ret;
  RMF_FOREACH(NodeID nid, get_alternatives_impl(type)) {
    ret.push_back(get_node().get_file().get_node(nid));
  }
  return ret;
}

RepresentationType AlternativesConst::get_representation_type(NodeID id) const {
  if (id == get_node().get_id()) return PARTICLE;

  Ints roots = get_node().get_value(roots_key_);
  for (unsigned int i = 0; i < roots.size(); ++i) {
    if (roots[i] == static_cast<int>(id.get_index())) {
      return RepresentationType(get_node().get_value(types_key_).get()[i]);
    }
  }
  RMF_THROW(Message("No such alternative representation"), UsageException);
}

namespace {
Floats get_resolutions_impl(NodeConstHandle root, AlternativesFactory af,
                            RepresentationType type) {
  Floats ret;
  if (af.get_is(root)) {
    RMF_FOREACH(NodeConstHandle a, af.get(root).get_alternatives(type)) {
      ret.push_back(get_resolution(a));
    }
  } else {
    RMF_FOREACH(NodeConstHandle ch, root.get_children()) {
      Floats cur = get_resolutions_impl(ch, af, type);
      ret.insert(ret.end(), cur.begin(), cur.end());
    }
  }
  return ret;
}
}

Floats get_resolutions(NodeConstHandle root, RepresentationType type,
                       double accuracy) {
  AlternativesFactory af(root.get_file());
  Floats unclustered = get_resolutions_impl(root, af, type);
  if (unclustered.empty()) unclustered.push_back(1.0);
  std::sort(unclustered.begin(), unclustered.end());
  double lb = unclustered[0];
  double ub = lb;
  Floats ret;
  RMF_FOREACH(double r, unclustered) {
    if (r > lb + accuracy) {
      ret.push_back(.5 * (lb + ub));
      lb = r;
    }
    ub = r;
  }
  ret.push_back(.5 * (lb + ub));
  return ret;
}

} /* namespace decorator */
} /* namespace RMF */

RMF_DISABLE_WARNINGS
<commit_msg>change resolution to 1.0/smallest radius<commit_after>/**
 *  \file RMF/Category.h
 *  \brief Handle read/write of Model data from/to files.
 *
 *  Copyright 2007-2013 IMP Inventors. All rights reserved.
 *
 */

#include "RMF/decorator/alternatives.h"
#include "RMF/decorator/physics.h"
#include <numeric>

RMF_ENABLE_WARNINGS

namespace RMF {
namespace decorator {

namespace {
double get_resolution_metric(double a, double b) {
  if (a < b) std::swap(a, b);
  return a / b - 1;
}

std::pair<double, bool> get_resolution_impl(NodeConstHandle root,
                                              IntermediateParticleFactory ipcf,
                                              GaussianParticleFactory gpf) {
  std::pair<double, bool> ret(std::numeric_limits<double>::max(), false);
  RMF_FOREACH(NodeConstHandle ch, root.get_children()) {
    std::pair<double, bool> cur = get_resolution_impl(ch, ipcf, gpf);
    ret.first = std::min(ret.first, cur.first);
    ret.second = ret.second || cur.second;
  }
  if (!ret.second) {
    if (ipcf.get_is(root)) {
      ret.first = ipcf.get(root).get_radius();
      ret.second = true;
    } else if (gpf.get_is(root)) {
      Vector3 sdfs = gpf.get(root).get_variances();
      ret.first = std::accumulate(sdfs.begin(), sdfs.end(), 0.0) / 3.0;
      ret.second = true;
    }
  }
  return ret;
}
}

AlternativesFactory::AlternativesFactory(FileHandle fh)
    : cat_(fh.get_category("alternatives")),
      types_key_(fh.get_key<IntsTag>(cat_, "types")),
      roots_key_(fh.get_key<IntsTag>(cat_, "roots")) {}

AlternativesFactory::AlternativesFactory(FileConstHandle fh)
    : cat_(fh.get_category("alternatives")),
      types_key_(fh.get_key<IntsTag>(cat_, "types")),
      roots_key_(fh.get_key<IntsTag>(cat_, "roots")) {}

NodeID AlternativesConst::get_alternative_impl(RepresentationType type,
                                               float resolution) const {
  if (!get_node().get_has_value(types_key_)) return get_node().get_id();

  double closest_resolution = get_resolution(get_node());
  int closest_index = -1;
  Nullable<Ints> types = get_node().get_value(types_key_);
  if (!types.get_is_null()) {
    Ints roots = get_node().get_value(roots_key_);
    for (unsigned int i = 0; i < types.get().size(); ++i) {
      if (types.get()[i] != type) continue;
      double cur_resolution =
          get_resolution(get_node().get_file().get_node(NodeID(roots[i])));
      if (get_resolution_metric(resolution, cur_resolution) <
          get_resolution_metric(resolution, closest_resolution)) {
        closest_index = i;
        closest_resolution = cur_resolution;
      }
    }
  }
  if (closest_index == -1) {
    return get_node().get_id();
  } else {
    return NodeID(get_node().get_value(roots_key_).get()[closest_index]);
  }
}

NodeIDs AlternativesConst::get_alternatives_impl(RepresentationType type)
    const {
  NodeIDs ret;
  if (type == PARTICLE) ret.push_back(get_node().get_id());

  if (get_node().get_has_value(roots_key_)) {
    Ints roots = get_node().get_value(roots_key_).get();
    Ints types = get_node().get_value(types_key_).get();

    for (unsigned int i = 0; i < roots.size(); ++i) {
      RMF_INTERNAL_CHECK(roots[i] != 0, "The root can't be an alternative rep");
      if (RepresentationType(types[i]) == type) ret.push_back(NodeID(roots[i]));
    }
  }
  return ret;
}

double get_resolution(NodeConstHandle root) {
  IntermediateParticleFactory ipcf(root.get_file());
  GaussianParticleFactory gpf(root.get_file());
  std::pair<double, bool> total = get_resolution_impl(root, ipcf, gpf);
  RMF_USAGE_CHECK(total.second,
                  std::string("No particles were found at ") + root.get_name());
  return 1.0 / total.first;
}

Alternatives::Alternatives(NodeHandle nh, IntsKey types_key, IntsKey roots_key)
    : AlternativesConst(nh, types_key, roots_key) {}

void Alternatives::add_alternative(NodeHandle root, RepresentationType type) {
  RMF_USAGE_CHECK(root.get_id() != NodeID(0),
                  "The root can't be an alternative");
  get_node()
      .get_shared_data()
      ->access_static_value(get_node().get_id(), types_key_)
      .push_back(type);
  get_node()
      .get_shared_data()
      ->access_static_value(get_node().get_id(), roots_key_)
      .push_back(root.get_id().get_index());

  RMF_INTERNAL_CHECK(get_alternatives(type).size() >= 1, "None found");
}

NodeConstHandle AlternativesConst::get_alternative(RepresentationType type,
                                                   double resolution) const {
  return get_node().get_file().get_node(get_alternative_impl(type, resolution));
}

NodeConstHandles AlternativesConst::get_alternatives(RepresentationType type)
    const {
  NodeConstHandles ret;
  RMF_FOREACH(NodeID nid, get_alternatives_impl(type)) {
    ret.push_back(get_node().get_file().get_node(nid));
  }
  return ret;
}

RepresentationType AlternativesConst::get_representation_type(NodeID id) const {
  if (id == get_node().get_id()) return PARTICLE;

  Ints roots = get_node().get_value(roots_key_);
  for (unsigned int i = 0; i < roots.size(); ++i) {
    if (roots[i] == static_cast<int>(id.get_index())) {
      return RepresentationType(get_node().get_value(types_key_).get()[i]);
    }
  }
  RMF_THROW(Message("No such alternative representation"), UsageException);
}

namespace {
Floats get_resolutions_impl(NodeConstHandle root, AlternativesFactory af,
                            RepresentationType type) {
  Floats ret;
  if (af.get_is(root)) {
    RMF_FOREACH(NodeConstHandle a, af.get(root).get_alternatives(type)) {
      ret.push_back(get_resolution(a));
    }
  } else {
    RMF_FOREACH(NodeConstHandle ch, root.get_children()) {
      Floats cur = get_resolutions_impl(ch, af, type);
      ret.insert(ret.end(), cur.begin(), cur.end());
    }
  }
  return ret;
}
}

Floats get_resolutions(NodeConstHandle root, RepresentationType type,
                       double accuracy) {
  AlternativesFactory af(root.get_file());
  Floats unclustered = get_resolutions_impl(root, af, type);
  if (unclustered.empty()) unclustered.push_back(1.0);
  std::sort(unclustered.begin(), unclustered.end());
  double lb = unclustered[0];
  double ub = lb;
  Floats ret;
  RMF_FOREACH(double r, unclustered) {
    if (r > lb + accuracy) {
      ret.push_back(.5 * (lb + ub));
      lb = r;
    }
    ub = r;
  }
  ret.push_back(.5 * (lb + ub));
  return ret;
}

} /* namespace decorator */
} /* namespace RMF */

RMF_DISABLE_WARNINGS
<|endoftext|>


<commit_before>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

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

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

Author(s) / Copyright (s): Damon Hart-Davis 2017
*/

/*
 * Driver for OTV0p2Base SystemStatsLine tests.
 */

#include <stdint.h>
#include <gtest/gtest.h>
#include <OTV0p2Base.h>
#include <OTRadValve.h>

#include "OTV0P2BASE_SystemStatsLine.h"

// Test basic instance creation, etc.
namespace Basics
    {
    // Working buffer for tests.
    static char buf[80];
    static OTV0P2BASE::BufPrint bp(buf, sizeof(buf));
    // Inputs/controls for stats report.
    static OTRadValve::ValveMode valveMode;
    static OTRadValve::RadValveMock modelledRadValve;
    static OTV0P2BASE::TemperatureC16Mock tempC16;
    static OTV0P2BASE::PseudoSensorOccupancyTracker occupancy;
    static OTV0P2BASE::SensorAmbientLightAdaptiveMock ambLight;
    // Dummy (non-functioning) temperature and relative humidity sensors.
    static OTV0P2BASE::HumiditySensorMock rh;
    static OTRadValve::NULLValveSchedule schedule;
    }
TEST(SystemStatsLine,Basics)
{
    // Reset inputs/controls for stats report so test is idempotent.
    Basics::bp.reset();
    Basics::valveMode.reset();
    Basics::modelledRadValve.reset();
    Basics::tempC16.reset();
    Basics::rh.reset();
    Basics::ambLight.reset();
    Basics::occupancy.reset();

    // Set a reasonable room temperature.
    Basics::tempC16.set((18 << 4) + 14);
    // Set a reasonable RH%.
    Basics::rh.set(50);

    // Create stats line instance wrapped round simple bounded buffer.
    OTV0P2BASE::SystemStatsLine<
        decltype(Basics::valveMode), &Basics::valveMode,
        decltype(Basics::modelledRadValve), &Basics::modelledRadValve,
        decltype(Basics::tempC16), &Basics::tempC16,
        decltype(Basics::rh), &Basics::rh,
        decltype(Basics::ambLight), &Basics::ambLight,
        decltype(Basics::occupancy), &Basics::occupancy,
        decltype(Basics::schedule), &Basics::schedule,
        true, // Enable JSON stats.
        decltype(Basics::bp), &Basics::bp> ssl1;

    // Buffer should remain empty before any explicit activity.
    ASSERT_EQ(0, Basics::bp.getSize());
    ASSERT_EQ('\0', Basics::buf[0]);

    // Generate a stats line.
    ssl1.serialStatusReport();

    // Buffer should contain status line starting with '='.
    ASSERT_LE(1, Basics::bp.getSize());
    ASSERT_EQ(OTV0P2BASE::SERLINE_START_CHAR_STATS, Basics::buf[0]);

    // Check entire status line including trailing line termination.
    EXPECT_STREQ("=F0%@18CE;{\"@\":\"\",\"H|%\":50,\"L\":0,\"occ|%\":0}\r\n", Basics::buf);

    // Clear the buffer.
    Basics::bp.reset();

    // Create stats line instance wrapped round simple bounded buffer.
    // In this case omit all the 'optional' values.
    OTV0P2BASE::SystemStatsLine<
        decltype(Basics::valveMode), &Basics::valveMode,
        OTRadValve::AbstractRadValve, (OTRadValve::AbstractRadValve *)NULL,
        OTV0P2BASE::TemperatureC16Base, (OTV0P2BASE::TemperatureC16Base *)NULL,
        OTV0P2BASE::HumiditySensorBase, (OTV0P2BASE::HumiditySensorBase *)NULL,
        OTV0P2BASE::SensorAmbientLightBase, (OTV0P2BASE::SensorAmbientLightBase *)NULL,
        OTV0P2BASE::PseudoSensorOccupancyTracker, (OTV0P2BASE::PseudoSensorOccupancyTracker *)NULL,
        OTRadValve::SimpleValveScheduleBase, (OTRadValve::SimpleValveScheduleBase *)NULL,
        false, // No JSON stats.
        decltype(Basics::bp), &Basics::bp> sslO;
    // Generate a stats line.
    sslO.serialStatusReport();
    // Check entire status line including trailing line termination.
    EXPECT_STREQ("=F\r\n", Basics::buf);

    // Clear the buffer.
    Basics::bp.reset();

    // Ensure that failing to provide a non-NULL printer does not cause a crash.
    OTV0P2BASE::SystemStatsLine<
        decltype(Basics::valveMode), &Basics::valveMode,
        OTRadValve::AbstractRadValve, (OTRadValve::AbstractRadValve *)NULL,
        OTV0P2BASE::TemperatureC16Base, (OTV0P2BASE::TemperatureC16Base *)NULL,
        OTV0P2BASE::HumiditySensorBase, (OTV0P2BASE::HumiditySensorBase *)NULL,
        OTV0P2BASE::SensorAmbientLightBase, (OTV0P2BASE::SensorAmbientLightBase *)NULL,
        OTV0P2BASE::PseudoSensorOccupancyTracker, (OTV0P2BASE::PseudoSensorOccupancyTracker *)NULL,
        OTRadValve::SimpleValveScheduleBase, (OTRadValve::SimpleValveScheduleBase *)NULL,
        false, // No JSON stats.
        Print, (Print *)NULL> sslBad;
    // Generate a stats line.
    sslBad.serialStatusReport();
    // Buffer should remain empty before any explicit activity.
    ASSERT_EQ(0, Basics::bp.getSize());
    ASSERT_EQ('\0', Basics::buf[0]);
}

<commit_msg>Trying to clear compile error on g++ 6.x<commit_after>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

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

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

Author(s) / Copyright (s): Damon Hart-Davis 2017
*/

/*
 * Driver for OTV0p2Base SystemStatsLine tests.
 */

#include <stdint.h>
#include <gtest/gtest.h>
#include <OTV0p2Base.h>
#include <OTRadValve.h>

#include "OTV0P2BASE_SystemStatsLine.h"

// Test basic instance creation, etc.
namespace Basics
    {
    // Working buffer for tests.
    static char buf[80];
    static OTV0P2BASE::BufPrint bp(buf, sizeof(buf));
    // Inputs/controls for stats report.
    static OTRadValve::ValveMode valveMode;
    static OTRadValve::RadValveMock modelledRadValve;
    static OTV0P2BASE::TemperatureC16Mock tempC16;
    static OTV0P2BASE::PseudoSensorOccupancyTracker occupancy;
    static OTV0P2BASE::SensorAmbientLightAdaptiveMock ambLight;
    // Dummy (non-functioning) temperature and relative humidity sensors.
    static OTV0P2BASE::HumiditySensorMock rh;
    static OTRadValve::NULLValveSchedule schedule;
    }
TEST(SystemStatsLine,Basics)
{
    // Reset inputs/controls for stats report so test is idempotent.
    Basics::bp.reset();
    Basics::valveMode.reset();
    Basics::modelledRadValve.reset();
    Basics::tempC16.reset();
    Basics::rh.reset();
    Basics::ambLight.reset();
    Basics::occupancy.reset();

    // Set a reasonable room temperature.
    Basics::tempC16.set((18 << 4) + 14);
    // Set a reasonable RH%.
    Basics::rh.set(50);

    // Create stats line instance wrapped round simple bounded buffer.
    OTV0P2BASE::SystemStatsLine<
        decltype(Basics::valveMode), &Basics::valveMode,
        decltype(Basics::modelledRadValve), &Basics::modelledRadValve,
        decltype(Basics::tempC16), &Basics::tempC16,
        decltype(Basics::rh), &Basics::rh,
        decltype(Basics::ambLight), &Basics::ambLight,
        decltype(Basics::occupancy), &Basics::occupancy,
        decltype(Basics::schedule), &Basics::schedule,
        true, // Enable JSON stats.
        decltype(Basics::bp), &Basics::bp> ssl1;

    // Buffer should remain empty before any explicit activity.
    ASSERT_EQ(0, Basics::bp.getSize());
    ASSERT_EQ('\0', Basics::buf[0]);

    // Generate a stats line.
    ssl1.serialStatusReport();

    // Buffer should contain status line starting with '='.
    ASSERT_LE(1, Basics::bp.getSize());
    ASSERT_EQ(OTV0P2BASE::SERLINE_START_CHAR_STATS, Basics::buf[0]);

    // Check entire status line including trailing line termination.
    EXPECT_STREQ("=F0%@18CE;{\"@\":\"\",\"H|%\":50,\"L\":0,\"occ|%\":0}\r\n", Basics::buf);

    // Clear the buffer.
    Basics::bp.reset();

    // Create stats line instance wrapped round simple bounded buffer.
    // In this case omit all the 'optional' values.
    OTV0P2BASE::SystemStatsLine<
        decltype(Basics::valveMode), &Basics::valveMode,
        OTRadValve::AbstractRadValve, (OTRadValve::AbstractRadValve *)NULL,
        OTV0P2BASE::TemperatureC16Base, (OTV0P2BASE::TemperatureC16Base *)NULL,
        OTV0P2BASE::HumiditySensorBase, (OTV0P2BASE::HumiditySensorBase *)NULL,
        OTV0P2BASE::SensorAmbientLightBase, (OTV0P2BASE::SensorAmbientLightBase *)NULL,
        OTV0P2BASE::PseudoSensorOccupancyTracker, (OTV0P2BASE::PseudoSensorOccupancyTracker *)NULL,
        OTRadValve::SimpleValveScheduleBase, (OTRadValve::SimpleValveScheduleBase *)NULL,
        false, // No JSON stats.
        decltype(Basics::bp), &Basics::bp> sslO;
    // Generate a stats line.
    sslO.serialStatusReport();
    // Check entire status line including trailing line termination.
    EXPECT_STREQ("=F\r\n", Basics::buf);

    // Clear the buffer.
    Basics::bp.reset();

// FIXME FIXME FIXME
//    // Ensure that failing to provide a non-NULL printer does not cause a crash.
//    OTV0P2BASE::SystemStatsLine<
//        decltype(Basics::valveMode), &Basics::valveMode,
//        OTRadValve::AbstractRadValve, (OTRadValve::AbstractRadValve *)NULL,
//        OTV0P2BASE::TemperatureC16Base, (OTV0P2BASE::TemperatureC16Base *)NULL,
//        OTV0P2BASE::HumiditySensorBase, (OTV0P2BASE::HumiditySensorBase *)NULL,
//        OTV0P2BASE::SensorAmbientLightBase, (OTV0P2BASE::SensorAmbientLightBase *)NULL,
//        OTV0P2BASE::PseudoSensorOccupancyTracker, (OTV0P2BASE::PseudoSensorOccupancyTracker *)NULL,
//        OTRadValve::SimpleValveScheduleBase, (OTRadValve::SimpleValveScheduleBase *)NULL,
//        false, // No JSON stats.
//        Print, (Print *)NULL> sslBad;
//    // Generate a stats line.
//    sslBad.serialStatusReport();
//    // Buffer should remain empty before any explicit activity.
//    ASSERT_EQ(0, Basics::bp.getSize());
//    ASSERT_EQ('\0', Basics::buf[0]);
}

<|endoftext|>


<commit_before>
/* mbed Microcontroller Library
 * Copyright (c) 2018 ARM Limited
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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.
 */
#ifdef DEVICE_WATCHDOG

#include "mbed_watchdog_mgr.h"

static bool is_watchdog_started = false; //boolean to control watchdog start and stop
#define MS_TO_US(x) ((x) * 1000) //macro to convert millisecond to microsecond
static uint32_t elapsed_ms = (HW_WATCHDOG_TIMEOUT / 2);
MBED_STATIC_ASSERT((HW_WATCHDOG_TIMEOUT > 0), "Timeout must be greater than zero");

/** mbed watchdog manager creates watchdog class instance with zero timeout
 *  as mbed watchdog manager is not going to register(not going to call "start" method of Watchdog class)
 *  its only going to call process to verify all registered users/threads in alive state
 *
 */
mbed::Watchdog watchdog(0, "Platform Watchdog");
/** Create singleton instance of LowPowerTicker for watchdog periodic call back of kick.
 */
static mbed::LowPowerTicker *get_ticker()
{
    static mbed::LowPowerTicker ticker;
    return &ticker;
}

/** Refreshes the watchdog timer.
 *
 * This function should be called periodically before the watchdog times out.
 * Otherwise, the system is reset.
 *
 * If the watchdog timer is not currently running this function does nothing
 */
static void mbed_wdog_manager_kick()
{
    core_util_critical_section_enter();
    hal_watchdog_kick();
    watchdog.process(((elapsed_ms <= 0) ? 1 : elapsed_ms));
    core_util_critical_section_exit();
}

uint32_t mbed_wdog_manager_get_max_timeout()
{
    const watchdog_features_t features = hal_watchdog_get_platform_features();
    return features.max_timeout;
}

bool mbed_wdog_manager_start()
{
    watchdog_status_t sts;
    MBED_ASSERT(HW_WATCHDOG_TIMEOUT < mbed_wdog_manager_get_max_timeout());
    core_util_critical_section_enter();
    if (is_watchdog_started) {
        core_util_critical_section_exit();
        return false;
    }
    watchdog_config_t config;
    config.timeout_ms = HW_WATCHDOG_TIMEOUT;
    sts = hal_watchdog_init(&config);
    if (sts == WATCHDOG_STATUS_OK) {
        is_watchdog_started = true;
    }
    core_util_critical_section_exit();
    if (is_watchdog_started){
        us_timestamp_t timeout = (MS_TO_US(((elapsed_ms <= 0) ? 1 : elapsed_ms)));
        get_ticker()->attach_us(mbed::callback(&mbed_wdog_manager_kick), timeout);
    }
    return is_watchdog_started;
}

bool mbed_wdog_manager_stop()
{
    watchdog_status_t sts;
    bool msts = true;
    core_util_critical_section_enter();
    if (is_watchdog_started) {
        sts = hal_watchdog_stop();
        if (sts != WATCHDOG_STATUS_OK) {
            msts = false;
        } else {
            get_ticker()->detach();
            is_watchdog_started = false;
        }

    } else {
        msts = false;
    }
    core_util_critical_section_exit();
    return msts;
}

uint32_t mbed_wdog_manager_get_timeout()
{
    return hal_watchdog_get_reload_value();
}

#endif // DEVICE_WATCHDOG
<commit_msg>fix for astyle<commit_after>
/* mbed Microcontroller Library
 * Copyright (c) 2018 ARM Limited
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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.
 */
#ifdef DEVICE_WATCHDOG

#include "mbed_watchdog_mgr.h"

static bool is_watchdog_started = false; //boolean to control watchdog start and stop
#define MS_TO_US(x) ((x) * 1000) //macro to convert millisecond to microsecond
static uint32_t elapsed_ms = (HW_WATCHDOG_TIMEOUT / 2);
MBED_STATIC_ASSERT((HW_WATCHDOG_TIMEOUT > 0), "Timeout must be greater than zero");

/** mbed watchdog manager creates watchdog class instance with zero timeout
 *  as mbed watchdog manager is not going to register(not going to call "start" method of Watchdog class)
 *  its only going to call process to verify all registered users/threads in alive state
 *
 */
mbed::Watchdog watchdog(0, "Platform Watchdog");
/** Create singleton instance of LowPowerTicker for watchdog periodic call back of kick.
 */
static mbed::LowPowerTicker *get_ticker()
{
    static mbed::LowPowerTicker ticker;
    return &ticker;
}

/** Refreshes the watchdog timer.
 *
 * This function should be called periodically before the watchdog times out.
 * Otherwise, the system is reset.
 *
 * If the watchdog timer is not currently running this function does nothing
 */
static void mbed_wdog_manager_kick()
{
    core_util_critical_section_enter();
    hal_watchdog_kick();
    watchdog.process(((elapsed_ms <= 0) ? 1 : elapsed_ms));
    core_util_critical_section_exit();
}

uint32_t mbed_wdog_manager_get_max_timeout()
{
    const watchdog_features_t features = hal_watchdog_get_platform_features();
    return features.max_timeout;
}

bool mbed_wdog_manager_start()
{
    watchdog_status_t sts;
    MBED_ASSERT(HW_WATCHDOG_TIMEOUT < mbed_wdog_manager_get_max_timeout());
    core_util_critical_section_enter();
    if (is_watchdog_started) {
        core_util_critical_section_exit();
        return false;
    }
    watchdog_config_t config;
    config.timeout_ms = HW_WATCHDOG_TIMEOUT;
    sts = hal_watchdog_init(&config);
    if (sts == WATCHDOG_STATUS_OK) {
        is_watchdog_started = true;
    }
    core_util_critical_section_exit();
    if (is_watchdog_started) {
        us_timestamp_t timeout = (MS_TO_US(((elapsed_ms <= 0) ? 1 : elapsed_ms)));
        get_ticker()->attach_us(mbed::callback(&mbed_wdog_manager_kick), timeout);
    }
    return is_watchdog_started;
}

bool mbed_wdog_manager_stop()
{
    watchdog_status_t sts;
    bool msts = true;
    core_util_critical_section_enter();
    if (is_watchdog_started) {
        sts = hal_watchdog_stop();
        if (sts != WATCHDOG_STATUS_OK) {
            msts = false;
        } else {
            get_ticker()->detach();
            is_watchdog_started = false;
        }

    } else {
        msts = false;
    }
    core_util_critical_section_exit();
    return msts;
}

uint32_t mbed_wdog_manager_get_timeout()
{
    return hal_watchdog_get_reload_value();
}

#endif // DEVICE_WATCHDOG
<|endoftext|>


<commit_before>//Author: Stefan Toman

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>

using namespace std;

struct interval {
	int id;
	long long end;

	interval(int id, long long end) : id(id), end(end) {};
};

bool operator<(const interval& a, const interval& b) {
	return a.end != b.end ? a.end > b.end : a.id > b.id;
}

int main() {
	int t;
	scanf("%d", &t);
	for (int i = 0; i < t; ++i) {
        int n;
        long long l, m, d;
		scanf("%I64d %d %I64d %I64d", &l, &n, &m, &d);
		long long w[n], r = 0;
		priority_queue<interval> q;
		for (int j = 0; j < n; j++) {
			scanf("%I64d", &w[j]);
			q.push(*new interval(j, w[j]));
		}
		priority_queue<interval> q2;
		int mused = 0;
		for (int j = 0; j < l; j++) {
			interval o = q.top();
			q.pop();
			q.push(*new interval(o.id, o.end + w[o.id]));
			if(mused < m) {
				q2.push(*new interval(mused, o.end + d));
				mused++;
				if(r < o.end + d) {
					r = o.end + d;
				}
			}
			else {
				interval o2 = q2.top();
				q2.pop();
				long long e = max(o2.end, o.end) + d;
				q2.push(*new interval(o2.id, e));
				if(r < e) {
					r = e;
				}
			}
		}

		printf("Case #%d: %I64d\n", i+1, r);
	}
	return 0;
}
<commit_msg>fix problem laundromatt<commit_after>//Author: Stefan Toman

#include <iostream>
#include <string.h>
#include <algorithm>
#include <queue>

using namespace std;

struct interval {
	int id;
	long long end;

	interval(int id, long long end) : id(id), end(end) {};
};

bool operator<(const interval& a, const interval& b) {
	return a.end != b.end ? a.end > b.end : a.id > b.id;
}

int main() {
	int t;
	cin >> t;
	for (int i = 0; i < t; ++i) {
        int n;
        long long l, m, d;
		cin >> l >> n >> m >> d;
		long long w[n], r = 0;
		priority_queue<interval> q;
		for (int j = 0; j < n; j++) {
			cin >> w[j];
			q.push(*new interval(j, w[j]));
		}
		priority_queue<interval> q2;
		int mused = 0;
		for (int j = 0; j < l; j++) {
			interval o = q.top();
			q.pop();
			q.push(*new interval(o.id, o.end + w[o.id]));
			if(mused < m) {
				q2.push(*new interval(mused, o.end + d));
				mused++;
				if(r < o.end + d) {
					r = o.end + d;
				}
			}
			else {
				interval o2 = q2.top();
				q2.pop();
				long long e = max(o2.end, o.end) + d;
				q2.push(*new interval(o2.id, e));
				if(r < e) {
					r = e;
				}
			}
		}

		cout << "Case #" << i+1 << ": " << r << endl;
	}
	return 0;
}
<|endoftext|>


<commit_before>#include "Obstacle.h"

Obstacle::Obstacle(Scene *s, double x, double y, double r): object_radius(r)
{
	this->scene = s;
	this->object_position = glm::vec2(x, y);
}

Obstacle::~Obstacle(void)
{

}

void Obstacle::Update(double delta_time)
{

}

void Obstacle::Draw()
{
	glEnable(GL_LINE_SMOOTH);
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

	float a, b;
	glPushMatrix();
	glTranslatef(object_position.x, object_position.x, 0.0);
	glBegin(GL_TRIANGLE_FAN);
	glColor3f(0.3f, 0.1f, 0.0f);

	a = (float)object_radius * (float)cos(359 * PI / 180.0f);
	b = (float)object_radius * (float)sin(359 * PI / 180.0f);
	for (int j = 0; j < 360; j++)
	{
		glVertex2f(a, b);
		a = (float)object_radius * (float)cos(j * PI / 180.0f);
		b = (float)object_radius * (float)sin(j * PI / 180.0f);
		glVertex2f(a, b);
	}

	glEnd();
	glPopMatrix();

	glDisable(GL_LINE_SMOOTH);
}

ostream& operator<<(ostream &o, const Obstacle &ob)
{
    o << "-------------- OBSTACLE --------------" << "\n";
    o << "--------------------------------------" << endl;
    return o;
}
<commit_msg>Obstacle<commit_after>#include "Obstacle.h"

Obstacle::Obstacle(Scene *s, double x, double y, double r): object_radius(r)
{
	this->scene = s;
	this->object_position = glm::vec2(x, y);
	object_scale = glm::vec2(0.03f, 0.03f);
	model_matrix = glm::mat4(1.0f);
	model_matrix = glm::scale(model_matrix, glm::vec3(object_scale, 1.0f));
	model_matrix = glm::translate(model_matrix, glm::vec3(object_position, 0.0f));
}

Obstacle::~Obstacle(void)
{

}

void Obstacle::Update(double delta_time)
{

}

void Obstacle::Draw()
{
	float a, b;
	glPushMatrix();
	GLfloat Matrix[16];
	MatrixToArray(Matrix, model_matrix, scene->GetViewMatrix());
	glLoadMatrixf(Matrix);
	glBegin(GL_TRIANGLE_FAN);
	glColor3f(0.3f, 0.1f, 0.0f);

	a = (float)object_radius * (float)cos(359 * PI / 180.0f);
	b = (float)object_radius * (float)sin(359 * PI / 180.0f);
	for (int j = 0; j < 360; j++)
	{
		glVertex2f(a, b);
		a = (float)object_radius * (float)cos(j * PI / 180.0f);
		b = (float)object_radius * (float)sin(j * PI / 180.0f);
		glVertex2f(a, b);
	}

	glEnd();
	glPopMatrix();
}

ostream& operator<<(ostream &o, const Obstacle &ob)
{
    o << "-------------- OBSTACLE --------------" << "\n";
    o << "--------------------------------------" << endl;
    return o;
}
<|endoftext|>


<commit_before>//  Copyright 2020 Tangent Animation
//
//  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,
//  including without limitation, as related to merchantability and fitness
//  for a particular purpose.
//
//  In no event shall any copyright holder be liable for any damages of any kind
//  arising from the use of this software, whether in contract, tort or otherwise.
//  See the License for the specific language governing permissions and
//  limitations under the License.

#include "renderPass.h"

#include "camera.h"
#include "renderBuffer.h"
#include "renderParam.h"
#include "utils.h"

#include <render/camera.h>
#include <render/scene.h>
#include <render/session.h>
#include <util/util_types.h>

#include <pxr/base/tf/staticTokens.h>
#include <pxr/imaging/hd/camera.h>
#include <pxr/imaging/hd/renderPassState.h>

PXR_NAMESPACE_OPEN_SCOPE

// clang-format off
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
#endif
TF_DEFINE_PRIVATE_TOKENS(_tokens,
    (color)
    (depth)
);
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
// clang-format on

HdCyclesRenderPass::HdCyclesRenderPass(HdCyclesRenderDelegate* delegate, HdRenderIndex* index,
                                       HdRprimCollection const& collection)
    : HdRenderPass(index, collection)
    , m_delegate(delegate)
{
}

HdCyclesRenderPass::~HdCyclesRenderPass() {}

void
HdCyclesRenderPass::_Execute(HdRenderPassStateSharedPtr const& renderPassState, TfTokenVector const& renderTags)
{
    auto* renderParam = reinterpret_cast<HdCyclesRenderParam*>(m_delegate->GetRenderParam());

    HdRenderPassAovBindingVector aovBindings = renderPassState->GetAovBindings();

    if (renderParam->GetAovBindings() != aovBindings) {
        renderParam->SetAovBindings(aovBindings);
        if (!aovBindings.empty()) {
            renderParam->SetDisplayAov(aovBindings[0]);
        }
    }

    const auto vp = renderPassState->GetViewport();

    GfMatrix4d projMtx = renderPassState->GetProjectionMatrix();
    GfMatrix4d viewMtx = renderPassState->GetWorldToViewMatrix();

    m_isConverged = renderParam->IsConverged();

    // XXX: Need to cast away constness to process updated camera params since
    // the Hydra camera doesn't update the Cycles camera directly.
    HdCyclesCamera* hdCam = const_cast<HdCyclesCamera*>(
        dynamic_cast<HdCyclesCamera const*>(renderPassState->GetCamera()));

    ccl::Camera* active_camera = renderParam->GetCyclesSession()->scene->camera;

    bool shouldUpdate = false;

    if (projMtx != m_projMtx || viewMtx != m_viewMtx) {
        m_projMtx = projMtx;
        m_viewMtx = viewMtx;

        const float fov_rad = atanf(1.0f / static_cast<float>(m_projMtx[1][1])) * 2.0f;
        hdCam->SetFOV(fov_rad);

        shouldUpdate = true;
    }

    if (!shouldUpdate)
        shouldUpdate = hdCam->IsDirty();

    if (shouldUpdate) {
        hdCam->ApplyCameraSettings(active_camera);

        // Needed for now, as houdini looks through a generated camera
        // and doesn't copy the projection type (as of 18.0.532)
        bool is_ortho = round(m_projMtx[3][3]) == 1.0;

        if (is_ortho) {
            active_camera->type = ccl::CameraType::CAMERA_ORTHOGRAPHIC;
        } else
            active_camera->type = ccl::CameraType::CAMERA_PERSPECTIVE;

        active_camera->tag_update();

        // DirectReset here instead of Interrupt for faster IPR camera orbits
        renderParam->DirectReset();
    }

    const auto width  = static_cast<int>(vp[2]);
    const auto height = static_cast<int>(vp[3]);

    if (width != m_width || height != m_height) {
        m_width  = width;
        m_height = height;

        // TODO: Due to the startup flow of Cycles, this gets called after a tiled render
        // has already started. Sometimes causing the original tiled render to complete
        // before actually rendering at the appropriate size. This seems to be a Cycles
        // issue, however the startup flow of HdCycles has LOTS of room for improvement...
        renderParam->SetViewport(m_width, m_height);

        // TODO: This is very hacky... But stops the tiled render double render issue...
        if (renderParam->IsTiledRender()) {
            renderParam->StartRender();
        }

        renderParam->Interrupt();
    }

    // Tiled renders early out because we do the blitting on render tile callback
    if (renderParam->IsTiledRender())
        return;

    if (!renderParam->GetCyclesSession())
        return;

    if (!renderParam->GetCyclesScene())
        return;

    ccl::DisplayBuffer* display = renderParam->GetCyclesSession()->display;

    if (!display)
        return;

    ccl::thread_scoped_lock display_lock = renderParam->GetCyclesSession()->acquire_display_lock();

    HdFormat colorFormat = display->half_float ? HdFormatFloat16Vec4 : HdFormatUNorm8Vec4;

    unsigned char* hpixels = (display->half_float) ? static_cast<unsigned char*>(display->rgba_half.host_pointer)
                                                   : static_cast<unsigned char*>(display->rgba_byte.host_pointer);

    if (!hpixels)
        return;

    int w = display->draw_width;
    int h = display->draw_height;

    if (w == 0 || h == 0)
        return;

    // Blit
    if (!aovBindings.empty()) {
        // Blit from the framebuffer to currently selected aovs...
        for (auto& aov : aovBindings) {
            if (!TF_VERIFY(aov.renderBuffer != nullptr)) {
                continue;
            }

            auto* rb = static_cast<HdCyclesRenderBuffer*>(aov.renderBuffer);
            rb->SetConverged(m_isConverged);

            // Needed as a stopgap, because Houdini dellocates renderBuffers
            // when changing render settings. This causes the current blit to
            // fail (Probably can be fixed with proper render thread management)
            if (!rb->WasUpdated()) {
                if (aov.aovName == renderParam->GetDisplayAovToken()) {
                    rb->Blit(colorFormat, w, h, 0, w, hpixels);
                }
            } else {
                rb->SetWasUpdated(false);
            }
        }
    }
}

PXR_NAMESPACE_CLOSE_SCOPE
<commit_msg>fixing null ptr camera<commit_after>//  Copyright 2020 Tangent Animation
//
//  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,
//  including without limitation, as related to merchantability and fitness
//  for a particular purpose.
//
//  In no event shall any copyright holder be liable for any damages of any kind
//  arising from the use of this software, whether in contract, tort or otherwise.
//  See the License for the specific language governing permissions and
//  limitations under the License.

#include "renderPass.h"

#include "camera.h"
#include "renderBuffer.h"
#include "renderParam.h"
#include "utils.h"

#include <render/camera.h>
#include <render/scene.h>
#include <render/session.h>
#include <util/util_types.h>

#include <pxr/base/tf/staticTokens.h>
#include <pxr/imaging/hd/camera.h>
#include <pxr/imaging/hd/renderPassState.h>

PXR_NAMESPACE_OPEN_SCOPE

// clang-format off
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
#endif
TF_DEFINE_PRIVATE_TOKENS(_tokens,
    (color)
    (depth)
);
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
// clang-format on

HdCyclesRenderPass::HdCyclesRenderPass(HdCyclesRenderDelegate* delegate, HdRenderIndex* index,
                                       HdRprimCollection const& collection)
    : HdRenderPass(index, collection)
    , m_delegate(delegate)
{
}

HdCyclesRenderPass::~HdCyclesRenderPass() {}

void
HdCyclesRenderPass::_Execute(HdRenderPassStateSharedPtr const& renderPassState, TfTokenVector const& renderTags)
{
    auto* renderParam = reinterpret_cast<HdCyclesRenderParam*>(m_delegate->GetRenderParam());

    HdRenderPassAovBindingVector aovBindings = renderPassState->GetAovBindings();

    if (renderParam->GetAovBindings() != aovBindings) {
        renderParam->SetAovBindings(aovBindings);
        if (!aovBindings.empty()) {
            renderParam->SetDisplayAov(aovBindings[0]);
        }
    }

    const auto vp = renderPassState->GetViewport();

    GfMatrix4d projMtx = renderPassState->GetProjectionMatrix();
    GfMatrix4d viewMtx = renderPassState->GetWorldToViewMatrix();

    m_isConverged = renderParam->IsConverged();

    // XXX: Need to cast away constness to process updated camera params since
    // the Hydra camera doesn't update the Cycles camera directly.
    auto hdCam = const_cast<HdCyclesCamera*>(dynamic_cast<HdCyclesCamera const*>(renderPassState->GetCamera()));

    bool shouldUpdate = false;

    if(hdCam) {
        ccl::Camera* active_camera = renderParam->GetCyclesSession()->scene->camera;

        if (projMtx != m_projMtx || viewMtx != m_viewMtx) {
            m_projMtx = projMtx;
            m_viewMtx = viewMtx;

            const float fov_rad = atanf(1.0f / static_cast<float>(m_projMtx[1][1])) * 2.0f;
            hdCam->SetFOV(fov_rad);

            shouldUpdate = true;
        }

        if (!shouldUpdate)
            shouldUpdate = hdCam->IsDirty();

        if (shouldUpdate) {
            hdCam->ApplyCameraSettings(active_camera);

            // Needed for now, as houdini looks through a generated camera
            // and doesn't copy the projection type (as of 18.0.532)
            bool is_ortho = round(m_projMtx[3][3]) == 1.0;

            if (is_ortho) {
                active_camera->type = ccl::CameraType::CAMERA_ORTHOGRAPHIC;
            } else
                active_camera->type = ccl::CameraType::CAMERA_PERSPECTIVE;

            active_camera->tag_update();

            // DirectReset here instead of Interrupt for faster IPR camera orbits
            renderParam->DirectReset();
        }
    }

    const auto width  = static_cast<int>(vp[2]);
    const auto height = static_cast<int>(vp[3]);

    if (width != m_width || height != m_height) {
        m_width  = width;
        m_height = height;

        // TODO: Due to the startup flow of Cycles, this gets called after a tiled render
        // has already started. Sometimes causing the original tiled render to complete
        // before actually rendering at the appropriate size. This seems to be a Cycles
        // issue, however the startup flow of HdCycles has LOTS of room for improvement...
        renderParam->SetViewport(m_width, m_height);

        // TODO: This is very hacky... But stops the tiled render double render issue...
        if (renderParam->IsTiledRender()) {
            renderParam->StartRender();
        }

        renderParam->Interrupt();
    }

    // Tiled renders early out because we do the blitting on render tile callback
    if (renderParam->IsTiledRender())
        return;

    if (!renderParam->GetCyclesSession())
        return;

    if (!renderParam->GetCyclesScene())
        return;

    ccl::DisplayBuffer* display = renderParam->GetCyclesSession()->display;

    if (!display)
        return;

    ccl::thread_scoped_lock display_lock = renderParam->GetCyclesSession()->acquire_display_lock();

    HdFormat colorFormat = display->half_float ? HdFormatFloat16Vec4 : HdFormatUNorm8Vec4;

    unsigned char* hpixels = (display->half_float) ? static_cast<unsigned char*>(display->rgba_half.host_pointer)
                                                   : static_cast<unsigned char*>(display->rgba_byte.host_pointer);

    if (!hpixels)
        return;

    int w = display->draw_width;
    int h = display->draw_height;

    if (w == 0 || h == 0)
        return;

    // Blit
    if (!aovBindings.empty()) {
        // Blit from the framebuffer to currently selected aovs...
        for (auto& aov : aovBindings) {
            if (!TF_VERIFY(aov.renderBuffer != nullptr)) {
                continue;
            }

            auto* rb = static_cast<HdCyclesRenderBuffer*>(aov.renderBuffer);
            rb->SetConverged(m_isConverged);

            // Needed as a stopgap, because Houdini dellocates renderBuffers
            // when changing render settings. This causes the current blit to
            // fail (Probably can be fixed with proper render thread management)
            if (!rb->WasUpdated()) {
                if (aov.aovName == renderParam->GetDisplayAovToken()) {
                    rb->Blit(colorFormat, w, h, 0, w, hpixels);
                }
            } else {
                rb->SetWasUpdated(false);
            }
        }
    }
}

PXR_NAMESPACE_CLOSE_SCOPE
<|endoftext|>