<commit_before>// Copyright 2019 Global Phasing Ltd.
//
// Compare two CIF files with monomer restraints.

#include <stdio.h>
#include <cstdlib>
#include <set>
#include <gemmi/gzread.hpp>    // for read_cif_gz
#include <gemmi/chemcomp.hpp>  // for make_chemcomp_from_block
#define GEMMI_PROG mondiff
#include "options.h"

using namespace gemmi;

enum OptionIndex { Verbose=3, Bond, BondEsd, Angle, AngleEsd, Relative };

static const option::Descriptor Usage[] = {
  { NoOp, 0, "", "", Arg::None,
    "Usage:"
    "\n  " EXE_NAME " [options] FILE1 FILE2"
    "\nOptions:"},
  CommonUsage[Help],
  CommonUsage[Version],
  { Verbose, 0, "v", "verbose", Arg::None, "  --verbose  \tVerbose output." },
  { NoOp, 0, "", "", Arg::None, "\nMinimal reported differences:" },
  { Bond, 0, "", "bond", Arg::Float,
    "  --bond=DELTA  \tdifference in distance value (default: 0.01)." },
  { BondEsd, 0, "", "bond-esd", Arg::Float,
    "  --bond-esd=DELTA  \tdifference in distance esd (default: 0.1)." },
  { Angle, 0, "", "angle", Arg::Float,
    "  --angle=DELTA  \tdifference in angle value (default: 0.1)." },
  { AngleEsd, 0, "", "angle-esd", Arg::Float,
    "  --angle-esd=DELTA  \tdifference in angle esd (default: 1.0)." },
  { Relative, 0, "", "rel", Arg::Float,
    "  --rel=SIGMA  \t abs(value difference) / esd > SIGMA (default: 0.0)." },
  { 0, 0, 0, 0, 0, 0 }
};

struct MinDelta {
  double bond = 0.01;
  double bond_esd = 0.1;
  double angle = 0.1;
  double angle_esd = 1.0;
  double rel = 0.0;
};

static
std::string str(const ChemComp& cc, const Restraints::Bond& b) {
  return gemmi::tostr("bond ", b.id1.atom, '-', b.id2.atom,
                      " (", cc.get_atom(b.id1.atom).chem_type,
                      '-', cc.get_atom(b.id2.atom).chem_type, ')');
}

static
std::string str(const ChemComp& cc, const Restraints::Angle& a) {
  return gemmi::tostr("angle ", a.id1.atom, '-', a.id2.atom, '-', a.id3.atom,
                      " (", cc.get_atom(a.id1.atom).chem_type,
                      '-', cc.get_atom(a.id2.atom).chem_type,
                      '-', cc.get_atom(a.id3.atom).chem_type, ')');
}

static
std::string str(const ChemComp&, const Restraints::Torsion& a) {
  return "torsion " + a.str();
}

static
std::string str(const ChemComp&, const Restraints::Chirality& a) {
  return "chirality " + a.str();
}

static
const char* mark(double delta, double eps) {
  if (delta < eps) return "";
  if (delta < 2*eps) return "*";
  if (delta < 4*eps) return "**";
  if (delta < 8*eps) return "***";
  return "****";
}

static
void compare_chemcomps(const ChemComp& cc1, const ChemComp& cc2,
                       const MinDelta& delta) {
  // atoms
  for (const ChemComp::Atom& a : cc1.atoms) {
    auto b = cc2.find_atom(a.id);
    if (b == cc2.atoms.end())
      printf("- atom %s (%s)\n", a.id.c_str(), a.chem_type.c_str());
    else if (a.chem_type != b->chem_type)
      printf("! atom %s (%s : %s)\n",
             a.id.c_str(), a.chem_type.c_str(), b->chem_type.c_str());
  }
  for (const ChemComp::Atom& a : cc2.atoms)
    if (cc1.find_atom(a.id) == cc1.atoms.end())
      printf("+ atom %s (%s)\n", a.id.c_str(), a.chem_type.c_str());

  // bonds
  for (const Restraints::Bond& a : cc1.rt.bonds) {
    auto b = cc2.rt.find_bond(a.id1, a.id2);
    if (b == cc2.rt.bonds.end()) {
      printf("- %s\n", str(cc1, a).c_str());
    } else {
      if (a.type != b->type)
        printf("! %-30s %s : %s\n", str(cc1, a).c_str(),
               bond_type_to_string(a.type),
               bond_type_to_string(b->type));
      double d = std::fabs(a.value - b->value);
      if ((d > delta.bond || std::fabs(a.esd - b->esd) > delta.bond_esd) &&
          d > delta.rel * std::min(a.esd, b->esd))
        printf("! %-30s %4s %.3f : %.3f   esd %.3f : %.3f\n",
               str(cc1, a).c_str(), mark(d, a.esd),
               a.value, b->value, a.esd, b->esd);
    }
  }
  for (const Restraints::Bond& a : cc2.rt.bonds)
    if (cc1.rt.find_bond(a.id1, a.id2) == cc1.rt.bonds.end())
      printf("+ %s\n", str(cc2, a).c_str());

  // angles
  for (const Restraints::Angle& a : cc1.rt.angles) {
    auto b = cc2.rt.find_angle(a.id1, a.id2, a.id3);
    if (b == cc2.rt.angles.end()) {
      printf("- %s\n", str(cc1, a).c_str());
    } else {
      double d = std::fabs(a.value - b->value);
      if ((d > delta.angle || std::fabs(a.esd - b->esd) > delta.angle_esd) &&
          d > delta.rel * std::min(a.esd, b->esd))
        printf("! %-30s %4s %6.2f : %6.2f   esd %.2f : %.2f\n",
               str(cc1, a).c_str(), mark(d, a.esd),
               a.value, b->value, a.esd, b->esd);
    }
  }
  for (const Restraints::Angle& a : cc2.rt.angles)
    if (cc1.rt.find_angle(a.id1, a.id2, a.id3) == cc1.rt.angles.end())
      printf("+ %s\n", str(cc2, a).c_str());

  // torsion angles
  for (const Restraints::Torsion& a : cc1.rt.torsions) {
    auto b = cc2.rt.find_torsion(a.id1, a.id2, a.id3, a.id4);
    if (b == cc2.rt.torsions.end()) {
      printf("- %s\n", str(cc1, a).c_str());
    } else {
      double d = std::fabs(a.value - b->value);
      if ((d > delta.angle || std::fabs(a.esd - b->esd) > delta.angle_esd) &&
          d > delta.rel * std::min(a.esd, b->esd))
        printf("! %-30s %4s %6.2f : %6.2f   esd %.2f : %.2f\n",
               str(cc1, a).c_str(), mark(d, a.esd),
               a.value, b->value, a.esd, b->esd);
    }
  }
  for (const Restraints::Torsion& a : cc2.rt.torsions)
    if (cc1.rt.find_torsion(a.id1, a.id2, a.id3,a.id4) == cc1.rt.torsions.end())
      printf("+ %s\n", str(cc2, a).c_str());

  // chiralities
  std::vector<bool> matched_chir(cc2.rt.chirs.size(), false);
  for (const Restraints::Chirality& a : cc1.rt.chirs) {
    if (a.sign == ChiralityType::Both)  // not interesting
      continue;
    auto b = cc2.rt.find_chir(a.id_ctr, a.id1, a.id2, a.id3);
    if (b != cc2.rt.chirs.end()) {
      if (a.sign != b->sign)
        printf("! %-30s %s : %s\n", str(cc1, a).c_str(),
               chirality_to_string(a.sign), chirality_to_string(b->sign));
      matched_chir[b - cc2.rt.chirs.begin()] = true;
      continue;
    }
    b = cc2.rt.find_chir(a.id_ctr, a.id1, a.id3, a.id2);
    if (b == cc2.rt.chirs.end()) {
      printf("- %s  %s\n", str(cc1, a).c_str(), chirality_to_string(a.sign));
    } else {
      matched_chir[b - cc2.rt.chirs.begin()] = true;
      if (b->sign == ChiralityType::Both || b->sign == a.sign)
        printf("! %-30s %s : -%s\n", str(cc1, a).c_str(),
               chirality_to_string(a.sign), chirality_to_string(b->sign));
    }
  }
  for (size_t i = 0; i != cc2.rt.chirs.size(); ++i)
    if (!matched_chir[i]) {
      const Restraints::Chirality& a = cc2.rt.chirs[i];
      printf("+ %s  %s\n", str(cc2, a).c_str(), chirality_to_string(a.sign));
    }

  // planes
  std::vector<bool> matched_planes(cc2.rt.planes.size(), false);
  std::vector<std::set<Restraints::AtomId>> planes2;
  planes2.reserve(cc2.rt.planes.size());
  for (const Restraints::Plane& b : cc2.rt.planes)
    planes2.emplace_back(b.ids.begin(), b.ids.end());
  for (const Restraints::Plane& a : cc1.rt.planes) {
    std::set<Restraints::AtomId> plane1(a.ids.begin(), a.ids.end());
    auto b = std::find(planes2.begin(), planes2.end(), plane1);
    if (b == planes2.end()) {
      printf("- plane %s\n", a.str().c_str());
      continue;
    }
    double b_esd = cc2.rt.planes[b - planes2.begin()].esd;
    if (std::fabs(a.esd - b_esd) > 0.02)
      printf("! plane %-53s esd %.2f : %.2f\n", a.str().c_str(), a.esd, b_esd);
    b->clear();
  }
  for (size_t i = 0; i != planes2.size(); ++i)
    if (!planes2[i].empty())
      printf("+ plane %s\n", cc2.rt.planes[i].str().c_str());
}

int GEMMI_MAIN(int argc, char **argv) {
  OptParser p(EXE_NAME);
  p.simple_parse(argc, argv, Usage);
  p.require_positional_args(2);
  bool verbose = p.options[Verbose];
  const char* path1 = p.nonOption(0);
  const char* path2 = p.nonOption(1);

  MinDelta delta;
  if (p.options[Bond])
    delta.bond = std::atof(p.options[Bond].arg);
  if (p.options[BondEsd])
    delta.bond_esd = std::atof(p.options[BondEsd].arg);
  if (p.options[Angle])
    delta.angle = std::atof(p.options[Angle].arg);
  if (p.options[AngleEsd])
    delta.angle_esd = std::atof(p.options[AngleEsd].arg);
  if (p.options[Relative])
    delta.rel = std::atof(p.options[Relative].arg);

  try {
    if (verbose)
      fprintf(stderr, "Reading %s ...\n", path1);
    cif::Document doc1 = read_cif_gz(path1);
    const cif::Block* block1 = &doc1.blocks.at(0);
    if (block1->name == "comp_list")
      block1 = &doc1.blocks.at(1);
    if (verbose)
      fprintf(stderr, "Reading %s ...\n", path2);
    cif::Document doc2 = read_cif_gz(path2);
    const cif::Block* block2 = doc2.find_block(block1->name);
    if (!block2)
      fail("Block " + block1->name + " not found in " + path2);
    ChemComp cc1 = make_chemcomp_from_block(*block1);
    ChemComp cc2 = make_chemcomp_from_block(*block2);
    compare_chemcomps(cc1, cc2, delta);
  } catch (std::runtime_error& e) {
    fprintf(stderr, "ERROR: %s\n", e.what());
    return 1;
  }
  return 0;
}

