<commit_before>#include <Object.h>
#include <sstream>
#include <gsl/gsl_math.h>
#include <fitsio.h>

Object::Object(unsigned int inid) : Image<double>(), segMap(*this) {
  id = inid;
  flag = 0;
  blend = s_g = -1;
  flux = centroid(0) = centroid(1) = -1;
}

Object::Object(std::string objfile) : Image<double>(), segMap(*this) {

  fitsfile *fptr;
  int status, nkeys, keypos, hdutype;
  char card[FLEN_CARD];
  char comment[FLEN_CARD];
  status = 0;

  fits_open_file(&fptr, objfile.c_str(), READONLY, &status);

  // open pHDU and read header
  fits_movabs_hdu(fptr, 1, &hdutype, &status);
  // recover object information from header keywords
  fits_read_key (fptr,TSTRING,"BASEFILE",card,comment, &status);
  basefilename = std::string(card);
  fits_read_key (fptr,TINT,"ID",&id,comment, &status);
  double grid_min_0, grid_max_0, grid_min_1, grid_max_1;
  fits_read_key (fptr,TDOUBLE,"XMIN",&grid_min_0,comment, &status);
  fits_read_key (fptr,TDOUBLE,"XMAX",&grid_max_0,comment, &status);
  fits_read_key (fptr,TDOUBLE,"YMIN",&grid_min_1,comment, &status);
  fits_read_key (fptr,TDOUBLE,"YMAX",&grid_max_1,comment, &status);
  Image<double>::accessGrid() = Grid(grid_min_0,grid_max_0,1,grid_min_1,grid_max_1,1);
  fits_read_key (fptr,TDOUBLE,"BG_MEAN",&noise_mean,comment, &status);
  fits_read_key (fptr,TDOUBLE,"BG_RMS",&noise_rms,comment, &status);
  fits_read_key (fptr,TSTRING,"NOISE",card,comment, &status);
  noisemodel = std::string(card);
  fits_read_key (fptr,TUSHORT,"FLAG",&flag,comment, &status);
  fits_read_key (fptr,TDOUBLE,"BLEND",&blend,comment, &status);
  fits_read_key (fptr,TDOUBLE,"S_G",&s_g,comment, &status);

  // if status is not 0 we have faced an error
  if (status != 0) {
    std::cout << "Object: header keywords erroneous!" << std::endl;
    std::terminate();
  }
  int naxis;
  fits_get_img_dim(fptr, &naxis, &status);
  if (naxis!=2) {
    std::cout << "Object: naxis != 2. This is not a FITS image!" << std::endl;
    std::terminate();
  }
  // first obtain the size of the image array
  long naxes[2] = {1,1};
  fits_get_img_size(fptr, naxis, naxes, &status);
  unsigned int axsize0, axsize1;
  axsize0 = naxes[0];
  axsize1 = naxes[1];
  long npixels = axsize0*axsize1;
  // grid is defined by XMIN..YMAX; this should have the same
  // number of pixels as the actual image
  if (npixels != Image<double>::getGrid().size()) {
    std::cout << "Object: Grid size from header keywords wrong" << std::endl;
    std::cout << npixels << " " << grid_min_0 << " " << grid_max_0 << " " << grid_min_1 << " " << grid_max_1 << " "<< Image<double>::getGrid().size();
    std::terminate();
  }
  data::resize(npixels);
  long firstpix[2] = {1,1};
  double vald;
  int imageformat, datatype;
  setFITSTypes(vald,imageformat,datatype);
  fits_read_pix(fptr, datatype, firstpix, npixels, NULL,Image<double>::c_array(), NULL, &status);

  // move to 1st extHDU for the segmentation map
  fits_movabs_hdu(fptr, 2, &hdutype, &status);
  segMap.resize(npixels);
  int vali;
  setFITSTypes(vali,imageformat,datatype);
  fits_read_pix(fptr, datatype, firstpix, npixels, NULL,segMap.c_array(), NULL, &status);
  segMap.accessGrid() = Image<double>::getGrid();

  // check if there is 2nd extHDU: the weight map
  if (!fits_movabs_hdu(fptr, 3, &hdutype, &status)) {
    weight.resize(npixels);
    setFITSTypes(vald,imageformat,datatype);
    fits_read_pix(fptr, datatype, firstpix, npixels, NULL,weight.c_array(), NULL, &status);
  }
  fits_close_file(fptr, &status);
}

unsigned int Object::getID() const {
  return id;
}

const NumVector<double>& Object::getWeightMap() const {
  return weight;
}

NumVector<double>& Object::accessWeightMap() {
  return weight;
}

const Point2D& Object::getCentroid() const {
  return centroid;
}

void Object::setCentroid(const Point2D& xc) {
  centroid = xc;
  std::ostringstream text;
  text << "# Centroid set to ("<< centroid(0) << "/" << centroid(1) <<  ") by user." << std::endl;
  history.append(text);
}

double Object::getFlux() const {
  return flux;
}

void Object::setFlux(double F) {
  flux = F;
  std::ostringstream text;
  text << "# Flux set to " << flux << " by user." <<std::endl;
  history.append(text);
}

NumMatrix<double> Object::get2ndBrightnessMoments() {
  const NumVector<double>& data = Image<double>::getData();
  const Grid& grid = Image<double>::getGrid();
  NumMatrix<double> Q(2,2);
  for (int i=0; i< grid.size(); i++) {
    Q(0,0) += gsl_pow_2(grid(i,0)-centroid(0)) * data(i);
    Q(0,1) += (grid(i,0)-centroid(0))*(grid(i,1)-centroid(1)) * data(i);
    Q(1,1) += gsl_pow_2(grid(i,1)-centroid(1)) * data(i);
  }
  Q(0,0) /= flux;
  Q(0,1) /= flux;
  Q(1,0) = Q(0,1);
  Q(1,1) /= flux;
  return Q;
}

// ellipticity as defined in Bartelmann & Schneider (2001)
complex<double> Object::getEllipticity() {
  NumMatrix<double> Q = get2ndBrightnessMoments();
  complex<double> I(0,1);
  complex<double> Q11(Q(0,0),0),Q22(Q(1,1),0),Q12(Q(0,1),0);
  complex<double> epsilon = (Q11 - Q22 + 2.*I*Q12)/(Q11+Q22 + 2.*sqrt(Q11*Q22-Q12*Q12));
  return epsilon;
  // axis ratio
  //double e = abs(epsilon);
  //double r = (1-e)/(1+e);
}

void Object::computeFluxCentroid() {
  const NumVector<double>& data = Image<double>::getData();
  const Grid& grid = Image<double>::getGrid();
  flux = 0;
  centroid(0) = centroid(1) = 0;
  for (int i=0; i< grid.size(); i++) {
    flux += data(i);
    centroid(0) += grid(i,0) * data(i);
    centroid(1) += grid(i,1) * data(i);
  }
  centroid(0) /= flux;
  centroid(1) /= flux;

  std::ostringstream text;
  text << "# Flux = " << flux << ", Centroid = ("<< centroid(0) << "/" << centroid(1);
  text <<  ")." <<std::endl;
  history.append(text);
}

unsigned short Object::getDetectionFlag() const {
  return flag;
}

void Object::setDetectionFlag(unsigned short inflag) {
  flag = inflag;
}

double Object::getNoiseMean() const {
  return noise_mean;
}

double Object::getNoiseRMS() const {
  return noise_rms;
}

void Object::setNoiseMeanRMS(double mean, double rms) {
  noise_mean = mean;
  noise_rms = rms;
}

void Object::setNoiseModel(std::string innoisemodel) {
  noisemodel = innoisemodel;
  history.append("# Setting noise model to "+noisemodel+"\n");
}

std::string Object::getNoiseModel() const {
  return noisemodel;
}

double Object::getBlendingProbability() const {
  return blend;
}

void Object::setBlendingProbability(double b) {
  blend = b;
}

double Object::getStarGalaxyProbability() const {
  return s_g;
}

void Object::setStarGalaxyProbability(double sg) {
  s_g = sg;
}

void Object::setBaseFilename(std::string filename) {
  basefilename = filename;
}

const SegmentationMap& Object::getSegmentationMap() const {
  return segMap;
}

SegmentationMap& Object::accessSegmentationMap() {
  return segMap;
}

const PixelCovarianceMatrix& Object::getPixelCovarianceMatrix() const {
  return cov;
}

PixelCovarianceMatrix& Object::accessPixelCovarianceMatrix() {
  return cov;
}

const CorrelationFunction& Object::getCorrelationFunction() const {
  return xi;
}

CorrelationFunction& Object::accessCorrelationFunction() {
  return xi;
}

void Object::save(std::string filename) {
  // write pixel data
  const NumVector<double>& data = Image<double>::getData();
  const Grid& grid = Image<double>::getGrid();
  writeFITSFile(filename,grid,data);

  // add object information to header
  int status = 0;
  fitsfile *outfptr;
  // open pHUD of fits file
  fits_open_data(&outfptr,filename.c_str(),READWRITE,&status);
  fits_write_key (outfptr,TSTRING,"BASEFILE",const_cast<char*>(basefilename.c_str()),"name of source file", &status);
  fits_write_key (outfptr,TINT,"ID",&id,"object id", &status);
  double buffer;
  buffer = grid.getStartPosition(0);
  fits_write_key (outfptr,TDOUBLE,"XMIN",&buffer,"min(X) in image pixels", &status);
  buffer = grid.getStopPosition(0);
  fits_write_key (outfptr,TDOUBLE,"XMAX",&buffer,"max(X) in image pixels", &status);
  buffer = grid.getStartPosition(1);
  fits_write_key (outfptr,TDOUBLE,"YMIN",&buffer,"min(Y) in image pixels", &status);
  buffer = grid.getStopPosition(1);
  fits_write_key (outfptr,TDOUBLE,"YMAX",&buffer,"max(Y) in image pixels", &status);
  fits_write_key (outfptr,TDOUBLE,"BG_MEAN",&noise_mean,"mean of background noise", &status);
  fits_write_key (outfptr,TDOUBLE,"BG_RMS",&noise_rms,"rms of background noise", &status);
  fits_write_key (outfptr,TSTRING,"NOISE",const_cast<char*>(noisemodel.c_str()),"noise model", &status);
  fits_write_key (outfptr,TUSHORT,"FLAG",&flag,"extraction flag", &status);
  fits_write_key (outfptr,TDOUBLE,"BLEND",&blend,"blending probability", &status);
  fits_write_key (outfptr,TDOUBLE,"S_G",&s_g,"stellarity", &status);
  fits_close_file(outfptr, &status);

  // save segMap
  //keywords.clear();
  addFITSExtension(filename,"SEGMAP",grid,segMap.getData());
  //if weight map provided, save it too
  if (weight.size() != 0)
    addFITSExtension(filename,"WEIGHT",grid,weight);
}
<commit_msg>fixed bug in Object::Object(string fitsfile)<commit_after>#include <Object.h>
#include <sstream>
#include <gsl/gsl_math.h>
#include <fitsio.h>

Object::Object(unsigned int inid) : Image<double>(), segMap(*this) {
  id = inid;
  flag = 0;
  blend = s_g = -1;
  flux = centroid(0) = centroid(1) = -1;
}

