#pragma execution_character_set("utf-8")
 
#include "pclvisualizer.h"
#include "ui_pclvisualizer.h"
#include <QColor>
#include <QColorDialog>
#include <QDebug>
#include <QFile>
#include <QFileDialog>
#include <QPainter>
#include <QTextList>
#include <QTextStream>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <pcl/io/vtk_io.h>
#include <iostream>
#include <ostream>
#include <istream>
#include <fstream>
#include <memory>
#include <QProcess>
#include <QProgressBar>
#include <pcl/io/vtk_lib_io.h>
#include "BasePCL.h"
#include "PointCloudAlg.h"
#include "ToolDialog.h"

PCLVisualizer::PCLVisualizer(QWidget* parent)
  : QMainWindow(parent)
  , point_size(5)             // 设置初始背景颜色
  , ui(new Ui::PCLVisualizer) //是否默认开启RGBA显示点云
  , isRBGA(true)              // 是否显示第二个点云
  , bgColor(0, 0, 50)         // 初始化顺序要和声明的顺序一致
{
  ui->setupUi(this);
  //  cout << "INIT" << endl;
  initPCV();

  //  //创建动作，工具栏以及菜单栏
  createActions();
  //    createMenus();
  // 创建工具栏
  createToolBars();

  //初始化点云数据
  initPointCloud();

  // 给QVTK配置PCLViewer显示
  viewer_.reset(new pcl::visualization::PCLVisualizer("viewer", false));
  //设置背景颜色
  viewer_->setBackgroundColor(double(bgColor.red()) / 255,
                              double(bgColor.green()) / 255,
                              double(bgColor.blue()) / 255);
 
   
  // 原始代码因为 pcl 1.13.1 涉及的 vtk 版本废弃，参考：https://blog.csdn.net/dyk4ever/article/details/126715543

  vtkSmartPointer<vtkRenderer>	renderer2 = vtkSmartPointer<vtkRenderer>::New();
  vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow2 =	  vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
  renderWindow2->AddRenderer(renderer2);

  viewer_.reset(new pcl::visualization::PCLVisualizer(renderer2, renderWindow2, "viewer", false));
  ui->qvtkWidget->setRenderWindow(viewer_->getRenderWindow());
  viewer_->setupInteractor(ui->qvtkWidget->interactor(), ui->qvtkWidget->renderWindow());
  viewer_->setBackgroundColor(0, 0, 0);
  ui->qvtkWidget->update();
  connectSS();


  viewer_->setPointCloudRenderingProperties(
    pcl::visualization::PCL_VISUALIZER_POINT_SIZE, point_size);
  // viewer_->addCoordinateSystem(1);
  viewer_->resetCamera();
  // 构建点云管理模块，并进行绑定
  this->cloudmanager=new CloudPointManagerClass(this); // 初始化点云管理器
  this->cloudmanager->bandingViewer(viewer_, this->ui->filesList); // 绑定点云显示窗口，点云文件列表管理模块
  // 初始化状态栏
  this->statusbarprogressBar = new QProgressBar();
  this->ui->statusbar->addPermanentWidget(this->statusbarprogressBar);
  qDebug() << "点云处理 系统,系统初始化完成。";
}

PCLVisualizer::~PCLVisualizer()
{
  delete ui;
  delete cloudmanager;
}

void PCLVisualizer::initPCV()
{
  //设置窗口名称
  QString str = "PointCloudViewer";
  this->setWindowTitle(str);

  showLogItem("PCV 系统", "系统正在初始化...");

  logStr = "主窗口位置已还原: " + QString("X-Y-Width-Height(%1,%2,%3,%4)")
                                    .arg(this->x())
                                    .arg(this->y())
                                    .arg(this->width())
                                    .arg(this->height());
  showLogItem("PCV 窗口", logStr);

  //写ini文件，记录当前窗口位置和大小：
  QString wstrFilePath =
    qApp->applicationDirPath() + "/setting.ini"; // .ini放在工程源文件目录下
  settings = new QSettings(
    wstrFilePath, QSettings::IniFormat); //用QSetting获取ini文件中的数据
  // settings->clear();                     //清空当前配置文件中的内容
}

void
PCLVisualizer::showLogItem(QString item, QString info)
{
  //--------------------LOG--------------------------
  logStr = "[" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") +
           "] " + " [ " + item + " ] " + info;
  logList.push_back(logStr);
  ui->logList->addItem(logStr);
  //--------------------LOG--------------------------
}

void
PCLVisualizer::saveSetting(QString key, QString value)
{
  //写ini文件，记录当前窗口位置和大小：
  QString wstrFilePath =
    qApp->applicationDirPath() + "/setting.ini"; // .ini放在工程源文件目录下
  QSettings* settings = new QSettings(
    wstrFilePath, QSettings::IniFormat); //用QSetting获取ini文件中的数据
  // settings->clear();                     //清空当前配置文件中的内容
  settings->setValue(key, value);
  settings->sync();
}

QVariant
PCLVisualizer::getSetting(QString name)
{
  return settings->value(name);
}