// vim:sw=2:ts=2:et:path^=../include,../third_party
<commit_msg>mondiff.cpp: catch std::out_of_range<commit_after>// Copyright 2019 Global Phasing Ltd.
//
// Compare two CIF files with monomer restraints.

#include <stdio.h>
#include <cstdlib>
#include <set>
#include <gemmi/gzread.hpp>    // for read_cif_gz
#include <gemmi/chemcomp.hpp>  // for make_chemcomp_from_block
#define GEMMI_PROG mondiff
#include "options.h"

using namespace gemmi;

enum OptionIndex { Verbose=3, Bond, BondEsd, Angle, AngleEsd, Relative };

static const option::Descriptor Usage[] = {
  { NoOp, 0, "", "", Arg::None,
    "Usage:"
    "\n  " EXE_NAME " [options] FILE1 FILE2"
    "\nOptions:"},
  CommonUsage[Help],
  CommonUsage[Version],
  { Verbose, 0, "v", "verbose", Arg::None, "  --verbose  \tVerbose output." },
  { NoOp, 0, "", "", Arg::None, "\nMinimal reported differences:" },
  { Bond, 0, "", "bond", Arg::Float,
    "  --bond=DELTA  \tdifference in distance value (default: 0.01)." },
  { BondEsd, 0, "", "bond-esd", Arg::Float,
    "  --bond-esd=DELTA  \tdifference in distance esd (default: 0.1)." },
  { Angle, 0, "", "angle", Arg::Float,
    "  --angle=DELTA  \tdifference in angle value (default: 0.1)." },
  { AngleEsd, 0, "", "angle-esd", Arg::Float,
    "  --angle-esd=DELTA  \tdifference in angle esd (default: 1.0)." },
  { Relative, 0, "", "rel", Arg::Float,
    "  --rel=SIGMA  \t abs(value difference) / esd > SIGMA (default: 0.0)." },
  { 0, 0, 0, 0, 0, 0 }
};

struct MinDelta {
  double bond = 0.01;
  double bond_esd = 0.1;
  double angle = 0.1;
  double angle_esd = 1.0;
  double rel = 0.0;
};

static
std::string str(const ChemComp& cc, const Restraints::Bond& b) {
  return gemmi::tostr("bond ", b.id1.atom, '-', b.id2.atom,
                      " (", cc.get_atom(b.id1.atom).chem_type,
                      '-', cc.get_atom(b.id2.atom).chem_type, ')');
}

static
std::string str(const ChemComp& cc, const Restraints::Angle& a) {
  return gemmi::tostr("angle ", a.id1.atom, '-', a.id2.atom, '-', a.id3.atom,
                      " (", cc.get_atom(a.id1.atom).chem_type,
                      '-', cc.get_atom(a.id2.atom).chem_type,
                      '-', cc.get_atom(a.id3.atom).chem_type, ')');
}

static
std::string str(const ChemComp&, const Restraints::Torsion& a) {
  return "torsion " + a.str();
}

static
std::string str(const ChemComp&, const Restraints::Chirality& a) {
  return "chirality " + a.str();
}

static
const char* mark(double delta, double eps) {
  if (delta < eps) return "";
  if (delta < 2*eps) return "*";
  if (delta < 4*eps) return "**";
  if (delta < 8*eps) return "***";
  return "****";
}

static
void compare_chemcomps(const ChemComp& cc1, const ChemComp& cc2,
                       const MinDelta& delta) {
  // atoms
  for (const ChemComp::Atom& a : cc1.atoms) {
    auto b = cc2.find_atom(a.id);
    if (b == cc2.atoms.end())
      printf("- atom %s (%s)\n", a.id.c_str(), a.chem_type.c_str());
    else if (a.chem_type != b->chem_type)
      printf("! atom %s (%s : %s)\n",
             a.id.c_str(), a.chem_type.c_str(), b->chem_type.c_str());
  }
  for (const ChemComp::Atom& a : cc2.atoms)
    if (cc1.find_atom(a.id) == cc1.atoms.end())
      printf("+ atom %s (%s)\n", a.id.c_str(), a.chem_type.c_str());

  // bonds
  for (const Restraints::Bond& a : cc1.rt.bonds) {
    auto b = cc2.rt.find_bond(a.id1, a.id2);
    if (b == cc2.rt.bonds.end()) {
      printf("- %s\n", str(cc1, a).c_str());
    } else {
      if (a.type != b->type)
        printf("! %-30s %s : %s\n", str(cc1, a).c_str(),
               bond_type_to_string(a.type),
               bond_type_to_string(b->type));
      double d = std::fabs(a.value - b->value);
      if ((d > delta.bond || std::fabs(a.esd - b->esd) > delta.bond_esd) &&
          d > delta.rel * std::min(a.esd, b->esd))
        printf("! %-30s %4s %.3f : %.3f   esd %.3f : %.3f\n",
               str(cc1, a).c_str(), mark(d, a.esd),
               a.value, b->value, a.esd, b->esd);
    }
  }
  for (const Restraints::Bond& a : cc2.rt.bonds)
    if (cc1.rt.find_bond(a.id1, a.id2) == cc1.rt.bonds.end())
      printf("+ %s\n", str(cc2, a).c_str());

  // angles
  for (const Restraints::Angle& a : cc1.rt.angles) {
    auto b = cc2.rt.find_angle(a.id1, a.id2, a.id3);
    if (b == cc2.rt.angles.end()) {
      printf("- %s\n", str(cc1, a).c_str());
    } else {
      double d = std::fabs(a.value - b->value);
      if ((d > delta.angle || std::fabs(a.esd - b->esd) > delta.angle_esd) &&
          d > delta.rel * std::min(a.esd, b->esd))
        printf("! %-30s %4s %6.2f : %6.2f   esd %.2f : %.2f\n",
               str(cc1, a).c_str(), mark(d, a.esd),
               a.value, b->value, a.esd, b->esd);
    }
  }
  for (const Restraints::Angle& a : cc2.rt.angles)
    if (cc1.rt.find_angle(a.id1, a.id2, a.id3) == cc1.rt.angles.end())
      printf("+ %s\n", str(cc2, a).c_str());

  // torsion angles
  for (const Restraints::Torsion& a : cc1.rt.torsions) {
    auto b = cc2.rt.find_torsion(a.id1, a.id2, a.id3, a.id4);
    if (b == cc2.rt.torsions.end()) {
      printf("- %s\n", str(cc1, a).c_str());
    } else {
      double d = std::fabs(a.value - b->value);
      if ((d > delta.angle || std::fabs(a.esd - b->esd) > delta.angle_esd) &&
          d > delta.rel * std::min(a.esd, b->esd))
        printf("! %-30s %4s %6.2f : %6.2f   esd %.2f : %.2f\n",
               str(cc1, a).c_str(), mark(d, a.esd),
               a.value, b->value, a.esd, b->esd);
    }
  }
  for (const Restraints::Torsion& a : cc2.rt.torsions)
    if (cc1.rt.find_torsion(a.id1, a.id2, a.id3,a.id4) == cc1.rt.torsions.end())
      printf("+ %s\n", str(cc2, a).c_str());

  // chiralities
  std::vector<bool> matched_chir(cc2.rt.chirs.size(), false);
  for (const Restraints::Chirality& a : cc1.rt.chirs) {
    if (a.sign == ChiralityType::Both)  // not interesting
      continue;
    auto b = cc2.rt.find_chir(a.id_ctr, a.id1, a.id2, a.id3);
    if (b != cc2.rt.chirs.end()) {
      if (a.sign != b->sign)
        printf("! %-30s %s : %s\n", str(cc1, a).c_str(),
               chirality_to_string(a.sign), chirality_to_string(b->sign));
      matched_chir[b - cc2.rt.chirs.begin()] = true;
      continue;
    }
    b = cc2.rt.find_chir(a.id_ctr, a.id1, a.id3, a.id2);
    if (b == cc2.rt.chirs.end()) {
      printf("- %s  %s\n", str(cc1, a).c_str(), chirality_to_string(a.sign));
    } else {
      matched_chir[b - cc2.rt.chirs.begin()] = true;
      if (b->sign == ChiralityType::Both || b->sign == a.sign)
        printf("! %-30s %s : -%s\n", str(cc1, a).c_str(),
               chirality_to_string(a.sign), chirality_to_string(b->sign));
    }
  }
  for (size_t i = 0; i != cc2.rt.chirs.size(); ++i)
    if (!matched_chir[i]) {
      const Restraints::Chirality& a = cc2.rt.chirs[i];
      printf("+ %s  %s\n", str(cc2, a).c_str(), chirality_to_string(a.sign));
    }

  // planes
  std::vector<bool> matched_planes(cc2.rt.planes.size(), false);
  std::vector<std::set<Restraints::AtomId>> planes2;
  planes2.reserve(cc2.rt.planes.size());
  for (const Restraints::Plane& b : cc2.rt.planes)
    planes2.emplace_back(b.ids.begin(), b.ids.end());
  for (const Restraints::Plane& a : cc1.rt.planes) {
    std::set<Restraints::AtomId> plane1(a.ids.begin(), a.ids.end());
    auto b = std::find(planes2.begin(), planes2.end(), plane1);
    if (b == planes2.end()) {
      printf("- plane %s\n", a.str().c_str());
      continue;
    }
    double b_esd = cc2.rt.planes[b - planes2.begin()].esd;
    if (std::fabs(a.esd - b_esd) > 0.02)
      printf("! plane %-53s esd %.2f : %.2f\n", a.str().c_str(), a.esd, b_esd);
    b->clear();
  }
  for (size_t i = 0; i != planes2.size(); ++i)
    if (!planes2[i].empty())
      printf("+ plane %s\n", cc2.rt.planes[i].str().c_str());
}

int GEMMI_MAIN(int argc, char **argv) {
  OptParser p(EXE_NAME);
  p.simple_parse(argc, argv, Usage);
  p.require_positional_args(2);
  bool verbose = p.options[Verbose];
  const char* path1 = p.nonOption(0);
  const char* path2 = p.nonOption(1);

  MinDelta delta;
  if (p.options[Bond])
    delta.bond = std::atof(p.options[Bond].arg);
  if (p.options[BondEsd])
    delta.bond_esd = std::atof(p.options[BondEsd].arg);
  if (p.options[Angle])
    delta.angle = std::atof(p.options[Angle].arg);
  if (p.options[AngleEsd])
    delta.angle_esd = std::atof(p.options[AngleEsd].arg);
  if (p.options[Relative])
    delta.rel = std::atof(p.options[Relative].arg);

  try {
    if (verbose)
      fprintf(stderr, "Reading %s ...\n", path1);
    cif::Document doc1 = read_cif_gz(path1);
    const cif::Block* block1 = &doc1.blocks.at(0);
    if (block1->name == "comp_list")
      block1 = &doc1.blocks.at(1);
    if (verbose)
      fprintf(stderr, "Reading %s ...\n", path2);
    cif::Document doc2 = read_cif_gz(path2);
    const cif::Block* block2 = doc2.find_block(block1->name);
    if (!block2)
      fail("Block " + block1->name + " not found in " + path2);
    ChemComp cc1 = make_chemcomp_from_block(*block1);
    ChemComp cc2 = make_chemcomp_from_block(*block2);
    compare_chemcomps(cc1, cc2, delta);
  } catch (std::runtime_error& e) {
    fprintf(stderr, "ERROR: %s\n", e.what());
    return 1;
  } catch (std::out_of_range& e) {
    fprintf(stderr, "ERROR: %s\n", e.what());
    return 2;
  }
  return 0;
}

