<commit_before>//-------------------------------------------------------------------------------
///
/// \file       scene.cpp
/// \author     Cem Yuksel (www.cemyuksel.com)
/// \version    1.0
/// \date       August 26, 2013
///
/// \brief Example source for CS 6620 - University of Utah.
///
//-------------------------------------------------------------------------------

#ifndef _SCENE_CPP_INCLUDED_
#define _SCENE_CPP_INCLUDED_

//-------------------------------------------------------------------------------

#include <vector>

#include "library/cyPoint.h"
typedef cyPoint3f Point3;

#include "library/cyMatrix3.h"
typedef cyMatrix3f Matrix3;

//-------------------------------------------------------------------------------

#ifndef min
# define min(a,b) ((a)<(b)?(a):(b))
#endif

#ifndef max
# define max(a,b) ((a)>(b)?(a):(b))
#endif

#define BIGFLOAT 1.0e30f

namespace scene{

//-------------------------------------------------------------------------------

class Ray
{
public:
    Point3 p, dir;

    Ray() {}
    Ray(const Point3 &_p, const Point3 &_dir) : p(_p), dir(_dir) {}
    void Normalize() { dir.Normalize(); }
};

//-------------------------------------------------------------------------------

class Node;

#define HIT_NONE            0
#define HIT_FRONT           1
#define HIT_BACK            2
#define HIT_FRONT_AND_BACK  (HIT_FRONT|HIT_BACK)

struct HitInfo
{
    float z;            // the distance from the ray center to the hit point
    const Node *node;   // the object node that was hit
    bool front;         // true if the ray hits the front side, false if the ray hits the back side

    HitInfo() { Init(); }
    void Init() { z=BIGFLOAT; node=NULL; front=true; }
};

//-------------------------------------------------------------------------------

class ItemBase
{
private:
    char *name;                 // The name of the item

public:
    ItemBase() : name(NULL) {}
    virtual ~ItemBase() { if ( name ) delete [] name; }

    const char* GetName() const { return name ? name : ""; }
    void SetName(const char *newName)
    {
        if ( name ) delete [] name;
        if ( newName ) {
            int n = strlen(newName);
            name = new char[n+1];
            for ( int i=0; i<n; i++ ) name[i] = newName[i];
            name[n] = '\0';
        } else { name = NULL; }
    }
};

template <class T> class ItemList : public std::vector<T*>
{
public:
    virtual ~ItemList() { DeleteAll(); }
    void DeleteAll() { int n=this->size(); for ( int i=0; i<n; i++ ) if ( this->at(i) ) delete this->at(i); }
};

template <class T> class ItemFileList
{
public:
    void Clear() { list.DeleteAll(); }
    void Append( T* item, const char *name ) { list.push_back( new FileInfo(item,name) ); }
    T* Find( const char *name ) const { int n=list.size(); for ( int i=0; i<n; i++ ) if ( list[i] && strcmp(name,list[i]->GetName())==0 ) return list[i]->GetObj(); return NULL; }

private:
    class FileInfo : public ItemBase
    {
    private:
        T *item;
    public:
        FileInfo() : item(NULL) {}
        FileInfo(T *_item, const char *name) : item(_item) { SetName(name); }
        ~FileInfo() { Delete(); }
        void Delete() { if (item) delete item; item=NULL; }
        void SetObj(T *_item) { Delete(); item=_item; }
        T* GetObj() { return item; }
    };

    ItemList<FileInfo> list;
};

//-------------------------------------------------------------------------------

class Transformation
{
private:
    Matrix3 tm;                     // Transformation matrix to the local space
    Point3 pos;                     // Translation part of the transformation matrix
    mutable Matrix3 itm;            // Inverse of the transformation matrix (cached)
public:
    Transformation() : pos(0,0,0) { tm.SetIdentity(); itm.SetIdentity(); }
    const Matrix3& GetTransform() const { return tm; }
    const Point3& GetPosition() const { return pos; }
    const Matrix3&  GetInverseTransform() const { return itm; }

    Point3 TransformTo( const Point3 &p ) const { return itm * (p - pos); } // Transform to the local coordinate system
    Point3 TransformFrom( const Point3 &p ) const { return tm*p + pos; }    // Transform from the local coordinate system

    // Transforms a vector to the local coordinate system (same as multiplication with the inverse transpose of the transformation)
    Point3 VectorTransformTo( const Point3 &dir ) const { return TransposeMult(tm,dir); }

    // Transforms a vector from the local coordinate system (same as multiplication with the inverse transpose of the transformation)
    Point3 VectorTransformFrom( const Point3 &dir ) const { return TransposeMult(itm,dir); }

    void Translate(Point3 p) { pos+=p; }
    void Rotate(Point3 axis, float degree) { Matrix3 m; m.SetRotation(axis,degree*(float)M_PI/180.0f); Transform(m); }
    void Scale(float sx, float sy, float sz) { Matrix3 m; m.Zero(); m[0]=sx; m[4]=sy; m[8]=sz; Transform(m); }
    void Transform(const Matrix3 &m) { tm*=m; pos=m*pos; tm.GetInverse(itm); }

    void InitTransform() { pos.Zero(); tm.SetIdentity(); itm.SetIdentity(); }

private:
    // Multiplies the given vector with the transpose of the given matrix
    static Point3 TransposeMult( const Matrix3 &m, const Point3 &dir )
    {
        Point3 d;
        d.x = m.GetColumn(0) % dir;
        d.y = m.GetColumn(1) % dir;
        d.z = m.GetColumn(2) % dir;
        return d;
    }
};

//-------------------------------------------------------------------------------

// Base class for all object types
class Object
{
public:
  virtual ~Object()=0;
  virtual bool IntersectRay( const Ray &ray, HitInfo &hit, int face=HIT_FRONT ) const=0;
  //void ViewportDisplay() const {} // used for OpenGL display
};
Object::~Object(){}

typedef ItemFileList<Object> ObjFileList;

//-------------------------------------------------------------------------------

class Node : public ItemBase, public Transformation
{
private:
    Node **child;               // Child nodes
    int numChild;               // The number of child nodes
    Object *obj;                // Object reference (merely points to the object, but does not own the object, so it doesn't get deleted automatically)
public:
    Node() : child(NULL), numChild(0), obj(NULL) {}

    void Init() { DeleteAllChildNodes(); obj=NULL; SetName(NULL); InitTransform(); } // Initialize the node deleting all child nodes

    // Hierarchy management
    int  GetNumChild() const { return numChild; }
    void SetNumChild(int n, int keepOld=false)
    {
        if ( n < 0 ) n=0;   // just to be sure
        Node **nc = NULL;   // new child pointer
        if ( n > 0 ) nc = new Node*[n];
        for ( int i=0; i<n; i++ ) nc[i] = NULL;
        if ( keepOld ) {
            int sn = min(n,numChild);
            for ( int i=0; i<sn; i++ ) nc[i] = child[i];
        }
        if ( child ) delete [] child;
        child = nc;
        numChild = n;
    }
    const Node* GetChild(int i) const       { return child[i]; }
    Node*       GetChild(int i)             { return child[i]; }
    void        SetChild(int i, Node *node) { child[i]=node; }
    void        AppendChild(Node *node)     { SetNumChild(numChild+1,true); SetChild(numChild-1,node); }
    void        RemoveChild(int i)          { for ( int j=i; j<numChild-1; j++) child[j]=child[j-1]; SetNumChild(numChild-1); }
    void        DeleteAllChildNodes()       { for ( int i=0; i<numChild; i++ ) { child[i]->DeleteAllChildNodes(); delete child[i]; } SetNumChild(0); }

    // Object management
    const Object*   GetObject() const { return obj; }
    Object*         GetObject() { return obj; }
    void            SetObject(Object *object) { obj=object; }

    // Transformations
    Ray ToNodeCoords( const Ray &ray ) const
    {
        Ray r;
        r.p   = TransformTo(ray.p);
        r.dir = TransformTo(ray.p + ray.dir) - r.p;
        return r;
    }
};

//-------------------------------------------------------------------------------

class Camera
{
public:
    Point3 pos, dir, up;
    float fov;
    int imgWidth, imgHeight;

    void Init()
    {
        pos.Set(0,0,0);
        dir.Set(0,0,-1);
        up.Set(0,1,0);
        fov = 40;
        imgWidth = 200;
        imgHeight = 150;
    }
};

//-------------------------------------------------------------------------------

typedef unsigned char uchar;

struct Color24
{
    uchar r, g, b;
};

//-------------------------------------------------------------------------------

class RenderImage
{
private:
    Color24 *img;
    float   *zbuffer;
    uchar   *zbuffer8;
    int     width, height;
    int     numRenderedPixels;
public:
    RenderImage() : img(NULL), zbuffer(NULL), zbuffer8(NULL), width(0), height(0), numRenderedPixels(0) {}
    void Init(int w, int h)
    {
        width=w;
        height=h;
        if (img) delete [] img;
        img = new Color24[width*height];
        if (zbuffer) delete [] zbuffer;
        zbuffer = new float[width*height];
        for(int i = 0; i < width * height; i++)
          zbuffer[i] = BIGFLOAT;
        if (zbuffer8) delete [] zbuffer8;
        zbuffer8 = NULL;
        ResetNumRenderedPixels();
    }
    void setBackground(Color24 c){
      for(int i = 0; i < width * height; i++)
        img[i] = c;
    }
    int         GetWidth() const    { return width; }
    int         GetHeight() const   { return height; }
    Color24*    GetPixels()         { return img; }
    float*      GetZBuffer()        { return zbuffer; }
    uchar*      GetZBufferImage()   { return zbuffer8; }

    void    ResetNumRenderedPixels()        { numRenderedPixels=0; }
    int     GetNumRenderedPixels() const    { return numRenderedPixels; }
    void    IncrementNumRenderPixel()       { numRenderedPixels++; }
    void    IncrementNumRenderPixel(int n)  { numRenderedPixels+=n; }   // not thread-safe
    bool    IsRenderDone() const            { return numRenderedPixels >= width*height; }