void
PCLVisualizer::createActions()
{
  //添加点云
  // addCloudAction =
  // new QAction(QIcon(":/images/files/cloud.png"), "添加点云", this);
  // addCloudAction->setShortcut(tr("Ctrl+O"));    //(b)
  // addCloudAction->setStatusTip(tr("添加点云")); //(c)

  //新建工作台
  newWorkStationAction =
    new QAction(QIcon(":/images/files/add.png"), tr("new"), this);
  newWorkStationAction->setShortcut(tr("Ctrl+N"));
  newWorkStationAction->setStatusTip(tr("new"));

  //打开点云文件
  newCloudAction =
    new QAction(QIcon(":/images/files/new2.png"), tr("打开点云文件"), this);
  //  exitAction->setShortcut(tr("Ctrl+Q"));
  newCloudAction->setStatusTip(tr("打开点云文件"));

  //复制点云
  copyCloudAction =
    new QAction(QIcon(":/images/files/copy.png"), tr("复制点云"), this);
  copyCloudAction->setShortcut(tr("Ctrl+C"));
  copyCloudAction->setStatusTip(tr("复制点云"));

  //剪切点云
  cutCloudAction =
    new QAction(QIcon(":/images/files/cut.png"), tr("剪切点云"), this);
  cutCloudAction->setShortcut(tr("Ctrl+X"));
  cutCloudAction->setStatusTip(tr("剪切点云"));

  //粘贴点云
  pasteCloudAction =
    new QAction(QIcon(":/images/files/paste.png"), tr("粘贴点云"), this);
  pasteCloudAction->setShortcut(tr("Ctrl+V"));
  pasteCloudAction->setStatusTip(tr("粘贴点云"));

  //查找点云文件
  searchCloudAction =
    new QAction(QIcon(":/images/files/search.png"), tr("查找点云文件"), this);
  searchCloudAction->setShortcut(tr("Ctrl+F"));
  searchCloudAction->setStatusTip(tr("查找点云文件"));

  //导出点云至PCD文件
  exportCloud2PCDAction = new QAction(
    QIcon(":/images/files/pointCloud.png"), tr("导出点云至PCD文件"), this);
  exportCloud2PCDAction->setStatusTip(tr("导出点云至PCD文件"));

  //导出点云至PLY文件
  exportCloud2PLYAction = new QAction(
    QIcon(":/images/files/cloud2.png"), tr("导出点云至PLY文件"), this);
  exportCloud2PLYAction->setStatusTip(tr("导出点云至PLY文件"));

  //导出点云至CSV文件
  export2CSVAction =
    new QAction(QIcon(":/images/files/CSV.png"), tr("导出点云至CSV文件"), this);
  export2CSVAction->setStatusTip(tr("导出点云至CSV文件"));

  //导出点云至TXT文件
  export2TXTAction =
    new QAction(QIcon(":/images/files/txt.png"), tr("导出点云至TXT文件"), this);
  export2TXTAction->setStatusTip(tr("导出点云至TXT文件"));

  //收藏点云文件
  starCloudAction =
    new QAction(QIcon(":/images/files/star.png"), tr("收藏点云文件"), this);
  starCloudAction->setStatusTip(tr("收藏点云文件"));

  //导出屏幕截图
  snapShotAction =
    new QAction(QIcon(":/images/files/snapshot.png"), tr("导出屏幕截图"), this);
  snapShotAction->setStatusTip(tr("导出屏幕截图"));

  //离群点移除
  outliersRemoveAction = new QAction(
    QIcon(":/images/algorithm/KMeans.png"), tr("outliersRemove"), this);
  outliersRemoveAction->setStatusTip(tr("outliersRemove"));

  //滤波平滑
  filterAction =
    new QAction(QIcon(":/images/algorithm/filter.png"), tr("滤波平滑"), this);
  filterAction->setStatusTip(tr("滤波平滑"));

  //点云下采样
  downSampleAction =
    new QAction(QIcon(":/images/algorithm/density.png"), "downSampling", this);
  downSampleAction->setStatusTip(tr("downSampling"));

  //点云拼接
  cloudSpliceAction =
    new QAction(QIcon(":/images/algorithm/pingjie.png"), "点云拼接", this);
  cloudSpliceAction->setStatusTip(tr("点云拼接"));

  //点云直方图
  HistogramAction =
    new QAction(QIcon(":/images/algorithm/Histogram.png"), "Histogram", this);
  HistogramAction->setStatusTip(tr("Histogram"));

  //表面重建
  surfaceAction =
    new QAction(QIcon(":/images/algorithm/matrix.png"), "surface", this);
  surfaceAction->setStatusTip(tr("surface"));

  //点云配准
  alignAction =
    new QAction(QIcon(":/images/algorithm/DBSCAN.png"), "点云配准", this);
  alignAction->setStatusTip(tr("点云配准"));
  // MLS细化
  MLSAction =
    new QAction(QIcon(":/images/algorithm/nihe.png"), "MLS细化", this);
  MLSAction->setStatusTip(tr("MLS细化"));
}

void PCLVisualizer::createMenus()
{}

void PCLVisualizer::createToolBars()
{
  //点云文件工具栏
  fileTool = addToolBar("cloudFile");

  fileTool->addAction(newWorkStationAction);
  // fileTool->addAction(addCloudAction);
  fileTool->addAction(ui->actionload_point_cloud);
  fileTool->addAction(newCloudAction);
  fileTool->addAction(copyCloudAction);
  fileTool->addAction(cutCloudAction);
  fileTool->addAction(pasteCloudAction);

  fileTool->addSeparator();

  fileTool->addAction(exportCloud2PCDAction);
  fileTool->addAction(exportCloud2PLYAction);
  fileTool->addAction(export2CSVAction);
  fileTool->addAction(export2TXTAction);
  fileTool->addAction(ui->actionExportLog);
  fileTool->addAction(snapShotAction);

  fileTool->addSeparator();

  fileTool->addAction(starCloudAction);
  fileTool->addAction(searchCloudAction);
  fileTool->addAction(ui->actionBGColor);

  //算法工具栏
  algorithmTool = addToolBar("algorithm");
  algorithmTool->addAction(ui->actionbestRemoval);
  algorithmTool->addAction(ui->actionbestFiltering);
  algorithmTool->addAction(ui->actionbestKeypoint);
  algorithmTool->addAction(ui->actionbestRegistration);
  algorithmTool->addAction(ui->actionbestSurface);
  algorithmTool->addAction(MLSAction);
  algorithmTool->addAction(HistogramAction);
}