Object::Object(std::string objfile) : Image<double>(), segMap(*this) {

  fitsfile *fptr;
  int status, nkeys, keypos, hdutype;
  char card[FLEN_CARD];
  char comment[FLEN_CARD];
  status = 0;

  fits_open_file(&fptr, objfile.c_str(), READONLY, &status);

  // open pHDU and read header
  fits_movabs_hdu(fptr, 1, &hdutype, &status);
  // recover object information from header keywords
  fits_read_key (fptr,TSTRING,"BASEFILE",card,comment, &status);
  basefilename = std::string(card);
  fits_read_key (fptr,TINT,"ID",&id,comment, &status);
  double grid_min_0, grid_max_0, grid_min_1, grid_max_1;
  fits_read_key (fptr,TDOUBLE,"XMIN",&grid_min_0,comment, &status);
  fits_read_key (fptr,TDOUBLE,"XMAX",&grid_max_0,comment, &status);
  fits_read_key (fptr,TDOUBLE,"YMIN",&grid_min_1,comment, &status);
  fits_read_key (fptr,TDOUBLE,"YMAX",&grid_max_1,comment, &status);
  Image<double>::accessGrid() = Grid(grid_min_0,grid_max_0,1,grid_min_1,grid_max_1,1);
  fits_read_key (fptr,TDOUBLE,"BG_MEAN",&noise_mean,comment, &status);
  fits_read_key (fptr,TDOUBLE,"BG_RMS",&noise_rms,comment, &status);
  fits_read_key (fptr,TSTRING,"NOISE",card,comment, &status);
  noisemodel = std::string(card);
  fits_read_key (fptr,TUSHORT,"FLAG",&flag,comment, &status);
  fits_read_key (fptr,TDOUBLE,"BLEND",&blend,comment, &status);
  fits_read_key (fptr,TDOUBLE,"S_G",&s_g,comment, &status);

  // if status is not 0 we have faced an error
  if (status != 0) {
    std::cout << "Object: header keywords erroneous!" << std::endl;
    std::terminate();
  }
  int naxis;
  fits_get_img_dim(fptr, &naxis, &status);
  if (naxis!=2) {
    std::cout << "Object: naxis != 2. This is not a FITS image!" << std::endl;
    std::terminate();
  }
  // first obtain the size of the image array
  long naxes[2] = {1,1};
  fits_get_img_size(fptr, naxis, naxes, &status);
  unsigned int axsize0, axsize1;
  axsize0 = naxes[0];
  axsize1 = naxes[1];
  long npixels = axsize0*axsize1;
  // grid is defined by XMIN..YMAX; this should have the same
  // number of pixels as the actual image
  if (npixels != Image<double>::getGrid().size()) {
    std::cout << "Object: Grid size from header keywords wrong" << std::endl;
    std::cout << npixels << " " << grid_min_0 << " " << grid_max_0 << " " << grid_min_1 << " " << grid_max_1 << " "<< Image<double>::getGrid().size();
    std::terminate();
  }
  Image<double>::resize(npixels);
  long firstpix[2] = {1,1};
  double vald;
  int imageformat, datatype;
  setFITSTypes(vald,imageformat,datatype);
  fits_read_pix(fptr, datatype, firstpix, npixels, NULL,Image<double>::c_array(), NULL, &status);

  // move to 1st extHDU for the segmentation map
  fits_movabs_hdu(fptr, 2, &hdutype, &status);
  segMap.resize(npixels);
  int vali;
  setFITSTypes(vali,imageformat,datatype);
  fits_read_pix(fptr, datatype, firstpix, npixels, NULL,segMap.c_array(), NULL, &status);
  segMap.accessGrid() = Image<double>::getGrid();

  // check if there is 2nd extHDU: the weight map
  if (!fits_movabs_hdu(fptr, 3, &hdutype, &status)) {
    weight.resize(npixels);
    setFITSTypes(vald,imageformat,datatype);
    fits_read_pix(fptr, datatype, firstpix, npixels, NULL,weight.c_array(), NULL, &status);
  }
  fits_close_file(fptr, &status);
}

unsigned int Object::getID() const {
  return id;
}

const NumVector<double>& Object::getWeightMap() const {
  return weight;
}

NumVector<double>& Object::accessWeightMap() {
  return weight;
}

const Point2D& Object::getCentroid() const {
  return centroid;
}

void Object::setCentroid(const Point2D& xc) {
  centroid = xc;
  std::ostringstream text;
  text << "# Centroid set to ("<< centroid(0) << "/" << centroid(1) <<  ") by user." << std::endl;
  history.append(text);
}

double Object::getFlux() const {
  return flux;
}

void Object::setFlux(double F) {
  flux = F;
  std::ostringstream text;
  text << "# Flux set to " << flux << " by user." <<std::endl;
  history.append(text);
}

NumMatrix<double> Object::get2ndBrightnessMoments() {
  const NumVector<double>& data = Image<double>::getData();
  const Grid& grid = Image<double>::getGrid();
  NumMatrix<double> Q(2,2);
  for (int i=0; i< grid.size(); i++) {
    Q(0,0) += gsl_pow_2(grid(i,0)-centroid(0)) * data(i);
    Q(0,1) += (grid(i,0)-centroid(0))*(grid(i,1)-centroid(1)) * data(i);
    Q(1,1) += gsl_pow_2(grid(i,1)-centroid(1)) * data(i);
  }
  Q(0,0) /= flux;
  Q(0,1) /= flux;
  Q(1,0) = Q(0,1);
  Q(1,1) /= flux;
  return Q;
}

// ellipticity as defined in Bartelmann & Schneider (2001)
complex<double> Object::getEllipticity() {
  NumMatrix<double> Q = get2ndBrightnessMoments();
  complex<double> I(0,1);
  complex<double> Q11(Q(0,0),0),Q22(Q(1,1),0),Q12(Q(0,1),0);
  complex<double> epsilon = (Q11 - Q22 + 2.*I*Q12)/(Q11+Q22 + 2.*sqrt(Q11*Q22-Q12*Q12));
  return epsilon;
  // axis ratio
  //double e = abs(epsilon);
  //double r = (1-e)/(1+e);
}

void Object::computeFluxCentroid() {
  const NumVector<double>& data = Image<double>::getData();
  const Grid& grid = Image<double>::getGrid();
  flux = 0;
  centroid(0) = centroid(1) = 0;
  for (int i=0; i< grid.size(); i++) {
    flux += data(i);
    centroid(0) += grid(i,0) * data(i);
    centroid(1) += grid(i,1) * data(i);
  }
  centroid(0) /= flux;
  centroid(1) /= flux;

  std::ostringstream text;
  text << "# Flux = " << flux << ", Centroid = ("<< centroid(0) << "/" << centroid(1);
  text <<  ")." <<std::endl;
  history.append(text);
}

unsigned short Object::getDetectionFlag() const {
  return flag;
}

void Object::setDetectionFlag(unsigned short inflag) {
  flag = inflag;
}

double Object::getNoiseMean() const {
  return noise_mean;
}

double Object::getNoiseRMS() const {
  return noise_rms;
}

void Object::setNoiseMeanRMS(double mean, double rms) {
  noise_mean = mean;
  noise_rms = rms;
}

void Object::setNoiseModel(std::string innoisemodel) {
  noisemodel = innoisemodel;
  history.append("# Setting noise model to "+noisemodel+"\n");
}

std::string Object::getNoiseModel() const {
  return noisemodel;
}

double Object::getBlendingProbability() const {
  return blend;
}

void Object::setBlendingProbability(double b) {
  blend = b;
}

double Object::getStarGalaxyProbability() const {
  return s_g;
}

void Object::setStarGalaxyProbability(double sg) {
  s_g = sg;
}

void Object::setBaseFilename(std::string filename) {
  basefilename = filename;
}

const SegmentationMap& Object::getSegmentationMap() const {
  return segMap;
}

SegmentationMap& Object::accessSegmentationMap() {
  return segMap;
}

const PixelCovarianceMatrix& Object::getPixelCovarianceMatrix() const {
  return cov;
}

PixelCovarianceMatrix& Object::accessPixelCovarianceMatrix() {
  return cov;
}

const CorrelationFunction& Object::getCorrelationFunction() const {
  return xi;
}

CorrelationFunction& Object::accessCorrelationFunction() {
  return xi;
}

void Object::save(std::string filename) {
  // write pixel data
  const NumVector<double>& data = Image<double>::getData();
  const Grid& grid = Image<double>::getGrid();
  writeFITSFile(filename,grid,data);

  // add object information to header
  int status = 0;
  fitsfile *outfptr;
  // open pHUD of fits file
  fits_open_data(&outfptr,filename.c_str(),READWRITE,&status);
  fits_write_key (outfptr,TSTRING,"BASEFILE",const_cast<char*>(basefilename.c_str()),"name of source file", &status);
  fits_write_key (outfptr,TINT,"ID",&id,"object id", &status);
  double buffer;
  buffer = grid.getStartPosition(0);
  fits_write_key (outfptr,TDOUBLE,"XMIN",&buffer,"min(X) in image pixels", &status);
  buffer = grid.getStopPosition(0);
  fits_write_key (outfptr,TDOUBLE,"XMAX",&buffer,"max(X) in image pixels", &status);
  buffer = grid.getStartPosition(1);
  fits_write_key (outfptr,TDOUBLE,"YMIN",&buffer,"min(Y) in image pixels", &status);
  buffer = grid.getStopPosition(1);
  fits_write_key (outfptr,TDOUBLE,"YMAX",&buffer,"max(Y) in image pixels", &status);
  fits_write_key (outfptr,TDOUBLE,"BG_MEAN",&noise_mean,"mean of background noise", &status);
  fits_write_key (outfptr,TDOUBLE,"BG_RMS",&noise_rms,"rms of background noise", &status);
  fits_write_key (outfptr,TSTRING,"NOISE",const_cast<char*>(noisemodel.c_str()),"noise model", &status);
  fits_write_key (outfptr,TUSHORT,"FLAG",&flag,"extraction flag", &status);
  fits_write_key (outfptr,TDOUBLE,"BLEND",&blend,"blending probability", &status);
  fits_write_key (outfptr,TDOUBLE,"S_G",&s_g,"stellarity", &status);
  fits_close_file(outfptr, &status);

  // save segMap
  //keywords.clear();
  addFITSExtension(filename,"SEGMAP",grid,segMap.getData());
  //if weight map provided, save it too
  if (weight.size() != 0)
    addFITSExtension(filename,"WEIGHT",grid,weight);
}
<|endoftext|>


<commit_before>#include "RigidRobot2DCSpace.h"
#include "EdgePlanner.h"
#include <GLdraw/GL.h>
#include <math/angle.h>
#include <math/random.h>
#include <math/sample.h>

RigidRobot2DCSpace::RigidRobot2DCSpace()
{
  angleDistanceWeight = 0.3;
  visibilityEpsilon = 0.01;
  domain.bmin.set(0,0);
  domain.bmax.set(1,1);
}

void RigidRobot2DCSpace::DrawWorkspaceGL() const
{
  //blank out background (light yellow)
  glColor3f(1,1,0.5);
  glBegin(GL_QUADS);
  glVertex2f(domain.bmin.x,domain.bmin.y);
  glVertex2f(domain.bmax.x,domain.bmin.y);
  glVertex2f(domain.bmax.x,domain.bmax.y);
  glVertex2f(domain.bmin.x,domain.bmax.y);
  glEnd();

  //draw obstacles (dark grey)
  glColor3f(0.2,0.2,0.2);
  obstacles.DrawGL();
}

void RigidRobot2DCSpace::DrawRobotGL(const Config& x) const
{
  RigidTransform2D T;
  T.t.set(x(0),x(1));
  T.R.setRotate(x(2));
  Geometric2DCollection temp = robot;
  temp.Transform(T);

  //draw robot greyish blue
  temp.DrawGL();
}

void RigidRobot2DCSpace::DrawGL(const Config& x) const
{
  DrawWorkspaceGL();

  glColor3f(0.3,0.3,0.7);
  DrawRobotGL(x);
}

void RigidRobot2DCSpace::Sample(Config& x)
{
  x.resize(3);
  x(0) = Rand(domain.bmin.x,domain.bmax.x);
  x(1) = Rand(domain.bmin.y,domain.bmax.y);
  x(2) = Rand()*TwoPi;
}

void RigidRobot2DCSpace::SampleNeighborhood(const Config& c,Real r,Config& x)
{
  Assert(c.n==3);
  Real dx,dy;
  SampleDisk(r,dx,dy);
  x = c;
  x(0) += dx;
  x(1) += dy;
  x(2) += Rand(-r,r)/angleDistanceWeight;
  x(2)=AngleNormalize(x(2));
}

int RigidRobot2DCSpace::NumObstacles()
{
  return 1+obstacles.NumObstacles();
}

std::string RigidRobot2DCSpace::ObstacleName(int obstacle)
{
  if(obstacle==0) return "domain";
  else {
    obstacle -= 1;
    char buf[64];
    sprintf(buf,"%s[%d]",obstacles.ObstacleTypeName(obstacle),obstacles.ObstacleIndex(obstacle));
    return buf;
  }
}

bool RigidRobot2DCSpace::IsFeasible(const Config& x,int obstacle)
{
  if(obstacle == 0)
    return domain.contains(Vector2(x(0),x(1)));
  obstacle -= 1;
  RigidTransform2D T;
  T.t.set(x(0),x(1));
  T.R.setRotate(x(2));
  Geometric2DCollection temp = robot;
  temp.Transform(T);
  if(obstacles.Collides(temp,obstacle)) return false;
  return true;
}

bool RigidRobot2DCSpace::IsFeasible(const Config& x)
{
  if(!domain.contains(Vector2(x(0),x(1)))) return false;
  RigidTransform2D T;
  T.t.set(x(0),x(1));
  T.R.setRotate(x(2));
  Geometric2DCollection temp = robot;
  temp.Transform(T);
  if(obstacles.Collides(temp)) return false;
  return true;
}

EdgePlanner* RigidRobot2DCSpace::LocalPlanner(const Config& a,const Config& b)
{
  return new BisectionEpsilonExplicitEdgePlanner(this,a,b,visibilityEpsilon);
}

EdgePlanner* RigidRobot2DCSpace::LocalPlanner(const Config& a,const Config& b,int obstacle)
{
  CSpace* space = new SubsetExplicitCSpace(this,obstacle);
  return new EdgePlannerWithCSpaceContainer(space,new BisectionEpsilonEdgePlanner(space,a,b,visibilityEpsilon));
}