    void    ComputeZBufferImage()
    {
        int size = width * height;
        if (zbuffer8) delete [] zbuffer8;
        zbuffer8 = new unsigned char[size];

        float zmin=BIGFLOAT, zmax=0;
        for ( int i=0; i<size; i++ ) {
            if ( zbuffer[i] == BIGFLOAT ) continue;
            if ( zmin > zbuffer[i] ) zmin = zbuffer[i];
            if ( zmax < zbuffer[i] ) zmax = zbuffer[i];
        }
        for ( int i=0; i<size; i++ ) {
            if ( zbuffer[i] == BIGFLOAT ) zbuffer8[i] = 33;
            else {
                float f = (zmax-zbuffer[i])/(zmax-zmin);
                int c = int(f * 200);
                if ( c < 0 ) f = 0;
                if ( c > 200 ) f = 200;
                zbuffer8[i] = c + 47;
            }
        }
    }

    bool SaveImage(const char *filename) const { return SavePPM(filename,&img[0].r,3); }
    bool SaveZImage(const char *filename) const { return SavePPM(filename,zbuffer8,1); }

private:
    bool SavePPM(const char *filename, uchar *data, int compCount) const
    {
        FILE *fp = fopen(filename,"wb");
        if ( !fp ) return false;
        fprintf(fp,"P6\n%d %d\n255\n", width, height);
        switch( compCount ) {
        case 1:
            for ( int i=0; i<width*height; i++ ) {
                uchar d[3] = { data[i], data[i], data[i] };
                fwrite(d,1,3,fp);
            }
            break;
        case 3:
            fwrite(data,width*height,3,fp);
            break;
        }
        fclose(fp);
        return true;
    }
};

//-------------------------------------------------------------------------------

}

#endif
<commit_msg>major rewrite of scene class, need to clean up and refactor majorly next<commit_after>// Copyright 2013 Sean McKenna
//
//    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.
//

// scene class (nodes, objects, transformation, camera, rendering, etc.)
// originally adapted from code provided by Cem Yuksel

// libraries, namespace
#ifndef _SCENE_CPP_INCLUDED_
#define _SCENE_CPP_INCLUDED_
#include <vector>
#include "library/cyPoint.h"
typedef cyPoint3f Point3;
#include "library/cyMatrix3.h"
typedef cyMatrix3f Matrix3;
using namespace std;

// custom functions
#ifndef min
#  define min(a, b) ((a) < (b) ? (a):(b))
#endif
#ifndef max
#  define max(a, b) ((a) > (b) ? (a):(b))
#endif
#define BIGFLOAT 1.0e30f

// declare namespace
namespace scene{

// Ray definition (point & direction)
class Ray{
  public:
    Point3 p, dir;
    Ray(){}
    Ray(const Point3 &_p, const Point3 &_dir): p(_p), dir(_dir) {}
    void Normalize(){
      dir.Normalize();
    }
};

// Node definition
class Node;

// Hit Info definition (for each node)
#define HIT_NONE 0
#define HIT_FRONT 1
#define HIT_BACK 2
#define HIT_FRONT_AND_BACK (HIT_FRONT | HIT_BACK)
struct HitInfo{

  // distance from ray to the hit point
  float z;

  // object node that is hit
  const Node *node;

  // returns true if the object is hit on a front face, false if back face
  bool front;

  // initialization
  HitInfo(){
    Init();
  }
  void Init(){
    z = BIGFLOAT;
    node = NULL;
    front = true;
  }
};

// Item Base definition
class ItemBase{
  private:

    // name of the item
    char *name;

  public:
    ItemBase(): name(NULL) {}
    virtual ~ItemBase(){
      if(name)
        delete [] name;
    }

    const char* GetName() const{
      return name ? name: "";
    }

    void SetName(const char *newName){
      if(name)
        delete [] name;
      if(newName){
        int n = strlen(newName);
        name = new char[n + 1];
        for(int i = 0; i < n; i++)
          name[i] = newName[i];
        name[n] = '\0';
      }else{
        name = NULL;
      }
    }
};

// ItemList template definition
template <class T> class ItemList: public vector <T*>{
  public:
    virtual ~ItemList(){
      DeleteAll();
    }
    void DeleteAll(){
      int n = this->size();
      for(int i = 0; i < n; i++)
        if(this->at(i))
          delete this->at(i);
    }
};

// ItemFileList template definition
template <class T> class ItemFileList{
  public:
    void Clear(){
      list.DeleteAll();
    }
    void Append(T* item, const char *name){
      list.push_back(new FileInfo(item, name));
    }
    T* Find(const char *name) const{
      int n = list.size();
      for(int i = 0; i < n; i++)
        if(list[i] && strcmp(name, list[i]->GetName()) == 0)
          return list[i]->GetObj();
        return NULL;
    }

  private:
    class FileInfo: public ItemBase{
      private:
        T *item;

      public:
        FileInfo(): item(NULL) {}
        FileInfo(T *_item, const char *name): item(_item){
          SetName(name);
        }
        ~FileInfo(){
          Delete();
        }
        void Delete(){
          if(item)
            delete item;
          item = NULL;
        }
        void SetObj(T *_item){
          Delete();
          item = _item;
        }
        T* GetObj(){
          return item;
        }
    };

    ItemList<FileInfo> list;
};

// Transformation definition
class Transformation{
  private:

    // transformation matrix (to local space)
    Matrix3 tm;

    // translation part of transformation
    Point3 pos;

    // inverse of transformation matrix (cached)
    mutable Matrix3 itm;

  public:
    Transformation(): pos(0, 0, 0){
      tm.SetIdentity();
      itm.SetIdentity();
    }
    const Matrix3& GetTransform() const{
      return tm;
    }
    const Point3& GetPosition() const{
      return pos;
    }
    const Matrix3& GetInverseTransform() const{
      return itm;
    }

    // transform into local space
    Point3 TransformTo(const Point3 &p) const{
      return itm * (p - pos);
    }

    // transform from local space
    Point3 TransformFrom(const Point3 &p) const{
      return tm * p + pos;
    }

    // transform vector to local space
    Point3 VectorTransformTo(const Point3 &dir) const{
      return TransposeMult(tm, dir);
    }

    // transform vector from local space
    Point3 VectorTransformFrom(const Point3 &dir) const{
      return TransposeMult(itm, dir);
    }

    void Translate(Point3 p){
      pos += p;
    }
    void Rotate(Point3 axis, float degree){
      Matrix3 m;
      m.SetRotation(axis, degree * (float) M_PI / 180.0);
      Transform(m);
    }
    void Scale(float sx, float sy, float sz){
      Matrix3 m;
      m.Zero();
      m[0] = sx;
      m[4] = sy;
      m[8] = sz;
      Transform(m);
    }
    void Transform(const Matrix3 &m){
      tm *= m;
      pos = m * pos;
      tm.GetInverse(itm);
    }
    void InitTransform(){
      pos.Zero();
      tm.SetIdentity();
      itm.SetIdentity();
    }

  private:

    // multiples given vector with transpose of the matrix
    static Point3 TransposeMult(const Matrix3 &m, const Point3 &dir){
      Point3 d;
      d.x = m.GetColumn(0) % dir;
      d.y = m.GetColumn(1) % dir;
      d.z = m.GetColumn(2) % dir;
      return d;
    }
};

// Object definition (extended for all kinds of objects attached to a node)
class Object{
  public:
    virtual ~Object() = 0;
    virtual bool IntersectRay(const Ray &ray, HitInfo &hit, int face = HIT_FRONT) const = 0;
};
Object::~Object(){}

// ObjectFileList definition
typedef ItemFileList<Object> ObjFileList;

// Node definition (stores objects)
class Node: public ItemBase, public Transformation{
  private:

    // child nodes
    Node **child;

    // number of child nodes
    int numChild;

    // object reference
    Object *obj;

  public:
    Node(): child(NULL), numChild(0), obj(NULL) {}

    // initialize the node, by deleting all its children
    void Init(){
      DeleteAllChildNodes();
      obj = NULL;
      SetName(NULL);
      InitTransform();
    }

    // managing hierarchy of nodes
    int GetNumChild() const{
      return numChild;
    }
    void SetNumChild(int n, int keepOld = false){
      if(n < 0)
        n = 0;

      // create a new child pointer
      Node **nc = NULL;

      if(n > 0)
        nc = new Node*[n];
      for(int i = 0; i < n; i++)
        nc[i] = NULL;
      if(keepOld){
        int sn = min(n, numChild);
        for(int i = 0; i < sn; i++)
          nc[i] = child[i];
      }
      if(child)
        delete [] child;
      child = nc;
      numChild = n;
    }

    const Node* GetChild(int i) const{
      return child[i];
    }
    Node* GetChild(int i){
      return child[i];
    }
    void SetChild(int i, Node *node){
      child[i] = node;
    }
    void AppendChild(Node *node){
      SetNumChild(numChild + 1, true);
      SetChild(numChild - 1, node);
    }
    void RemoveChild(int i){
      for(int j = i; j < numChild - 1; j++)
        child[j] = child[j - 1];
      SetNumChild(numChild - 1);
    }
    void DeleteAllChildNodes(){
      for(int i = 0; i < numChild; i++){
        child[i]->DeleteAllChildNodes();
        delete child[i];
      }
      SetNumChild(0);
    }

    // managing node objects
    const Object* GetObject() const{
      return obj;
    }
    Object* GetObject(){
      return obj;
    }
    void SetObject(Object *object){
      obj = object;
    }

    // transformations
    Ray ToNodeCoords(const Ray &ray) const{
      Ray r;
      r.p = TransformTo(ray.p);
      r.dir = TransformTo(ray.p + ray.dir) - r.p;
      return r;
    }
};

// Camera definition
class Camera{
  public:
    Point3 pos, dir, up;
    float fov;
    int imgWidth, imgHeight;

    void Init(){
      pos.Set(0, 0, 0);
      dir.Set(0, 0, -1);
      up.Set(0, 1, 0);
      fov = 40;
      imgWidth = 200;
      imgHeight = 150;
    }
};

// Color definition
typedef unsigned char uchar;
struct Color24{
  uchar r, g, b;
};

// RenderImage definition
class RenderImage{
  private:
    Color24 *img;
    float *zbuffer;
    uchar *zbuffer8;
    int width, height;
    int numRenderedPixels;