void PCLVisualizer::test()
{
  qDebug() << "Hello World!";
}

void PCLVisualizer::initPointCloud()
{
  //// Setup the cloud pointer
  //cloud_.reset(new PointCloudT);
  //cloud.reset(new PointCloudT);
  //cloud_noise.reset(new PointCloudT);
  //cloud_filtered.reset(new PointCloudT);
  //cloud_filtered_guass.reset(new PointCloudT);
  //cloud_filtered_guass_down.reset(new PointCloudT);
  //cloud_filtered_out.reset(new PointCloudT);

  //cloud_in.reset(new PointCloudT);
  //cloud_tr.reset(new PointCloudT);
  //cloud_RE.reset(new PointCloudT);

  //cloudRGBA_.reset(new PointCloudTRGBA);

  //// The number of points in the cloud
  //cloud_->resize(1000);
  //cloudRGBA_->resize(1000);
  //// Fill the cloud with random points
  //for (size_t i = 0; i < cloud_->points.size(); ++i) {
  //  cloud_->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);
  //  cloud_->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
  //  cloud_->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);
  //}

  //// 获取点云内的最大点和最小点
  //pcl::getMinMax3D(*cloud_, p_min, p_max);
  //maxLen = getMaxValue(p_max, p_min);

  ////拷贝一份给RGBA点云
  //pcl::copyPointCloud(*cloud_, *cloudRGBA_);

  //showLogItem("PCV 主窗口", "点云初始化完成");
}

//连接信号槽
void PCLVisualizer::connectSS()
{

  connect(ui->actionload_point_cloud,
          &QAction::triggered,
          this,
          &PCLVisualizer::loadPCDFile);
  connect(ui->actionsave_point_cloud,
          &QAction::triggered,
          this,
          &PCLVisualizer::savePCDFile);
  connect(ui->actionCoordinateSystem,
          &QAction::triggered,
          this,
          &PCLVisualizer::AddCoordinateSystem);
  // Connect "Load" and "Save" buttons and their functions

  //增加新工作台功能
  connect(newWorkStationAction,
          &QAction::triggered,
          this,
          &PCLVisualizer::newWorkStation);
}

void PCLVisualizer::savePCDFile()
{
  //QString filename =
  //  getSaveFilePath(this,
  //                               tr("Open point cloud"),
  //                               "/home/",
  //                               tr("Point cloud data(*.pcd *.ply)"));
  //PCL_INFO("File chosen: %s\n", filename.toUtf8().constData());

  //if (filename.isEmpty())
  //  return;
  //int return_status;
  //if (filename.endsWith(".pcd", Qt::CaseInsensitive))
  //  return_status = pcl::io::savePCDFileBinary(filename, *cloud_);
  //else if (filename.endsWith(".ply", Qt::CaseInsensitive))
  //  return_status = pcl::io::savePLYFileBinary(filename, *cloud_);
  //else {
  //  filename.append(".ply");
  //  return_status = pcl::io::savePLYFileBinary(filename, *cloud_);
  //}
  //if (return_status != 0) {
  //  PCL_ERROR("Error writing point cloud %s\n", filename.toUtf8().constData());
  //  return;
  //}
}

void PCLVisualizer::loadPCDFile()
{
  QString fileFormat, fileName, fileBaseName, pointCount, filePath, fileSuffix,
    lastPath;
  //读取文件名
  //记住上一次加载的路径

  lastPath = getSetting("FilePath/lastPath").toString();

  QString filePathWithName =
    QFileDialog::getOpenFileName(this,  tr("Open point cloud or Soil SCane File xyz"),
									lastPath,
                                 tr("Point cloud data  (*.pcd *.ply *.xyz)"));
  QFileInfo fileInfo;
  fileInfo = QFileInfo(filePathWithName);
  //文件名
  fileName = fileInfo.fileName();
  //文件后缀
  fileSuffix = fileInfo.suffix();
  //绝对路径
  filePath = fileInfo.absolutePath();
  fileBaseName = fileInfo.baseName();
   //qDebug() << fileName << endl
   //        << fileSuffix << endl
   //        << filePath << endl
   //        << fileInfo.baseName() << endl
   //        << fileInfo.completeBaseName();

  showLogItem("点云文件选择", filePath);
  // PCL_INFO("File chosen: %s\n", filePathWithName.toUtf8().constData());

  pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud_tmp(new pcl::PointCloud<pcl::PointXYZRGBA>);

  if (filePathWithName.isEmpty()) {
    showLogItem("文件加载失败", filePathWithName);
    return;
  }

  showLogItem("文件加载成功", filePathWithName);
  // 将当前文件路径保存，下次使用
  saveSetting("FilePath/lastPath", filePathWithName);

  //判断文件类型然后加载点云
  int return_status;
  showLogItem("点云加载中", "文件格式为：" + fileFormat);

  return_status = cloudmanager->addPointCloud(filePathWithName, fileSuffix);

  //判断是否加载成功
  if (return_status != 0) {
      PCL_ERROR("Error reading point cloud %s\n",
          filePathWithName.toUtf8().constData());

      showLogItem("点云加载中", fileName + " 文件读取失败。");
      return;
  }
  PCL_INFO("file has loaded\n");

  showLogItem("点云加载完成", fileFormat);
 
}