Real RigidRobot2DCSpace::Distance(const Config& x, const Config& y)
{
  Real d2 = Vector2(x(0),x(1)).distanceSquared(Vector2(y(0),y(1)));
  return Sqrt(d2 + Sqr(angleDistanceWeight)*Sqr(AngleDiff(AngleNormalize(x(2)),AngleNormalize(y(2)))));
}

void RigidRobot2DCSpace::Interpolate(const Config& x,const Config& y,Real u,Config& out)
{
  CSpace::Interpolate(x,y,u,out);
  out(2) = AngleInterp(AngleNormalize(x(2)),AngleNormalize(y(2)),u);
}

void RigidRobot2DCSpace::Midpoint(const Config& x,const Config& y,Config& out)
{
  CSpace::Interpolate(x,y,0.5,out);
  out(2) = AngleInterp(AngleNormalize(x(2)),AngleNormalize(y(2)),0.5);
}

void RigidRobot2DCSpace::Properties(PropertyMap& map) const
{
  map.set("cartesian",0);
  map.set("geodesic",1);
  map.set("metric","weighted euclidean");
  vector<Real> w(3,1.0);
  w[2] = angleDistanceWeight;
  map.setArray("metricWeights",w);
  Real v = (domain.bmax.x-domain.bmin.x)*(domain.bmax.y-domain.bmin.y)*angleDistanceWeight*TwoPi;
  map.set("volume",v);
  map.set("diameter",Sqrt(domain.bmin.distanceSquared(domain.bmax)+Sqr(angleDistanceWeight*TwoPi)));
  vector<Real> bmin(3),bmax(3);
  domain.bmin.get(bmin[0],bmin[1]);
  domain.bmax.get(bmax[0],bmax[1]);
  bmin[2]=0;
  bmax[2]=TwoPi;
  map.setArray("minimum",bmin);
  map.setArray("maximum",bmax);
}
<commit_msg>Outlines now drawn<commit_after>#include "RigidRobot2DCSpace.h"
#include "EdgePlanner.h"
#include <GLdraw/GL.h>
#include <math/angle.h>
#include <math/random.h>
#include <math/sample.h>

RigidRobot2DCSpace::RigidRobot2DCSpace()
{
  angleDistanceWeight = 0.3;
  visibilityEpsilon = 0.01;
  domain.bmin.set(0,0);
  domain.bmax.set(1,1);
}

void RigidRobot2DCSpace::DrawWorkspaceGL() const
{
  //blank out background (light yellow)
  glColor3f(1,1,0.5);
  glBegin(GL_QUADS);
  glVertex2f(domain.bmin.x,domain.bmin.y);
  glVertex2f(domain.bmax.x,domain.bmin.y);
  glVertex2f(domain.bmax.x,domain.bmax.y);
  glVertex2f(domain.bmin.x,domain.bmax.y);
  glEnd();

  //draw obstacles (dark grey)
  glColor3f(0.2,0.2,0.2);
  obstacles.DrawGL();
}

void RigidRobot2DCSpace::DrawRobotGL(const Config& x) const
{
  RigidTransform2D T;
  T.t.set(x(0),x(1));
  T.R.setRotate(x(2));
  Geometric2DCollection temp = robot;
  temp.Transform(T);

  //draw robot greyish blue
  temp.DrawGL();
  //draw outline
  glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT);
  glLineWidth(1.0);
  glColor3f(0,0,0);
  temp.DrawOutlinesGL();
  glPopAttrib();

}

void RigidRobot2DCSpace::DrawGL(const Config& x) const
{
  DrawWorkspaceGL();

  glColor3f(0.3,0.3,0.7);
  DrawRobotGL(x);
}

void RigidRobot2DCSpace::Sample(Config& x)
{
  x.resize(3);
  x(0) = Rand(domain.bmin.x,domain.bmax.x);
  x(1) = Rand(domain.bmin.y,domain.bmax.y);
  x(2) = Rand()*TwoPi;
}

void RigidRobot2DCSpace::SampleNeighborhood(const Config& c,Real r,Config& x)
{
  Assert(c.n==3);
  Real dx,dy;
  SampleDisk(r,dx,dy);
  x = c;
  x(0) += dx;
  x(1) += dy;
  x(2) += Rand(-r,r)/angleDistanceWeight;
  x(2)=AngleNormalize(x(2));
}

int RigidRobot2DCSpace::NumObstacles()
{
  return 1+obstacles.NumObstacles();
}

std::string RigidRobot2DCSpace::ObstacleName(int obstacle)
{
  if(obstacle==0) return "domain";
  else {
    obstacle -= 1;
    char buf[64];
    sprintf(buf,"%s[%d]",obstacles.ObstacleTypeName(obstacle),obstacles.ObstacleIndex(obstacle));
    return buf;
  }
}

bool RigidRobot2DCSpace::IsFeasible(const Config& x,int obstacle)
{
  if(obstacle == 0)
    return domain.contains(Vector2(x(0),x(1)));
  obstacle -= 1;
  RigidTransform2D T;
  T.t.set(x(0),x(1));
  T.R.setRotate(x(2));
  Geometric2DCollection temp = robot;
  temp.Transform(T);
  if(obstacles.Collides(temp,obstacle)) return false;
  return true;
}

bool RigidRobot2DCSpace::IsFeasible(const Config& x)
{
  if(!domain.contains(Vector2(x(0),x(1)))) return false;
  RigidTransform2D T;
  T.t.set(x(0),x(1));
  T.R.setRotate(x(2));
  Geometric2DCollection temp = robot;
  temp.Transform(T);
  if(obstacles.Collides(temp)) return false;
  return true;
}

EdgePlanner* RigidRobot2DCSpace::LocalPlanner(const Config& a,const Config& b)
{
  return new BisectionEpsilonExplicitEdgePlanner(this,a,b,visibilityEpsilon);
}

EdgePlanner* RigidRobot2DCSpace::LocalPlanner(const Config& a,const Config& b,int obstacle)
{
  CSpace* space = new SubsetExplicitCSpace(this,obstacle);
  return new EdgePlannerWithCSpaceContainer(space,new BisectionEpsilonEdgePlanner(space,a,b,visibilityEpsilon));
}

Real RigidRobot2DCSpace::Distance(const Config& x, const Config& y)
{
  Real d2 = Vector2(x(0),x(1)).distanceSquared(Vector2(y(0),y(1)));
  return Sqrt(d2 + Sqr(angleDistanceWeight)*Sqr(AngleDiff(AngleNormalize(x(2)),AngleNormalize(y(2)))));
}

void RigidRobot2DCSpace::Interpolate(const Config& x,const Config& y,Real u,Config& out)
{
  CSpace::Interpolate(x,y,u,out);
  out(2) = AngleInterp(AngleNormalize(x(2)),AngleNormalize(y(2)),u);
}

void RigidRobot2DCSpace::Midpoint(const Config& x,const Config& y,Config& out)
{
  CSpace::Interpolate(x,y,0.5,out);
  out(2) = AngleInterp(AngleNormalize(x(2)),AngleNormalize(y(2)),0.5);
}

void RigidRobot2DCSpace::Properties(PropertyMap& map) const
{
  map.set("cartesian",0);
  map.set("geodesic",1);
  map.set("metric","weighted euclidean");
  vector<Real> w(3,1.0);
  w[2] = angleDistanceWeight;
  map.setArray("metricWeights",w);
  Real v = (domain.bmax.x-domain.bmin.x)*(domain.bmax.y-domain.bmin.y)*angleDistanceWeight*TwoPi;
  map.set("volume",v);
  map.set("diameter",Sqrt(domain.bmin.distanceSquared(domain.bmax)+Sqr(angleDistanceWeight*TwoPi)));
  vector<Real> bmin(3),bmax(3);
  domain.bmin.get(bmin[0],bmin[1]);
  domain.bmax.get(bmax[0],bmax[1]);
  bmin[2]=0;
  bmax[2]=TwoPi;
  map.setArray("minimum",bmin);
  map.setArray("maximum",bmax);
}
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_CSV_UTILS_DATASOURCE_HPP
#define MAPNIK_CSV_UTILS_DATASOURCE_HPP

// mapnik
#include <mapnik/debug.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/geometry_correct.hpp>
#include <mapnik/wkt/wkt_factory.hpp>
#include <mapnik/json/geometry_parser.hpp>
#include <mapnik/util/conversions.hpp>
#include <mapnik/csv/csv_grammar.hpp>
#include <mapnik/util/trim.hpp>
// boost
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-local-typedef"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#include <boost/algorithm/string.hpp>
#pragma GCC diagnostic pop

#include <string>
#include <cstdio>
#include <algorithm>

#ifndef _WINDOWS
#define CSV_MEMORY_MAPPED_FILE
#endif

namespace csv_utils
{

static const mapnik::csv_line_grammar<char const*> line_g;

template <typename Iterator>
static mapnik::csv_line parse_line(Iterator start, Iterator end, char separator, char quote, std::size_t num_columns)
{
    mapnik::csv_line values;
    if (num_columns > 0) values.reserve(num_columns);
    boost::spirit::standard::blank_type blank;
    if (!boost::spirit::qi::phrase_parse(start, end, (line_g)(separator, quote), blank, values))
    {
        throw std::runtime_error("Failed to parse CSV line:\n" + std::string(start, end));
    }
    return values;
}

static inline mapnik::csv_line parse_line(std::string const& line_str, char separator, char quote)
{
    auto start = line_str.c_str();
    auto end   = start + line_str.length();
    return parse_line(start, end, separator, quote, 0);
}

static inline bool is_likely_number(std::string const& value)
{
    return( strspn( value.c_str(), "e-.+0123456789" ) == value.size() );
}

struct ignore_case_equal_pred
{
    bool operator () (unsigned char a, unsigned char b) const
    {
        return std::tolower(a) == std::tolower(b);
    }
};

inline bool ignore_case_equal(std::string const& s0, std::string const& s1)
{
    return std::equal(s0.begin(), s0.end(),
                      s1.begin(), ignore_case_equal_pred());
}

template <class CharT, class Traits, class Allocator>
std::basic_istream<CharT, Traits>& getline_csv(std::istream& is, std::basic_string<CharT,Traits,Allocator>& s, CharT delim, CharT quote)
{
    typename std::basic_string<CharT,Traits,Allocator>::size_type nread = 0;
    typename std::basic_istream<CharT, Traits>::sentry sentry(is, true);
    if (sentry)
    {
        std::basic_streambuf<CharT, Traits>* buf = is.rdbuf();
        s.clear();
        bool has_quote = false;
        while (nread < s.max_size())
        {
            int c1 = buf->sbumpc();
            if (Traits::eq_int_type(c1, Traits::eof()))
            {
                is.setstate(std::ios_base::eofbit);
                break;
            }
            else
            {
                ++nread;
                CharT c = Traits::to_char_type(c1);
                if (Traits::eq(c, quote))
                    has_quote = !has_quote;
                if (!Traits::eq(c, delim) || has_quote)
                    s.push_back(c);
                else
                    break;// Character is extracted but not appended.
            }
        }
    }
    if (nread == 0 || nread >= s.max_size())
        is.setstate(std::ios_base::failbit);

    return is;
}

}

namespace detail {

template <typename T>
std::size_t file_length(T & stream)
{
    stream.seekg(0, std::ios::end);
    return stream.tellg();
}

static inline char detect_separator(std::string const& str)
{
    char separator = ','; // default
    int num_commas = std::count(str.begin(), str.end(), ',');
    // detect tabs
    int num_tabs = std::count(str.begin(), str.end(), '\t');
    if (num_tabs > 0)
    {
        if (num_tabs > num_commas)
        {
            separator = '\t';
            MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected tab separator";
        }
    }
    else // pipes
    {
        int num_pipes = std::count(str.begin(), str.end(), '|');
        if (num_pipes > num_commas)
        {
            separator = '|';
            MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected '|' separator";
        }
        else // semicolons
        {
            int num_semicolons = std::count(str.begin(), str.end(), ';');
            if (num_semicolons > num_commas)
            {
                separator = ';';
                MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected ';' separator";
            }
        }
    }
    return separator;
}

template <typename T>
std::tuple<char,bool,char> autodect_newline_and_quote(T & stream, std::size_t file_length)
{
    // autodetect newlines
    char newline = '\n';
    bool has_newline = false;
    char quote = '"';
    bool has_quote = false;
    static std::size_t const max_size = 4000;
    std::size_t size = std::min(file_length, max_size);
    for (std::size_t lidx = 0; lidx < size; ++lidx)
    {
        char c = static_cast<char>(stream.get());
        if (c == '\r')
        {
            newline = '\r';
            has_newline = true;
            //break;
        }
        if (c == '\n')
        {
            has_newline = true;
            //break;
        }
        else if (!has_quote && c == '\'')
        {
            quote = '\'';
            has_quote = true;
        }
    }
    return std::make_tuple(newline, has_newline, quote);
}

struct geometry_column_locator
{
    geometry_column_locator()
        : type(UNKNOWN), index(-1), index2(-1) {}