// vim:sw=2:ts=2:et:path^=../include,../third_party
<|endoftext|>


<commit_before>// Copyright 2019 Global Phasing Ltd.
//
// convert MTZ to SF-mmCIF

// TODO:
//  - cell parameters may be different in CELL and DCELL records, check for it
//  - what to do with _refln.status
//  - check that the FP column is not from Refmac
//  - should we allow for repeated column name in MTZ?

#include <algorithm>
#include <stdio.h>
#include <gemmi/mtz.hpp>
#include <gemmi/fileutil.hpp> // for file_open
#include <gemmi/atox.hpp>     // for read_word
#include <gemmi/version.hpp>  // for GEMMI_VERSION
#define GEMMI_PROG mtz2cif
#include "options.h"

enum OptionIndex { Verbose=3, Spec, PrintSpec };

static const option::Descriptor Usage[] = {
  { NoOp, 0, "", "", Arg::None,
    "Usage:\n  " EXE_NAME " [options] MTZ_FILE CIF_FILE"
    "\nOptions:"},
  { Help, 0, "h", "help", Arg::None, "  -h, --help  \tPrint usage and exit." },
  { Version, 0, "V", "version", Arg::None,
    "  -V, --version  \tPrint version and exit." },
  { Verbose, 0, "v", "verbose", Arg::None, "  --verbose  \tVerbose output." },
  { Spec, 0, "", "spec", Arg::Required,
    "  --spec=FILE  \tColumn and format specification." },
  { PrintSpec, 0, "", "print-spec", Arg::None,
    "  --print-spec  \tPrint default spec and exit." },
  { NoOp, 0, "", "", Arg::None,
    "\nIf CIF_FILE is -, the output is printed to stdout."
    "\nIf spec is -, it is read from stdin."
    "\n\nLines in the spec file have format:"
    "\n  [FLAG] COLUMN TYPE TAG [FORMAT]"
    "\nfor example:"
    "\n  SIGF_native * SIGF_meas_au 12.5e"
    "\n  FREE I pdbx_r_free_flag 3d"
    "\nFLAG (optional) is either ? or &:"
    "\n  ? = column is skipped if absent in the MTZ file."
    "\n  & = column is skipped if previous line was skipped. Example:"
    "\n      ? I    J intensity_meas"
    "\n      & SIGI Q intensity_sigma"
    "\nCOLUMN is MTZ column label. Columns H K L are added if not specified."
    "\n  Alternative labels can be separated with | (e.g. FREE|FreeR_flag)."
    "\nTYPE is used for checking the columm type, unless it is '*'."
    "\nTAG does not include category name, it is only the part after _refln."
    "\nFORMAT (optional) is printf-like floating-point format:"
    "\n - one of e, f, g with optional flag, width and precision"
    "\n - flag is one of + - # _; '_' stands for ' ', for example '_.4f'"
    "\n - since all numbers in MTZ are stored as float, the integer columns use"
    "\n   the same format as float. The format of _refln.status is ignored."
  },
  { 0, 0, 0, 0, 0, 0 }
};

struct Trans {
  int col_idx;
  bool is_status;
  std::string refln_tag;
  std::string format;
};

static const char* default_spec[] = {
  "H H index_h",
  "K H index_k",
  "L H index_l",
  "? I       J intensity_meas",
  "& SIGI    Q intensity_sigma",
  "? I(+)    K pdbx_I_plus",
  "& SIGI(+) M pdbx_I_plus_sigma",
  "? I(-)    K pdbx_I_minus",
  "& SIGI(-) M pdbx_I_minus_sigma",
  "? FP      F F_meas_au", // check also if the MTZ is not from refmac
  "& SIGFP   Q F_meas_sigma_au",
  "? F(+)    G pdbx_F_plus",
  "& SIGF(+) L pdbx_F_plus_sigma",
  "? F(-)    G pdbx_F_minus",
  "& SIGF(-) L pdbx_F_minus_sigma",
  "? FREE|RFREE|FreeR_flag I status",
  "? FWT|2FOFCWT      F pdbx_FWT",
  "& PHWT|PH2FOFCWT   P pdbx_PHWT",
  "? DELFWT|FOFCWT    F pdbx_DELFWT",
  "& DELPHWT|PHFOFCWT P pdbx_DELPHWT",
};

static int find_column_index(const std::string& column, const gemmi::Mtz& mtz) {
  int idx = -1;
  for (const std::string& label : gemmi::split_str(column, '|')) {
    for (size_t i = 0; i != mtz.columns.size(); ++i) {
      if (mtz.columns[i].label == label) {
        if (idx == -1)
          idx = (int) i;
        else
          fprintf(stderr, "Warning: duplicate column %s\n", label.c_str());
      }
    }
    if (idx != -1)
      break;
  }
  return idx;
}

void check_format(const std::string& fmt) {
  // expected format: [#_+-]?\d*(\.\d+)?[fFgGeEc]
  if (fmt.find('%') != std::string::npos)
    gemmi::fail("Specify format without %. Got: " + fmt);
  size_t pos = 0;
  if (fmt[0] == '_' || fmt[0] == '+' || fmt[0] == '-' || fmt[0] == '#')
   pos = 1;
  while (gemmi::is_digit(fmt[pos]))
    ++pos;
  if (fmt[pos] == '.' && gemmi::is_digit(fmt[pos+1])) {
    pos += 2;
    while (gemmi::is_digit(fmt[pos]))
      ++pos;
  }
  if (std::isalpha(fmt[pos]))
    ++pos;
  if (fmt[pos] != '\0')
    gemmi::fail("wrong format : " + fmt + "\nCorrect examples: g, .4f, 12.5e");
  char c = gemmi::alpha_up(fmt.back());
  if (c != 'F' && c != 'G' && c != 'E')
    gemmi::fail("expected floating-point format, got: " + fmt);
}

static std::vector<Trans> parse_spec(const gemmi::Mtz& mtz,
                                     const std::vector<std::string>& lines) {
  std::vector<Trans> spec;
  size_t prev_size = 0;
  bool discard = false;
  for (const std::string& line : lines) {
    Trans tr;
    const char* p = line.c_str();
    if (*p == '&') {
      if (discard)
        continue;
    } else {
      prev_size = spec.size();
      discard = false;
    }
    bool optional = (*p == '?' || *p == '&');
    if (optional)
      ++p;
    std::string column = gemmi::read_word(p, &p);
    std::string type = gemmi::read_word(p, &p);
    if (type.size() != 1)
      gemmi::fail("Spec error: MTZ type '" + type + "' is not one character,"
                  "\nin line: " + line);
    tr.refln_tag = gemmi::read_word(p, &p);
    if (tr.refln_tag[0] == '_' || tr.refln_tag.find('.') != std::string::npos)
      gemmi::fail("Spec error: expected tag part after _refln., got: " +
                  tr.refln_tag + "\nin line: " + line);
    tr.col_idx = find_column_index(column, mtz);
    if (tr.col_idx == -1) {
      if (!optional)
        gemmi::fail("Column not found: " + column);
      spec.resize(prev_size);
      discard = true;
      continue;
    }
    const gemmi::Mtz::Column& col = mtz.columns[tr.col_idx];
    if (type[0] != '*' && col.type != type[0])
      gemmi::fail("Column " + col.label + " has type " +
                  std::string(1, col.type) + " not " + type);
    tr.is_status = gemmi::iequal(tr.refln_tag, "status");
    std::string fmt = gemmi::read_word(p, &p);
    if (fmt.empty()) {
      tr.format = "%g";
    } else if (!tr.is_status) {
      check_format(fmt);
      tr.format = "%" + fmt;
      if (tr.format[1] == '_')
        tr.format[1] = ' ';
    }
    spec.push_back(tr);
  }
  return spec;
}

#if defined(__GNUC__)
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif

static void write_cif(const gemmi::Mtz& mtz, const std::vector<Trans> spec,
                      FILE* out) {
  std::string id = ".";
  fprintf(out, "# Converted from MTZ by gemmi-mtz2cif " GEMMI_VERSION "\n");
  fprintf(out, "# MTZ title: %s\n", mtz.title.c_str());
  for (size_t i = 0; i != mtz.history.size(); ++i)
    fprintf(out, "# MTZ history #%zu: %s\n", i, mtz.history[i].c_str());
  fprintf(out, "data_%s\n\n", "mtz");
  fprintf(out, "_entry.id %s\n\n", id.c_str());
  const gemmi::UnitCell& cell = mtz.get_cell();
  fprintf(out, "_cell.entry_id %s\n", id.c_str());
  fprintf(out, "_cell.length_a    %8.3f\n", cell.a);
  fprintf(out, "_cell.length_b    %8.3f\n", cell.b);
  fprintf(out, "_cell.length_c    %8.3f\n", cell.c);
  fprintf(out, "_cell.angle_alpha %8.3f\n", cell.alpha);
  fprintf(out, "_cell.angle_beta  %8.3f\n", cell.beta);
  fprintf(out, "_cell.angle_gamma %8.3f\n\n", cell.gamma);
  if (const gemmi::SpaceGroup* sg = mtz.spacegroup) {
    fprintf(out, "_symmetry.entry_id %s\n", id.c_str());
    fprintf(out, "_symmetry.space_group_name_H-M %s\n", sg->hm);
    fprintf(out, "_symmetry.Int_Tables_number %d\n\n", sg->number);
    // could write _symmetry_equiv.pos_as_xyz, but would it be useful?
  }
  fprintf(out, "loop_\n");
  for (const Trans& tr : spec) {
    const gemmi::Mtz::Column& col = mtz.columns.at(tr.col_idx);
    const gemmi::Mtz::Dataset& ds = mtz.dataset(col.dataset_number);
    fprintf(out, "_refln.%-24s # %-14s from dataset %s\n",
            tr.refln_tag.c_str(), col.label.c_str(), ds.dataset_name.c_str());
  }
  for (int i = 0; i != mtz.nreflections; ++i) {
    const float* row = &mtz.raw_data[i*mtz.ncol];
    bool first = true;
    for (const Trans& tr : spec) {
      if (first)
        first = false;
      else
        fputc(' ', out);
      float v = row[tr.col_idx];
      if (tr.is_status)
        fputc(v ? 'f' : '<', out);
      else
        fprintf(out, tr.format.c_str(), v);
    }
    fputc('\n', out);
  }
}