  public:
    RenderImage(): img(NULL), zbuffer(NULL), zbuffer8(NULL), width(0), height(0), numRenderedPixels(0) {}
    void Init(int w, int h){
      width = w;
      height = h;
      if(img)
        delete [] img;
      img = new Color24[width * height];
      if(zbuffer)
        delete [] zbuffer;
      zbuffer = new float[width * height];
      for(int i = 0; i < width * height; i++)
        zbuffer[i] = BIGFLOAT;
      if(zbuffer8)
        delete [] zbuffer8;
      zbuffer8 = NULL;
      ResetNumRenderedPixels();
    }
    void setBackground(Color24 c){
      for(int i = 0; i < width * height; i++)
        img[i] = c;
    }

    int GetWidth() const{
      return width;
    }
    int GetHeight() const{
      return height;
    }
    Color24* GetPixels(){
      return img;
    }
    float* GetZBuffer(){
      return zbuffer;
    }
    uchar* GetZBufferImage(){
      return zbuffer8;
    }

    void ResetNumRenderedPixels(){
      numRenderedPixels = 0;
    }
    int GetNumRenderedPixels() const{
      return numRenderedPixels;
    }
    void IncrementNumRenderPixel(){
      numRenderedPixels++;
    }
    void IncrementNumRenderPixel(int n){
      numRenderedPixels += n;
    }
    bool IsRenderDone() const{
      return numRenderedPixels >= width * height;
    }

    void ComputeZBufferImage(){
      int size = width * height;
      if(zbuffer8)
        delete [] zbuffer8;
      zbuffer8 = new unsigned char[size];

      float zmin = BIGFLOAT, zmax = 0;
      for(int i = 0; i < size; i++){
        if(zbuffer[i] == BIGFLOAT)
          continue;
        if(zmin > zbuffer[i])
          zmin = zbuffer[i];
        if(zmax < zbuffer[i])
          zmax = zbuffer[i];
      }
      for(int i = 0; i < size; i++){
        if(zbuffer[i] == BIGFLOAT)
          zbuffer8[i] = 33;
        else{
          float f = (zmax - zbuffer[i]) / (zmax - zmin);
          int c = int(f * 200);
          if(c < 0)
            f = 0;
          if(c > 200)
            f = 200;
          zbuffer8[i] = c + 47;
        }
      }
    }

    bool SaveImage(const char *filename) const{
      return SavePPM(filename, &img[0].r, 3);
    }
    bool SaveZImage(const char *filename) const{
      return SavePPM(filename, zbuffer8, 1);
    }

  private:
    bool SavePPM(const char *filename, uchar *data, int compCount) const{
      FILE *fp = fopen(filename, "wb");
      if(!fp)
        return false;
      fprintf(fp, "P6\n%d %d\n255\n", width, height);
      switch(compCount){
        case 1:
          for(int i = 0; i < width * height; i++){
            uchar d[3] = {data[i], data[i], data[i]};
            fwrite(d, 1, 3, fp);
          }
          break;
        case 3:
          fwrite(data, width * height, 3, fp);
          break;
      }
      fclose(fp);
      return true;
    }
};

}
#endif
<|endoftext|>


<commit_before>#include "process.h"

// POSIX
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>

// POSIX++
#include <cerrno>
#include <cstdlib>
#include <climits>
#include <cstring>

// PDTK
#include <specialized/procstat.h>
#include <specialized/eventbackend.h>
#include <cxxutils/error_helpers.h>
#include <cxxutils/colors.h>

enum class command : uint8_t
{
  invoke = 0,
  executable,
  arguments,
  environment,
  environmentvar,
  workingdir,
  priority,
  uid,
  gid,
  euid,
  egid,
  resource
};

static inline vfifo& operator << (vfifo& vq, command cmd) noexcept
{ return vq << static_cast<uint8_t>(cmd); }

static std::unordered_map<pid_t, Process*> process_map; // do not try to own Process memory

void Process::init_once(void) noexcept
{
  static bool ok = true;
  if(ok)
  {
    ok = false;
    struct sigaction actions;
    actions.sa_handler = &reaper;
    ::sigemptyset(&actions.sa_mask);
    actions.sa_flags = SA_RESTART | SA_NOCLDSTOP;

    flaw(::sigaction(SIGCHLD, &actions, nullptr) == posix::error_response, posix::critical, , std::exit(1),
         "An 'impossible' situation has occurred.")
  }
}

void Process::reaper(int sig) noexcept
{
  flaw(sig != SIGCHLD, posix::warning, posix::error(std::errc::invalid_argument),,
       "Process::reaper() has been called improperly")

  pid_t pid = posix::error_response; // set value just in case
  int status = 0;
  while((pid = ::waitpid(pid_t(-1), &status, WNOHANG)) != posix::error_response) // get the next dead process (if there is one)... while the currently reaped process was valid
  {
    auto process_map_iter = process_map.find(pid); // find dead process
    if(process_map_iter != process_map.end()) // if the dead process exists...
    {
      Process* p = process_map_iter->second;
#ifndef ENABLE_PROCESS_EVENT_TRACKING
      Object::enqueue_copy(p->finished, posix::invalid_descriptor, EventData_t(EventFlags::ExitEvent, p->processId(), p->processId(), status, sig));
#endif
      EventBackend::remove(p->getStdOut());
      EventBackend::remove(p->getStdErr());
      posix::close(p->getStdOut());
      posix::close(p->getStdErr());
      posix::close(p->getStdIn());
      p->m_state = Process::State::Finished;
      process_map.erase(process_map_iter); // remove finished process from the process map
    }
  }
}

Process::Process(void) noexcept
  : m_state(State::Initializing)
{
  init_once();
  process_map.emplace(processId(), this); // add self to process map
#ifdef ENABLE_PROCESS_EVENT_TRACKING
  Object::connect(processId(), EventFlags::ExecEvent, started);
  Object::connect(processId(), EventFlags::ExitEvent, finished);
#endif
}

Process::~Process(void) noexcept
{
  if(m_state == State::Running)
    sendSignal(posix::signal::Kill);

  Object::disconnect(getStdOut(), EventFlags::Readable);
  Object::disconnect(getStdErr(), EventFlags::Readable);
  Object::disconnect(processId(), EventFlags::ExecEvent);
  Object::disconnect(processId(), EventFlags::ExitEvent);
  m_state = State::Invalid;
}

bool Process::write_then_read(void) noexcept
{
  if(m_iobuf.empty() || // ensure there is data to write
     !writeStdIn(m_iobuf) || // write data to pipe
     !waitReadStdOut(1000) || // wait to read pipe
     !readStdOut(m_iobuf) || // read data from pipe
     (m_iobuf >> errno).hadError()) // read return value
    return false;
  return errno == posix::success_response;
}

bool Process::setArguments(const std::vector<std::string>& arguments) noexcept
{
  m_iobuf.reset();
  m_iobuf << command::arguments;
  for(const std::string& arg : arguments)
    m_iobuf << arg;
  return write_then_read();
}

bool Process::setEnvironment(const std::unordered_map<std::string, std::string>& environment) noexcept
{
  m_iobuf.reset();
  m_iobuf << command::environment;
  for(const std::pair<std::string, std::string>& p : environment)
    m_iobuf << p.first << p.second;
  return write_then_read();
}

bool Process::setEnvironmentVariable(const std::string& name, const std::string& value) noexcept
{
  m_iobuf.reset();
  m_iobuf << command::environment << name << value;
  return write_then_read();
}

bool Process::setResourceLimit(Process::Resource which, rlim_t limit) noexcept
{
  m_iobuf.reset();
  m_iobuf << command::resource
          << static_cast<int>(which)
          << static_cast<rlim_t>(RLIM_SAVED_CUR)
          << limit;
  return write_then_read();
}

bool Process::setWorkingDirectory(const std::string& dir) noexcept
{
  m_iobuf.reset();
  m_iobuf << command::workingdir << dir;
  return write_then_read();
}

bool Process::setExecutable(const std::string& executable) noexcept
{
  m_iobuf.reset();
  m_iobuf << command::executable << executable;
  return write_then_read();
}

bool Process::setUserID(uid_t id) noexcept
{
  if(posix::getpwuid(id) == nullptr)
    return false;

  m_iobuf.reset();
  m_iobuf << command::uid << id;
  return write_then_read();
}

bool Process::setGroupID(gid_t id) noexcept
{
  if(posix::getgrgid(id) == nullptr)
    return false;

  m_iobuf.reset();
  m_iobuf << command::gid << id;
  return write_then_read();
}

bool Process::setEffectiveUserID(uid_t id) noexcept
{
  if(posix::getpwuid(id) == nullptr)
    return false;

  m_iobuf.reset();
  m_iobuf << command::euid << id;
  return write_then_read();
}

bool Process::setEffectiveGroupID(gid_t id) noexcept
{
  if(posix::getgrgid(id) == nullptr)
    return false;

  m_iobuf.reset();
  m_iobuf << command::egid << id;
  return write_then_read();
}

bool Process::setPriority(int nval) noexcept
{
#if defined(PRIO_MIN) && defined(PRIO_MAX)
  if(nval < PRIO_MIN || nval > PRIO_MAX)
    return false;
#else
#warning PRIO_MIN or PRIO_MAX is not defined.  The safegaurd in Process::setPriority() is disabled.
#endif

  m_iobuf.reset();
  m_iobuf << command::priority << nval;
  return write_then_read();
}

#ifdef _XOPEN_SOURCE_EXTENDED
bool Process::sendSignal(posix::signal::EId id, int value) const noexcept
{
  return posix::signal::send(processId(), id, value);
}
#endif

bool Process::invoke(void) noexcept
{
  flaw(m_state != State::Initializing, posix::severe, posix::error(std::errc::device_or_resource_busy), false,
       "Called Process::invoke() on an active process!")

  m_iobuf.reset();
  m_iobuf << command::invoke;

  if(!writeStdIn(m_iobuf))
    return false;

  m_state = State::Invalid;
  state();

#ifndef ENABLE_PROCESS_EVENT_TRACKING
  Object::enqueue_copy(started, posix::invalid_descriptor, EventData_t(EventFlags::ExecEvent, processId(), processId()));
#endif

  Object::connect(getStdOut(), EventFlags::Readable, stdoutMessage);
  Object::connect(getStdErr(), EventFlags::Readable, stderrMessage);

  m_state = State::Running;
  return errno == posix::success_response;
}