    enum { UNKNOWN = 0, WKT, GEOJSON, LON_LAT } type;
    std::size_t index;
    std::size_t index2;
};

static inline void locate_geometry_column(std::string const& header, std::size_t index, geometry_column_locator & locator)
{
    std::string lower_val(header);
    std::transform(lower_val.begin(), lower_val.end(), lower_val.begin(), ::tolower);
    if (lower_val == "wkt" || (lower_val.find("geom") != std::string::npos))
    {
        locator.type = geometry_column_locator::WKT;
        locator.index = index;
    }
    else if (lower_val == "geojson")
    {
        locator.type = geometry_column_locator::GEOJSON;
        locator.index = index;
    }
    else if (lower_val == "x" || lower_val == "lon"
             || lower_val == "lng" || lower_val == "long"
             || (lower_val.find("longitude") != std::string::npos))
    {
        locator.index = index;
        locator.type = geometry_column_locator::LON_LAT;
    }

    else if (lower_val == "y"
             || lower_val == "lat"
             || (lower_val.find("latitude") != std::string::npos))
    {
        locator.index2 = index;
        locator.type = geometry_column_locator::LON_LAT;
    }
}

static mapnik::geometry::geometry<double> extract_geometry(std::vector<std::string> const& row, geometry_column_locator const& locator)
{
    mapnik::geometry::geometry<double> geom;
    if (locator.type == geometry_column_locator::WKT)
    {
        if (mapnik::from_wkt(row[locator.index], geom))
        {
            // correct orientations ..
            mapnik::geometry::correct(geom);
        }
        else
        {
            throw std::runtime_error("Failed to parse WKT:" + row[locator.index]);
        }
    }
    else if (locator.type == geometry_column_locator::GEOJSON)
    {

        if (!mapnik::json::from_geojson(row[locator.index], geom))
        {
            throw std::runtime_error("Failed to parse GeoJSON:" + row[locator.index]);
        }
    }
    else if (locator.type == geometry_column_locator::LON_LAT)
    {
        double x, y;
        if (!mapnik::util::string2double(row[locator.index],x))
        {
            throw std::runtime_error("Failed to parse Longitude(Easting):" + row[locator.index]);
        }
        if (!mapnik::util::string2double(row[locator.index2],y))
        {
            throw std::runtime_error("Failed to parse Latitude(Northing):" + row[locator.index2]);
        }
        geom = mapnik::geometry::point<double>(x,y);
    }
    return geom;
}

template <typename Feature, typename Headers, typename Values, typename Locator, typename Transcoder>
void process_properties(Feature & feature, Headers const& headers, Values const& values, Locator const& locator, Transcoder const& tr)
{
    auto val_beg = values.begin();
    auto val_end = values.end();
    auto num_headers = headers.size();
    for (std::size_t i = 0; i < num_headers; ++i)
    {
        std::string const& fld_name = headers.at(i);
        if (val_beg == val_end)
        {
            feature.put(fld_name,tr.transcode(""));
            continue;
        }
        std::string value = mapnik::util::trim_copy(*val_beg++);
        int value_length = value.length();

        if (locator.index == i && (locator.type == detail::geometry_column_locator::WKT
                                   || locator.type == detail::geometry_column_locator::GEOJSON)  ) continue;

        bool matched = false;
        bool has_dot = value.find(".") != std::string::npos;
        if (value.empty() ||
            (value_length > 20) ||
            (value_length > 1 && !has_dot && value[0] == '0'))
        {
            matched = true;
            feature.put(fld_name,std::move(tr.transcode(value.c_str())));
        }
        else if (csv_utils::is_likely_number(value))
        {
            bool has_e = value.find("e") != std::string::npos;
            if (has_dot || has_e)
            {
                double float_val = 0.0;
                if (mapnik::util::string2double(value,float_val))
                {
                    matched = true;
                    feature.put(fld_name,float_val);
                }
            }
            else
            {
                mapnik::value_integer int_val = 0;
                if (mapnik::util::string2int(value,int_val))
                {
                    matched = true;
                    feature.put(fld_name,int_val);
                }
            }
        }
        if (!matched)
        {
            if (csv_utils::ignore_case_equal(value, "true"))
            {
                feature.put(fld_name, true);
            }
            else if (csv_utils::ignore_case_equal(value, "false"))
            {
                feature.put(fld_name, false);
            }
            else // fallback to string
            {
                feature.put(fld_name,std::move(tr.transcode(value.c_str())));
            }
        }
    }
}

}// ns detail

#endif // MAPNIK_CSV_UTILS_DATASOURCE_HPP
<commit_msg>update to use latest grammar + use std:: qualifier for 'C' lib functions<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_CSV_UTILS_DATASOURCE_HPP
#define MAPNIK_CSV_UTILS_DATASOURCE_HPP

// mapnik
#include <mapnik/debug.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/geometry_correct.hpp>
#include <mapnik/wkt/wkt_factory.hpp>
#include <mapnik/json/geometry_parser.hpp>
#include <mapnik/util/conversions.hpp>
#include <mapnik/csv/csv_grammar.hpp>
#include <mapnik/util/trim.hpp>
// boost
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-local-typedef"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#include <boost/algorithm/string.hpp>
#pragma GCC diagnostic pop

#include <string>
#include <cstdio>
#include <algorithm>

#ifndef _WINDOWS
#define CSV_MEMORY_MAPPED_FILE
#endif

namespace csv_utils
{

static const mapnik::csv_line_grammar<char const*> line_g;
static const mapnik::csv_white_space_skipper<char const*> skipper;

template <typename Iterator>
static mapnik::csv_line parse_line(Iterator start, Iterator end, char separator, char quote, std::size_t num_columns)
{
    mapnik::csv_line values;
    if (num_columns > 0) values.reserve(num_columns);
    if (!boost::spirit::qi::phrase_parse(start, end, (line_g)(separator, quote), skipper, values))
    {
        throw std::runtime_error("Failed to parse CSV line:\n" + std::string(start, end));
    }
    return values;
}

static inline mapnik::csv_line parse_line(std::string const& line_str, char separator, char quote)
{
    auto start = line_str.c_str();
    auto end   = start + line_str.length();
    return parse_line(start, end, separator, quote, 0);
}

static inline bool is_likely_number(std::string const& value)
{
    return (std::strspn( value.c_str(), "e-.+0123456789" ) == value.size());
}

struct ignore_case_equal_pred
{
    bool operator () (unsigned char a, unsigned char b) const
    {
        return std::tolower(a) == std::tolower(b);
    }
};

inline bool ignore_case_equal(std::string const& s0, std::string const& s1)
{
    return std::equal(s0.begin(), s0.end(),
                      s1.begin(), ignore_case_equal_pred());
}

template <class CharT, class Traits, class Allocator>
std::basic_istream<CharT, Traits>& getline_csv(std::istream& is, std::basic_string<CharT,Traits,Allocator>& s, CharT delim, CharT quote)
{
    typename std::basic_string<CharT,Traits,Allocator>::size_type nread = 0;
    typename std::basic_istream<CharT, Traits>::sentry sentry(is, true);
    if (sentry)
    {
        std::basic_streambuf<CharT, Traits>* buf = is.rdbuf();
        s.clear();
        bool has_quote = false;
        while (nread < s.max_size())
        {
            int c1 = buf->sbumpc();
            if (Traits::eq_int_type(c1, Traits::eof()))
            {
                is.setstate(std::ios_base::eofbit);
                break;
            }
            else
            {
                ++nread;
                CharT c = Traits::to_char_type(c1);
                if (Traits::eq(c, quote))
                    has_quote = !has_quote;
                if (!Traits::eq(c, delim) || has_quote)
                    s.push_back(c);
                else
                    break;// Character is extracted but not appended.
            }
        }
    }
    if (nread == 0 || nread >= s.max_size())
        is.setstate(std::ios_base::failbit);

    return is;
}

}

namespace detail {

template <typename T>
std::size_t file_length(T & stream)
{
    stream.seekg(0, std::ios::end);
    return stream.tellg();
}

static inline char detect_separator(std::string const& str)
{
    char separator = ','; // default
    int num_commas = std::count(str.begin(), str.end(), ',');
    // detect tabs
    int num_tabs = std::count(str.begin(), str.end(), '\t');
    if (num_tabs > 0)
    {
        if (num_tabs > num_commas)
        {
            separator = '\t';
            MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected tab separator";
        }
    }
    else // pipes
    {
        int num_pipes = std::count(str.begin(), str.end(), '|');
        if (num_pipes > num_commas)
        {
            separator = '|';
            MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected '|' separator";
        }
        else // semicolons
        {
            int num_semicolons = std::count(str.begin(), str.end(), ';');
            if (num_semicolons > num_commas)
            {
                separator = ';';
                MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected ';' separator";
            }
        }
    }
    return separator;
}

template <typename T>
std::tuple<char,bool,char> autodect_newline_and_quote(T & stream, std::size_t file_length)
{
    // autodetect newlines
    char newline = '\n';
    bool has_newline = false;
    char quote = '"';
    bool has_quote = false;
    static std::size_t const max_size = 4000;
    std::size_t size = std::min(file_length, max_size);
    for (std::size_t lidx = 0; lidx < size; ++lidx)
    {
        char c = static_cast<char>(stream.get());
        if (c == '\r')
        {
            newline = '\r';
            has_newline = true;
            //break;
        }
        if (c == '\n')
        {
            has_newline = true;
            //break;
        }
        else if (!has_quote && c == '\'')
        {
            quote = '\'';
            has_quote = true;
        }
    }
    return std::make_tuple(newline, has_newline, quote);
}

struct geometry_column_locator
{
    geometry_column_locator()
        : type(UNKNOWN), index(-1), index2(-1) {}

    enum { UNKNOWN = 0, WKT, GEOJSON, LON_LAT } type;
    std::size_t index;
    std::size_t index2;
};

static inline void locate_geometry_column(std::string const& header, std::size_t index, geometry_column_locator & locator)
{
    std::string lower_val(header);
    std::transform(lower_val.begin(), lower_val.end(), lower_val.begin(), ::tolower);
    if (lower_val == "wkt" || (lower_val.find("geom") != std::string::npos))
    {
        locator.type = geometry_column_locator::WKT;
        locator.index = index;
    }
    else if (lower_val == "geojson")
    {
        locator.type = geometry_column_locator::GEOJSON;
        locator.index = index;
    }
    else if (lower_val == "x" || lower_val == "lon"
             || lower_val == "lng" || lower_val == "long"
             || (lower_val.find("longitude") != std::string::npos))
    {
        locator.index = index;
        locator.type = geometry_column_locator::LON_LAT;
    }

    else if (lower_val == "y"
             || lower_val == "lat"
             || (lower_val.find("latitude") != std::string::npos))
    {
        locator.index2 = index;
        locator.type = geometry_column_locator::LON_LAT;
    }
}

static mapnik::geometry::geometry<double> extract_geometry(std::vector<std::string> const& row, geometry_column_locator const& locator)
{
    mapnik::geometry::geometry<double> geom;
    if (locator.type == geometry_column_locator::WKT)
    {
        if (mapnik::from_wkt(row[locator.index], geom))
        {
            // correct orientations ..
            mapnik::geometry::correct(geom);
        }
        else
        {
            throw std::runtime_error("Failed to parse WKT:" + row[locator.index]);
        }
    }
    else if (locator.type == geometry_column_locator::GEOJSON)
    {

        if (!mapnik::json::from_geojson(row[locator.index], geom))
        {
            throw std::runtime_error("Failed to parse GeoJSON:" + row[locator.index]);
        }
    }
    else if (locator.type == geometry_column_locator::LON_LAT)
    {
        double x, y;
        if (!mapnik::util::string2double(row[locator.index],x))
        {
            throw std::runtime_error("Failed to parse Longitude(Easting):" + row[locator.index]);
        }
        if (!mapnik::util::string2double(row[locator.index2],y))
        {
            throw std::runtime_error("Failed to parse Latitude(Northing):" + row[locator.index2]);
        }
        geom = mapnik::geometry::point<double>(x,y);
    }
    return geom;
}

template <typename Feature, typename Headers, typename Values, typename Locator, typename Transcoder>
void process_properties(Feature & feature, Headers const& headers, Values const& values, Locator const& locator, Transcoder const& tr)
{
    auto val_beg = values.begin();
    auto val_end = values.end();
    auto num_headers = headers.size();
    for (std::size_t i = 0; i < num_headers; ++i)
    {
        std::string const& fld_name = headers.at(i);
        if (val_beg == val_end)
        {
            feature.put(fld_name,tr.transcode(""));
            continue;
        }
        std::string value = mapnik::util::trim_copy(*val_beg++);
        int value_length = value.length();

        if (locator.index == i && (locator.type == detail::geometry_column_locator::WKT
                                   || locator.type == detail::geometry_column_locator::GEOJSON)  ) continue;

        bool matched = false;
        bool has_dot = value.find(".") != std::string::npos;
        if (value.empty() ||
            (value_length > 20) ||
            (value_length > 1 && !has_dot && value[0] == '0'))
        {
            matched = true;
            feature.put(fld_name,std::move(tr.transcode(value.c_str())));
        }
        else if (csv_utils::is_likely_number(value))
        {
            bool has_e = value.find("e") != std::string::npos;
            if (has_dot || has_e)
            {
                double float_val = 0.0;
                if (mapnik::util::string2double(value,float_val))
                {
                    matched = true;
                    feature.put(fld_name,float_val);
                }
            }
            else
            {
                mapnik::value_integer int_val = 0;
                if (mapnik::util::string2int(value,int_val))
                {
                    matched = true;
                    feature.put(fld_name,int_val);
                }
            }
        }
        if (!matched)
        {
            if (csv_utils::ignore_case_equal(value, "true"))
            {
                feature.put(fld_name, true);
            }
            else if (csv_utils::ignore_case_equal(value, "false"))
            {
                feature.put(fld_name, false);
            }
            else // fallback to string
            {
                feature.put(fld_name,std::move(tr.transcode(value.c_str())));
            }
        }
    }
}

}// ns detail