int GEMMI_MAIN(int argc, char **argv) {
  OptParser p(EXE_NAME);
  p.simple_parse(argc, argv, Usage);
  if (p.options[PrintSpec]) {
    for (const char* line : default_spec)
      printf("%s\n", line);
    return 0;
  }
  p.require_positional_args(2);
  bool verbose = p.options[Verbose];
  const char* mtz_path = p.nonOption(0);
  const char* cif_path = p.nonOption(1);
  gemmi::Mtz mtz;
  if (verbose) {
    fprintf(stderr, "Reading %s ...\n", mtz_path);
    mtz.warnings = stderr;
  }
  try {
    gemmi::fileptr_t f_in = gemmi::file_open(mtz_path, "rb");
    mtz.read_all_headers(f_in.get());
    mtz.read_raw_data(f_in.get());
  } catch (std::runtime_error& e) {
    fprintf(stderr, "ERROR reading %s: %s\n", mtz_path, e.what());
    return 1;
  }
  if (verbose)
    fprintf(stderr, "Writing %s ...\n", cif_path);
  std::vector<Trans> spec;
  try {
  std::vector<std::string> lines;
    if (p.options[Spec]) {
      char buf[256];
      const char* spec_path = p.options[Spec].arg;
      gemmi::fileptr_t f_spec = gemmi::file_open_or(spec_path, "r", stdin);
      while (fgets(buf, sizeof(buf), f_spec.get()) != NULL) {
        const char* start = gemmi::skip_blank(buf);
        if (*start != '\0' && *start != '#')
          lines.emplace_back(start);
      }
    } else {
      lines.reserve(sizeof(default_spec) / sizeof(default_spec[0]));
      for (const char* line : default_spec)
        lines.emplace_back(line);
    }
    spec = parse_spec(mtz, lines);
    if (spec.empty())
      gemmi::fail("Empty translation spec");
    for (size_t i = 0; i != spec.size(); ++i)
      for (size_t j = i + 1; j != spec.size(); ++j)
        if (spec[i].refln_tag == spec[j].refln_tag)
          gemmi::fail("duplicated output tag: " + spec[i].refln_tag);
    // H, K, L must be the first columns in MTZ and are required in _refln
    for (int i = 2; i != -1; --i)
      if (!gemmi::in_vector_f([&](const Trans& t) { return t.col_idx == i; },
                              spec)) {
        spec.insert(spec.begin(), Trans{i, false, "index_h", "%g "});
        spec.front().refln_tag.back() += i;
      }
  } catch (std::runtime_error& e) {
    fprintf(stderr, "Problem in translation spec: %s\n", e.what());
    return 2;
  }
  try {
    gemmi::fileptr_t f_out = gemmi::file_open_or(cif_path, "w", stdout);
    write_cif(mtz, spec, f_out.get());
  } catch (std::runtime_error& e) {
    fprintf(stderr, "ERROR writing %s: %s\n", cif_path, e.what());
    return 3;
  }
  return 0;
}

// vim:sw=2:ts=2:et:path^=../include,../third_party
<commit_msg>mtz2cif: fix _refln.status<commit_after>// Copyright 2019 Global Phasing Ltd.
//
// convert MTZ to SF-mmCIF

// TODO:
//  - cell parameters may be different in CELL and DCELL records, check for it
//  - what to do with _refln.status
//  - check that the FP column is not from Refmac
//  - should we allow for repeated column name in MTZ?

#include <algorithm>
#include <stdio.h>
#include <gemmi/mtz.hpp>
#include <gemmi/fileutil.hpp> // for file_open
#include <gemmi/atox.hpp>     // for read_word
#include <gemmi/version.hpp>  // for GEMMI_VERSION
#define GEMMI_PROG mtz2cif
#include "options.h"

enum OptionIndex { Verbose=3, Spec, PrintSpec };

static const option::Descriptor Usage[] = {
  { NoOp, 0, "", "", Arg::None,
    "Usage:\n  " EXE_NAME " [options] MTZ_FILE CIF_FILE"
    "\nOptions:"},
  { Help, 0, "h", "help", Arg::None, "  -h, --help  \tPrint usage and exit." },
  { Version, 0, "V", "version", Arg::None,
    "  -V, --version  \tPrint version and exit." },
  { Verbose, 0, "v", "verbose", Arg::None, "  --verbose  \tVerbose output." },
  { Spec, 0, "", "spec", Arg::Required,
    "  --spec=FILE  \tColumn and format specification." },
  { PrintSpec, 0, "", "print-spec", Arg::None,
    "  --print-spec  \tPrint default spec and exit." },
  { NoOp, 0, "", "", Arg::None,
    "\nIf CIF_FILE is -, the output is printed to stdout."
    "\nIf spec is -, it is read from stdin."
    "\n\nLines in the spec file have format:"
    "\n  [FLAG] COLUMN TYPE TAG [FORMAT]"
    "\nfor example:"
    "\n  SIGF_native * SIGF_meas_au 12.5e"
    "\n  FREE I pdbx_r_free_flag 3d"
    "\nFLAG (optional) is either ? or &:"
    "\n  ? = column is skipped if absent in the MTZ file."
    "\n  & = column is skipped if previous line was skipped. Example:"
    "\n      ? I    J intensity_meas"
    "\n      & SIGI Q intensity_sigma"
    "\nCOLUMN is MTZ column label. Columns H K L are added if not specified."
    "\n  Alternative labels can be separated with | (e.g. FREE|FreeR_flag)."
    "\nTYPE is used for checking the columm type, unless it is '*'."
    "\nTAG does not include category name, it is only the part after _refln."
    "\nFORMAT (optional) is printf-like floating-point format:"
    "\n - one of e, f, g with optional flag, width and precision"
    "\n - flag is one of + - # _; '_' stands for ' ', for example '_.4f'"
    "\n - since all numbers in MTZ are stored as float, the integer columns use"
    "\n   the same format as float. The format of _refln.status is ignored."
  },
  { 0, 0, 0, 0, 0, 0 }
};

struct Trans {
  int col_idx;
  bool is_status;
  std::string refln_tag;
  std::string format;
};

static const char* default_spec[] = {
  "H H index_h",
  "K H index_k",
  "L H index_l",
  "? I       J intensity_meas",
  "& SIGI    Q intensity_sigma",
  "? I(+)    K pdbx_I_plus",
  "& SIGI(+) M pdbx_I_plus_sigma",
  "? I(-)    K pdbx_I_minus",
  "& SIGI(-) M pdbx_I_minus_sigma",
  "? FP      F F_meas_au", // check also if the MTZ is not from refmac
  "& SIGFP   Q F_meas_sigma_au",
  "? F(+)    G pdbx_F_plus",
  "& SIGF(+) L pdbx_F_plus_sigma",
  "? F(-)    G pdbx_F_minus",
  "& SIGF(-) L pdbx_F_minus_sigma",
  "? FREE|RFREE|FreeR_flag I status",
  "? FWT|2FOFCWT      F pdbx_FWT",
  "& PHWT|PH2FOFCWT   P pdbx_PHWT",
  "? DELFWT|FOFCWT    F pdbx_DELFWT",
  "& DELPHWT|PHFOFCWT P pdbx_DELPHWT",
};

static int find_column_index(const std::string& column, const gemmi::Mtz& mtz) {
  int idx = -1;
  for (const std::string& label : gemmi::split_str(column, '|')) {
    for (size_t i = 0; i != mtz.columns.size(); ++i) {
      if (mtz.columns[i].label == label) {
        if (idx == -1)
          idx = (int) i;
        else
          fprintf(stderr, "Warning: duplicate column %s\n", label.c_str());
      }
    }
    if (idx != -1)
      break;
  }
  return idx;
}

void check_format(const std::string& fmt) {
  // expected format: [#_+-]?\d*(\.\d+)?[fFgGeEc]
  if (fmt.find('%') != std::string::npos)
    gemmi::fail("Specify format without %. Got: " + fmt);
  size_t pos = 0;
  if (fmt[0] == '_' || fmt[0] == '+' || fmt[0] == '-' || fmt[0] == '#')
   pos = 1;
  while (gemmi::is_digit(fmt[pos]))
    ++pos;
  if (fmt[pos] == '.' && gemmi::is_digit(fmt[pos+1])) {
    pos += 2;
    while (gemmi::is_digit(fmt[pos]))
      ++pos;
  }
  if (std::isalpha(fmt[pos]))
    ++pos;
  if (fmt[pos] != '\0')
    gemmi::fail("wrong format : " + fmt + "\nCorrect examples: g, .4f, 12.5e");
  char c = gemmi::alpha_up(fmt.back());
  if (c != 'F' && c != 'G' && c != 'E')
    gemmi::fail("expected floating-point format, got: " + fmt);
}

static std::vector<Trans> parse_spec(const gemmi::Mtz& mtz,
                                     const std::vector<std::string>& lines) {
  std::vector<Trans> spec;
  size_t prev_size = 0;
  bool discard = false;
  for (const std::string& line : lines) {
    Trans tr;
    const char* p = line.c_str();
    if (*p == '&') {
      if (discard)
        continue;
    } else {
      prev_size = spec.size();
      discard = false;
    }
    bool optional = (*p == '?' || *p == '&');
    if (optional)
      ++p;
    std::string column = gemmi::read_word(p, &p);
    std::string type = gemmi::read_word(p, &p);
    if (type.size() != 1)
      gemmi::fail("Spec error: MTZ type '" + type + "' is not one character,"
                  "\nin line: " + line);
    tr.refln_tag = gemmi::read_word(p, &p);
    if (tr.refln_tag[0] == '_' || tr.refln_tag.find('.') != std::string::npos)
      gemmi::fail("Spec error: expected tag part after _refln., got: " +
                  tr.refln_tag + "\nin line: " + line);
    tr.col_idx = find_column_index(column, mtz);
    if (tr.col_idx == -1) {
      if (!optional)
        gemmi::fail("Column not found: " + column);
      spec.resize(prev_size);
      discard = true;
      continue;
    }
    const gemmi::Mtz::Column& col = mtz.columns[tr.col_idx];
    if (type[0] != '*' && col.type != type[0])
      gemmi::fail("Column " + col.label + " has type " +
                  std::string(1, col.type) + " not " + type);
    tr.is_status = gemmi::iequal(tr.refln_tag, "status");
    std::string fmt = gemmi::read_word(p, &p);
    if (fmt.empty()) {
      tr.format = "%g";
    } else if (!tr.is_status) {
      check_format(fmt);
      tr.format = "%" + fmt;
      if (tr.format[1] == '_')
        tr.format[1] = ' ';
    }
    spec.push_back(tr);
  }
  return spec;
}

#if defined(__GNUC__)
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif

static void write_cif(const gemmi::Mtz& mtz, const std::vector<Trans> spec,
                      FILE* out) {
  std::string id = ".";
  fprintf(out, "# Converted from MTZ by gemmi-mtz2cif " GEMMI_VERSION "\n");
  fprintf(out, "# MTZ title: %s\n", mtz.title.c_str());
  for (size_t i = 0; i != mtz.history.size(); ++i)
    fprintf(out, "# MTZ history #%zu: %s\n", i, mtz.history[i].c_str());
  fprintf(out, "data_%s\n\n", "mtz");
  fprintf(out, "_entry.id %s\n\n", id.c_str());
  const gemmi::UnitCell& cell = mtz.get_cell();
  fprintf(out, "_cell.entry_id %s\n", id.c_str());
  fprintf(out, "_cell.length_a    %8.3f\n", cell.a);
  fprintf(out, "_cell.length_b    %8.3f\n", cell.b);
  fprintf(out, "_cell.length_c    %8.3f\n", cell.c);
  fprintf(out, "_cell.angle_alpha %8.3f\n", cell.alpha);
  fprintf(out, "_cell.angle_beta  %8.3f\n", cell.beta);
  fprintf(out, "_cell.angle_gamma %8.3f\n\n", cell.gamma);
  if (const gemmi::SpaceGroup* sg = mtz.spacegroup) {
    fprintf(out, "_symmetry.entry_id %s\n", id.c_str());
    fprintf(out, "_symmetry.space_group_name_H-M %s\n", sg->hm);
    fprintf(out, "_symmetry.Int_Tables_number %d\n\n", sg->number);
    // could write _symmetry_equiv.pos_as_xyz, but would it be useful?
  }
  fprintf(out, "loop_\n");
  for (const Trans& tr : spec) {
    const gemmi::Mtz::Column& col = mtz.columns.at(tr.col_idx);
    const gemmi::Mtz::Dataset& ds = mtz.dataset(col.dataset_number);
    fprintf(out, "_refln.%-24s # %-14s from dataset %s\n",
            tr.refln_tag.c_str(), col.label.c_str(), ds.dataset_name.c_str());
  }
  for (int i = 0; i != mtz.nreflections; ++i) {
    const float* row = &mtz.raw_data[i*mtz.ncol];
    bool first = true;
    for (const Trans& tr : spec) {
      if (first)
        first = false;
      else
        fputc(' ', out);
      float v = row[tr.col_idx];
      if (tr.is_status)
        fputc(v == 0. ? 'f' : 'o', out);
      else
        fprintf(out, tr.format.c_str(), v);
    }
    fputc('\n', out);
  }
}