Process::State Process::state(void) noexcept
{
  switch(m_state)
  {
    case State::Finished:
    case State::Initializing:
      break;
    default:
      process_state_t data;
      flaw(::procstat(processId(), &data) == posix::error_response, posix::severe, m_state = State::Invalid, m_state,
           "Process %i does not exist.", processId()); // process _must_ exist
      switch (data.state)
      {
        case WaitingInterruptable:
        case WaitingUninterruptable:
          m_state = State::Waiting; break;
        case Zombie : m_state = State::Zombie ; break;
        case Stopped: m_state = State::Stopped; break;
        case Running: m_state = State::Running; break;
      }
  }
  return m_state;
}
<commit_msg>portability fixes<commit_after>#include "process.h"

// POSIX
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>

// POSIX++
#include <cerrno>
#include <cstdlib>
#include <climits>
#include <cstring>

// PDTK
#include <specialized/procstat.h>
#include <specialized/eventbackend.h>
#include <cxxutils/error_helpers.h>
#include <cxxutils/colors.h>

enum class command : uint8_t
{
  invoke = 0,
  executable,
  arguments,
  environment,
  environmentvar,
  workingdir,
  priority,
  uid,
  gid,
  euid,
  egid,
  resource
};

static inline vfifo& operator << (vfifo& vq, command cmd) noexcept
{ return vq << static_cast<uint8_t>(cmd); }

static std::unordered_map<pid_t, Process*> process_map; // do not try to own Process memory

void Process::init_once(void) noexcept
{
  static bool ok = true;
  if(ok)
  {
    ok = false;
    struct sigaction actions;
    actions.sa_handler = &reaper;
    ::sigemptyset((&actions.sa_mask));
    actions.sa_flags = SA_RESTART | SA_NOCLDSTOP;

    flaw(::sigaction(SIGCHLD, &actions, nullptr) == posix::error_response, posix::critical, , std::exit(1),
         "An 'impossible' situation has occurred.")
  }
}

void Process::reaper(int sig) noexcept
{
  flaw(sig != SIGCHLD, posix::warning, posix::error(std::errc::invalid_argument),,
       "Process::reaper() has been called improperly")

  pid_t pid = posix::error_response; // set value just in case
  int status = 0;
  while((pid = ::waitpid(pid_t(-1), &status, WNOHANG)) != posix::error_response) // get the next dead process (if there is one)... while the currently reaped process was valid
  {
    auto process_map_iter = process_map.find(pid); // find dead process
    if(process_map_iter != process_map.end()) // if the dead process exists...
    {
      Process* p = process_map_iter->second;
#ifndef ENABLE_PROCESS_EVENT_TRACKING
      Object::enqueue_copy(p->finished, posix::invalid_descriptor, EventData_t(EventFlags::ExitEvent, p->processId(), p->processId(), status, sig));
#endif
      EventBackend::remove(p->getStdOut());
      EventBackend::remove(p->getStdErr());
      posix::close(p->getStdOut());
      posix::close(p->getStdErr());
      posix::close(p->getStdIn());
      p->m_state = Process::State::Finished;
      process_map.erase(process_map_iter); // remove finished process from the process map
    }
  }
}

Process::Process(void) noexcept
  : m_state(State::Initializing)
{
  init_once();
  process_map.emplace(processId(), this); // add self to process map
#ifdef ENABLE_PROCESS_EVENT_TRACKING
  Object::connect(processId(), EventFlags::ExecEvent, started);
  Object::connect(processId(), EventFlags::ExitEvent, finished);
#endif
}

Process::~Process(void) noexcept
{
#ifdef _XOPEN_SOURCE_EXTENDED
  if(m_state == State::Running)
    sendSignal(posix::signal::Kill);
#endif

  Object::disconnect(getStdOut(), EventFlags::Readable);
  Object::disconnect(getStdErr(), EventFlags::Readable);
  Object::disconnect(processId(), EventFlags::ExecEvent);
  Object::disconnect(processId(), EventFlags::ExitEvent);
  m_state = State::Invalid;
}

bool Process::write_then_read(void) noexcept
{
  if(m_iobuf.empty() || // ensure there is data to write
     !writeStdIn(m_iobuf) || // write data to pipe
     !waitReadStdOut(1000) || // wait to read pipe
     !readStdOut(m_iobuf) || // read data from pipe
     (m_iobuf >> errno).hadError()) // read return value
    return false;
  return errno == posix::success_response;
}

bool Process::setArguments(const std::vector<std::string>& arguments) noexcept
{
  m_iobuf.reset();
  m_iobuf << command::arguments;
  for(const std::string& arg : arguments)
    m_iobuf << arg;
  return write_then_read();
}

bool Process::setEnvironment(const std::unordered_map<std::string, std::string>& environment) noexcept
{
  m_iobuf.reset();
  m_iobuf << command::environment;
  for(const std::pair<std::string, std::string>& p : environment)
    m_iobuf << p.first << p.second;
  return write_then_read();
}

bool Process::setEnvironmentVariable(const std::string& name, const std::string& value) noexcept
{
  m_iobuf.reset();
  m_iobuf << command::environment << name << value;
  return write_then_read();
}

bool Process::setResourceLimit(Process::Resource which, rlim_t limit) noexcept
{
  m_iobuf.reset();
  m_iobuf << command::resource
          << static_cast<int>(which)
          << static_cast<rlim_t>(RLIM_SAVED_CUR)
          << limit;
  return write_then_read();
}

bool Process::setWorkingDirectory(const std::string& dir) noexcept
{
  m_iobuf.reset();
  m_iobuf << command::workingdir << dir;
  return write_then_read();
}

bool Process::setExecutable(const std::string& executable) noexcept
{
  m_iobuf.reset();
  m_iobuf << command::executable << executable;
  return write_then_read();
}

bool Process::setUserID(uid_t id) noexcept
{
  if(posix::getpwuid(id) == nullptr)
    return false;

  m_iobuf.reset();
  m_iobuf << command::uid << id;
  return write_then_read();
}

bool Process::setGroupID(gid_t id) noexcept
{
  if(posix::getgrgid(id) == nullptr)
    return false;

  m_iobuf.reset();
  m_iobuf << command::gid << id;
  return write_then_read();
}

bool Process::setEffectiveUserID(uid_t id) noexcept
{
  if(posix::getpwuid(id) == nullptr)
    return false;

  m_iobuf.reset();
  m_iobuf << command::euid << id;
  return write_then_read();
}

bool Process::setEffectiveGroupID(gid_t id) noexcept
{
  if(posix::getgrgid(id) == nullptr)
    return false;

  m_iobuf.reset();
  m_iobuf << command::egid << id;
  return write_then_read();
}

bool Process::setPriority(int nval) noexcept
{
#if defined(PRIO_MIN) && defined(PRIO_MAX)
  if(nval < PRIO_MIN || nval > PRIO_MAX)
    return false;
#else
#warning PRIO_MIN or PRIO_MAX is not defined.  The safegaurd in Process::setPriority() is disabled.
#endif

  m_iobuf.reset();
  m_iobuf << command::priority << nval;
  return write_then_read();
}

#ifdef _XOPEN_SOURCE_EXTENDED
bool Process::sendSignal(posix::signal::EId id, int value) const noexcept
{
  return posix::signal::send(processId(), id, value);
}
#endif

bool Process::invoke(void) noexcept
{
  flaw(m_state != State::Initializing, posix::severe, posix::error(std::errc::device_or_resource_busy), false,
       "Called Process::invoke() on an active process!")

  m_iobuf.reset();
  m_iobuf << command::invoke;

  if(!writeStdIn(m_iobuf))
    return false;

  m_state = State::Invalid;
  state();

#ifndef ENABLE_PROCESS_EVENT_TRACKING
  Object::enqueue_copy(started, posix::invalid_descriptor, EventData_t(EventFlags::ExecEvent, processId(), processId()));
#endif

  Object::connect(getStdOut(), EventFlags::Readable, stdoutMessage);
  Object::connect(getStdErr(), EventFlags::Readable, stderrMessage);

  m_state = State::Running;
  return errno == posix::success_response;
}

Process::State Process::state(void) noexcept
{
  switch(m_state)
  {
    case State::Finished:
    case State::Initializing:
      break;
    default:
      process_state_t data;
      flaw(::procstat(processId(), &data) == posix::error_response, posix::severe, m_state = State::Invalid, m_state,
           "Process %i does not exist.", processId()); // process _must_ exist
      switch (data.state)
      {
        case WaitingInterruptable:
        case WaitingUninterruptable:
          m_state = State::Waiting; break;
        case Zombie : m_state = State::Zombie ; break;
        case Stopped: m_state = State::Stopped; break;
        case Running: m_state = State::Running; break;
      }
  }
  return m_state;
}
<|endoftext|>


<commit_before>/*
  Copyright 2003-2009 John Plevyak, All Rights Reserved
*/
#define EXTERN
#include <signal.h>
#include <sys/resource.h>
#include "defs.h"

int do_unit_tests = 0;
static char pyc_ifa_log[256];

static void
help(ArgumentState *arg_state, char *arg_unused) {
  char ver[30];
  get_version(ver);
  fprintf(stderr, "PYC Version %s ", ver);
  fprintf(stderr,
#include "COPYRIGHT.i"
);
  usage(arg_state, arg_unused);
}

static void
version(ArgumentState *arg_state, char *arg_unused) {
  char ver[30];
  get_version(ver);
  fprintf(stderr, "PYC Version %s ", ver);
  fprintf(stderr,
#include "COPYRIGHT.i"
);
  exit(0);
}

static void
license(ArgumentState *arg_state, char *arg_unused) {
  fprintf(stderr,
#include "LICENSE.i"
);
  exit(0);
}