void PCLVisualizer::loadPLYFile()
{
  QString fileFormat, fileName, fileBaseName, pointCount, filePath, fileSuffix;
  //读取文件名
  QString lastPath = getSetting("FilePath/lastPath").toString();
  filePathWithName =
      QFileDialog::getOpenFileName(this,
                                 tr("Open point cloud"),
          lastPath,
                                 tr("Point cloud data (*.pcd *.ply)"));
  QFileInfo fileInfo;
  fileInfo = QFileInfo(filePathWithName);
  //文件名
  fileName = fileInfo.fileName();
  //文件后缀
  fileSuffix = fileInfo.suffix();
  //绝对路径
  filePath = fileInfo.absolutePath();
  fileBaseName = fileInfo.baseName();

  QString cloudFile = fileName + " [" + filePath + "]";
  QListWidgetItem* item = new QListWidgetItem;
  //  item->setBackgroundColor(QColor(220, 230, 250));
  item->setBackground(QBrush(QColor(220, 230, 250)));
  item->setData(Qt::DisplayRole, cloudFile);
  item->setData(Qt::CheckStateRole, Qt::Checked);
  ui->filesList->addItem(item);
}

void PCLVisualizer::chooseAxis()
{
 
}

void PCLVisualizer::chooseColorMode()
{
 
 
}

void PCLVisualizer::IncPointSize()
{
  // setValue函数会自动调用 SpinBox的触发动作
  ui->pointSizeEdt->setValue(++point_size);
}

void PCLVisualizer::DecPointSize()
{
  if (point_size == 1)
    return;
  ui->pointSizeEdt->setValue(--point_size);
}

void PCLVisualizer::AddCoordinateSystem()
{
  QString str = ui->actionCoordinateSystem->text();
  if (str.compare("CoordinateSystem [OFF]") == 0) {
    qDebug() << str;
    ui->actionCoordinateSystem->setText("CoordinateSystem [ON]");
    viewer_->addCoordinateSystem();
  } else {
    ui->actionCoordinateSystem->setText("CoordinateSystem [OFF]");
    viewer_->removeCoordinateSystem();
  }
  ui->qvtkWidget->update();
}


//软件关闭时动作
void
PCLVisualizer::closeEvent(QCloseEvent* event)
{
  //写ini文件，记录当前窗口位置和大小：
  QString wstrFilePath =
    qApp->applicationDirPath() + "/setting.ini"; // .ini放在工程源文件目录下
  QSettings* settings = new QSettings(
    wstrFilePath, QSettings::IniFormat); //用QSetting获取ini文件中的数据
  // settings->clear();                     //清空当前配置文件中的内容
  settings->setValue("WindowGeometry/x", this->x());
  settings->setValue("WindowGeometry/y", this->y());
  settings->setValue("WindowGeometry/width", this->width());
  settings->setValue("WindowGeometry/height", this->height());
  qDebug() << "Position is right:" << this->x() << " " << this->y() << " "
           << this->width() << " " << this->height();
}

void
PCLVisualizer::on_actionUp_triggered()
{
  //if (!cloud_->empty()) {
  //  viewer_->setCameraPosition(0.5 * (p_min.x + p_max.x),
  //                             0.5 * (p_min.y + p_max.y),
  //                             p_max.z + 2 * maxLen,
  //                             0.5 * (p_min.x + p_max.x),
  //                             0.5 * (p_min.y + p_max.y),
  //                             p_max.z,
  //                             0,
  //                             1,
  //                             0);
  //  ui->qvtkWidget->update();
  //}
}

void
PCLVisualizer::on_actionBottom_triggered()
{
  //if (!cloud_->empty()) {
  //  viewer_->setCameraPosition(0.5 * (p_min.x + p_max.x),
  //                             0.5 * (p_min.y + p_max.y),
  //                             p_min.z - 2 * maxLen,
  //                             0.5 * (p_min.x + p_max.x),
  //                             0.5 * (p_min.y + p_max.y),
  //                             p_min.z,
  //                             0,
  //                             1,
  //                             0);
  //  ui->qvtkWidget->update();
  //}
}

void
PCLVisualizer::on_actionFront_triggered()
{
  //if (!cloud_->empty()) {
  //  viewer_->setCameraPosition(0.5 * (p_min.x + p_max.x),
  //                             p_min.y - 2 * maxLen,
  //                             0.5 * (p_min.z + p_max.z),
  //                             0.5 * (p_min.x + p_max.x),
  //                             p_min.y,
  //                             0.5 * (p_min.z + p_max.z),
  //                             0,
  //                             0,
  //                             1);
  //  ui->qvtkWidget->update();
  //}
}

void
PCLVisualizer::on_actionBack_triggered()
{
  //if (!cloud_->empty()) {
  //  viewer_->setCameraPosition(0.5 * (p_min.x + p_max.x),
  //                             p_max.y + 2 * maxLen,
  //                             0.5 * (p_min.z + p_max.z),
  //                             0.5 * (p_min.x + p_max.x),
  //                             p_min.y,
  //                             0.5 * (p_min.z + p_max.z),
  //                             0,
  //                             0,
  //                             1);
  //  ui->qvtkWidget->update();
  //}
}

void
PCLVisualizer::on_actionLeft_triggered()
{
 
}