int GEMMI_MAIN(int argc, char **argv) {
  OptParser p(EXE_NAME);
  p.simple_parse(argc, argv, Usage);
  if (p.options[PrintSpec]) {
    for (const char* line : default_spec)
      printf("%s\n", line);
    return 0;
  }
  p.require_positional_args(2);
  bool verbose = p.options[Verbose];
  const char* mtz_path = p.nonOption(0);
  const char* cif_path = p.nonOption(1);
  gemmi::Mtz mtz;
  if (verbose) {
    fprintf(stderr, "Reading %s ...\n", mtz_path);
    mtz.warnings = stderr;
  }
  try {
    gemmi::fileptr_t f_in = gemmi::file_open(mtz_path, "rb");
    mtz.read_all_headers(f_in.get());
    mtz.read_raw_data(f_in.get());
  } catch (std::runtime_error& e) {
    fprintf(stderr, "ERROR reading %s: %s\n", mtz_path, e.what());
    return 1;
  }
  if (verbose)
    fprintf(stderr, "Writing %s ...\n", cif_path);
  std::vector<Trans> spec;
  try {
  std::vector<std::string> lines;
    if (p.options[Spec]) {
      char buf[256];
      const char* spec_path = p.options[Spec].arg;
      gemmi::fileptr_t f_spec = gemmi::file_open_or(spec_path, "r", stdin);
      while (fgets(buf, sizeof(buf), f_spec.get()) != NULL) {
        const char* start = gemmi::skip_blank(buf);
        if (*start != '\0' && *start != '#')
          lines.emplace_back(start);
      }
    } else {
      lines.reserve(sizeof(default_spec) / sizeof(default_spec[0]));
      for (const char* line : default_spec)
        lines.emplace_back(line);
    }
    spec = parse_spec(mtz, lines);
    if (spec.empty())
      gemmi::fail("Empty translation spec");
    for (size_t i = 0; i != spec.size(); ++i)
      for (size_t j = i + 1; j != spec.size(); ++j)
        if (spec[i].refln_tag == spec[j].refln_tag)
          gemmi::fail("duplicated output tag: " + spec[i].refln_tag);
    // H, K, L must be the first columns in MTZ and are required in _refln
    for (int i = 2; i != -1; --i)
      if (!gemmi::in_vector_f([&](const Trans& t) { return t.col_idx == i; },
                              spec)) {
        spec.insert(spec.begin(), Trans{i, false, "index_h", "%g "});
        spec.front().refln_tag.back() += i;
      }
  } catch (std::runtime_error& e) {
    fprintf(stderr, "Problem in translation spec: %s\n", e.what());
    return 2;
  }
  try {
    gemmi::fileptr_t f_out = gemmi::file_open_or(cif_path, "w", stdout);
    write_cif(mtz, spec, f_out.get());
  } catch (std::runtime_error& e) {
    fprintf(stderr, "ERROR writing %s: %s\n", cif_path, e.what());
    return 3;
  }
  return 0;
}

// vim:sw=2:ts=2:et:path^=../include,../third_party
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Cloudius Systems
 */

#include "core/reactor.hh"
#include "core/sstring.hh"
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
#include <bitset>
#include <limits>
#include <cctype>

class char_filter {
    using uchar = unsigned char;
public:
    template <typename Pred>
    explicit char_filter(Pred pred) {
        for (unsigned i = 0; i <= std::numeric_limits<uchar>::max(); ++i) {
            _filter.set(i, pred(i));
        }
    }
    bool operator()(char v) const { return _filter.test(uchar(v)); }
private:
    std::bitset<1 << std::numeric_limits<uchar>::digits> _filter;
};

char_filter tchar([](char c) {
    return std::isalnum(c) || std::string("-!#$%&'\\*\\+.^_`|~").find_first_of(c) != std::string::npos;
});
char_filter op_char([](char c) { return std::isupper(c); });
char_filter sp_char([](char c) { return std::isspace(c); });
char_filter nsp_char([](char c) { return !std::isspace(c); });
char_filter digit_char([](char c) { return std::isdigit(c); });

class http_server {
    std::vector<server_socket> _listeners;
public:
    void listen(ipv4_addr addr) {
        listen_options lo;
        lo.reuse_address = true;
        _listeners.push_back(engine.listen(make_ipv4_address(addr), lo));
        do_accepts(_listeners.size() - 1);
    }
    void do_accepts(int which) {
        _listeners[which].accept().then([this, which] (connected_socket fd, socket_address addr) mutable {
            (new connection(*this, std::move(fd), addr))->read().rescue([this] (auto get_ex) {
                try {
                    get_ex();
                } catch (std::exception& ex) {
                    std::cout << "request error " << ex.what() << "\n";
                }
            });
            do_accepts(which);
        }).rescue([] (auto get_ex) {
            try {
                get_ex();
            } catch (std::exception& ex) {
                std::cout << "accept failed: " << ex.what() << "\n";
            }
        });
    }
    class connection {
        connected_socket _fd;
        input_stream<char> _read_buf;
        output_stream<char> _write_buf;
        bool _eof = false;
        static constexpr size_t limit = 4096;
        using tmp_buf = temporary_buffer<char>;
        struct request {
            sstring _method;
            sstring _url;
            sstring _version;
            std::unordered_map<sstring, sstring> _headers;
        };
        sstring _last_header_name;
        struct response {
            sstring _response_line;
            sstring _body;
            std::unordered_map<sstring, sstring> _headers;
        };
        std::unique_ptr<request> _req;
        std::unique_ptr<response> _resp;
        std::queue<std::unique_ptr<response>> _pending_responses;
    public:
        connection(http_server& server, connected_socket&& fd, socket_address addr)
            : _fd(std::move(fd)), _read_buf(_fd.input())
            , _write_buf(_fd.output()) {}
        future<> read() {
            return _read_buf.read_until(limit, '\n').then([this] (tmp_buf start_line) {
                if (!start_line.size()) {
                    _eof = true;
                    maybe_done();
                    return make_ready_future<>();
                }
                _req = std::make_unique<request>();
                size_t pos = 0;
                size_t end = start_line.size();
                while (pos < end && op_char(start_line[pos])) {
                    ++pos;
                }
                if (pos == 0) {
                    return bad(std::move(_req));
                }
                _req->_method = sstring(start_line.begin(), pos);
                if (pos == end || start_line[pos++] != ' ') {
                    return bad(std::move(_req));
                }
                auto url = pos;
                while (pos < end && nsp_char(start_line[pos])) {
                    ++pos;
                }
                _req->_url = sstring(start_line.begin() + url, pos - url);
                if (pos == end || start_line[pos++] != ' ') {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != 'H') {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != 'T') {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != 'T') {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != 'P') {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != '/') {
                    return bad(std::move(_req));
                }
                auto ver = pos;
                if (pos == end || !digit_char(start_line[pos++])) {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != '.') {
                    return bad(std::move(_req));
                }
                if (pos == end || !digit_char(start_line[pos++])) {
                    return bad(std::move(_req));
                }
                _req->_version = sstring(start_line.begin() + ver, pos - ver);
                if (pos == end || start_line[pos++] != '\r') {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != '\n') {
                    return bad(std::move(_req));
                }
                if (pos != end) {
                    return bad(std::move(_req));
                }
                if (_req->_method != "GET") {
                    return bad(std::move(_req));
                }
                return _read_buf.read_until(limit, '\n').then([this] (tmp_buf header) {
                    return parse_header(std::move(header));
                });
            });
        }
        future<> parse_header(tmp_buf header) {
            if (header.size() == 2 && header[0] == '\r' && header[1] == '\n') {
                generate_response(std::move(_req));
                read().rescue([this] (auto get_ex) mutable {
                    try {
                        get_ex();
                    } catch (std::exception& ex) {
                        std::cout << "read failed with " << ex.what() << "\n";
                        this->maybe_done();
                    }
                });
                return make_ready_future<>();
            }
            size_t pos = 0;
            size_t end = header.size();
            if (end < 2 || header[end-2] != '\r' || header[end-1] != '\n') {
                return bad(std::move(_req));
            }
            while (tchar(header[pos])) {
                ++pos;
            }
            if (pos) {
                sstring name = sstring(header.begin(), pos);
                while (pos != end && sp_char(header[pos])) {
                    ++pos;
                }
                if (pos == end || header[pos++] != ':') {
                    return bad(std::move(_req));
                }
                while (pos != end && sp_char(header[pos])) {
                    ++pos;
                }
                while (pos != end && sp_char(header[end-1])) {
                    --end;
                }
                sstring value = sstring(header.begin() + pos, end - pos);
                _req->_headers[name] = std::move(value);
                _last_header_name = std::move(name);
            } else {
                while (sp_char(header[pos])) {
                    ++pos;
                }
                while (pos != end && sp_char(header[end-1])) {
                    --end;
                }
                if (!pos || end == pos) {
                    return bad(std::move(_req));
                }
                _req->_headers[_last_header_name] += " ";
                _req->_headers[_last_header_name] += sstring(header.begin() + pos, end - pos);
            }
            return _read_buf.read_until(limit, '\n').then([this] (tmp_buf header) {
                return parse_header(std::move(header));
            });
        }
        future<> bad(std::unique_ptr<request> req) {
            auto resp = std::make_unique<response>();
            resp->_response_line = "HTTP/1.1 400 BAD REQUEST\r\n\r\n";
            respond(std::move(resp));
            _eof = true;
            throw std::runtime_error("failed to parse request");
        }
        void respond(std::unique_ptr<response> resp) {
            if (!_resp) {
                _resp = std::move(resp);
                start_response();
            } else {
                _pending_responses.push(std::move(resp));
            }
        }
        void start_response() {
            _resp->_headers["Content-Length"] = to_sstring(_resp->_body.size());
            _write_buf.write(_resp->_response_line.begin(), _resp->_response_line.size()).then(
                    [this] (size_t n) mutable {
                return write_response_headers(_resp->_headers.begin());
            }).then([this] (size_t done) {
                return _write_buf.write("\r\n", 2);
            }).then([this] (size_t done) mutable {
                return write_body();
            }).then([this] (size_t done) {
                return _write_buf.flush();
            }).then([this] (bool done) {
                _resp.reset();
                if (!_pending_responses.empty()) {
                    _resp = std::move(_pending_responses.front());
                    _pending_responses.pop();
                    start_response();
                } else {
                    maybe_done();
                }
            });
        }
        future<size_t> write_response_headers(std::unordered_map<sstring, sstring>::iterator hi) {
            if (hi == _resp->_headers.end()) {
                return make_ready_future<size_t>(0);
            }
            return _write_buf.write(hi->first.begin(), hi->first.size()).then(
                    [hi, this] (size_t done) mutable {
                return _write_buf.write(": ", 2);
            }).then([hi, this] (size_t done) mutable {
                return _write_buf.write(hi->second.begin(), hi->second.size());
            }).then([hi, this] (size_t done) mutable {
                return _write_buf.write("\r\n", 2);
            }).then([hi, this] (size_t done) mutable {
                return write_response_headers(++hi);
            });
        }
        void generate_response(std::unique_ptr<request> req) {
            auto resp = std::make_unique<response>();
            resp->_response_line = "HTTP/1.1 200 OK\r\n";
            resp->_headers["Content-Type"] = "text/html";
            resp->_body = "<title>this is the future</title>
Future!!
";
            respond(std::move(resp));
        }
        future<size_t> write_body() {
            return _write_buf.write(_resp->_body.begin(), _resp->_body.size());
        }
        void maybe_done() {
            if (_eof && !_req && !_resp && _pending_responses.empty()) {
                delete this;
            }
        }
    };
};