static ArgumentDescription arg_desc[] = {
  {"debug_info", 'g', "Produce Debugging Information", "F", &codegen_debug, "PYC_DEBUG_INFO", NULL},
  {"optimize", 'O', "Optimize", "F", &codegen_optimize, "PYC_OPTIMIZE", NULL},
#ifdef DEBUG
  {"test", 't', "Unit Test", "F", &do_unit_tests, "PYC_TEST", NULL},
  {"test_scoping", ' ', "Test Scoping", "F", &test_scoping, "PYC_TEST_SCOPING", NULL},
#endif
  {"html", ' ', "Output as HTML", "F", &fdump_html, "PYC_HTML", NULL},
  {"ifalog", 'l', "IFA Log", "S256", pyc_ifa_log, "PYC_IFA_LOG", log_flags_arg},
  {"system_directory", 'D', "System Directory", "S511", system_dir, "PYC_SYSTEM_DIRECTORY", NULL},
  {"verbose", 'v', "Verbosity Level", "+", &verbose_level, "PYC_VERBOSE", NULL},
  {"debug", 'd', "Debugging Level", "+", &debug_level, "PYC_DEBUG", NULL},
  {"license", ' ', "Show License", NULL, NULL, NULL, license},
  {"version", ' ', "Version", NULL, NULL, NULL, version},
  {"help", 'h', "Help", NULL, NULL, NULL, help},
  {0}
};

static ArgumentState arg_state("pyc", arg_desc);

static void init_system() {
  struct rlimit nfiles;
  assert(!getrlimit(RLIMIT_NOFILE, &nfiles));
  nfiles.rlim_cur = nfiles.rlim_max;
  assert(!setrlimit(RLIMIT_NOFILE, &nfiles));
  assert(!getrlimit(RLIMIT_NOFILE, &nfiles));
}

void compile(cchar *fn) {
  if (ifa_analyze(fn) < 0)
    fail("program does not type");
  if (ifa_optimize() < 0)
    fail("unable to optimize program");
  if (fgraph)
    ifa_graph(fn);
  if (fdump_html) {
    char mktree_dir[512];
    strcpy(mktree_dir, system_dir);
    ifa_html(fn, mktree_dir);
  }
  if (fcg) {
    c_codegen_write_c(pdb->fa, if1->top->fun, fn);
    c_codegen_compile(fn);
  }
  return;
}

cchar *mod_name_from_filename(cchar *n) {
  cchar *start = strrchr(n, '/');
  if (!start) start = n; else start++;
  cchar *end = strrchr(n, '.');
  assert(end);
  return dupstr(start,end);
}

int main(int argc, char *argv[]) {
  MEM_INIT();
  INIT_RAND64(0x1234567);
  process_args(&arg_state, argc, argv);
  ifa_verbose = verbose_level;
  ifa_debug = debug_level;
  if (arg_state.nfile_arguments < 1)
    usage(&arg_state, 0);
  init_system();
  init_config();
  if (pyc_ifa_log[0])
    init_logs();
  Service::start_all();
  if (do_unit_tests) {
    int r = UnitTest::run_all();
    Service::stop_all();
    _exit(r);
  }
  Py_Initialize();
  PyEval_InitThreads();
  PyArena *arena = PyArena_New();
  cchar *first_filename = 0;
  Vec<PycModule *> mods;
  for (int i = -1; i < arg_state.nfile_arguments; i++) {
    cchar *filename = 0;
    if (i < 0) {
      char fn[256];
      strcpy(fn, system_dir);
      strcat(fn, "/__pyc__.py");
      filename = dupstr(fn);
    } else
      filename = arg_state.file_argument[i];
    if (!i)
      first_filename = filename;
    mod_ty mod = file_to_mod(filename, arena);
    if (mod)
      mods.add(new PycModule(mod, filename, i < 0));
  }
  if (mods.n > 1) {
    ast_to_if1(mods, arena);
    compile(first_filename);
  }
  PyArena_Free(arena);
  Py_Finalize();
  Service::stop_all();
  exit(0);
  return 0;
}
<commit_msg>return error code from c_compile as exit() value<commit_after>/*
  Copyright 2003-2009 John Plevyak, All Rights Reserved
*/
#define EXTERN
#include <signal.h>
#include <sys/resource.h>
#include "defs.h"

int do_unit_tests = 0;
static char pyc_ifa_log[256];

static void
help(ArgumentState *arg_state, char *arg_unused) {
  char ver[30];
  get_version(ver);
  fprintf(stderr, "PYC Version %s ", ver);
  fprintf(stderr,
#include "COPYRIGHT.i"
);
  usage(arg_state, arg_unused);
}

static void
version(ArgumentState *arg_state, char *arg_unused) {
  char ver[30];
  get_version(ver);
  fprintf(stderr, "PYC Version %s ", ver);
  fprintf(stderr,
#include "COPYRIGHT.i"
);
  exit(0);
}

static void
license(ArgumentState *arg_state, char *arg_unused) {
  fprintf(stderr,
#include "LICENSE.i"
);
  exit(0);
}

static ArgumentDescription arg_desc[] = {
  {"debug_info", 'g', "Produce Debugging Information", "F", &codegen_debug, "PYC_DEBUG_INFO", NULL},
  {"optimize", 'O', "Optimize", "F", &codegen_optimize, "PYC_OPTIMIZE", NULL},
#ifdef DEBUG
  {"test", 't', "Unit Test", "F", &do_unit_tests, "PYC_TEST", NULL},
  {"test_scoping", ' ', "Test Scoping", "F", &test_scoping, "PYC_TEST_SCOPING", NULL},
#endif
  {"html", ' ', "Output as HTML", "F", &fdump_html, "PYC_HTML", NULL},
  {"ifalog", 'l', "IFA Log", "S256", pyc_ifa_log, "PYC_IFA_LOG", log_flags_arg},
  {"system_directory", 'D', "System Directory", "S511", system_dir, "PYC_SYSTEM_DIRECTORY", NULL},
  {"verbose", 'v', "Verbosity Level", "+", &verbose_level, "PYC_VERBOSE", NULL},
  {"debug", 'd', "Debugging Level", "+", &debug_level, "PYC_DEBUG", NULL},
  {"license", ' ', "Show License", NULL, NULL, NULL, license},
  {"version", ' ', "Version", NULL, NULL, NULL, version},
  {"help", 'h', "Help", NULL, NULL, NULL, help},
  {0}
};

static ArgumentState arg_state("pyc", arg_desc);

static void init_system() {
  struct rlimit nfiles;
  assert(!getrlimit(RLIMIT_NOFILE, &nfiles));
  nfiles.rlim_cur = nfiles.rlim_max;
  assert(!setrlimit(RLIMIT_NOFILE, &nfiles));
  assert(!getrlimit(RLIMIT_NOFILE, &nfiles));
}

void compile(cchar *fn) {
  if (ifa_analyze(fn) < 0)
    fail("program does not type");
  if (ifa_optimize() < 0)
    fail("unable to optimize program");
  if (fgraph)
    ifa_graph(fn);
  if (fdump_html) {
    char mktree_dir[512];
    strcpy(mktree_dir, system_dir);
    ifa_html(fn, mktree_dir);
  }
  if (fcg) {
    c_codegen_write_c(pdb->fa, if1->top->fun, fn);
    if (c_codegen_compile(fn))
      fail("compilation failure");
  }
  return;
}

cchar *mod_name_from_filename(cchar *n) {
  cchar *start = strrchr(n, '/');
  if (!start) start = n; else start++;
  cchar *end = strrchr(n, '.');
  assert(end);
  return dupstr(start,end);
}

int main(int argc, char *argv[]) {
  MEM_INIT();
  INIT_RAND64(0x1234567);
  process_args(&arg_state, argc, argv);
  ifa_verbose = verbose_level;
  ifa_debug = debug_level;
  if (arg_state.nfile_arguments < 1)
    usage(&arg_state, 0);
  init_system();
  init_config();
  if (pyc_ifa_log[0])
    init_logs();
  Service::start_all();
  if (do_unit_tests) {
    int r = UnitTest::run_all();
    Service::stop_all();
    _exit(r);
  }
  Py_Initialize();
  PyEval_InitThreads();
  PyArena *arena = PyArena_New();
  cchar *first_filename = 0;
  Vec<PycModule *> mods;
  for (int i = -1; i < arg_state.nfile_arguments; i++) {
    cchar *filename = 0;
    if (i < 0) {
      char fn[256];
      strcpy(fn, system_dir);
      strcat(fn, "/__pyc__.py");
      filename = dupstr(fn);
    } else
      filename = arg_state.file_argument[i];
    if (!i)
      first_filename = filename;
    mod_ty mod = file_to_mod(filename, arena);
    if (mod)
      mods.add(new PycModule(mod, filename, i < 0));
  }
  if (mods.n > 1) {
    ast_to_if1(mods, arena);
    compile(first_filename);
  }
  PyArena_Free(arena);
  Py_Finalize();
  Service::stop_all();
  exit(0);
  return 0;
}
<|endoftext|>


<commit_before>// [WriteFile Name=LineOfSightGeoElement, Category=Analysis]
// [Legal]
// Copyright 2019 Esri.

// 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.
// [Legal]

#include "LineOfSightGeoElement.h"

#include "ArcGISSceneLayer.h"
#include "ArcGISTiledElevationSource.h"
#include "GeoElementLineOfSight.h"
#include "GeometryEngine.h"
#include "GraphicsOverlay.h"
#include "ModelSceneSymbol.h"
#include "PointBuilder.h"
#include "Scene.h"
#include "SceneQuickView.h"
#include "SimpleMarkerSymbol.h"
#include "SimpleRenderer.h"

#include <QStandardPaths>
#include <QDir>

using namespace Esri::ArcGISRuntime;

namespace
{

// Initial fixed point to observe taxi.
const Point observationPoint(-73.9853, 40.7484, 200, SpatialReference::wgs84());

// Waypoints around the block for taxi to drive.
const std::array<Point, 4> waypoints = {{
                                          { -73.984513, 40.748469, 2, SpatialReference::wgs84() },
                                          { -73.985068, 40.747786, 2, SpatialReference::wgs84() },
                                          { -73.983452, 40.747091, 2, SpatialReference::wgs84() },
                                          { -73.982961, 40.747762, 2, SpatialReference::wgs84() }
                                        }};

// Path of Taxi 3D CAD model.
QString dolmusPath()
{
  auto homepath = QStandardPaths::standardLocations(QStandardPaths::HomeLocation).last();
  return QDir(homepath).filePath("ArcGIS/Runtime/Data/3D/dolmus_3ds/dolmus.3ds");
}

}