void
PCLVisualizer::on_actionRight_triggered()
{
 
}

double
PCLVisualizer::getMinValue(pcl::PointXYZRGBA p1, pcl::PointXYZRGBA p2)
{
  double min = 0;

  if (p1.x - p2.x > p1.y - p2.y) {
    min = p1.y - p2.y;
  } else {
    min = p1.x - p2.x;
  }

  if (min > p1.z - p2.z) {
    min = p1.z - p2.z;
  }
  return min;
}

double
PCLVisualizer::getMaxValue(pcl::PointXYZRGBA p1, pcl::PointXYZRGBA p2)
{
  double max = 0;

  if (p1.x - p2.x > p1.y - p2.y) {
    max = p1.x - p2.x;

  } else {
    max = p1.y - p2.y;
  }

  if (max < p1.z - p2.z) {
    max = p1.z - p2.z;
  }

  return max;
}

void
PCLVisualizer::openProgressDlg(int num = 500)
{
  //创建一个进度对话框
  QProgressDialog* progressDialog = new QProgressDialog(this);
  QFont font("ZYSong18030", 12);
  progressDialog->setFont(font);
  progressDialog->setWindowModality(Qt::WindowModal); //(d)
  progressDialog->setMinimumDuration(5);              //(e)
  progressDialog->setWindowTitle(tr("Please Wait"));  //(f)
  progressDialog->setLabelText(tr("Processing..."));  //(g)
  progressDialog->setCancelButtonText(tr("Cancel"));  //(h)
  progressDialog->setRange(0, num); //设置进度对话框的步进范围
  for (int i = 1; i < num + 1; i++) {
    Sleep(10);
    progressDialog->setValue(i);       //(i)
    if (progressDialog->wasCanceled()) //(j)
      return;
  }
}

void PCLVisualizer::showStatusBarTip(QString text, size_t value)
{
    this->ui->statusbar->showMessage(text);
    if (this->statusbarprogressBar->maximum() > value) {
        this->statusbarprogressBar->setValue(value);
    }
    else {
        this->statusbarprogressBar->setValue(this->statusbarprogressBar->maximum());
    }
    return;
}

void
PCLVisualizer::updateCloudInfo()
{
  // TODO 暂时只更新点云数量
  //ui->pointCountEdt->setText(QString::number(cloud_->points.size()));
}

void
PCLVisualizer::best_filter()
{

}

void
PCLVisualizer::cloudTransform(pcl::PointCloud< pcl::PointXYZRGBA>::Ptr cloud_in,
    pcl::PointCloud< pcl::PointXYZRGBA>::Ptr cloud_tr,
                              double theta,
                              double z)
{
  //设置旋转矩阵和平移向量
  Eigen::Matrix4d transformation_matrix = Eigen::Matrix4d::Identity();

  //旋转角度
  // double theta = M_PI / 4;  // The angle of rotation in radians
  transformation_matrix(0, 0) = cos(theta);
  transformation_matrix(0, 1) = -sin(theta);
  transformation_matrix(1, 0) = sin(theta);
  transformation_matrix(1, 1) = cos(theta);

  double x = ui->lineEdit_X->text().toDouble();
  double y = ui->lineEdit_Y->text().toDouble();
  double zz = ui->lineEdit_Z->text().toDouble();
  qDebug() << "x:" << x << " y:" << y << " z:" << zz << endl;
  transformation_matrix(0, 3) = x;  // 0.1m
  transformation_matrix(1, 3) = y;  // 0.1m
  transformation_matrix(2, 3) = zz; // 0.1m
                                    // transformation_matrix(2, 3) = z; // 0.1m
  // Z轴方向上的平移
  // A translation on Z axis (0.4 meters)
  // transformation_matrix(2, 3) = z; // 0.1m

  // Display in terminal the transformation matrix
  // qDebug() << "对源点云进行旋转平移：" << "\n";
  print4x4Matrix(transformation_matrix);

  //将原始点云先旋转平移
  // Executing the transformation
  pcl::transformPointCloud(*cloud_in, *cloud_tr, transformation_matrix);
}

void
PCLVisualizer::ICP_aligin(pcl::IterativeClosestPoint<pcl::PointXYZRGBA, pcl::PointXYZRGBA>::Ptr icp,
    pcl::PointCloud< pcl::PointXYZRGBA>::Ptr cloud_in,
    pcl::PointCloud< pcl::PointXYZRGBA>::Ptr cloud_RE)
{
  icp->setMaximumIterations(1);
  icp->setInputSource(cloud_RE);
  icp->setInputTarget(cloud_in);
  icp->align(*cloud_RE);
  icp->setMaximumIterations(1); // We set this variable to 1 for the next time
                                // we will call .align () function
  //qDebug() << "Applied " << iterations << " iteration(s)" << "\n";
  if (icp->hasConverged()) {
    qDebug() << "\nHasConverged: " << icp->hasConverged()
              << ", getFitnessScore: " << icp->getFitnessScore() << "\n";
    cout << "Transformation: \n" << icp->getFinalTransformation() << endl;
    print4x4Matrix(icp->getFinalTransformation().cast<double>());
    qDebug() << get4x4MatrixStr(icp->getFinalTransformation().cast<double>())
             << endl;
    ////ui->matEdit.setText(get4x4MatrixStr(icp->getFinalTransformation().cast<double>()));
    ui->matEdit->setPlainText(
      get4x4MatrixStr(icp->getFinalTransformation().cast<double>()));
  } else {
    PCL_ERROR("\nICP has not converged.\n");
    // return (-1);
  }
}