int main(int ac, char** av) {
    http_server server;
    namespace bpo = boost::program_options;
    bpo::options_description opts;
    opts.add_options()
       ("help", "show help message")
       ;
    opts.add(reactor::get_options_description());
    bpo::variables_map configuration;
    bpo::store(bpo::command_line_parser(ac, av).options(opts).run(), configuration);
    bpo::notify(configuration);
    if (configuration.count("help")) {
        std::cout << opts << "\n";
        return 1;
    }
    engine.configure(configuration);
    engine.when_started().then([&server] {
        uint16_t port = 10000;
        std::cout << "Seastar HTTP server listening on port " << port << " ...\n";
        server.listen({{}, port});
    });
    engine.run();
    return 0;
}

<commit_msg>Run http with smp<commit_after>/*
 * Copyright 2014 Cloudius Systems
 */

#include "core/reactor.hh"
#include "core/sstring.hh"
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
#include <bitset>
#include <limits>
#include <cctype>

class char_filter {
    using uchar = unsigned char;
public:
    template <typename Pred>
    explicit char_filter(Pred pred) {
        for (unsigned i = 0; i <= std::numeric_limits<uchar>::max(); ++i) {
            _filter.set(i, pred(i));
        }
    }
    bool operator()(char v) const { return _filter.test(uchar(v)); }
private:
    std::bitset<1 << std::numeric_limits<uchar>::digits> _filter;
};

char_filter tchar([](char c) {
    return std::isalnum(c) || std::string("-!#$%&'\\*\\+.^_`|~").find_first_of(c) != std::string::npos;
});
char_filter op_char([](char c) { return std::isupper(c); });
char_filter sp_char([](char c) { return std::isspace(c); });
char_filter nsp_char([](char c) { return !std::isspace(c); });
char_filter digit_char([](char c) { return std::isdigit(c); });

class http_server {
    std::vector<server_socket> _listeners;
public:
    void listen(ipv4_addr addr) {
        listen_options lo;
        lo.reuse_address = true;
        _listeners.push_back(engine.listen(make_ipv4_address(addr), lo));
        do_accepts(_listeners.size() - 1);
    }
    void do_accepts(int which) {
        _listeners[which].accept().then([this, which] (connected_socket fd, socket_address addr) mutable {
            (new connection(*this, std::move(fd), addr))->read().rescue([this] (auto get_ex) {
                try {
                    get_ex();
                } catch (std::exception& ex) {
                    std::cout << "request error " << ex.what() << "\n";
                }
            });
            do_accepts(which);
        }).rescue([] (auto get_ex) {
            try {
                get_ex();
            } catch (std::exception& ex) {
                std::cout << "accept failed: " << ex.what() << "\n";
            }
        });
    }
    class connection {
        connected_socket _fd;
        input_stream<char> _read_buf;
        output_stream<char> _write_buf;
        bool _eof = false;
        static constexpr size_t limit = 4096;
        using tmp_buf = temporary_buffer<char>;
        struct request {
            sstring _method;
            sstring _url;
            sstring _version;
            std::unordered_map<sstring, sstring> _headers;
        };
        sstring _last_header_name;
        struct response {
            sstring _response_line;
            sstring _body;
            std::unordered_map<sstring, sstring> _headers;
        };
        std::unique_ptr<request> _req;
        std::unique_ptr<response> _resp;
        std::queue<std::unique_ptr<response>> _pending_responses;
    public:
        connection(http_server& server, connected_socket&& fd, socket_address addr)
            : _fd(std::move(fd)), _read_buf(_fd.input())
            , _write_buf(_fd.output()) {}
        future<> read() {
            return _read_buf.read_until(limit, '\n').then([this] (tmp_buf start_line) {
                if (!start_line.size()) {
                    _eof = true;
                    maybe_done();
                    return make_ready_future<>();
                }
                _req = std::make_unique<request>();
                size_t pos = 0;
                size_t end = start_line.size();
                while (pos < end && op_char(start_line[pos])) {
                    ++pos;
                }
                if (pos == 0) {
                    return bad(std::move(_req));
                }
                _req->_method = sstring(start_line.begin(), pos);
                if (pos == end || start_line[pos++] != ' ') {
                    return bad(std::move(_req));
                }
                auto url = pos;
                while (pos < end && nsp_char(start_line[pos])) {
                    ++pos;
                }
                _req->_url = sstring(start_line.begin() + url, pos - url);
                if (pos == end || start_line[pos++] != ' ') {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != 'H') {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != 'T') {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != 'T') {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != 'P') {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != '/') {
                    return bad(std::move(_req));
                }
                auto ver = pos;
                if (pos == end || !digit_char(start_line[pos++])) {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != '.') {
                    return bad(std::move(_req));
                }
                if (pos == end || !digit_char(start_line[pos++])) {
                    return bad(std::move(_req));
                }
                _req->_version = sstring(start_line.begin() + ver, pos - ver);
                if (pos == end || start_line[pos++] != '\r') {
                    return bad(std::move(_req));
                }
                if (pos == end || start_line[pos++] != '\n') {
                    return bad(std::move(_req));
                }
                if (pos != end) {
                    return bad(std::move(_req));
                }
                if (_req->_method != "GET") {
                    return bad(std::move(_req));
                }
                return _read_buf.read_until(limit, '\n').then([this] (tmp_buf header) {
                    return parse_header(std::move(header));
                });
            });
        }
        future<> parse_header(tmp_buf header) {
            if (header.size() == 2 && header[0] == '\r' && header[1] == '\n') {
                generate_response(std::move(_req));
                read().rescue([this] (auto get_ex) mutable {
                    try {
                        get_ex();
                    } catch (std::exception& ex) {
                        std::cout << "read failed with " << ex.what() << "\n";
                        this->maybe_done();
                    }
                });
                return make_ready_future<>();
            }
            size_t pos = 0;
            size_t end = header.size();
            if (end < 2 || header[end-2] != '\r' || header[end-1] != '\n') {
                return bad(std::move(_req));
            }
            while (tchar(header[pos])) {
                ++pos;
            }
            if (pos) {
                sstring name = sstring(header.begin(), pos);
                while (pos != end && sp_char(header[pos])) {
                    ++pos;
                }
                if (pos == end || header[pos++] != ':') {
                    return bad(std::move(_req));
                }
                while (pos != end && sp_char(header[pos])) {
                    ++pos;
                }
                while (pos != end && sp_char(header[end-1])) {
                    --end;
                }
                sstring value = sstring(header.begin() + pos, end - pos);
                _req->_headers[name] = std::move(value);
                _last_header_name = std::move(name);
            } else {
                while (sp_char(header[pos])) {
                    ++pos;
                }
                while (pos != end && sp_char(header[end-1])) {
                    --end;
                }
                if (!pos || end == pos) {
                    return bad(std::move(_req));
                }
                _req->_headers[_last_header_name] += " ";
                _req->_headers[_last_header_name] += sstring(header.begin() + pos, end - pos);
            }
            return _read_buf.read_until(limit, '\n').then([this] (tmp_buf header) {
                return parse_header(std::move(header));
            });
        }
        future<> bad(std::unique_ptr<request> req) {
            auto resp = std::make_unique<response>();
            resp->_response_line = "HTTP/1.1 400 BAD REQUEST\r\n\r\n";
            respond(std::move(resp));
            _eof = true;
            throw std::runtime_error("failed to parse request");
        }
        void respond(std::unique_ptr<response> resp) {
            if (!_resp) {
                _resp = std::move(resp);
                start_response();
            } else {
                _pending_responses.push(std::move(resp));
            }
        }
        void start_response() {
            _resp->_headers["Content-Length"] = to_sstring(_resp->_body.size());
            _write_buf.write(_resp->_response_line.begin(), _resp->_response_line.size()).then(
                    [this] (size_t n) mutable {
                return write_response_headers(_resp->_headers.begin());
            }).then([this] (size_t done) {
                return _write_buf.write("\r\n", 2);
            }).then([this] (size_t done) mutable {
                return write_body();
            }).then([this] (size_t done) {
                return _write_buf.flush();
            }).then([this] (bool done) {
                _resp.reset();
                if (!_pending_responses.empty()) {
                    _resp = std::move(_pending_responses.front());
                    _pending_responses.pop();
                    start_response();
                } else {
                    maybe_done();
                }
            });
        }
        future<size_t> write_response_headers(std::unordered_map<sstring, sstring>::iterator hi) {
            if (hi == _resp->_headers.end()) {
                return make_ready_future<size_t>(0);
            }
            return _write_buf.write(hi->first.begin(), hi->first.size()).then(
                    [hi, this] (size_t done) mutable {
                return _write_buf.write(": ", 2);
            }).then([hi, this] (size_t done) mutable {
                return _write_buf.write(hi->second.begin(), hi->second.size());
            }).then([hi, this] (size_t done) mutable {
                return _write_buf.write("\r\n", 2);
            }).then([hi, this] (size_t done) mutable {
                return write_response_headers(++hi);
            });
        }
        void generate_response(std::unique_ptr<request> req) {
            auto resp = std::make_unique<response>();
            resp->_response_line = "HTTP/1.1 200 OK\r\n";
            resp->_headers["Content-Type"] = "text/html";
            resp->_body = "<title>this is the future</title>
Future!!
";
            respond(std::move(resp));
        }
        future<size_t> write_body() {
            return _write_buf.write(_resp->_body.begin(), _resp->_body.size());
        }
        void maybe_done() {
            if (_eof && !_req && !_resp && _pending_responses.empty()) {
                delete this;
            }
        }
    };
};

int main(int ac, char** av) {
    http_server server;
    namespace bpo = boost::program_options;
    bpo::options_description opts;
    opts.add_options()
       ("help", "show help message")
       ;
    opts.add(reactor::get_options_description());
    opts.add(smp::get_options_description());
    bpo::variables_map configuration;
    bpo::store(bpo::command_line_parser(ac, av).options(opts).run(), configuration);
    bpo::notify(configuration);
    if (configuration.count("help")) {
        std::cout << opts << "\n";
        return 1;
    }
    smp::configure(configuration);
    engine.when_started().then([&server] {
        uint16_t port = 10000;
        std::cout << "Seastar HTTP server listening on port " << port << " ...\n";
        server.listen({{}, port});
    });
    engine.run();
    return 0;
}