#endif // MAPNIK_CSV_UTILS_DATASOURCE_HPP
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2016 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef OGR_INDEX_HH
#define OGR_INDEX_HH

// stl
#include <fstream>
#include <vector>

// mapnik
#include <mapnik/box2d.hpp>
#include <mapnik/query.hpp>

using mapnik::box2d;
using mapnik::query;

template <typename filterT, typename IStream = std::ifstream>
class ogr_index
{
public:
    static void query(const filterT& filter, IStream& file, std::vector<int>& pos);
private:
    ogr_index();
    ~ogr_index();
    ogr_index(const ogr_index&);
    ogr_index& operator=(const ogr_index&);
    static int read_ndr_integer(IStream& in);
    static void read_envelope(IStream& in, box2d<double>& envelope);
    static void query_node(const filterT& filter, IStream& in, std::vector<int>& pos);
};

template <typename filterT, typename IStream>
void ogr_index<filterT, IStream>::query(const filterT& filter,IStream & file,std::vector<int>& pos)
{
    file.seekg(16,std::ios::beg);
    query_node(filter,file,pos);
}

template <typename filterT, typename IStream>
void ogr_index<filterT,IStream>::query_node(const filterT& filter, IStream& file, std::vector<int>& ids)
{
    const int offset = read_ndr_integer(file);

    box2d<double> node_ext;
    read_envelope(file, node_ext);

    const int num_shapes = read_ndr_integer(file);

    if (! filter.pass(node_ext))
    {
        file.seekg(offset + num_shapes * 4 + 4, std::ios::cur);
        return;
    }

    for (int i = 0; i < num_shapes; ++i)
    {
        const int id = read_ndr_integer(file);
        ids.push_back(id);
    }

    const int children = read_ndr_integer(file);

    for (int j = 0; j < children; ++j)
    {
        query_node(filter, file, ids);
    }
}

template <typename filterT, typename IStream>
int ogr_index<filterT,IStream>::read_ndr_integer(IStream& file)
{
    char b[4];
    file.read(b, 4);
    return (b[0] & 0xff) | (b[1] & 0xff) << 8 | (b[2] & 0xff) << 16 | (b[3] & 0xff) << 24;
}

template <typename filterT, typename IStream>
void ogr_index<filterT, IStream>::read_envelope(IStream& file, box2d<double>& envelope)
{
    file.read(reinterpret_cast<char*>(&envelope), sizeof(envelope));
}

#endif // OGR_INDEX_HH
<commit_msg>minor formatting<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2016 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef OGR_INDEX_HPP
#define OGR_INDEX_HPP

// stl
#include <fstream>
#include <vector>

// mapnik
#include <mapnik/box2d.hpp>
#include <mapnik/query.hpp>

using mapnik::box2d;
using mapnik::query;

template <typename filterT, typename IStream = std::ifstream>
class ogr_index
{
public:
    static void query(const filterT& filter, IStream& file, std::vector<int>& pos);
private:
    ogr_index();
    ~ogr_index();
    ogr_index(const ogr_index&);
    ogr_index& operator=(const ogr_index&);
    static int read_ndr_integer(IStream& in);
    static void read_envelope(IStream& in, box2d<double>& envelope);
    static void query_node(const filterT& filter, IStream& in, std::vector<int>& pos);
};

template <typename filterT, typename IStream>
void ogr_index<filterT, IStream>::query(const filterT& filter,IStream & file,std::vector<int>& pos)
{
    file.seekg(16,std::ios::beg);
    query_node(filter,file,pos);
}

template <typename filterT, typename IStream>
void ogr_index<filterT,IStream>::query_node(const filterT& filter, IStream& file, std::vector<int>& ids)
{
    const int offset = read_ndr_integer(file);

    box2d<double> node_ext;
    read_envelope(file, node_ext);

    const int num_shapes = read_ndr_integer(file);

    if (! filter.pass(node_ext))
    {
        file.seekg(offset + num_shapes * 4 + 4, std::ios::cur);
        return;
    }

    for (int i = 0; i < num_shapes; ++i)
    {
        const int id = read_ndr_integer(file);
        ids.push_back(id);
    }

    const int children = read_ndr_integer(file);

    for (int j = 0; j < children; ++j)
    {
        query_node(filter, file, ids);
    }
}

template <typename filterT, typename IStream>
int ogr_index<filterT,IStream>::read_ndr_integer(IStream& file)
{
    char b[4];
    file.read(b, 4);
    return (b[0] & 0xff) | (b[1] & 0xff) << 8 | (b[2] & 0xff) << 16 | (b[3] & 0xff) << 24;
}

template <typename filterT, typename IStream>
void ogr_index<filterT, IStream>::read_envelope(IStream& file, box2d<double>& envelope)
{
    file.read(reinterpret_cast<char*>(&envelope), sizeof(envelope));
}

#endif // OGR_INDEX_HPP
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993 - 2008 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// logDetail.cpp : Plugin module for logging server events to stdout
//

#include <iostream>
#include <sstream>
#include "bzfsAPI.h"

BZ_GET_PLUGIN_VERSION

enum action {join , auth , part};

class LogDetail:public bz_EventHandler
{
public:
  LogDetail();
  virtual ~LogDetail();
  virtual void process(bz_EventData *eventData);
private:
  std::string displayPlayerPrivs(int playerID);
  std::string displayCallsign(bz_ApiString callsign);
  std::string displayCallsign(int playerID);
  std::string displayBZid(int playerID);
  std::string displayTeam(bz_eTeamType team);
  virtual void listPlayers(action act, bz_PlayerJoinPartEventData_V1 *data);
};

LogDetail logDetailHandler;

BZF_PLUGIN_CALL int bz_Load(const char * /*commandLine */ )
{
  bz_registerEvent(bz_eSlashCommandEvent, &logDetailHandler);
  bz_registerEvent(bz_eRawChatMessageEvent, &logDetailHandler);
  bz_registerEvent(bz_eServerMsgEvent, &logDetailHandler);
  bz_registerEvent(bz_ePlayerJoinEvent, &logDetailHandler);
  bz_registerEvent(bz_ePlayerPartEvent, &logDetailHandler);
  bz_registerEvent(bz_ePlayerAuthEvent, &logDetailHandler);
  bz_registerEvent(bz_eMessageFilteredEvent, &logDetailHandler);
  bz_debugMessage(4, "logDetail plugin loaded");
  return 0;
}

BZF_PLUGIN_CALL int bz_Unload(void)
{
  bz_removeEvent(bz_eSlashCommandEvent, &logDetailHandler);
  bz_removeEvent(bz_eRawChatMessageEvent, &logDetailHandler);
  bz_removeEvent(bz_eServerMsgEvent, &logDetailHandler);
  bz_removeEvent(bz_ePlayerJoinEvent, &logDetailHandler);
  bz_removeEvent(bz_ePlayerPartEvent, &logDetailHandler);
  bz_removeEvent(bz_ePlayerAuthEvent, &logDetailHandler);
  bz_removeEvent(bz_eMessageFilteredEvent, &logDetailHandler);
  bz_debugMessage(4, "logDetail plugin unloaded");
  return 0;
}

LogDetail::LogDetail()
{
  bz_debugMessage(0, "SERVER-STATUS Running");
  bz_debugMessagef(0, "SERVER-MAPNAME %s", bz_getPublicDescription().c_str());
  listPlayers(join, NULL);
}

LogDetail::~LogDetail()
{
  listPlayers(part, NULL);
  bz_debugMessage(0, "SERVER-STATUS Stopped");
}

void LogDetail::process(bz_EventData *eventData)
{
  bz_ChatEventData_V1 *chatData = (bz_ChatEventData_V1 *) eventData;
  bz_ServerMsgEventData_V1 *serverMsgData = (bz_ServerMsgEventData_V1 *) eventData;
  bz_SlashCommandEventData_V1 *cmdData = (bz_SlashCommandEventData_V1 *) eventData;
  bz_PlayerJoinPartEventData_V1 *joinPartData = (bz_PlayerJoinPartEventData_V1 *) eventData;
  bz_PlayerAuthEventData_V1 *authData = (bz_PlayerAuthEventData_V1 *) eventData;
  bz_MessageFilteredEventData_V1 *filteredData = (bz_MessageFilteredEventData_V1 *) eventData;
  char temp[9] = { 0 };

  if (eventData) {
    switch (eventData->eventType) {
    case bz_eSlashCommandEvent:
      // Slash commands are case insensitive
      // Tokenize the stream and check the first word
      // /report -> MSG-REPORT
      // anything -> MSG-COMMAND

      strncpy(temp, cmdData->message.c_str(), 8);

      if (strcasecmp(temp, "/REPORT ") == 0) {
	bz_debugMessagef(0, "MSG-REPORT %s %s",
			 displayCallsign(cmdData->from).c_str(),
			 cmdData->message.c_str()+8);
      } else {
	bz_debugMessagef(0, "MSG-COMMAND %s %s",
			 displayCallsign(cmdData->from).c_str(),
			 cmdData->message.c_str()+1);
      }
      break;
    case bz_eRawChatMessageEvent:
      if ((chatData->to == BZ_ALLUSERS) && (chatData->team == eNoTeam)) {
	bz_debugMessagef(0, "MSG-BROADCAST %s %s",
			 displayCallsign(chatData->from).c_str(),
			 chatData->message.c_str());
      } else if (chatData->to == BZ_NULLUSER) {
	if (chatData->team == eAdministrators) {
	  bz_debugMessagef(0, "MSG-ADMIN %s %s",
			   displayCallsign(chatData->from).c_str(),
			   chatData->message.c_str());
	} else {
	  bz_debugMessagef(0, "MSG-TEAM %s %s %s",
			   displayCallsign(chatData->from).c_str(),
			   displayTeam(chatData->team).c_str(),
			   chatData->message.c_str());
	}
      } else {
	  bz_debugMessagef(0, "MSG-DIRECT %s %s %s",
			   displayCallsign(chatData->from).c_str(),
			   displayCallsign(chatData->to).c_str(),
			   chatData->message.c_str());
      }
      break;
    case bz_eMessageFilteredEvent:
      bz_debugMessagef(0, "MSG-FILTERED %s %s",
		       displayCallsign(filteredData->playerID).c_str(),
		       filteredData->filteredMessage.c_str());
      break;
    case bz_eServerMsgEvent:
      if ((serverMsgData->to == BZ_ALLUSERS) && (serverMsgData->team == eNoTeam)) {
	bz_debugMessagef(0, "MSG-BROADCAST 6:SERVER %s",
			 serverMsgData->message.c_str());
      } else if (serverMsgData->to == BZ_NULLUSER) {
	if (serverMsgData->team == eAdministrators) {
	  bz_debugMessagef(0, "MSG-ADMIN 6:SERVER %s",
			   serverMsgData->message.c_str());
	} else {
	  bz_debugMessagef(0, "MSG-TEAM 6:SERVER %s %s",
			   displayTeam(serverMsgData->team ).c_str(),
			   chatData->message.c_str());
	}
      } else {
	bz_debugMessagef(0, "MSG-DIRECT 6:SERVER %s %s",
			 displayCallsign(serverMsgData->to ).c_str(),
			 serverMsgData->message.c_str());
      }
      break;
    case bz_ePlayerJoinEvent:
      {
	if (joinPartData->record) {
	  bz_debugMessagef(0, "PLAYER-JOIN %s #%d%s %s %s",
			   displayCallsign(joinPartData->record->callsign).c_str(),
			   joinPartData->playerID,
			   displayBZid(joinPartData->playerID).c_str(),
			   displayTeam(joinPartData->record->team).c_str(),
			   displayPlayerPrivs(joinPartData->playerID).c_str());
	  listPlayers(join, joinPartData);
	}
      }
      break;
    case bz_ePlayerPartEvent:
      bz_debugMessagef(0, "PLAYER-PART %s #%d%s %s",
		       displayCallsign(joinPartData->playerID).c_str(),
		       joinPartData->playerID,
		       displayBZid(joinPartData->playerID).c_str(),
		       joinPartData->reason.c_str());
      listPlayers(part, joinPartData);
      break;
    case bz_ePlayerAuthEvent:
      bz_debugMessagef(0, "PLAYER-AUTH %s %s",
		       displayCallsign(authData->playerID).c_str(),
		       displayPlayerPrivs(authData->playerID).c_str()),
	listPlayers(join, joinPartData);
      break;
    default:
      break;
    }
  }
}