void
PCLVisualizer::best_aligin()
{

}

void
PCLVisualizer::best_surface()
{

}

void
PCLVisualizer::newWorkStation()
{
  PCLVisualizer* newPCV = new PCLVisualizer;
  //新建的工作窗口位于之前窗口的右下方
  newPCV->setGeometry(
    this->x() + 20, this->y() + 50, this->width(), this->height());
  newPCV->show();
}

void
PCLVisualizer::on_actionBGColor_triggered()
{
  QColor color = QColorDialog::getColor(
    bgColor, this); //打开颜色选择窗口，并用当前颜色初始化
  if (color.isValid()) {
    bgColor = color;
    qDebug() << "color: " << bgColor.red() << " " << bgColor.green() << " "
             << bgColor.blue();
    viewer_->setBackgroundColor(double(bgColor.red()) / 255,
                                double(bgColor.green()) / 255,
                                double(bgColor.blue()) / 255);
    ui->qvtkWidget->update();
  }
}

void
PCLVisualizer::on_actionabout_triggered()
{}

void
PCLVisualizer::on_comboBox_Color_currentIndexChanged(const QString& arg1)
{

 
}

void
PCLVisualizer::on_actionCoordinateSystem_triggered()
{}

void
PCLVisualizer::on_actionCameraview_triggered()
{}

void
PCLVisualizer::on_pointSizeEdt_valueChanged(int arg1)
{
 
}

void
PCLVisualizer::on_actionbestSurface_triggered()
{
 
}

void
PCLVisualizer::on_actionbestRemoval_triggered()
{

 
}

void
PCLVisualizer::on_actionbestFiltering_triggered()
{
 
}

void
PCLVisualizer::on_actionbestRegistration_triggered()
{
 
}

void
PCLVisualizer::on_actionbestKeypoint_triggered()
{
 
}

void
PCLVisualizer::on_actionTXT_triggered()
{}

void
PCLVisualizer::on_actionExportLog_triggered()
{

  QString filename = QFileDialog::getSaveFileName(
    this, tr("Save Log"),  tr("Log(*.txt)"));
  PCL_INFO("File chosen: %s\n", filename.toUtf8().constData());

  QFile file;
  file.setFileName(filename);
  QByteArray data;

  QString log;
  for (auto it = logList.begin(); it != logList.end(); ++it) {
    log.append(*it + "\n");
  }
  if (file.open(QIODevice::WriteOnly)) {
    QByteArray res2 = log.toUtf8(); // toLatin1()转为QByteArray
    file.write(res2);
    file.close();
  }
}

void
PCLVisualizer::on_actionRedo_triggered()
{

}

void
PCLVisualizer::on_actionquit_triggered()
{}

double
obb(pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud) //点云OBB有向包围盒
{
  pcl::MomentOfInertiaEstimation<pcl::PointXYZRGBA> feature_extractor;
  feature_extractor.setInputCloud(cloud);
  feature_extractor.compute();

  std::vector<float> moment_of_inertia;
  std::vector<float> eccentricity;
  pcl::PointXYZRGBA min_point_OBB;
  pcl::PointXYZRGBA max_point_OBB;
  pcl::PointXYZRGBA position_OBB;
  Eigen::Matrix3f rotational_matrix_OBB;
  float major_value, middle_value, minor_value;
  Eigen::Vector3f major_vector, middle_vector, minor_vector;
  Eigen::Vector3f mass_center;

  feature_extractor.getMomentOfInertia(moment_of_inertia);
  feature_extractor.getEccentricity(eccentricity);
  feature_extractor.getOBB(
    min_point_OBB, max_point_OBB, position_OBB, rotational_matrix_OBB);
  feature_extractor.getEigenValues(major_value, middle_value, minor_value);
  feature_extractor.getEigenVectors(major_vector, middle_vector, minor_vector);
  feature_extractor.getMassCenter(mass_center);

  return (max_point_OBB.x - min_point_OBB.x) *
         (max_point_OBB.y - min_point_OBB.y) *
         (max_point_OBB.z - min_point_OBB.z);

}

double
aabb(pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud) //点云AABB包围盒
{
  pcl::MomentOfInertiaEstimation<pcl::PointXYZRGBA> feature_extractor;
  feature_extractor.setInputCloud(cloud);
  feature_extractor.compute();

  std::vector<float> moment_of_inertia;
  std::vector<float> eccentricity;

  pcl::PointXYZRGBA min_point_AABB; // AABB包围盒
  pcl::PointXYZRGBA max_point_AABB;

  Eigen::Vector3f major_vector, middle_vector, minor_vector;
  Eigen::Vector3f mass_center;

  feature_extractor.getMomentOfInertia(moment_of_inertia);
  feature_extractor.getEccentricity(eccentricity);
  feature_extractor.getAABB(min_point_AABB, max_point_AABB);
  feature_extractor.getEigenVectors(major_vector, middle_vector, minor_vector);
  feature_extractor.getMassCenter(mass_center);

  return (max_point_AABB.x - min_point_AABB.x) *
         (max_point_AABB.y - min_point_AABB.y) *
         (max_point_AABB.z - min_point_AABB.z);
}