LineOfSightGeoElement::LineOfSightGeoElement(QObject* parent /* = nullptr */):
  QObject(parent),
  m_scene(new Scene(Basemap::imagery(this), this))
{
  // create a new elevation source from Terrain3D rest service
  ArcGISTiledElevationSource* elevationSource = new ArcGISTiledElevationSource(
        QUrl("https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"), this);

  // add the elevation source to the scene to display elevation
  m_scene->baseSurface()->elevationSources()->append(elevationSource);

  // Load up the buildings that will block the line of sight.
  ArcGISSceneLayer* buildings = new ArcGISSceneLayer(
        QUrl("https://tiles.arcgis.com/tiles/z2tnIkrLQ2BRzr6P/arcgis/rest/services/New_York_LoD2_3D_Buildings/SceneServer/layers/0"));
  m_scene->operationalLayers()->append(buildings);

  // Trigger animation of taxi every 100ms.
  m_animation.setInterval(100);
  m_animation.callOnTimeout(this, &LineOfSightGeoElement::animate);
}

LineOfSightGeoElement::~LineOfSightGeoElement() = default;

void LineOfSightGeoElement::init()
{
  // Register classes for QML
  qmlRegisterType<SceneQuickView>("Esri.Samples", 1, 0, "SceneView");
  qmlRegisterType<LineOfSightGeoElement>("Esri.Samples", 1, 0, "LineOfSightGeoElementSample");
}

double LineOfSightGeoElement::heightZ() const
{
  if (m_observer)
  {
    const Point p = geometry_cast<Point>(m_observer->geometry());
    return p.isValid() ? p.z() : 0.0;
  }
  else
  {
    return 0.0;
  }
}
void LineOfSightGeoElement::setHeightZ(double z)
{
  if (!m_observer)
    return;

  const Point p = geometry_cast<Point>(m_observer->geometry());
  if (p.isValid())
  {
    PointBuilder builder(p);
    builder.setZ(z);
    m_observer->setGeometry(builder.toGeometry());
    emit heightZChanged();
  }
}

SceneQuickView* LineOfSightGeoElement::sceneView() const
{
  return m_sceneView;
}

// Set the view (created in QML)
void LineOfSightGeoElement::setSceneView(SceneQuickView* sceneView)
{
  if (!sceneView || sceneView == m_sceneView)
  {
    return;
  }

  m_sceneView = sceneView;
  m_sceneView->setArcGISScene(m_scene);
  emit sceneViewChanged();

  // Setup the graphics overlay - ensure that all z-position are relative to the height of the surface.
  GraphicsOverlay* graphicsOverlay = new GraphicsOverlay(this);
  {
    LayerSceneProperties properties = graphicsOverlay->sceneProperties();
    properties.setSurfacePlacement(SurfacePlacement::Relative);
    graphicsOverlay->setSceneProperties(properties);
  }
  m_sceneView->graphicsOverlays()->append(graphicsOverlay);

  // Set up the renderer so that we can orient the taxi using the `HEADING` attribute.
  SimpleRenderer* renderer3D = new SimpleRenderer(this);
  {
    RendererSceneProperties properties = renderer3D->sceneProperties();
    properties.setHeadingExpression("[HEADING]");
    renderer3D->setSceneProperties(properties);
  }
  graphicsOverlay->setRenderer(renderer3D);

  // Create our observation point as a red sphere.
  m_observer = new Graphic(observationPoint, new SimpleMarkerSymbol(SimpleMarkerSymbolStyle::Circle, Qt::red, 5.f), this);
  emit heightZChanged(); // We now have a point to extract the Z-height from.
  graphicsOverlay->graphics()->append(m_observer);

  // Get our Taxi model. We will attempt to load it and continue our setup after it has loaded.
  ModelSceneSymbol* taxiSymbol = new ModelSceneSymbol(dolmusPath(), 1.0, this);
  taxiSymbol->setAnchorPosition(SceneSymbolAnchorPosition::Bottom);

  connect(taxiSymbol, &ModelSceneSymbol::doneLoading, this,
          [this, graphicsOverlay, taxiSymbol](Error error)
  {
    if (!error.isEmpty())
    {
      return;
    }

    // Create taxi from loaded taxi symbol, with an initial "HEADING" attribute for orientation.
    m_taxi = new Graphic(waypoints.front(), taxiSymbol, this);
    m_taxi->attributes()->insertAttribute("HEADING", 0.0);
    graphicsOverlay->graphics()->append(m_taxi);

    // Set up our line of sight analysis.
    AnalysisOverlay*  analysisOverlay = new AnalysisOverlay(this);
    m_sceneView->analysisOverlays()->append(analysisOverlay);

    GeoElementLineOfSight* lineOfSight = new GeoElementLineOfSight(m_observer, m_taxi, this);
    analysisOverlay->analyses()->append(lineOfSight);

    connect(lineOfSight, &GeoElementLineOfSight::targetVisibilityChanged, this,
            [this](LineOfSightTargetVisibility targetVisibility)
    {
      // Select taxi whenever there is a clear line of sight from observer position.
      m_taxi->setSelected(targetVisibility == LineOfSightTargetVisibility::Visible);
    });

    Camera camera(observationPoint, 700, -30, 45, 0);
    m_sceneView->setViewpointCamera(camera, 0);

    m_animation.start();
  });

  taxiSymbol->load();
}

void LineOfSightGeoElement::animate()
{
  // Goal point to travel to
  Point waypoint = waypoints.at(m_waypointIndex);

  // Calculate azimuth between current location and goal location for taxi.
  Point location = geometry_cast<Point>(m_taxi->geometry());

  if (!location.isValid())
    return;

  GeodeticDistanceResult distance = GeometryEngine::distanceGeodetic(
        location, waypoint, LinearUnit::meters(), AngularUnit::degrees(), GeodeticCurveType::Geodesic);

  // Move taxi one metre along the line between its current position and the goal location.
  QList<Point> newPoints = GeometryEngine::moveGeodetic(
  { location }, 1.0, LinearUnit::meters(), distance.azimuth1(), AngularUnit::degrees(), GeodeticCurveType::Geodesic);
  if (newPoints.size() > 0)
  {
    location = newPoints.last();
  }

  // Update taxi position and orientation.
  m_taxi->setGeometry(geometry_cast<Geometry>(location));
  m_taxi->attributes()->replaceAttribute("HEADING", distance.azimuth1());

  // When taxi is close enough to the waypoint then increment the index for a new goal next animation step.
  // Index is cyclic and an element of [0, 3].
  if (distance.distance() <= 2) {
    m_waypointIndex = (m_waypointIndex + 1) % waypoints.size();
  }
}
<commit_msg>Included missing import for mac.<commit_after>// [WriteFile Name=LineOfSightGeoElement, Category=Analysis]
// [Legal]
// Copyright 2019 Esri.

// 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.
// [Legal]

#include "LineOfSightGeoElement.h"

#include "ArcGISSceneLayer.h"
#include "ArcGISTiledElevationSource.h"
#include "GeoElementLineOfSight.h"
#include "GeometryEngine.h"
#include "GraphicsOverlay.h"
#include "ModelSceneSymbol.h"
#include "PointBuilder.h"
#include "Scene.h"
#include "SceneQuickView.h"
#include "SimpleMarkerSymbol.h"
#include "SimpleRenderer.h"

#include <array>

#include <QStandardPaths>
#include <QDir>

using namespace Esri::ArcGISRuntime;

namespace
{

// Initial fixed point to observe taxi.
const Point observationPoint(-73.9853, 40.7484, 200, SpatialReference::wgs84());

// Waypoints around the block for taxi to drive.
const std::array<Point, 4> waypoints = {{
                                          { -73.984513, 40.748469, 2, SpatialReference::wgs84() },
                                          { -73.985068, 40.747786, 2, SpatialReference::wgs84() },
                                          { -73.983452, 40.747091, 2, SpatialReference::wgs84() },
                                          { -73.982961, 40.747762, 2, SpatialReference::wgs84() }
                                        }};

// Path of Taxi 3D CAD model.
QString dolmusPath()
{
  auto homepath = QStandardPaths::standardLocations(QStandardPaths::HomeLocation).last();
  return QDir(homepath).filePath("ArcGIS/Runtime/Data/3D/dolmus_3ds/dolmus.3ds");
}

}

LineOfSightGeoElement::LineOfSightGeoElement(QObject* parent /* = nullptr */):
  QObject(parent),
  m_scene(new Scene(Basemap::imagery(this), this))
{
  // create a new elevation source from Terrain3D rest service
  ArcGISTiledElevationSource* elevationSource = new ArcGISTiledElevationSource(
        QUrl("https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"), this);

  // add the elevation source to the scene to display elevation
  m_scene->baseSurface()->elevationSources()->append(elevationSource);

  // Load up the buildings that will block the line of sight.
  ArcGISSceneLayer* buildings = new ArcGISSceneLayer(
        QUrl("https://tiles.arcgis.com/tiles/z2tnIkrLQ2BRzr6P/arcgis/rest/services/New_York_LoD2_3D_Buildings/SceneServer/layers/0"));
  m_scene->operationalLayers()->append(buildings);

  // Trigger animation of taxi every 100ms.
  m_animation.setInterval(100);
  m_animation.callOnTimeout(this, &LineOfSightGeoElement::animate);
}

LineOfSightGeoElement::~LineOfSightGeoElement() = default;

void LineOfSightGeoElement::init()
{
  // Register classes for QML
  qmlRegisterType<SceneQuickView>("Esri.Samples", 1, 0, "SceneView");
  qmlRegisterType<LineOfSightGeoElement>("Esri.Samples", 1, 0, "LineOfSightGeoElementSample");
}

double LineOfSightGeoElement::heightZ() const
{
  if (m_observer)
  {
    const Point p = geometry_cast<Point>(m_observer->geometry());
    return p.isValid() ? p.z() : 0.0;
  }
  else
  {
    return 0.0;
  }
}
void LineOfSightGeoElement::setHeightZ(double z)
{
  if (!m_observer)
    return;

  const Point p = geometry_cast<Point>(m_observer->geometry());
  if (p.isValid())
  {
    PointBuilder builder(p);
    builder.setZ(z);
    m_observer->setGeometry(builder.toGeometry());
    emit heightZChanged();
  }
}