std::string LogDetail::displayBZid(int playerID)
{
  std::ostringstream bzid;

  bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerID);
  if (player) {
    if (player->globalUser)
      bzid << " BZid:" << player->bzID.c_str();
    bz_freePlayerRecord( player );
  }

  return bzid.str();
}

std::string LogDetail::displayPlayerPrivs(int playerID)
{
  std::ostringstream playerPrivs;

  bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerID);
  if (player) {
    playerPrivs << "IP:" << player->ipAddress.c_str();
    if (player->verified) playerPrivs << " VERIFIED";
    if (player->globalUser) playerPrivs << " GLOBALUSER";
    if (player->admin) playerPrivs << " ADMIN";
    if (player->op) playerPrivs << " OPERATOR";
    bz_freePlayerRecord(player);
  } else {
    playerPrivs << "IP:0.0.0.0";
  }

  return playerPrivs.str();
}

std::string LogDetail::displayCallsign(bz_ApiString callsign)
{
  std::ostringstream result;

  result << (unsigned int)strlen(callsign.c_str()) << ":" << callsign.c_str();

  return result.str();
}

std::string LogDetail::displayCallsign(int playerID)
{
  std::ostringstream callsign;
  bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerID);
  if (player) {
    callsign << (unsigned int)strlen(player->callsign.c_str()) << ":";
    callsign << player->callsign.c_str();
    bz_freePlayerRecord(player);
  } else {
    callsign << "7:UNKNOWN";
  }
  return callsign.str();
}

std::string LogDetail::displayTeam(bz_eTeamType team)
{
  // Display the player team
  switch (team) {
    case eRogueTeam:
      return std::string("ROGUE");
    case eRedTeam:
      return std::string("RED");
    case eGreenTeam:
      return std::string("GREEN");
    case eBlueTeam:
      return std::string("BLUE");
    case ePurpleTeam:
      return std::string("PURPLE");
    case eRabbitTeam:
      return std::string("RABBIT");
    case eHunterTeam:
      return std::string("HUNTER");
    case eObservers:
      return std::string("OBSERVER");
    default :
      return std::string("NOTEAM");
  }
}

void LogDetail::listPlayers(action act, bz_PlayerJoinPartEventData_V1 * data)
{
  bz_APIIntList *playerList = bz_newIntList();
  bz_BasePlayerRecord *player;
  std::ostringstream msg;
  char playerStatus;
  int numPlayers;

  bz_getPlayerIndexList(playerList);

  bz_debugMessage(4, "Players:");
  //
  // Count number of players
  //
  numPlayers = 0;
  for (unsigned int i = 0; i < playerList->size(); i++) {
    player = bz_getPlayerByIndex(playerList->get(i));
    if (player) {
      if ((player->callsign != "") && (act == join || act == auth || (data && (player->playerID != data->playerID))))
	numPlayers++;
      bz_freePlayerRecord(player);
    }
  }

  //
  // Display number of players and callsign in the following format:
  //
  // PLAYERS (nn) [G]cc:callsign
  // nn - number of players
  // G  - global auth identifier (+|-| |@)
  // cc - count of characters in player callsign
  // callsign - player callsign
  //
  // eg.
  // PLAYERS (2) [@]7:Thumper [ ]3:xxx()
  //
  msg.str("");
  msg << "PLAYERS (" << numPlayers << ") ";
  for (unsigned int i = 0; i < playerList->size(); i++) {
    player = bz_getPlayerByIndex(playerList->get(i));
    if (player) {
      if ((player->callsign != "") && (act == join || act == auth || (data && (player->playerID != data->playerID)))) {
	playerStatus = ' ';
	if (player->globalUser)
	  playerStatus = '+';
	if (player->verified)
	  playerStatus = '+';
	if (player->admin && ! bz_hasPerm(player->playerID, bz_perm_hideAdmin))
	  playerStatus = '@';
	msg << "[" << playerStatus << "]";
	msg << player->callsign.size() << ':';
	msg << player->callsign.c_str();
      }
      bz_freePlayerRecord(player);
    }
  }
  bz_debugMessage(0, msg.str().c_str());

  bz_deleteIntList(playerList);
}

// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>Fix spacing between player callsigns and add placeholder for extra data<commit_after>/* bzflag
 * Copyright (c) 1993 - 2008 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// logDetail.cpp : Plugin module for logging server events to stdout
//

#include <iostream>
#include <sstream>
#include "bzfsAPI.h"

BZ_GET_PLUGIN_VERSION

enum action {join , auth , part};

class LogDetail:public bz_EventHandler
{
public:
  LogDetail();
  virtual ~LogDetail();
  virtual void process(bz_EventData *eventData);
private:
  std::string displayPlayerPrivs(int playerID);
  std::string displayCallsign(bz_ApiString callsign);
  std::string displayCallsign(int playerID);
  std::string displayBZid(int playerID);
  std::string displayTeam(bz_eTeamType team);
  virtual void listPlayers(action act, bz_PlayerJoinPartEventData_V1 *data);
};

LogDetail logDetailHandler;

BZF_PLUGIN_CALL int bz_Load(const char * /*commandLine */ )
{
  bz_registerEvent(bz_eSlashCommandEvent, &logDetailHandler);
  bz_registerEvent(bz_eRawChatMessageEvent, &logDetailHandler);
  bz_registerEvent(bz_eServerMsgEvent, &logDetailHandler);
  bz_registerEvent(bz_ePlayerJoinEvent, &logDetailHandler);
  bz_registerEvent(bz_ePlayerPartEvent, &logDetailHandler);
  bz_registerEvent(bz_ePlayerAuthEvent, &logDetailHandler);
  bz_registerEvent(bz_eMessageFilteredEvent, &logDetailHandler);
  bz_debugMessage(4, "logDetail plugin loaded");
  return 0;
}

BZF_PLUGIN_CALL int bz_Unload(void)
{
  bz_removeEvent(bz_eSlashCommandEvent, &logDetailHandler);
  bz_removeEvent(bz_eRawChatMessageEvent, &logDetailHandler);
  bz_removeEvent(bz_eServerMsgEvent, &logDetailHandler);
  bz_removeEvent(bz_ePlayerJoinEvent, &logDetailHandler);
  bz_removeEvent(bz_ePlayerPartEvent, &logDetailHandler);
  bz_removeEvent(bz_ePlayerAuthEvent, &logDetailHandler);
  bz_removeEvent(bz_eMessageFilteredEvent, &logDetailHandler);
  bz_debugMessage(4, "logDetail plugin unloaded");
  return 0;
}

LogDetail::LogDetail()
{
  bz_debugMessage(0, "SERVER-STATUS Running");
  bz_debugMessagef(0, "SERVER-MAPNAME %s", bz_getPublicDescription().c_str());
  listPlayers(join, NULL);
}

LogDetail::~LogDetail()
{
  listPlayers(part, NULL);
  bz_debugMessage(0, "SERVER-STATUS Stopped");
}

void LogDetail::process(bz_EventData *eventData)
{
  bz_ChatEventData_V1 *chatData = (bz_ChatEventData_V1 *) eventData;
  bz_ServerMsgEventData_V1 *serverMsgData = (bz_ServerMsgEventData_V1 *) eventData;
  bz_SlashCommandEventData_V1 *cmdData = (bz_SlashCommandEventData_V1 *) eventData;
  bz_PlayerJoinPartEventData_V1 *joinPartData = (bz_PlayerJoinPartEventData_V1 *) eventData;
  bz_PlayerAuthEventData_V1 *authData = (bz_PlayerAuthEventData_V1 *) eventData;
  bz_MessageFilteredEventData_V1 *filteredData = (bz_MessageFilteredEventData_V1 *) eventData;
  char temp[9] = { 0 };

  if (eventData) {
    switch (eventData->eventType) {
    case bz_eSlashCommandEvent:
      // Slash commands are case insensitive
      // Tokenize the stream and check the first word
      // /report -> MSG-REPORT
      // anything -> MSG-COMMAND

      strncpy(temp, cmdData->message.c_str(), 8);

      if (strcasecmp(temp, "/REPORT ") == 0) {
	bz_debugMessagef(0, "MSG-REPORT %s %s",
			 displayCallsign(cmdData->from).c_str(),
			 cmdData->message.c_str()+8);
      } else {
	bz_debugMessagef(0, "MSG-COMMAND %s %s",
			 displayCallsign(cmdData->from).c_str(),
			 cmdData->message.c_str()+1);
      }
      break;
    case bz_eRawChatMessageEvent:
      if ((chatData->to == BZ_ALLUSERS) && (chatData->team == eNoTeam)) {
	bz_debugMessagef(0, "MSG-BROADCAST %s %s",
			 displayCallsign(chatData->from).c_str(),
			 chatData->message.c_str());
      } else if (chatData->to == BZ_NULLUSER) {
	if (chatData->team == eAdministrators) {
	  bz_debugMessagef(0, "MSG-ADMIN %s %s",
			   displayCallsign(chatData->from).c_str(),
			   chatData->message.c_str());
	} else {
	  bz_debugMessagef(0, "MSG-TEAM %s %s %s",
			   displayCallsign(chatData->from).c_str(),
			   displayTeam(chatData->team).c_str(),
			   chatData->message.c_str());
	}
      } else {
	  bz_debugMessagef(0, "MSG-DIRECT %s %s %s",
			   displayCallsign(chatData->from).c_str(),
			   displayCallsign(chatData->to).c_str(),
			   chatData->message.c_str());
      }
      break;
    case bz_eMessageFilteredEvent:
      bz_debugMessagef(0, "MSG-FILTERED %s %s",
		       displayCallsign(filteredData->playerID).c_str(),
		       filteredData->filteredMessage.c_str());
      break;
    case bz_eServerMsgEvent:
      if ((serverMsgData->to == BZ_ALLUSERS) && (serverMsgData->team == eNoTeam)) {
	bz_debugMessagef(0, "MSG-BROADCAST 6:SERVER %s",
			 serverMsgData->message.c_str());
      } else if (serverMsgData->to == BZ_NULLUSER) {
	if (serverMsgData->team == eAdministrators) {
	  bz_debugMessagef(0, "MSG-ADMIN 6:SERVER %s",
			   serverMsgData->message.c_str());
	} else {
	  bz_debugMessagef(0, "MSG-TEAM 6:SERVER %s %s",
			   displayTeam(serverMsgData->team ).c_str(),
			   chatData->message.c_str());
	}
      } else {
	bz_debugMessagef(0, "MSG-DIRECT 6:SERVER %s %s",
			 displayCallsign(serverMsgData->to ).c_str(),
			 serverMsgData->message.c_str());
      }
      break;
    case bz_ePlayerJoinEvent:
      {
	if (joinPartData->record) {
	  bz_debugMessagef(0, "PLAYER-JOIN %s #%d%s %s %s",
			   displayCallsign(joinPartData->record->callsign).c_str(),
			   joinPartData->playerID,
			   displayBZid(joinPartData->playerID).c_str(),
			   displayTeam(joinPartData->record->team).c_str(),
			   displayPlayerPrivs(joinPartData->playerID).c_str());
	  listPlayers(join, joinPartData);
	}
      }
      break;
    case bz_ePlayerPartEvent:
      bz_debugMessagef(0, "PLAYER-PART %s #%d%s %s",
		       displayCallsign(joinPartData->playerID).c_str(),
		       joinPartData->playerID,
		       displayBZid(joinPartData->playerID).c_str(),
		       joinPartData->reason.c_str());
      listPlayers(part, joinPartData);
      break;
    case bz_ePlayerAuthEvent:
      bz_debugMessagef(0, "PLAYER-AUTH %s %s",
		       displayCallsign(authData->playerID).c_str(),
		       displayPlayerPrivs(authData->playerID).c_str()),
	listPlayers(join, joinPartData);
      break;
    default:
      break;
    }
  }
}