<|endoftext|>


<commit_before>//
//                 INTEL CORPORATION PROPRIETARY INFORMATION
//
//    This software is supplied under the terms of a license agreement or
//    nondisclosure agreement with Intel Corporation and may not be copied
//    or disclosed except in accordance with the terms of that agreement.
//    Copyright (C) 2014 Intel Corporation. All Rights Reserved.
//

#include <algorithm>
#include "VolumeViewer.h"
#include "TransferFunctionEditor.h"
#include "LightEditor.h"
#include "SliceWidget.h"
#include "PLYGeometryFile.h"

VolumeViewer::VolumeViewer(const std::vector<std::string> &filenames, bool showFrameRate) : renderer(NULL), transferFunction(NULL), osprayWindow(NULL), autoRotationRate(0.025f) {

    //! Default window size.
    resize(1024, 768);

    //! Create an OSPRay renderer.
    renderer = ospNewRenderer("raycast_volume_renderer");  exitOnCondition(renderer == NULL, "could not create OSPRay renderer object");

    //! Create an OSPRay window and set it as the central widget, but don't let it start rendering until we're done with setup.
    osprayWindow = new QOSPRayWindow(this, renderer, showFrameRate);  setCentralWidget(osprayWindow);

    //! Set the window bounds based on the OSPRay world bounds (always [(0,0,0), (1,1,1)) for volumes).
    osprayWindow->setWorldBounds(osp::box3f(osp::vec3f(0.0f), osp::vec3f(1.0f)));

    //! Create an OSPRay light source.
    light = ospNewLight(NULL, "DirectionalLight");  ospSet3f(light, "direction", 1.0f, -2.0f, -1.0f);  ospSet3f(light, "color", 1.0f, 1.0f, 1.0f);

    //! Set the light source on the renderer.
    ospCommit(light);  ospSetData(renderer, "lights", ospNewData(1, OSP_OBJECT, &light));

    //! Create an OSPRay transfer function.
    transferFunction = ospNewTransferFunction("piecewise_linear");  exitOnCondition(transferFunction == NULL, "could not create OSPRay transfer function object");

    //! Configure the user interface widgets and callbacks.
    initUserInterfaceWidgets();

    //! Commit the transfer function only after the initial colors and alphas have been set (workaround for Qt signalling issue).
    ospCommit(transferFunction);

    //! Create and configure the OSPRay state.
    initObjects(filenames);

    //! Show the window.
    show();

}

void VolumeViewer::autoRotate(bool set) {

    if(osprayWindow == NULL)
        return;

    if(autoRotateAction != NULL)
        autoRotateAction->setChecked(set);

    if(set) {
        osprayWindow->setRotationRate(autoRotationRate);
        osprayWindow->updateGL();
    }
    else {
        osprayWindow->setRotationRate(0.);
    }
}

void VolumeViewer::addSlice(std::string filename) {

    //! Use dynamic geometry model for slices.
    std::vector<OSPModel> dynamicModels;
    dynamicModels.push_back(dynamicModel);

    //! Create a slice widget and add it to the dock. This widget modifies the slice directly.
    SliceWidget * sliceWidget = new SliceWidget(dynamicModels, osp::box3f(osp::vec3f(0.0f), osp::vec3f(1.0f)));
    connect(sliceWidget, SIGNAL(sliceChanged()), this, SLOT(render()));
    sliceWidgetsLayout.addWidget(sliceWidget);

    //! Load state from file if specified.
    if(!filename.empty())
        sliceWidget->load(filename);
}

void VolumeViewer::addGeometry(std::string filename) {

    //! For now we assume PLY geometry files. Later we can support other geometry formats.

    //! Get filename if not specified.
    if(filename.empty())
        filename = QFileDialog::getOpenFileName(this, tr("Load geometry"), ".", "PLY files (*.ply)").toStdString();

    if(filename.empty())
        return;

    //! Load the geometry.
    PLYGeometryFile geometryFile(filename);

    //! Add the OSPRay triangle mesh to all models.
    OSPTriangleMesh triangleMesh = geometryFile.getOSPTriangleMesh();

    for(unsigned int i=0; i<models.size(); i++) {
        ospAddGeometry(models[i], triangleMesh);
        ospCommit(models[i]);
    }
}

void VolumeViewer::importObjectsFromFile(const std::string &filename) {

    //! Create an OSPRay model.
    OSPModel model = ospNewModel();

    //! Load OSPRay objects from a file.
    OSPObjectCatalog catalog = ospImportObjects(filename.c_str());

    //! For now we set the same transfer function on all volumes.
    for (size_t i=0 ; catalog->entries[i] ; i++) if (catalog->entries[i]->type == OSP_VOLUME) ospSetObject(catalog->entries[i]->object, "transferFunction", transferFunction);

    //! Add the loaded volume(s) to the model.
    for (size_t i=0 ; catalog->entries[i] ; i++) if (catalog->entries[i]->type == OSP_VOLUME) ospAddVolume(model, (OSPVolume) catalog->entries[i]->object);

    //! Keep vector of all loaded volume(s).
    for (size_t i=0 ; catalog->entries[i] ; i++) if (catalog->entries[i]->type == OSP_VOLUME) volumes.push_back((OSPVolume) catalog->entries[i]->object);

    //! Commit the OSPRay object state.
    ospCommitCatalog(catalog);  ospCommit(model);  models.push_back(model);

}

void VolumeViewer::initObjects(const std::vector<std::string> &filenames) {

    //! Create model for dynamic geometry.
    dynamicModel = ospNewModel();
    ospCommit(dynamicModel);

    //! Set the dynamic model on the renderer.
    ospSetObject(renderer, "dynamic_model", dynamicModel);

    //! Load OSPRay objects from files.
    for (size_t i=0 ; i < filenames.size() ; i++) importObjectsFromFile(filenames[i]);

}

void VolumeViewer::initUserInterfaceWidgets() {

    //! Add the "auto rotate" widget and callback.
    QToolBar *toolbar = addToolBar("toolbar");
    autoRotateAction = new QAction("Auto rotate", this);
    autoRotateAction->setCheckable(true);
    connect(autoRotateAction, SIGNAL(toggled(bool)), this, SLOT(autoRotate(bool)));
    toolbar->addAction(autoRotateAction);

    //! Add the "next timestep" widget and callback.
    QAction *nextTimeStepAction = new QAction("Next timestep", this);
    connect(nextTimeStepAction, SIGNAL(triggered()), this, SLOT(nextTimeStep()));
    toolbar->addAction(nextTimeStepAction);

    //! Add the "play timesteps" widget and callback.
    QAction *playTimeStepsAction = new QAction("Play timesteps", this);
    playTimeStepsAction->setCheckable(true);
    connect(playTimeStepsAction, SIGNAL(toggled(bool)), this, SLOT(playTimeSteps(bool)));
    toolbar->addAction(playTimeStepsAction);

    //! Connect the "play timesteps" timer.
    connect(&playTimeStepsTimer, SIGNAL(timeout()), this, SLOT(nextTimeStep()));

    //! Add the "add slice" widget and callback.
    QAction *addSliceAction = new QAction("Add slice", this);
    connect(addSliceAction, SIGNAL(triggered()), this, SLOT(addSlice()));
    toolbar->addAction(addSliceAction);

    //! Add the "add geometry" widget and callback.
    QAction *addGeometryAction = new QAction("Add geometry", this);
    connect(addGeometryAction, SIGNAL(triggered()), this, SLOT(addGeometry()));
    toolbar->addAction(addGeometryAction);

    //! Create the transfer function editor dock widget, this widget modifies the transfer function directly.
    QDockWidget *transferFunctionEditorDockWidget = new QDockWidget("Transfer Function Editor", this);
    transferFunctionEditor = new TransferFunctionEditor(transferFunction);
    transferFunctionEditorDockWidget->setWidget(transferFunctionEditor);
    connect(transferFunctionEditor, SIGNAL(transferFunctionChanged()), this, SLOT(commitVolumes()));
    connect(transferFunctionEditor, SIGNAL(transferFunctionChanged()), this, SLOT(render()));
    addDockWidget(Qt::LeftDockWidgetArea, transferFunctionEditorDockWidget);

    //! Set the transfer function editor widget to its minimum allowed height, to leave room for other dock widgets.
    transferFunctionEditor->setMaximumHeight(transferFunctionEditor->minimumSize().height());

    //! Create the light editor dock widget, this widget modifies the light directly.
    QDockWidget *lightEditorDockWidget = new QDockWidget("Light Editor", this);
    LightEditor *lightEditor = new LightEditor(light);
    lightEditorDockWidget->setWidget(lightEditor);
    connect(lightEditor, SIGNAL(lightChanged()), this, SLOT(render()));
    addDockWidget(Qt::LeftDockWidgetArea, lightEditorDockWidget);

    //! Create a scrollable dock widget for any added slices.
    QDockWidget *slicesDockWidget = new QDockWidget("Slices", this);
    QScrollArea *slicesScrollArea = new QScrollArea();
    QWidget *slicesWidget = new QWidget();
    slicesWidget->setLayout(&sliceWidgetsLayout);
    slicesScrollArea->setWidget(slicesWidget);
    slicesScrollArea->setWidgetResizable(true);
    slicesDockWidget->setWidget(slicesScrollArea);
    addDockWidget(Qt::LeftDockWidgetArea, slicesDockWidget);

}
<commit_msg>disabling light editor widget in volume viewer, pending UI improvements.<commit_after>//
//                 INTEL CORPORATION PROPRIETARY INFORMATION
//
//    This software is supplied under the terms of a license agreement or
//    nondisclosure agreement with Intel Corporation and may not be copied
//    or disclosed except in accordance with the terms of that agreement.
//    Copyright (C) 2014 Intel Corporation. All Rights Reserved.
//

#include <algorithm>
#include "VolumeViewer.h"
#include "TransferFunctionEditor.h"
#include "LightEditor.h"
#include "SliceWidget.h"
#include "PLYGeometryFile.h"

VolumeViewer::VolumeViewer(const std::vector<std::string> &filenames, bool showFrameRate) : renderer(NULL), transferFunction(NULL), osprayWindow(NULL), autoRotationRate(0.025f) {

    //! Default window size.
    resize(1024, 768);

    //! Create an OSPRay renderer.
    renderer = ospNewRenderer("raycast_volume_renderer");  exitOnCondition(renderer == NULL, "could not create OSPRay renderer object");

    //! Create an OSPRay window and set it as the central widget, but don't let it start rendering until we're done with setup.
    osprayWindow = new QOSPRayWindow(this, renderer, showFrameRate);  setCentralWidget(osprayWindow);

    //! Set the window bounds based on the OSPRay world bounds (always [(0,0,0), (1,1,1)) for volumes).
    osprayWindow->setWorldBounds(osp::box3f(osp::vec3f(0.0f), osp::vec3f(1.0f)));

    //! Create an OSPRay light source.
    light = ospNewLight(NULL, "DirectionalLight");  ospSet3f(light, "direction", 1.0f, -2.0f, -1.0f);  ospSet3f(light, "color", 1.0f, 1.0f, 1.0f);

    //! Set the light source on the renderer.
    ospCommit(light);  ospSetData(renderer, "lights", ospNewData(1, OSP_OBJECT, &light));

    //! Create an OSPRay transfer function.
    transferFunction = ospNewTransferFunction("piecewise_linear");  exitOnCondition(transferFunction == NULL, "could not create OSPRay transfer function object");

    //! Configure the user interface widgets and callbacks.
    initUserInterfaceWidgets();

    //! Commit the transfer function only after the initial colors and alphas have been set (workaround for Qt signalling issue).
    ospCommit(transferFunction);

    //! Create and configure the OSPRay state.
    initObjects(filenames);

    //! Show the window.
    show();

}