SceneQuickView* LineOfSightGeoElement::sceneView() const
{
  return m_sceneView;
}

// Set the view (created in QML)
void LineOfSightGeoElement::setSceneView(SceneQuickView* sceneView)
{
  if (!sceneView || sceneView == m_sceneView)
  {
    return;
  }

  m_sceneView = sceneView;
  m_sceneView->setArcGISScene(m_scene);
  emit sceneViewChanged();

  // Setup the graphics overlay - ensure that all z-position are relative to the height of the surface.
  GraphicsOverlay* graphicsOverlay = new GraphicsOverlay(this);
  {
    LayerSceneProperties properties = graphicsOverlay->sceneProperties();
    properties.setSurfacePlacement(SurfacePlacement::Relative);
    graphicsOverlay->setSceneProperties(properties);
  }
  m_sceneView->graphicsOverlays()->append(graphicsOverlay);

  // Set up the renderer so that we can orient the taxi using the `HEADING` attribute.
  SimpleRenderer* renderer3D = new SimpleRenderer(this);
  {
    RendererSceneProperties properties = renderer3D->sceneProperties();
    properties.setHeadingExpression("[HEADING]");
    renderer3D->setSceneProperties(properties);
  }
  graphicsOverlay->setRenderer(renderer3D);

  // Create our observation point as a red sphere.
  m_observer = new Graphic(observationPoint, new SimpleMarkerSymbol(SimpleMarkerSymbolStyle::Circle, Qt::red, 5.f), this);
  emit heightZChanged(); // We now have a point to extract the Z-height from.
  graphicsOverlay->graphics()->append(m_observer);

  // Get our Taxi model. We will attempt to load it and continue our setup after it has loaded.
  ModelSceneSymbol* taxiSymbol = new ModelSceneSymbol(dolmusPath(), 1.0, this);
  taxiSymbol->setAnchorPosition(SceneSymbolAnchorPosition::Bottom);

  connect(taxiSymbol, &ModelSceneSymbol::doneLoading, this,
          [this, graphicsOverlay, taxiSymbol](Error error)
  {
    if (!error.isEmpty())
    {
      return;
    }

    // Create taxi from loaded taxi symbol, with an initial "HEADING" attribute for orientation.
    m_taxi = new Graphic(waypoints.front(), taxiSymbol, this);
    m_taxi->attributes()->insertAttribute("HEADING", 0.0);
    graphicsOverlay->graphics()->append(m_taxi);

    // Set up our line of sight analysis.
    AnalysisOverlay*  analysisOverlay = new AnalysisOverlay(this);
    m_sceneView->analysisOverlays()->append(analysisOverlay);

    GeoElementLineOfSight* lineOfSight = new GeoElementLineOfSight(m_observer, m_taxi, this);
    analysisOverlay->analyses()->append(lineOfSight);

    connect(lineOfSight, &GeoElementLineOfSight::targetVisibilityChanged, this,
            [this](LineOfSightTargetVisibility targetVisibility)
    {
      // Select taxi whenever there is a clear line of sight from observer position.
      m_taxi->setSelected(targetVisibility == LineOfSightTargetVisibility::Visible);
    });

    Camera camera(observationPoint, 700, -30, 45, 0);
    m_sceneView->setViewpointCamera(camera, 0);

    m_animation.start();
  });

  taxiSymbol->load();
}

void LineOfSightGeoElement::animate()
{
  // Goal point to travel to
  Point waypoint = waypoints.at(m_waypointIndex);

  // Calculate azimuth between current location and goal location for taxi.
  Point location = geometry_cast<Point>(m_taxi->geometry());

  if (!location.isValid())
    return;

  GeodeticDistanceResult distance = GeometryEngine::distanceGeodetic(
        location, waypoint, LinearUnit::meters(), AngularUnit::degrees(), GeodeticCurveType::Geodesic);

  // Move taxi one metre along the line between its current position and the goal location.
  QList<Point> newPoints = GeometryEngine::moveGeodetic(
  { location }, 1.0, LinearUnit::meters(), distance.azimuth1(), AngularUnit::degrees(), GeodeticCurveType::Geodesic);
  if (newPoints.size() > 0)
  {
    location = newPoints.last();
  }

  // Update taxi position and orientation.
  m_taxi->setGeometry(geometry_cast<Geometry>(location));
  m_taxi->attributes()->replaceAttribute("HEADING", distance.azimuth1());

  // When taxi is close enough to the waypoint then increment the index for a new goal next animation step.
  // Index is cyclic and an element of [0, 3].
  if (distance.distance() <= 2) {
    m_waypointIndex = (m_waypointIndex + 1) % waypoints.size();
  }
}
<|endoftext|>


<commit_before>// ========================================================================== //
// This file is part of DO++, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2014 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

#include <limits>
#include <stdint.h>
#include <vector>

#include <gtest/gtest.h>

#include "AssertHelpers.hpp"
#include "pixel.hpp"
#include "color_conversion.hpp"

using namespace std;
using namespace DO;

// ========================================================================== //
// Define the set of integral channel types, which we will test.
typedef testing::Types<float, double> FloatingPointChannelTypes;

// ========================================================================== //
// Define the parameterized test case.
template <typename ChannelType>
class TestConvertColorConversion : public testing::Test {};
TYPED_TEST_CASE_P(TestConvertColorConversion);

// ========================================================================== //
// RGB <-> RGBA.
TEST(TestConvertColorConversionBetweenRGB, test_rgb_to_rgba)
{
    Pixel<double, Rgb> rgb(1,1,1);
    Pixel<double, Rgba> rgba;
    convert_color(rgb, rgba);
    EXPECT_EQ(Vector4d(1,1,1,1), rgba);
}

TEST(TestConvertColorConversionBetweenRGB, test_rgb_to_rgba)
{
    Pixel<double, Rgba> rgba(1,1,1,1);
    Pixel<double, Rgb> rgb;
    convert_color(rgba, rgb);
    EXPECT_EQ(Vector3d(1,1,1), rgb);
}

// ========================================================================== //
// RGB <-> grayscale.
TYPED_TEST_P(TestConvertColorConversion, test_rgb_to_gray)
{
  typedef TypeParam T;
  // Using the explicit function.
  {
    Matrix<T, 3, 1> rgb(1,1,1);
    T gray;
    rgb_to_gray(rgb, gray);
    EXPECT_NEAR(gray, 1, T(1e-3));
  }
  // Using the unified API.
  {
    Pixel<T, Rgb> rgb(1,1,1);
    T gray;
    convert_color(rgb, gray);
    EXPECT_NEAR(gray, 1, T(1e-3));
  }
}

TYPED_TEST_P(TestConvertColorConversion, test_gray_to_rgb)
{
  // Using the explicit function.
  typedef TypeParam T;
  {
    const T src_gray = T(0.5);
    const Matrix<T, 3, 1> expected_rgb(src_gray, src_gray, src_gray);
    Matrix<T, 3, 1> actual_rgb;
    gray_to_rgb(src_gray, actual_rgb);
    EXPECT_EQ(expected_rgb, actual_rgb);
  }
  // Using the unified API.
  {
    const T src_gray = T(0.5);
    const Pixel<T, Rgb> expected_rgb(src_gray, src_gray, src_gray);
    Pixel<T, Rgb> actual_rgb;
    convert_color(src_gray, actual_rgb);
    EXPECT_EQ(expected_rgb, actual_rgb);
  }
}

// ========================================================================== //
// RGB <-> YUV.
TYPED_TEST_P(TestConvertColorConversion, test_rgb_to_yuv)
{
  typedef TypeParam T;
  typedef Matrix<T, 3, 1> Vec3;
  const Vec3 rgb[] = {
    Vec3(1, 0, 0),
    Vec3(0, 1, 0),
    Vec3(0, 0, 1)
  };
  const Vec3 expected_yuv[] = {
    Vec3(0.299, 0.492*(0-0.299), 0.877*(1-0.299)),
    Vec3(0.587, 0.492*(0-0.587), 0.877*(0-0.587)),
    Vec3(0.114, 0.492*(1-0.114), 0.877*(0-0.114))
  };
  // Using the explicit function.
  for (int i = 0; i < 3; ++i)
  {
    Vec3 actual_yuv;
    rgb_to_yuv(rgb[i], actual_yuv);
    EXPECT_MATRIX_NEAR(expected_yuv[i], actual_yuv, T(1e-3));
  }
  // Using the unified API.
  for (int i = 0; i < 3; ++i)
  {
    const Pixel<T, Rgb> rgb_pixel(rgb[i]);
    const Pixel<T, Yuv> expected_yuv_pixel(expected_yuv[i]);
    Pixel<T, Yuv> actual_yuv_pixel;
    convert_color(rgb_pixel, actual_yuv_pixel);
    EXPECT_MATRIX_NEAR(expected_yuv_pixel, actual_yuv_pixel, T(1e-3));
  }
}

TYPED_TEST_P(TestConvertColorConversion, test_yuv_to_rgb)
{
  typedef TypeParam T;
  typedef Matrix<T, 3, 1> Vec3;
  const Vec3 yuv[] = {
    Vec3(1, 0    , 0    ),
    Vec3(0, 0.436, 0    ),
    Vec3(0, 0    , 0.615)
  };
  const Vec3 expected_rgb[] = {
    Vec3(1            ,  1            , 1            ),
    Vec3(0            , -0.39465*0.436, 2.03211*0.436),
    Vec3(1.13983*0.615, -0.58060*0.615, 0            )
  };
  // Using the explicit function.
  for (int i = 0; i < 3; ++i)
  {
    Vec3 actual_rgb;
    yuv_to_rgb(yuv[i], actual_rgb);
    EXPECT_MATRIX_NEAR(expected_rgb[i], actual_rgb, T(1e-3));
  }
  // Using the unified API.
  for (int i = 0; i < 3; ++i)
  {
    const Pixel<T, Yuv> yuv_pixel(yuv[i]);
    const Pixel<T, Yuv> expected_rgb_pixel(expected_rgb[i]);
    Pixel<T, Rgb> actual_rgb_pixel;
    convert_color(yuv_pixel, actual_rgb_pixel);
    EXPECT_MATRIX_NEAR(expected_rgb_pixel, actual_rgb_pixel, T(1e-3));
  }
}