void
PCLVisualizer::on_actiongetAllGeo_triggered()
{
  vtkSmartPointer<vtkPLYReader> reader = vtkSmartPointer<vtkPLYReader>::New();
  reader->SetFileName(filePathWithName.toUtf8());
  reader->Update();
  vtkSmartPointer<vtkTriangleFilter> tri =
    vtkSmartPointer<vtkTriangleFilter>::New();
  tri->SetInputData(reader->GetOutput());
  tri->Update();
  vtkSmartPointer<vtkMassProperties> poly =
    vtkSmartPointer<vtkMassProperties>::New();
  poly->SetInputData(tri->GetOutput());
  poly->Update();

  double pV = poly->GetVolumeProjected();
  double pX = poly->GetVolumeX();
  double pY = poly->GetVolumeY();
  double pZ = poly->GetVolumeZ();
  double vol = poly->GetVolume();          //体积
  double area = poly->GetSurfaceArea();    //表面积
  double maxArea = poly->GetMaxCellArea(); //最大单元面积
  double minArea = poly->GetMinCellArea(); //最小单元面积

  pcl::PLYReader PLY_reader;
  pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGBA>);
  PLY_reader.read<pcl::PointXYZRGBA>(filePathWithName.toUtf8().constData(), *cloud);

  double vol_aabb = aabb(cloud);
  double vol_obb = obb(cloud);

  ui->lineEdit_AABB->setText(QString::number(vol_aabb));
  ui->lineEdit_OBB->setText(QString::number(vol_obb));
  ui->lineEdit_area->setText(QString::number(area));
  ui->lineEdit_maxUnitAera->setText(QString::number(maxArea));
  ui->lineEdit_minUnitAera->setText(QString::number(minArea));
  ui->lineEdit_vol->setText(QString::number(vol));

  // QMessageBox::information(
  //  this, "几何属性提取成功", "表面积、体积等计算完成", "确定");

  cout << "vol: " << vol << endl;
  cout << "area: " << area << endl;
  cout << "maxArea: " << maxArea << endl;
  cout << "minArea: " << minArea << endl;
  cout << "pV: " << pV << endl;
  cout << "pX: " << pX << endl;
  cout << "pY: " << pY << endl;
  cout << "pZ: " << pZ << endl;
}

void
PCLVisualizer::on_actionplaneSeg_triggered()
{

}

//显示PLY文件
void
PCLVisualizer::on_actionarea_triggered()
{
  loadPLYFile();
  // pcl::PolygonMesh mesh;
  // pcl::io::loadPLYFile(filePathWithName, mesh);
  // viewer_->removePointCloud("cloud");
  // viewer_->addPolygonMesh(mesh, "my");
  // viewer_->resetCamera();
  // ui->qvtkWidget->update();
}

void
PCLVisualizer::on_actionvol_triggered()
{
  QMessageBox::information(
    this, "几何属性提取成功", "表面积、体积等计算完成", "确定");
}

////////////////////////////////////////////////////
// 离群点、滤波、重建表面
////////////////////////////////////////////////////

/// <summary>
/// 统计离群点去除
/// </summary>
void PCLVisualizer::on_actionStatisticalRemove_triggered()
{
    StatisticalOutlierRemovalWindows tool(this);
    tool.bandingComboxAndPoint(this->cloudmanager->getItemNames());
    tool.exec();
    
    QString in_cloud_name = tool.getComboxSelect();
    // -- LOG ---
    logStr = "[" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") +"] " + "[统计离群点去除] " + "Point Cloud Plane Segmentation Done.";
    logList.push_back(in_cloud_name);
    ui->logList->addItem(logStr);
    // --- end LOG ----
   std::shared_ptr<FileListManagerAbstractItem> item_ptr= this->cloudmanager->getItemFromName(in_cloud_name);


}

/// <summary>
/// 密度离群点去除
/// </summary>
void PCLVisualizer::on_actionDB_triggered()
{
    RadiusOutlierRemovalWindows tool(this);
    tool.bandingComboxAndPoint(this->cloudmanager->getItemNames());
    tool.exec();


}


void PCLVisualizer::on_actionBilateral_triggered()
{
    BilateralFilterWindows tool(this);
    tool.bandingComboxAndPoint(this->cloudmanager->getItemNames());
    tool.exec();


}

void PCLVisualizer::on_actionMedian2_triggered()
{
    MedianFilterWindows tool(this);
    tool.bandingComboxAndPoint(this->cloudmanager->getItemNames());
    tool.exec();
}

void PCLVisualizer::on_actionGP_triggered()
{
    DialogTriangulationSurfaceMesh tool(this);
    tool.bandingComboxAndPoint(this->cloudmanager->getItemNames());
    tool.exec();
    QString in_cloud_name = tool.getComboxSelect();
    // -- LOG ---
    logStr = "[" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") + "] " + "[贪婪三角网构建启动] " + "Point Cloud Plane Segmentation Done.";
    logList.push_back(in_cloud_name);
    ui->logList->addItem(logStr);
    // --- end LOG ----
    std::shared_ptr<CloudPointItemClass> item_ptr = this->cloudmanager->getCloudPointItemFromName(in_cloud_name);
  
    QString meshpath=tool.ExcuteCmd(item_ptr->pointCloud);
    this->cloudmanager->addMeshSTL(meshpath);
    // -- LOG ---
    logStr = "[" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") + "] " + "[贪婪三角网构建结束] " + "FILE has Save:"+ meshpath;
    logList.push_back(in_cloud_name);
    ui->logList->addItem(logStr);
    // --- end LOG ----
}

void PCLVisualizer::on_actionPoisson_triggered()
{
    PoissonReSurfaceWindows tool(this);
    tool.bandingComboxAndPoint(this->cloudmanager->getItemNames());
    tool.exec();
}