void VolumeViewer::autoRotate(bool set) {

    if(osprayWindow == NULL)
        return;

    if(autoRotateAction != NULL)
        autoRotateAction->setChecked(set);

    if(set) {
        osprayWindow->setRotationRate(autoRotationRate);
        osprayWindow->updateGL();
    }
    else {
        osprayWindow->setRotationRate(0.);
    }
}

void VolumeViewer::addSlice(std::string filename) {

    //! Use dynamic geometry model for slices.
    std::vector<OSPModel> dynamicModels;
    dynamicModels.push_back(dynamicModel);

    //! Create a slice widget and add it to the dock. This widget modifies the slice directly.
    SliceWidget * sliceWidget = new SliceWidget(dynamicModels, osp::box3f(osp::vec3f(0.0f), osp::vec3f(1.0f)));
    connect(sliceWidget, SIGNAL(sliceChanged()), this, SLOT(render()));
    sliceWidgetsLayout.addWidget(sliceWidget);

    //! Load state from file if specified.
    if(!filename.empty())
        sliceWidget->load(filename);
}

void VolumeViewer::addGeometry(std::string filename) {

    //! For now we assume PLY geometry files. Later we can support other geometry formats.

    //! Get filename if not specified.
    if(filename.empty())
        filename = QFileDialog::getOpenFileName(this, tr("Load geometry"), ".", "PLY files (*.ply)").toStdString();

    if(filename.empty())
        return;

    //! Load the geometry.
    PLYGeometryFile geometryFile(filename);

    //! Add the OSPRay triangle mesh to all models.
    OSPTriangleMesh triangleMesh = geometryFile.getOSPTriangleMesh();

    for(unsigned int i=0; i<models.size(); i++) {
        ospAddGeometry(models[i], triangleMesh);
        ospCommit(models[i]);
    }
}

void VolumeViewer::importObjectsFromFile(const std::string &filename) {

    //! Create an OSPRay model.
    OSPModel model = ospNewModel();

    //! Load OSPRay objects from a file.
    OSPObjectCatalog catalog = ospImportObjects(filename.c_str());

    //! For now we set the same transfer function on all volumes.
    for (size_t i=0 ; catalog->entries[i] ; i++) if (catalog->entries[i]->type == OSP_VOLUME) ospSetObject(catalog->entries[i]->object, "transferFunction", transferFunction);

    //! Add the loaded volume(s) to the model.
    for (size_t i=0 ; catalog->entries[i] ; i++) if (catalog->entries[i]->type == OSP_VOLUME) ospAddVolume(model, (OSPVolume) catalog->entries[i]->object);

    //! Keep vector of all loaded volume(s).
    for (size_t i=0 ; catalog->entries[i] ; i++) if (catalog->entries[i]->type == OSP_VOLUME) volumes.push_back((OSPVolume) catalog->entries[i]->object);

    //! Commit the OSPRay object state.
    ospCommitCatalog(catalog);  ospCommit(model);  models.push_back(model);

}

void VolumeViewer::initObjects(const std::vector<std::string> &filenames) {

    //! Create model for dynamic geometry.
    dynamicModel = ospNewModel();
    ospCommit(dynamicModel);

    //! Set the dynamic model on the renderer.
    ospSetObject(renderer, "dynamic_model", dynamicModel);

    //! Load OSPRay objects from files.
    for (size_t i=0 ; i < filenames.size() ; i++) importObjectsFromFile(filenames[i]);

}

void VolumeViewer::initUserInterfaceWidgets() {

    //! Add the "auto rotate" widget and callback.
    QToolBar *toolbar = addToolBar("toolbar");
    autoRotateAction = new QAction("Auto rotate", this);
    autoRotateAction->setCheckable(true);
    connect(autoRotateAction, SIGNAL(toggled(bool)), this, SLOT(autoRotate(bool)));
    toolbar->addAction(autoRotateAction);

    //! Add the "next timestep" widget and callback.
    QAction *nextTimeStepAction = new QAction("Next timestep", this);
    connect(nextTimeStepAction, SIGNAL(triggered()), this, SLOT(nextTimeStep()));
    toolbar->addAction(nextTimeStepAction);

    //! Add the "play timesteps" widget and callback.
    QAction *playTimeStepsAction = new QAction("Play timesteps", this);
    playTimeStepsAction->setCheckable(true);
    connect(playTimeStepsAction, SIGNAL(toggled(bool)), this, SLOT(playTimeSteps(bool)));
    toolbar->addAction(playTimeStepsAction);

    //! Connect the "play timesteps" timer.
    connect(&playTimeStepsTimer, SIGNAL(timeout()), this, SLOT(nextTimeStep()));

    //! Add the "add slice" widget and callback.
    QAction *addSliceAction = new QAction("Add slice", this);
    connect(addSliceAction, SIGNAL(triggered()), this, SLOT(addSlice()));
    toolbar->addAction(addSliceAction);

    //! Add the "add geometry" widget and callback.
    QAction *addGeometryAction = new QAction("Add geometry", this);
    connect(addGeometryAction, SIGNAL(triggered()), this, SLOT(addGeometry()));
    toolbar->addAction(addGeometryAction);

    //! Create the transfer function editor dock widget, this widget modifies the transfer function directly.
    QDockWidget *transferFunctionEditorDockWidget = new QDockWidget("Transfer Function Editor", this);
    transferFunctionEditor = new TransferFunctionEditor(transferFunction);
    transferFunctionEditorDockWidget->setWidget(transferFunctionEditor);
    connect(transferFunctionEditor, SIGNAL(transferFunctionChanged()), this, SLOT(commitVolumes()));
    connect(transferFunctionEditor, SIGNAL(transferFunctionChanged()), this, SLOT(render()));
    addDockWidget(Qt::LeftDockWidgetArea, transferFunctionEditorDockWidget);

    //! Set the transfer function editor widget to its minimum allowed height, to leave room for other dock widgets.
    transferFunctionEditor->setMaximumHeight(transferFunctionEditor->minimumSize().height());

    //! Create the light editor dock widget, this widget modifies the light directly.
    //! Disable for now pending UI improvements...
    /* QDockWidget *lightEditorDockWidget = new QDockWidget("Light Editor", this);
    LightEditor *lightEditor = new LightEditor(light);
    lightEditorDockWidget->setWidget(lightEditor);
    connect(lightEditor, SIGNAL(lightChanged()), this, SLOT(render()));
    addDockWidget(Qt::LeftDockWidgetArea, lightEditorDockWidget); */

    //! Create a scrollable dock widget for any added slices.
    QDockWidget *slicesDockWidget = new QDockWidget("Slices", this);
    QScrollArea *slicesScrollArea = new QScrollArea();
    QWidget *slicesWidget = new QWidget();
    slicesWidget->setLayout(&sliceWidgetsLayout);
    slicesScrollArea->setWidget(slicesWidget);
    slicesScrollArea->setWidgetResizable(true);
    slicesDockWidget->setWidget(slicesScrollArea);
    addDockWidget(Qt::LeftDockWidgetArea, slicesDockWidget);

}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: ik_module.hxx,v $
 *
 *  $Revision: 1.1 $
 *
 *  last change: $Author: np $ $Date: 2002-11-01 17:11:59 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  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., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef ARY_IDL_IK_MODULE_HXX
#define ARY_IDL_IK_MODULE_HXX

// USED SERVICES
    // BASE CLASSES
#include <ary/idl/ik_ce.hxx>
    // COMPONENTS
    // PARAMETERS

namespace ary
{
namespace idl
{

class CePilot;

namespace ifc_module
{

using ifc_ce::Dyn_CeIterator;
using ifc_ce::DocText;

struct attr : public ifc_ce::attr
{
    // KORR
    //   This has to be changed that way, that the differencing takes place
    //   within hfi_module.cxx and not here.
    //   So the class CePilot is not needed here, etc.
    //   Too much scope pollution.
    static void         Get_AllChildrenSeparated(
                            std::vector< const CodeEntity* > & o_nestedModules,
                            std::vector< const CodeEntity* > & o_services,
                            std::vector< const CodeEntity* > & o_interfaces,
                            std::vector< const CodeEntity* > & o_structs,
                            std::vector< const CodeEntity* > & o_exceptions,
                            std::vector< const CodeEntity* > & o_enums,
                            std::vector< const CodeEntity* > & o_typedefs,
                            std::vector< const CodeEntity* > & o_constantGroups,
                            std::vector< const CodeEntity* > & o_singletons,
                            const CePilot &     i_pilot,
                            const CodeEntity &  i_ce );

    static void         Get_SubModules(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_Services(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_Interfaces(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_Structs(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_Exceptions(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_Enums(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_Typedefs(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_ConstantsGroups(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
};

struct xref : public ifc_ce::xref
{
};

struct doc : public ifc_ce::doc
{
};

}   // namespace ifc_module

}   // namespace idl
}   // namespace ary

#endif
<commit_msg>INTEGRATION: CWS ooo19126 (1.1.132); FILE MERGED 2005/09/05 13:09:26 rt 1.1.132.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: ik_module.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 16:16:18 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef ARY_IDL_IK_MODULE_HXX
#define ARY_IDL_IK_MODULE_HXX

// USED SERVICES
    // BASE CLASSES
#include <ary/idl/ik_ce.hxx>
    // COMPONENTS
    // PARAMETERS

namespace ary
{
namespace idl
{

class CePilot;

namespace ifc_module
{

using ifc_ce::Dyn_CeIterator;
using ifc_ce::DocText;

struct attr : public ifc_ce::attr
{
    // KORR
    //   This has to be changed that way, that the differencing takes place
    //   within hfi_module.cxx and not here.
    //   So the class CePilot is not needed here, etc.
    //   Too much scope pollution.
    static void         Get_AllChildrenSeparated(
                            std::vector< const CodeEntity* > & o_nestedModules,
                            std::vector< const CodeEntity* > & o_services,
                            std::vector< const CodeEntity* > & o_interfaces,
                            std::vector< const CodeEntity* > & o_structs,
                            std::vector< const CodeEntity* > & o_exceptions,
                            std::vector< const CodeEntity* > & o_enums,
                            std::vector< const CodeEntity* > & o_typedefs,
                            std::vector< const CodeEntity* > & o_constantGroups,
                            std::vector< const CodeEntity* > & o_singletons,
                            const CePilot &     i_pilot,
                            const CodeEntity &  i_ce );

    static void         Get_SubModules(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_Services(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_Interfaces(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_Structs(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_Exceptions(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_Enums(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_Typedefs(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
    static void         Get_ConstantsGroups(
                            Dyn_CeIterator &    o_result,
                            const CodeEntity &  i_ce );
};

struct xref : public ifc_ce::xref
{
};

struct doc : public ifc_ce::doc
{
};

}   // namespace ifc_module

}   // namespace idl
}   // namespace ary

#endif
<|endoftext|>