// ========================================================================== //
// YUV <-> Gray
TYPED_TEST_P(TestConvertColorConversion, test_yuv_to_gray)
{
  typedef TypeParam T;
  typedef Matrix<T, 3, 1> Vec3;
  const Vec3 yuv(1, 0, 0);
  {
    T gray;
    yuv_to_gray(yuv, gray);
    EXPECT_EQ(1, gray);
  }
  {
    Pixel<T, Yuv> yuv_pixel(yuv);
    T gray;
    convert_color<T, Yuv>(yuv, gray);
    EXPECT_EQ(1, gray);
  }
}

TYPED_TEST_P(TestConvertColorConversion, test_gray_to_yuv)
{
  typedef TypeParam T;
  typedef Matrix<T, 3, 1> Vec3;
  T gray = T(0.5);
  {
    Vec3 yuv;
    const Vec3 expected_yuv(0.5, 0, 0);
    gray_to_yuv(gray, yuv);
    EXPECT_EQ(expected_yuv, yuv);
  }
  {
    Pixel<T, Yuv> yuv;
    const Pixel<T, Yuv> expected_yuv(0.5, 0, 0);
    convert_color(gray, yuv);
    EXPECT_EQ(expected_yuv, yuv);
  }
}

// ========================================================================== //
// Register all typed tests and instantiate them.
REGISTER_TYPED_TEST_CASE_P(TestConvertColorConversion,
                           test_rgb_to_gray,
                           test_gray_to_rgb,
                           test_rgb_to_yuv,
                           test_yuv_to_rgb,
                           test_yuv_to_gray,
                           test_gray_to_yuv);
INSTANTIATE_TYPED_TEST_CASE_P(Core_Pixel_ColorConversion,
                              TestConvertColorConversion,
                              FloatingPointChannelTypes);

// ========================================================================== //
// Run the tests.
int main(int argc, char** argv)
{
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}<commit_msg>Fix test names.<commit_after>// ========================================================================== //
// This file is part of DO++, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2014 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

#include <limits>
#include <stdint.h>
#include <vector>

#include <gtest/gtest.h>

#include "AssertHelpers.hpp"
#include "pixel.hpp"
#include "color_conversion.hpp"

using namespace std;
using namespace DO;

// ========================================================================== //
// Define the set of integral channel types, which we will test.
typedef testing::Types<float, double> FloatingPointChannelTypes;

// ========================================================================== //
// Define the parameterized test case.
template <typename ChannelType>
class TestConvertColorConversion : public testing::Test {};
TYPED_TEST_CASE_P(TestConvertColorConversion);

// ========================================================================== //
// RGB <-> RGBA.
TEST(TestConvertColorConversion, test_rgb_to_rgba)
{
    Pixel<double, Rgb> rgb(1,1,1);
    Pixel<double, Rgba> rgba;
    convert_color(rgb, rgba);
    EXPECT_EQ(Vector4d(1,1,1,1), rgba);
}

TEST(TestConvertColorConversion, test_rgba_to_rgb)
{
    Pixel<double, Rgba> rgba(1,1,1,1);
    Pixel<double, Rgb> rgb;
    convert_color(rgba, rgb);
    EXPECT_EQ(Vector3d(1,1,1), rgb);
}

// ========================================================================== //
// RGB <-> grayscale.
TYPED_TEST_P(TestConvertColorConversion, test_rgb_to_gray)
{
  typedef TypeParam T;
  // Using the explicit function.
  {
    Matrix<T, 3, 1> rgb(1,1,1);
    T gray;
    rgb_to_gray(rgb, gray);
    EXPECT_NEAR(gray, 1, T(1e-3));
  }
  // Using the unified API.
  {
    Pixel<T, Rgb> rgb(1,1,1);
    T gray;
    convert_color(rgb, gray);
    EXPECT_NEAR(gray, 1, T(1e-3));
  }
}

TYPED_TEST_P(TestConvertColorConversion, test_gray_to_rgb)
{
  // Using the explicit function.
  typedef TypeParam T;
  {
    const T src_gray = T(0.5);
    const Matrix<T, 3, 1> expected_rgb(src_gray, src_gray, src_gray);
    Matrix<T, 3, 1> actual_rgb;
    gray_to_rgb(src_gray, actual_rgb);
    EXPECT_EQ(expected_rgb, actual_rgb);
  }
  // Using the unified API.
  {
    const T src_gray = T(0.5);
    const Pixel<T, Rgb> expected_rgb(src_gray, src_gray, src_gray);
    Pixel<T, Rgb> actual_rgb;
    convert_color(src_gray, actual_rgb);
    EXPECT_EQ(expected_rgb, actual_rgb);
  }
}

// ========================================================================== //
// RGB <-> YUV.
TYPED_TEST_P(TestConvertColorConversion, test_rgb_to_yuv)
{
  typedef TypeParam T;
  typedef Matrix<T, 3, 1> Vec3;
  const Vec3 rgb[] = {
    Vec3(1, 0, 0),
    Vec3(0, 1, 0),
    Vec3(0, 0, 1)
  };
  const Vec3 expected_yuv[] = {
    Vec3(0.299, 0.492*(0-0.299), 0.877*(1-0.299)),
    Vec3(0.587, 0.492*(0-0.587), 0.877*(0-0.587)),
    Vec3(0.114, 0.492*(1-0.114), 0.877*(0-0.114))
  };
  // Using the explicit function.
  for (int i = 0; i < 3; ++i)
  {
    Vec3 actual_yuv;
    rgb_to_yuv(rgb[i], actual_yuv);
    EXPECT_MATRIX_NEAR(expected_yuv[i], actual_yuv, T(1e-3));
  }
  // Using the unified API.
  for (int i = 0; i < 3; ++i)
  {
    const Pixel<T, Rgb> rgb_pixel(rgb[i]);
    const Pixel<T, Yuv> expected_yuv_pixel(expected_yuv[i]);
    Pixel<T, Yuv> actual_yuv_pixel;
    convert_color(rgb_pixel, actual_yuv_pixel);
    EXPECT_MATRIX_NEAR(expected_yuv_pixel, actual_yuv_pixel, T(1e-3));
  }
}

TYPED_TEST_P(TestConvertColorConversion, test_yuv_to_rgb)
{
  typedef TypeParam T;
  typedef Matrix<T, 3, 1> Vec3;
  const Vec3 yuv[] = {
    Vec3(1, 0    , 0    ),
    Vec3(0, 0.436, 0    ),
    Vec3(0, 0    , 0.615)
  };
  const Vec3 expected_rgb[] = {
    Vec3(1            ,  1            , 1            ),
    Vec3(0            , -0.39465*0.436, 2.03211*0.436),
    Vec3(1.13983*0.615, -0.58060*0.615, 0            )
  };
  // Using the explicit function.
  for (int i = 0; i < 3; ++i)
  {
    Vec3 actual_rgb;
    yuv_to_rgb(yuv[i], actual_rgb);
    EXPECT_MATRIX_NEAR(expected_rgb[i], actual_rgb, T(1e-3));
  }
  // Using the unified API.
  for (int i = 0; i < 3; ++i)
  {
    const Pixel<T, Yuv> yuv_pixel(yuv[i]);
    const Pixel<T, Yuv> expected_rgb_pixel(expected_rgb[i]);
    Pixel<T, Rgb> actual_rgb_pixel;
    convert_color(yuv_pixel, actual_rgb_pixel);
    EXPECT_MATRIX_NEAR(expected_rgb_pixel, actual_rgb_pixel, T(1e-3));
  }
}

// ========================================================================== //
// YUV <-> Gray
TYPED_TEST_P(TestConvertColorConversion, test_yuv_to_gray)
{
  typedef TypeParam T;
  typedef Matrix<T, 3, 1> Vec3;
  const Vec3 yuv(1, 0, 0);
  {
    T gray;
    yuv_to_gray(yuv, gray);
    EXPECT_EQ(1, gray);
  }
  {
    Pixel<T, Yuv> yuv_pixel(yuv);
    T gray;
    convert_color<T, Yuv>(yuv, gray);
    EXPECT_EQ(1, gray);
  }
}

TYPED_TEST_P(TestConvertColorConversion, test_gray_to_yuv)
{
  typedef TypeParam T;
  typedef Matrix<T, 3, 1> Vec3;
  T gray = T(0.5);
  {
    Vec3 yuv;
    const Vec3 expected_yuv(0.5, 0, 0);
    gray_to_yuv(gray, yuv);
    EXPECT_EQ(expected_yuv, yuv);
  }
  {
    Pixel<T, Yuv> yuv;
    const Pixel<T, Yuv> expected_yuv(0.5, 0, 0);
    convert_color(gray, yuv);
    EXPECT_EQ(expected_yuv, yuv);
  }
}

// ========================================================================== //
// Register all typed tests and instantiate them.
REGISTER_TYPED_TEST_CASE_P(TestConvertColorConversion,
                           test_rgb_to_gray,
                           test_gray_to_rgb,
                           test_rgb_to_yuv,
                           test_yuv_to_rgb,
                           test_yuv_to_gray,
                           test_gray_to_yuv);
INSTANTIATE_TYPED_TEST_CASE_P(Core_Pixel_ColorConversion,
                              TestConvertColorConversion,
                              FloatingPointChannelTypes);

// ========================================================================== //
// Run the tests.
int main(int argc, char** argv)
{
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/common/chrome_switches.h"

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Popup) {
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableExperimentalExtensionApis);
  ASSERT_TRUE(RunExtensionTest("popup_api")) << message_;
}
<commit_msg>Disable flaky (crashing) ExtensionApiTest.Popup<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/common/chrome_switches.h"

// This test has been disabled because it was crashing
// browsertests. crbug.com/26599
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_Popup) {
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableExperimentalExtensionApis);
  ASSERT_TRUE(RunExtensionTest("popup_api")) << message_;
}
<|endoftext|>