std::string LogDetail::displayBZid(int playerID)
{
  std::ostringstream bzid;

  bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerID);
  if (player) {
    if (player->globalUser)
      bzid << " BZid:" << player->bzID.c_str();
    bz_freePlayerRecord( player );
  }

  return bzid.str();
}

std::string LogDetail::displayPlayerPrivs(int playerID)
{
  std::ostringstream playerPrivs;

  bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerID);
  if (player) {
    playerPrivs << "IP:" << player->ipAddress.c_str();
    if (player->verified) playerPrivs << " VERIFIED";
    if (player->globalUser) playerPrivs << " GLOBALUSER";
    if (player->admin) playerPrivs << " ADMIN";
    if (player->op) playerPrivs << " OPERATOR";
    bz_freePlayerRecord(player);
  } else {
    playerPrivs << "IP:0.0.0.0";
  }

  return playerPrivs.str();
}

std::string LogDetail::displayCallsign(bz_ApiString callsign)
{
  std::ostringstream result;

  result << (unsigned int)strlen(callsign.c_str()) << ":" << callsign.c_str();

  return result.str();
}

std::string LogDetail::displayCallsign(int playerID)
{
  std::ostringstream callsign;
  bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerID);
  if (player) {
    callsign << (unsigned int)strlen(player->callsign.c_str()) << ":";
    callsign << player->callsign.c_str();
    bz_freePlayerRecord(player);
  } else {
    callsign << "7:UNKNOWN";
  }
  return callsign.str();
}

std::string LogDetail::displayTeam(bz_eTeamType team)
{
  // Display the player team
  switch (team) {
    case eRogueTeam:
      return std::string("ROGUE");
    case eRedTeam:
      return std::string("RED");
    case eGreenTeam:
      return std::string("GREEN");
    case eBlueTeam:
      return std::string("BLUE");
    case ePurpleTeam:
      return std::string("PURPLE");
    case eRabbitTeam:
      return std::string("RABBIT");
    case eHunterTeam:
      return std::string("HUNTER");
    case eObservers:
      return std::string("OBSERVER");
    default :
      return std::string("NOTEAM");
  }
}

void LogDetail::listPlayers(action act, bz_PlayerJoinPartEventData_V1 * data)
{
  bz_APIIntList *playerList = bz_newIntList();
  bz_BasePlayerRecord *player;
  std::ostringstream msg;
  char playerStatus;
  int numPlayers;

  bz_getPlayerIndexList(playerList);

  bz_debugMessage(4, "Players:");
  //
  // Count number of players
  //
  numPlayers = 0;
  for (unsigned int i = 0; i < playerList->size(); i++) {
    player = bz_getPlayerByIndex(playerList->get(i));
    if (player) {
      if ((player->callsign != "") && (act == join || act == auth || (data && (player->playerID != data->playerID))))
	numPlayers++;
      bz_freePlayerRecord(player);
    }
  }

  //
  // Display number of players and callsign in the following format:
  //
  // PLAYERS (nn) [G]cc:callsign
  // nn - number of players
  // G  - global auth identifier (+|-| |@)
  // cc - count of characters in player callsign
  // callsign - player callsign
  //
  // eg.
  // PLAYERS (2) [@]7:Thumper() [ ]3:xxx()
  // Extra data codes in between the parenthesis (currently empty)
  //
  msg.str("");
  msg << "PLAYERS (" << numPlayers << ") ";
  for (unsigned int i = 0; i < playerList->size(); i++) {
    player = bz_getPlayerByIndex(playerList->get(i));
    if (player) {
      if ((player->callsign != "") && (act == join || act == auth || (data && (player->playerID != data->playerID)))) {
	playerStatus = ' ';
	if (player->globalUser)
	  playerStatus = '+';
	if (player->verified)
	  playerStatus = '+';
	if (player->admin && ! bz_hasPerm(player->playerID, bz_perm_hideAdmin))
	  playerStatus = '@';
	msg << "[" << playerStatus << "]";
	msg << player->callsign.size() << ':';
	msg << player->callsign.c_str();
	msg << "() ";
      }
      bz_freePlayerRecord(player);
    }
  }
  bz_debugMessage(0, msg.str().c_str());

  bz_deleteIntList(playerList);
}

// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>#include <output.hpp>
#include <opengl.hpp>
#include <core.hpp>
#include "../../shared/config.hpp"
/* TODO: this file should be included in some header maybe(plugin.hpp) */
#include <linux/input-event-codes.h>

class wayfire_expo : public wayfire_plugin_t {
    private:
        key_callback toggle_cb, press_cb, move_cb;
        wayfire_button action_button;

        int max_steps;

        render_hook_t renderer;

        struct {
            bool active = false;
            bool moving = false;
            bool in_zoom = false;
            bool button_pressed = false;
            bool first_press_skipped = false;

            int zoom_delta = 1;
        } state;
        int target_vx, target_vy;

        std::vector<std::vector<wf_workspace_stream*>> streams;
        signal_callback_t resized_cb;

    public:
    void init(wayfire_config *config) {
        grab_interface->name = "expo";
        grab_interface->compatAll = false;
        grab_interface->compat.insert("screenshot");

        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());
        streams.resize(vw);

        for (int i = 0; i < vw; i++) {
            for (int j = 0;j < vh; j++) {
                streams[i].push_back(new wf_workspace_stream);
                streams[i][j]->tex = streams[i][j]->fbuff = -1;
                streams[i][j]->ws = {i, j};
            }
        }

        auto section = config->get_section("expo");
        max_steps = section->get_duration("duration", 20);
        auto toggle_key = section->get_key("toggle", {MODIFIER_SUPER, KEY_E});

        if (!toggle_key.keyval || !toggle_key.mod)
            return;

        toggle_cb = [=] (weston_keyboard *kbd, uint32_t key) {
            activate();
        };

        core->input->add_key(toggle_key.mod, toggle_key.keyval, &toggle_cb, output);

        action_button = section->get_button("action", {0, BTN_LEFT});
        grab_interface->callbacks.keyboard.key =
            [=] (weston_keyboard *kbd, uint32_t key, uint32_t st) {
                /* TODO: check if we use the same mod */
                if (st != WL_KEYBOARD_KEY_STATE_RELEASED || key != toggle_key.keyval)
                    return;

                if (!state.first_press_skipped) {
                    state.first_press_skipped = true;
                    return;
                }
                deactivate();
        };

        grab_interface->callbacks.pointer.motion = [=] (weston_pointer *ptr,
                weston_pointer_motion_event *ev)
        {
            handle_pointer_move(ptr);
        };

        using namespace std::placeholders;
        grab_interface->callbacks.pointer.button = std::bind(
                std::mem_fn(&wayfire_expo::handle_pointer_button), this, _1, _2, _3);

        renderer = std::bind(std::mem_fn(&wayfire_expo::render), this);

        resized_cb = [=] (signal_data*) {
            for (int i = 0; i < vw; i++) {
                for (int j = 0; j < vh; j++) {
                    GL_CALL(glDeleteTextures(1, &streams[i][j]->tex));
                    GL_CALL(glDeleteFramebuffers(1, &streams[i][j]->fbuff));
                    streams[i][j]->tex = streams[i][j]->fbuff = -1;
                }
            }
        };

        output->signal->connect_signal("output-resized", &resized_cb);
    }

    void activate()
    {
        if (!output->activate_plugin(grab_interface))
            return;

        grab_interface->grab();

        state.active = true;
        state.in_zoom = true;
        state.button_pressed = false;
        state.moving = false;
        state.first_press_skipped = false;

        state.zoom_delta = 1;

        GetTuple(vx, vy, output->workspace->get_current_workspace());

        target_vx = vx;
        target_vy = vy;

        calculate_zoom(true);

        output->render->set_renderer(renderer);
        output->render->auto_redraw(true);
        output->focus_view(nullptr, core->get_current_seat());
    }

    void deactivate()
    {
        state.in_zoom = true;
        state.zoom_delta = -1;
        state.moving = false;

        output->workspace->set_workspace(std::make_tuple(target_vx, target_vy));
        calculate_zoom(false);
        update_zoom();
    }

    int sx, sy;
    wayfire_view moving_view;
    void handle_pointer_move(weston_pointer *ptr)
    {
        if (state.button_pressed && !state.in_zoom) {
            state.button_pressed = false;
            start_move();
        }

        if (!state.moving || !moving_view)
            return;

        int cx = wl_fixed_to_int(ptr->x);
        int cy = wl_fixed_to_int(ptr->y);

        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());

        moving_view->move(moving_view->geometry.origin.x + (cx - sx) * vw,
                moving_view->geometry.origin.y + (cy - sy) * vh);

        sx = cx;
        sy = cy;

        update_target_workspace(sx, sy);
    }

    void start_move()
    {
        state.moving = true;
    }

    wayfire_view find_view_at(int sx, int sy)
    {
        auto og = output->get_full_geometry();
        sx -= og.origin.x;
        sy -= og.origin.y;

        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());
        sx *= vw;
        sy *= vh;

        GetTuple(vx, vy, output->workspace->get_current_workspace());
        sx -= vx * output->handle->width;
        sy -= vy * output->handle->height;

        wayfire_view search = nullptr;
        output->workspace->for_each_view([&search, og, sx, sy] (wayfire_view v) {
            if (!search && point_inside({sx + og.origin.x, sy + og.origin.y}, v->geometry))
            search = v;
        });

        return search;
    }

    void update_target_workspace(int x, int y) {
        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());
        auto og = output->get_full_geometry();
        x -= og.origin.x;
        y -= og.origin.y;

        /* TODO: these are approximate, maybe won't work between them */
        int ew = output->handle->width / vw;
        int eh = output->handle->height / vh;

        target_vx = x / ew;
        target_vy = y / eh;
    }

    void handle_pointer_button(weston_pointer *ptr, uint32_t button, uint32_t state)
    {
        auto kbd = weston_seat_get_keyboard(ptr->seat);
        if (kbd->modifiers.mods_depressed != action_button.mod)
            return;
        if (button != action_button.button)
            return;

        if (state == WL_POINTER_BUTTON_STATE_RELEASED && !this->state.moving) {
            deactivate();
        } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
            this->state.moving = false;
        } else {
            this->state.button_pressed = true;
            sx = wl_fixed_to_int(ptr->x);
            sy = wl_fixed_to_int(ptr->y);

            moving_view = find_view_at(sx, sy);
            update_target_workspace(sx, sy);
        }
    }

    struct {
        float scale_x, scale_y,
              off_x, off_y;
    } render_params;

    void render()
    {
        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());
        GetTuple(vx, vy, output->workspace->get_current_workspace());
        GetTuple(w,  h,  output->get_screen_size());

        OpenGL::use_default_program();

        float angle;
        switch(output->get_transform()) {
            case WL_OUTPUT_TRANSFORM_NORMAL:
                angle = 0;
                break;
            case WL_OUTPUT_TRANSFORM_90:
                angle = 3 * M_PI / 2;
                break;
            case WL_OUTPUT_TRANSFORM_180:
                angle = M_PI;
                break;
            case WL_OUTPUT_TRANSFORM_270:
                angle = M_PI / 2;
            default:
                break;
        }

        glm::mat4 matrix;
        matrix = glm::rotate(matrix, angle, glm::vec3(0, 0, 1));
        matrix = glm::translate(matrix, glm::vec3(render_params.off_x, render_params.off_y, 0));
        matrix = glm::scale(matrix, glm::vec3(render_params.scale_x, render_params.scale_y, 1));

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        for(int j = 0; j < vw; j++) {
            for(int i = 0; i < vh; i++) {
                if (!streams[i][j]->running) {
                    output->render->workspace_stream_start(streams[i][j]);
                } else {
                    output->render->workspace_stream_update(streams[i][j]);
                }

#define EDGE_OFFSET 13
#define MOSAIC 0

                int mosaic_factor = EDGE_OFFSET - (1 - ((i + j) & 1)) * MOSAIC;
                wayfire_geometry g = {
                    .origin = {(i - vx) * w + mosaic_factor,
                               (j - vy) * h + mosaic_factor},
                    .size = {w - 2 * mosaic_factor, h - 2 * mosaic_factor}};

                OpenGL::render_transformed_texture(streams[i][j]->tex, g, {}, matrix,
                        glm::vec4(1), TEXTURE_TRANSFORM_INVERT_Y | TEXTURE_TRANSFORM_USE_DEVCOORD);
            }
        }

        if (state.in_zoom)
            update_zoom();
    }

    struct tup {
        float begin, end;
    };

    struct {
        int steps = 0;
        tup scale_x, scale_y,
            off_x, off_y;
    } zoom_target;

    void calculate_zoom(bool zoom_in)
    {
        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());

        float center_w = vw / 2.f;
        float center_h = vh / 2.f;

        if (zoom_in) {
            zoom_target.steps = 0;
        } else {
            zoom_target.steps = max_steps;
        }

        int mosaic_factor = (EDGE_OFFSET - (1 - ((target_vx + target_vy) & 1)) * MOSAIC);
        float mf_x = 2. * mosaic_factor / output->handle->width;
        float mf_y = 2. * mosaic_factor / output->handle->height;

        zoom_target.scale_x = {1, 1.f / vw};
        zoom_target.scale_y = {1, 1.f / vh};
        zoom_target.off_x   = {-mf_x, ((target_vx - center_w) * 2.f + 1.f) / vw};
        zoom_target.off_y   = { mf_y, ((center_h - target_vy) * 2.f - 1.f) / vh};
    }

    void update_zoom()
    {
        render_params.scale_x = GetProgress(zoom_target.scale_x.begin,
                zoom_target.scale_x.end, zoom_target.steps, max_steps);
        render_params.scale_y = GetProgress(zoom_target.scale_y.begin,
                zoom_target.scale_y.end, zoom_target.steps, max_steps);
        render_params.off_x = GetProgress(zoom_target.off_x.begin,
                zoom_target.off_x.end, zoom_target.steps, max_steps);
        render_params.off_y = GetProgress(zoom_target.off_y.begin,
                zoom_target.off_y.end, zoom_target.steps, max_steps);

        zoom_target.steps += state.zoom_delta;

        if (zoom_target.steps == max_steps + 1 && state.zoom_delta == 1) {
            state.in_zoom = false;
        } else if (state.zoom_delta == -1 && zoom_target.steps == -1) {
            state.in_zoom = false;
            finalize_and_exit();
        }
    }

    void finalize_and_exit()
    {
        state.active = false;
        output->deactivate_plugin(grab_interface);
        grab_interface->ungrab();

        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());

        for (int i = 0; i < vw; i++) {
            for (int j = 0; j < vh; j++) {
                output->render->workspace_stream_stop(streams[i][j]);
            }
        }

        output->render->reset_renderer();
        output->render->auto_redraw(false);
    }
};