int ShowPclvisualizer(QWidget* parent)
{
    vtkOutputWindow::SetGlobalWarningDisplay(0); //不弹出vtkOutputWindow窗口

    QPixmap pixmap("logo1.png");  // 读取图片
    QSplashScreen splash(pixmap); //

    //读取ini文件中上一次关闭软件时候的窗口位置和大小：
    qDebug() << qApp->applicationDirPath() << endl;
    QString wstrFilePath = qApp->applicationDirPath() + "/setting.ini";
    QSettings* settings = new QSettings(
        wstrFilePath, QSettings::IniFormat); //用QSetting获取ini文件中的数据
    int x = settings->value("WindowGeometry/x").toInt();
    int y = settings->value("WindowGeometry/y").toInt();
    int width = settings->value("WindowGeometry/width").toInt();
    int height = settings->value("WindowGeometry/height").toInt();
    qDebug() << "Position is right:" << x << " " << y << " " << width << " "
        << height;
    QDesktopWidget* desktopWidget = QApplication::desktop();
    QRect clientRect = desktopWidget->availableGeometry();
    QRect targRect0 = QRect(clientRect.width() / 4,
        clientRect.height() / 4,
        clientRect.width() / 2,
        clientRect.height() / 2);
    QRect targRect = QRect(x, y, width, height);
    if (
        width == 0 || height == 0 || x < 0 || x > clientRect.width() || y < 0 ||
        y >	  clientRect.height()) //如果上一次关闭软件的时候，窗口位置不正常，则本次显示在显示器的正中央
    {
        targRect = targRect0;
        qDebug() << "Position is not right:" << x << " " << y << " " << width << " " << height;
    }

    PCLVisualizer* w=new PCLVisualizer(parent);
    w->setAttribute(Qt::WA_DeleteOnClose);// 关闭时自动释放
    w->setGeometry(targRect); //设置主窗口的大小
    w->show();

    //splash.finish(w); //在主体对象初始化完成后结束启动动画

    return 0;
}



////////////////// 测试点云构网 ////////////////////////////////////////////////////
void PCLVisualizer::Test_BestSurface() {
    // 加载点云
    best_surface();
}

CloudPointManagerClass::CloudPointManagerClass(QWidget* parent)
{
   

}


CloudPointManagerClass::~CloudPointManagerClass()
{
}

int CloudPointManagerClass::bandingViewer(pcl::visualization::PCLVisualizer::Ptr viewerPtr, QListWidget* filelist)
{
    this->viewerPtr = viewerPtr;
    this->filelist = filelist;
    return 0;
}

int CloudPointManagerClass::addPointCloud(QString filePathWithName, QString fileFormat)
{
    int return_status;
    pcl::PointCloud< pcl::PointXYZRGBA>::Ptr cloud_tmp(new pcl::PointCloud< pcl::PointXYZRGBA>);
    if (filePathWithName.endsWith(".pcd", Qt::CaseInsensitive)) {
        return_status =
            pcl::io::loadPCDFile(filePathWithName.toUtf8().constData(), *cloud_tmp);
        fileFormat = "PCD";

    }
    else if (filePathWithName.endsWith(".xyz", Qt::CaseInsensitive)) {

        // 1. 打开文件
        QString filePathWithName = "your_file_path.txt";
        QFile file(filePathWithName);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            qDebug() << "Failed to open the file.";
            return -1;
        }

        // 2. 读取文件内容
        QTextStream in(&file);
        int pointcount = 0;
        QStringList templist;

        // 3. 处理每一行数据
        while (!in.atEnd()) {
            QString line = in.readLine();
            templist = line.split(' ');

            // 4. 处理分割得到的数据
            if (templist.size() >= 3) {
                // 开始相关的点云存储设置
                double x = templist[0].toDouble() / 100.0;
                double y = templist[1].toDouble() / 100.0;
                double z = templist[2].toDouble() / 100.0;

                pcl::PointXYZRGBA p;
                p.x = templist[0].toDouble() / 100.0;
                p.y = templist[1].toDouble() / 100.0;
                p.z = templist[2].toDouble() / 100.0;//坐标
                cloud_tmp->points.push_back(p);

            }
        }

        // 5. 关闭文件
        file.close();
        cloud_tmp->is_dense = false;
        return_status = 0;
    }
    else {
        return_status = pcl::io::loadPLYFile(filePathWithName.toUtf8().constData(), *cloud_tmp);
        fileFormat = "PLY";
    }

 
    // 生成点云对象
    std::shared_ptr<CloudPointItemClass>  cloudItem(new CloudPointItemClass);
    QFileInfo fileinfo(filePathWithName);
    cloudItem->name = fileinfo.fileName();// 文件名
    cloudItem->filepath = filePathWithName;
    cloudItem->pointCloud.reset(new  pcl::PointCloud< pcl::PointXYZRGBA>);
    pcl::copyPointCloud(*cloud_tmp, *(cloudItem->pointCloud)); // 这样写可能存在问题

    // 绑定item
    this->DataList.insert(cloudItem->name, cloudItem);
    // 添加到 filelist
    QListWidgetItem* item = new QListWidgetItem;
 
    item->setBackground(QBrush(QColor(220, 230, 250)));
    item->setData(Qt::DisplayRole, cloudItem->name);
    item->setData(Qt::CheckStateRole, Qt::Checked);
    this->filelist->addItem(item);
    // 添加到渲染
    cloudItem->ViewerShow(this->viewerPtr);
    cloudItem->ZoomOnExtend(this->viewerPtr);
    qDebug() << "The number of points :" << cloudItem->pointCloud->points.size();
    return 0;
}