extern "C" {
    wayfire_plugin_t *newInstance() {
        return new wayfire_expo();
    }
}
<commit_msg>Expo: add configuration option for offset between workspaces<commit_after>#include <output.hpp>
#include <opengl.hpp>
#include <core.hpp>
#include "../../shared/config.hpp"
/* TODO: this file should be included in some header maybe(plugin.hpp) */
#include <linux/input-event-codes.h>

class wayfire_expo : public wayfire_plugin_t {
    private:
        key_callback toggle_cb, press_cb, move_cb;
        wayfire_button action_button;

        int max_steps;

        render_hook_t renderer;

        struct {
            bool active = false;
            bool moving = false;
            bool in_zoom = false;
            bool button_pressed = false;
            bool first_press_skipped = false;

            int zoom_delta = 1;
        } state;
        int target_vx, target_vy;

        std::vector<std::vector<wf_workspace_stream*>> streams;
        signal_callback_t resized_cb;

        int delimiter_offset;

    public:
    void init(wayfire_config *config) {
        grab_interface->name = "expo";
        grab_interface->compatAll = false;
        grab_interface->compat.insert("screenshot");

        auto section = config->get_section("expo");
        auto toggle_key = section->get_key("toggle", {MODIFIER_SUPER, KEY_E});

        if (!toggle_key.keyval || !toggle_key.mod)
            return;

        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());
        streams.resize(vw);

        for (int i = 0; i < vw; i++) {
            for (int j = 0;j < vh; j++) {
                streams[i].push_back(new wf_workspace_stream);
                streams[i][j]->tex = streams[i][j]->fbuff = -1;
                streams[i][j]->ws = {i, j};
            }
        }

        max_steps = section->get_duration("duration", 20);
        delimiter_offset = section->get_int("offset", 10);

        toggle_cb = [=] (weston_keyboard *kbd, uint32_t key) {
            activate();
        };

        core->input->add_key(toggle_key.mod, toggle_key.keyval, &toggle_cb, output);

        action_button = section->get_button("action", {0, BTN_LEFT});
        grab_interface->callbacks.keyboard.key =
            [=] (weston_keyboard *kbd, uint32_t key, uint32_t st) {
                /* TODO: check if we use the same mod */
                if (st != WL_KEYBOARD_KEY_STATE_RELEASED || key != toggle_key.keyval)
                    return;

                if (!state.first_press_skipped) {
                    state.first_press_skipped = true;
                    return;
                }
                deactivate();
        };

        grab_interface->callbacks.pointer.motion = [=] (weston_pointer *ptr,
                weston_pointer_motion_event *ev)
        {
            handle_pointer_move(ptr);
        };

        using namespace std::placeholders;
        grab_interface->callbacks.pointer.button = std::bind(
                std::mem_fn(&wayfire_expo::handle_pointer_button), this, _1, _2, _3);

        renderer = std::bind(std::mem_fn(&wayfire_expo::render), this);

        resized_cb = [=] (signal_data*) {
            for (int i = 0; i < vw; i++) {
                for (int j = 0; j < vh; j++) {
                    GL_CALL(glDeleteTextures(1, &streams[i][j]->tex));
                    GL_CALL(glDeleteFramebuffers(1, &streams[i][j]->fbuff));
                    streams[i][j]->tex = streams[i][j]->fbuff = -1;
                }
            }
        };

        output->signal->connect_signal("output-resized", &resized_cb);
    }

    void activate()
    {
        if (!output->activate_plugin(grab_interface))
            return;

        grab_interface->grab();

        state.active = true;
        state.in_zoom = true;
        state.button_pressed = false;
        state.moving = false;
        state.first_press_skipped = false;

        state.zoom_delta = 1;

        GetTuple(vx, vy, output->workspace->get_current_workspace());

        target_vx = vx;
        target_vy = vy;

        calculate_zoom(true);

        output->render->set_renderer(renderer);
        output->render->auto_redraw(true);
        output->focus_view(nullptr, core->get_current_seat());
    }

    void deactivate()
    {
        state.in_zoom = true;
        state.zoom_delta = -1;
        state.moving = false;

        output->workspace->set_workspace(std::make_tuple(target_vx, target_vy));
        calculate_zoom(false);
        update_zoom();
    }

    int sx, sy;
    wayfire_view moving_view;
    void handle_pointer_move(weston_pointer *ptr)
    {
        if (state.button_pressed && !state.in_zoom) {
            state.button_pressed = false;
            start_move();
        }

        if (!state.moving || !moving_view)
            return;

        int cx = wl_fixed_to_int(ptr->x);
        int cy = wl_fixed_to_int(ptr->y);

        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());

        moving_view->move(moving_view->geometry.origin.x + (cx - sx) * vw,
                moving_view->geometry.origin.y + (cy - sy) * vh);

        sx = cx;
        sy = cy;

        update_target_workspace(sx, sy);
    }

    void start_move()
    {
        state.moving = true;
    }

    wayfire_view find_view_at(int sx, int sy)
    {
        auto og = output->get_full_geometry();
        sx -= og.origin.x;
        sy -= og.origin.y;

        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());
        sx *= vw;
        sy *= vh;

        GetTuple(vx, vy, output->workspace->get_current_workspace());
        sx -= vx * output->handle->width;
        sy -= vy * output->handle->height;

        wayfire_view search = nullptr;
        output->workspace->for_each_view([&search, og, sx, sy] (wayfire_view v) {
            if (!search && point_inside({sx + og.origin.x, sy + og.origin.y}, v->geometry))
            search = v;
        });

        return search;
    }

    void update_target_workspace(int x, int y) {
        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());
        auto og = output->get_full_geometry();
        x -= og.origin.x;
        y -= og.origin.y;

        /* TODO: these are approximate, maybe won't work between them */
        int ew = output->handle->width / vw;
        int eh = output->handle->height / vh;

        target_vx = x / ew;
        target_vy = y / eh;
    }

    void handle_pointer_button(weston_pointer *ptr, uint32_t button, uint32_t state)
    {
        auto kbd = weston_seat_get_keyboard(ptr->seat);
        if (kbd->modifiers.mods_depressed != action_button.mod)
            return;
        if (button != action_button.button)
            return;

        if (state == WL_POINTER_BUTTON_STATE_RELEASED && !this->state.moving) {
            deactivate();
        } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
            this->state.moving = false;
        } else {
            this->state.button_pressed = true;
            sx = wl_fixed_to_int(ptr->x);
            sy = wl_fixed_to_int(ptr->y);

            moving_view = find_view_at(sx, sy);
            update_target_workspace(sx, sy);
        }
    }

    struct {
        float scale_x, scale_y,
              off_x, off_y;
    } render_params;

    void render()
    {
        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());
        GetTuple(vx, vy, output->workspace->get_current_workspace());
        GetTuple(w,  h,  output->get_screen_size());

        OpenGL::use_default_program();

        float angle;
        switch(output->get_transform()) {
            case WL_OUTPUT_TRANSFORM_NORMAL:
                angle = 0;
                break;
            case WL_OUTPUT_TRANSFORM_90:
                angle = 3 * M_PI / 2;
                break;
            case WL_OUTPUT_TRANSFORM_180:
                angle = M_PI;
                break;
            case WL_OUTPUT_TRANSFORM_270:
                angle = M_PI / 2;
            default:
                break;
        }

        glm::mat4 matrix;
        matrix = glm::rotate(matrix, angle, glm::vec3(0, 0, 1));
        matrix = glm::translate(matrix, glm::vec3(render_params.off_x, render_params.off_y, 0));
        matrix = glm::scale(matrix, glm::vec3(render_params.scale_x, render_params.scale_y, 1));

        glClear(GL_COLOR_BUFFER_BIT);
        for(int j = 0; j < vh; j++) {
            for(int i = 0; i < vw; i++) {
                if (!streams[i][j]->running) {
                    output->render->workspace_stream_start(streams[i][j]);
                } else {
                    output->render->workspace_stream_update(streams[i][j]);
                }

                wayfire_geometry g = {
                    .origin = {(i - vx) * w + delimiter_offset,
                               (j - vy) * h + delimiter_offset},
                    .size = {w - 2 * delimiter_offset, h - 2 * delimiter_offset}};

                OpenGL::render_transformed_texture(streams[i][j]->tex, g, {}, matrix,
                        glm::vec4(1), TEXTURE_TRANSFORM_INVERT_Y | TEXTURE_TRANSFORM_USE_DEVCOORD);
            }
        }

        if (state.in_zoom)
            update_zoom();
    }

    struct tup {
        float begin, end;
    };

    struct {
        int steps = 0;
        tup scale_x, scale_y,
            off_x, off_y;
    } zoom_target;

    void calculate_zoom(bool zoom_in)
    {
        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());

        float center_w = vw / 2.f;
        float center_h = vh / 2.f;

        if (zoom_in) {
            zoom_target.steps = 0;
        } else {
            zoom_target.steps = max_steps;
        }

        float mf_x = 2. * delimiter_offset / output->handle->width;
        float mf_y = 2. * delimiter_offset / output->handle->height;

        zoom_target.scale_x = {1, 1.f / vw};
        zoom_target.scale_y = {1, 1.f / vh};
        zoom_target.off_x   = {-mf_x, ((target_vx - center_w) * 2.f + 1.f) / vw};
        zoom_target.off_y   = { mf_y, ((center_h - target_vy) * 2.f - 1.f) / vh};
    }

    void update_zoom()
    {
        render_params.scale_x = GetProgress(zoom_target.scale_x.begin,
                zoom_target.scale_x.end, zoom_target.steps, max_steps);
        render_params.scale_y = GetProgress(zoom_target.scale_y.begin,
                zoom_target.scale_y.end, zoom_target.steps, max_steps);
        render_params.off_x = GetProgress(zoom_target.off_x.begin,
                zoom_target.off_x.end, zoom_target.steps, max_steps);
        render_params.off_y = GetProgress(zoom_target.off_y.begin,
                zoom_target.off_y.end, zoom_target.steps, max_steps);

        zoom_target.steps += state.zoom_delta;

        if (zoom_target.steps == max_steps + 1 && state.zoom_delta == 1) {
            state.in_zoom = false;
        } else if (state.zoom_delta == -1 && zoom_target.steps == -1) {
            state.in_zoom = false;
            finalize_and_exit();
        }
    }

    void finalize_and_exit()
    {
        state.active = false;
        output->deactivate_plugin(grab_interface);
        grab_interface->ungrab();

        GetTuple(vw, vh, output->workspace->get_workspace_grid_size());

        for (int i = 0; i < vw; i++) {
            for (int j = 0; j < vh; j++) {
                output->render->workspace_stream_stop(streams[i][j]);
            }
        }

        output->render->reset_renderer();
        output->render->auto_redraw(false);
    }
};

extern "C" {
    wayfire_plugin_t *newInstance() {
        return new wayfire_expo();
    }
}
<|endoftext|>