<commit_before>// Filename: collisionPlane.cxx
// Created by:  drose (25Apr00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#include "collisionPlane.h"
#include "collisionHandler.h"
#include "collisionEntry.h"
#include "collisionSphere.h"
#include "collisionLine.h"
#include "collisionRay.h"
#include "collisionSegment.h"
#include "config_collide.h"
#include "pointerToArray.h"
#include "geomNode.h"
#include "geom.h"
#include "datagram.h"
#include "datagramIterator.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "boundingPlane.h"
#include "geom.h"
#include "geomTrifans.h"
#include "geomLinestrips.h"
#include "geomVertexWriter.h"

PStatCollector CollisionPlane::_volume_pcollector("Collision Volumes:CollisionPlane");
PStatCollector CollisionPlane::_test_pcollector("Collision Tests:CollisionPlane");
TypeHandle CollisionPlane::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::make_copy
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
CollisionSolid *CollisionPlane::
make_copy() {
  return new CollisionPlane(*this);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::xform
//       Access: Public, Virtual
//  Description: Transforms the solid by the indicated matrix.
////////////////////////////////////////////////////////////////////
void CollisionPlane::
xform(const LMatrix4f &mat) {
  _plane = _plane * mat;
  CollisionSolid::xform(mat);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::get_collision_origin
//       Access: Public, Virtual
//  Description: Returns the point in space deemed to be the "origin"
//               of the solid for collision purposes.  The closest
//               intersection point to this origin point is considered
//               to be the most significant.
////////////////////////////////////////////////////////////////////
LPoint3f CollisionPlane::
get_collision_origin() const {
  // No real sensible origin exists for a plane.  We return 0, 0, 0,
  // without even bothering to ensure that that point exists on the
  // plane.
  return LPoint3f::origin();
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::get_volume_pcollector
//       Access: Public, Virtual
//  Description: Returns a PStatCollector that is used to count the
//               number of bounding volume tests made against a solid
//               of this type in a given frame.
////////////////////////////////////////////////////////////////////
PStatCollector CollisionPlane::
get_volume_pcollector() {
  return _volume_pcollector;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::get_test_pcollector
//       Access: Public, Virtual
//  Description: Returns a PStatCollector that is used to count the
//               number of intersection tests made against a solid
//               of this type in a given frame.
////////////////////////////////////////////////////////////////////
PStatCollector CollisionPlane::
get_test_pcollector() {
  return _test_pcollector;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::output
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
void CollisionPlane::
output(ostream &out) const {
  out << "cplane, (" << _plane << ")";
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::compute_internal_bounds
//       Access: Protected, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
PT(BoundingVolume) CollisionPlane::
compute_internal_bounds() const {
  return new BoundingPlane(_plane);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::test_intersection_from_sphere
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
PT(CollisionEntry) CollisionPlane::
test_intersection_from_sphere(const CollisionEntry &entry) const {
  const CollisionSphere *sphere;
  DCAST_INTO_R(sphere, entry.get_from(), 0);

  const LMatrix4f &wrt_mat = entry.get_wrt_mat();

  LPoint3f from_center = sphere->get_center() * wrt_mat;
  LVector3f from_radius_v =
    LVector3f(sphere->get_radius(), 0.0f, 0.0f) * wrt_mat;
  float from_radius = length(from_radius_v);

  float dist = dist_to_plane(from_center);
  if (dist > from_radius) {
    // No intersection.
    return NULL;
  }

  if (collide_cat.is_debug()) {
    collide_cat.debug()
      << "intersection detected from " << entry.get_from_node_path() << " into "
      << entry.get_into_node_path() << "\n";
  }
  PT(CollisionEntry) new_entry = new CollisionEntry(entry);

  LVector3f from_normal = get_normal() * entry.get_inv_wrt_mat();

  LVector3f normal = (has_effective_normal() && sphere->get_respect_effective_normal()) ? get_effective_normal() : get_normal();

  new_entry->set_surface_normal(normal);
  new_entry->set_surface_point(from_center - get_normal() * dist);
  new_entry->set_interior_point(from_center - get_normal() * from_radius);

  return new_entry;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::test_intersection_from_line
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
PT(CollisionEntry) CollisionPlane::
test_intersection_from_line(const CollisionEntry &entry) const {
  const CollisionLine *line;
  DCAST_INTO_R(line, entry.get_from(), 0);

  const LMatrix4f &wrt_mat = entry.get_wrt_mat();

  LPoint3f from_origin = line->get_origin() * wrt_mat;
  LVector3f from_direction = line->get_direction() * wrt_mat;

  float t;
  if (!_plane.intersects_line(t, from_origin, from_direction)) {
    // No intersection.
    return NULL;
  }

  if (collide_cat.is_debug()) {
    collide_cat.debug()
      << "intersection detected from " << entry.get_from_node_path()
      << " into " << entry.get_into_node_path() << "\n";
  }
  PT(CollisionEntry) new_entry = new CollisionEntry(entry);

  LPoint3f into_intersection_point = from_origin + t * from_direction;

  LVector3f normal = (has_effective_normal() && line->get_respect_effective_normal()) ? get_effective_normal() : get_normal();

  new_entry->set_surface_normal(normal);
  new_entry->set_surface_point(into_intersection_point);

  return new_entry;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::test_intersection_from_ray
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
PT(CollisionEntry) CollisionPlane::
test_intersection_from_ray(const CollisionEntry &entry) const {
  const CollisionRay *ray;
  DCAST_INTO_R(ray, entry.get_from(), 0);

  const LMatrix4f &wrt_mat = entry.get_wrt_mat();

  LPoint3f from_origin = ray->get_origin() * wrt_mat;
  LVector3f from_direction = ray->get_direction() * wrt_mat;

  float t;
  if (!_plane.intersects_line(t, from_origin, from_direction)) {
    // No intersection.
    return NULL;
  }

  if (t < 0.0f) {
    // The intersection point is before the start of the ray.
    return NULL;
  }

  if (collide_cat.is_debug()) {
    collide_cat.debug()
      << "intersection detected from " << entry.get_from_node_path()
      << " into " << entry.get_into_node_path() << "\n";
  }
  PT(CollisionEntry) new_entry = new CollisionEntry(entry);

  LPoint3f into_intersection_point = from_origin + t * from_direction;

  LVector3f normal = (has_effective_normal() && ray->get_respect_effective_normal()) ? get_effective_normal() : get_normal();

  new_entry->set_surface_normal(normal);
  new_entry->set_surface_point(into_intersection_point);

  return new_entry;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::test_intersection_from_segment
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
PT(CollisionEntry) CollisionPlane::
test_intersection_from_segment(const CollisionEntry &entry) const {
  const CollisionSegment *segment;
  DCAST_INTO_R(segment, entry.get_from(), 0);

  const LMatrix4f &wrt_mat = entry.get_wrt_mat();

  LPoint3f from_a = segment->get_point_a() * wrt_mat;
  LPoint3f from_b = segment->get_point_b() * wrt_mat;
  LVector3f from_direction = from_b - from_a;

  float t;
  if (!_plane.intersects_line(t, from_a, from_direction)) {
    // No intersection.
    return NULL;
  }

  if (t < 0.0f || t > 1.0f) {
    // The intersection point is before the start of the segment or
    // after the end of the segment.
    return NULL;
  }

  if (collide_cat.is_debug()) {
    collide_cat.debug()
      << "intersection detected from " << entry.get_from_node_path()
      << " into " << entry.get_into_node_path() << "\n";
  }
  PT(CollisionEntry) new_entry = new CollisionEntry(entry);

  LPoint3f into_intersection_point = from_a + t * from_direction;

  LVector3f normal = (has_effective_normal() && segment->get_respect_effective_normal()) ? get_effective_normal() : get_normal();

  new_entry->set_surface_normal(normal);
  new_entry->set_surface_point(into_intersection_point);

  return new_entry;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::fill_viz_geom
//       Access: Protected, Virtual
//  Description: Fills the _viz_geom GeomNode up with Geoms suitable
//               for rendering this solid.
////////////////////////////////////////////////////////////////////
void CollisionPlane::
fill_viz_geom() {
  if (collide_cat.is_debug()) {
    collide_cat.debug()
      << "Recomputing viz for " << *this << "\n";
  }

  // Since we can't represent an infinite plane, we'll have to be
  // satisfied with drawing a big polygon.  Choose four points on the
  // plane to be the corners of the polygon.

  // We must choose four points fairly reasonably spread apart on
  // the plane.  We'll start with a center point and one corner
  // point, and then use cross products to find the remaining three
  // corners of a square.

  // The center point will be on the axis with the largest
  // coefficent.  The first corner will be diagonal in the other two
  // dimensions.

  LPoint3f cp;
  LVector3f p1, p2, p3, p4;

  LVector3f normal = get_normal();
  float D = _plane[3];

  if (fabs(normal[0]) > fabs(normal[1]) &&
      fabs(normal[0]) > fabs(normal[2])) {
    // X has the largest coefficient.
    cp.set(-D / normal[0], 0.0f, 0.0f);
    p1 = LPoint3f(-(normal[1] + normal[2] + D)/normal[0], 1.0f, 1.0f) - cp;

  } else if (fabs(normal[1]) > fabs(normal[2])) {
    // Y has the largest coefficient.
    cp.set(0.0f, -D / normal[1], 0.0f);
    p1 = LPoint3f(1.0f, -(normal[0] + normal[2] + D)/normal[1], 1.0f) - cp;

  } else {
    // Z has the largest coefficient.
    cp.set(0.0f, 0.0f, -D / normal[2]);
    p1 = LPoint3f(1.0f, 1.0f, -(normal[0] + normal[1] + D)/normal[2]) - cp;
  }

  p1.normalize();
  p2 = cross(normal, p1);
  p3 = cross(normal, p2);
  p4 = cross(normal, p3);

  static const double plane_scale = 10.0;

  PT(GeomVertexData) vdata = new GeomVertexData
    ("collision", GeomVertexFormat::get_v3(),
     Geom::UH_static);
  GeomVertexWriter vertex(vdata, InternalName::get_vertex());

  vertex.add_data3f(cp + p1 * plane_scale);
  vertex.add_data3f(cp + p2 * plane_scale);
  vertex.add_data3f(cp + p3 * plane_scale);
  vertex.add_data3f(cp + p4 * plane_scale);

  PT(GeomTrifans) body = new GeomTrifans(Geom::UH_static);
  body->add_consecutive_vertices(0, 4);
  body->close_primitive();

  PT(GeomLinestrips) border = new GeomLinestrips(Geom::UH_static);
  border->add_consecutive_vertices(0, 4);
  border->add_vertex(0);
  border->close_primitive();

  PT(Geom) geom1 = new Geom(vdata);
  geom1->add_primitive(body);

  PT(Geom) geom2 = new Geom(vdata);
  geom2->add_primitive(border);

  _viz_geom->add_geom(geom1, get_solid_viz_state());
  _viz_geom->add_geom(geom2, get_wireframe_viz_state());

  _bounds_viz_geom->add_geom(geom1, get_solid_bounds_viz_state());
  _bounds_viz_geom->add_geom(geom2, get_wireframe_bounds_viz_state());
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::write_datagram
//       Access: Public
//  Description: Function to write the important information in
//               the particular object to a Datagram
////////////////////////////////////////////////////////////////////
void CollisionPlane::
write_datagram(BamWriter *manager, Datagram &me)
{
  CollisionSolid::write_datagram(manager, me);
  _plane.write_datagram(me);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::fillin
//       Access: Protected
//  Description: Function that reads out of the datagram (or asks
//               manager to read) all of the data that is needed to
//               re-create this object and stores it in the appropiate
//               place
////////////////////////////////////////////////////////////////////
void CollisionPlane::
fillin(DatagramIterator& scan, BamReader* manager)
{
  CollisionSolid::fillin(scan, manager);
  _plane.read_datagram(scan);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::make_CollisionPlane
//       Access: Protected
//  Description: Factory method to generate a CollisionPlane object
////////////////////////////////////////////////////////////////////
TypedWritable* CollisionPlane::
make_CollisionPlane(const FactoryParams &params)
{
  CollisionPlane *me = new CollisionPlane;
  DatagramIterator scan;
  BamReader *manager;

  parse_params(params, scan, manager);
  me->fillin(scan, manager);
  return me;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::register_with_factory
//       Access: Public, Static
//  Description: Factory method to generate a CollisionPlane object
////////////////////////////////////////////////////////////////////
void CollisionPlane::
register_with_read_factory()
{
  BamReader::get_factory()->register_factory(get_class_type(), make_CollisionPlane);
}
<commit_msg>formatting<commit_after>// Filename: collisionPlane.cxx
// Created by:  drose (25Apr00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#include "collisionPlane.h"
#include "collisionHandler.h"
#include "collisionEntry.h"
#include "collisionSphere.h"
#include "collisionLine.h"
#include "collisionRay.h"
#include "collisionSegment.h"
#include "config_collide.h"
#include "pointerToArray.h"
#include "geomNode.h"
#include "geom.h"
#include "datagram.h"
#include "datagramIterator.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "boundingPlane.h"
#include "geom.h"
#include "geomTrifans.h"
#include "geomLinestrips.h"
#include "geomVertexWriter.h"

PStatCollector CollisionPlane::_volume_pcollector("Collision Volumes:CollisionPlane");
PStatCollector CollisionPlane::_test_pcollector("Collision Tests:CollisionPlane");
TypeHandle CollisionPlane::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::make_copy
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
CollisionSolid *CollisionPlane::
make_copy() {
  return new CollisionPlane(*this);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::xform
//       Access: Public, Virtual
//  Description: Transforms the solid by the indicated matrix.
////////////////////////////////////////////////////////////////////
void CollisionPlane::
xform(const LMatrix4f &mat) {
  _plane = _plane * mat;
  CollisionSolid::xform(mat);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::get_collision_origin
//       Access: Public, Virtual
//  Description: Returns the point in space deemed to be the "origin"
//               of the solid for collision purposes.  The closest
//               intersection point to this origin point is considered
//               to be the most significant.
////////////////////////////////////////////////////////////////////
LPoint3f CollisionPlane::
get_collision_origin() const {
  // No real sensible origin exists for a plane.  We return 0, 0, 0,
  // without even bothering to ensure that that point exists on the
  // plane.
  return LPoint3f::origin();
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::get_volume_pcollector
//       Access: Public, Virtual
//  Description: Returns a PStatCollector that is used to count the
//               number of bounding volume tests made against a solid
//               of this type in a given frame.
////////////////////////////////////////////////////////////////////
PStatCollector CollisionPlane::
get_volume_pcollector() {
  return _volume_pcollector;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::get_test_pcollector
//       Access: Public, Virtual
//  Description: Returns a PStatCollector that is used to count the
//               number of intersection tests made against a solid
//               of this type in a given frame.
////////////////////////////////////////////////////////////////////
PStatCollector CollisionPlane::
get_test_pcollector() {
  return _test_pcollector;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::output
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
void CollisionPlane::
output(ostream &out) const {
  out << "cplane, (" << _plane << ")";
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::compute_internal_bounds
//       Access: Protected, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
PT(BoundingVolume) CollisionPlane::
compute_internal_bounds() const {
  return new BoundingPlane(_plane);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::test_intersection_from_sphere
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
PT(CollisionEntry) CollisionPlane::
test_intersection_from_sphere(const CollisionEntry &entry) const {
  const CollisionSphere *sphere;
  DCAST_INTO_R(sphere, entry.get_from(), 0);

  const LMatrix4f &wrt_mat = entry.get_wrt_mat();

  LPoint3f from_center = sphere->get_center() * wrt_mat;
  LVector3f from_radius_v =
    LVector3f(sphere->get_radius(), 0.0f, 0.0f) * wrt_mat;
  float from_radius = length(from_radius_v);

  float dist = dist_to_plane(from_center);
  if (dist > from_radius) {
    // No intersection.
    return NULL;
  }

  if (collide_cat.is_debug()) {
    collide_cat.debug()
      << "intersection detected from " << entry.get_from_node_path()
      << " into " << entry.get_into_node_path() << "\n";
  }
  PT(CollisionEntry) new_entry = new CollisionEntry(entry);

  LVector3f from_normal = get_normal() * entry.get_inv_wrt_mat();

  LVector3f normal = (
    has_effective_normal() && sphere->get_respect_effective_normal())
    ? get_effective_normal() : get_normal();

  new_entry->set_surface_normal(normal);
  new_entry->set_surface_point(from_center - get_normal() * dist);
  new_entry->set_interior_point(from_center - get_normal() * from_radius);

  return new_entry;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::test_intersection_from_line
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
PT(CollisionEntry) CollisionPlane::
test_intersection_from_line(const CollisionEntry &entry) const {
  const CollisionLine *line;
  DCAST_INTO_R(line, entry.get_from(), 0);

  const LMatrix4f &wrt_mat = entry.get_wrt_mat();

  LPoint3f from_origin = line->get_origin() * wrt_mat;
  LVector3f from_direction = line->get_direction() * wrt_mat;

  float t;
  if (!_plane.intersects_line(t, from_origin, from_direction)) {
    // No intersection.
    return NULL;
  }

  if (collide_cat.is_debug()) {
    collide_cat.debug()
      << "intersection detected from " << entry.get_from_node_path()
      << " into " << entry.get_into_node_path() << "\n";
  }
  PT(CollisionEntry) new_entry = new CollisionEntry(entry);

  LPoint3f into_intersection_point = from_origin + t * from_direction;

  LVector3f normal = (has_effective_normal() && line->get_respect_effective_normal()) ? get_effective_normal() : get_normal();

  new_entry->set_surface_normal(normal);
  new_entry->set_surface_point(into_intersection_point);

  return new_entry;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::test_intersection_from_ray
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
PT(CollisionEntry) CollisionPlane::
test_intersection_from_ray(const CollisionEntry &entry) const {
  const CollisionRay *ray;
  DCAST_INTO_R(ray, entry.get_from(), 0);

  const LMatrix4f &wrt_mat = entry.get_wrt_mat();

  LPoint3f from_origin = ray->get_origin() * wrt_mat;
  LVector3f from_direction = ray->get_direction() * wrt_mat;

  float t;
  if (!_plane.intersects_line(t, from_origin, from_direction)) {
    // No intersection.
    return NULL;
  }

  if (t < 0.0f) {
    // The intersection point is before the start of the ray.
    return NULL;
  }

  if (collide_cat.is_debug()) {
    collide_cat.debug()
      << "intersection detected from " << entry.get_from_node_path()
      << " into " << entry.get_into_node_path() << "\n";
  }
  PT(CollisionEntry) new_entry = new CollisionEntry(entry);

  LPoint3f into_intersection_point = from_origin + t * from_direction;

  LVector3f normal = (has_effective_normal() && ray->get_respect_effective_normal()) ? get_effective_normal() : get_normal();

  new_entry->set_surface_normal(normal);
  new_entry->set_surface_point(into_intersection_point);

  return new_entry;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::test_intersection_from_segment
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
PT(CollisionEntry) CollisionPlane::
test_intersection_from_segment(const CollisionEntry &entry) const {
  const CollisionSegment *segment;
  DCAST_INTO_R(segment, entry.get_from(), 0);

  const LMatrix4f &wrt_mat = entry.get_wrt_mat();

  LPoint3f from_a = segment->get_point_a() * wrt_mat;
  LPoint3f from_b = segment->get_point_b() * wrt_mat;
  LVector3f from_direction = from_b - from_a;

  float t;
  if (!_plane.intersects_line(t, from_a, from_direction)) {
    // No intersection.
    return NULL;
  }

  if (t < 0.0f || t > 1.0f) {
    // The intersection point is before the start of the segment or
    // after the end of the segment.
    return NULL;
  }

  if (collide_cat.is_debug()) {
    collide_cat.debug()
      << "intersection detected from " << entry.get_from_node_path()
      << " into " << entry.get_into_node_path() << "\n";
  }
  PT(CollisionEntry) new_entry = new CollisionEntry(entry);

  LPoint3f into_intersection_point = from_a + t * from_direction;

  LVector3f normal = (has_effective_normal() && segment->get_respect_effective_normal()) ? get_effective_normal() : get_normal();

  new_entry->set_surface_normal(normal);
  new_entry->set_surface_point(into_intersection_point);

  return new_entry;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::fill_viz_geom
//       Access: Protected, Virtual
//  Description: Fills the _viz_geom GeomNode up with Geoms suitable
//               for rendering this solid.
////////////////////////////////////////////////////////////////////
void CollisionPlane::
fill_viz_geom() {
  if (collide_cat.is_debug()) {
    collide_cat.debug()
      << "Recomputing viz for " << *this << "\n";
  }

  // Since we can't represent an infinite plane, we'll have to be
  // satisfied with drawing a big polygon.  Choose four points on the
  // plane to be the corners of the polygon.

  // We must choose four points fairly reasonably spread apart on
  // the plane.  We'll start with a center point and one corner
  // point, and then use cross products to find the remaining three
  // corners of a square.

  // The center point will be on the axis with the largest
  // coefficent.  The first corner will be diagonal in the other two
  // dimensions.

  LPoint3f cp;
  LVector3f p1, p2, p3, p4;

  LVector3f normal = get_normal();
  float D = _plane[3];

  if (fabs(normal[0]) > fabs(normal[1]) &&
      fabs(normal[0]) > fabs(normal[2])) {
    // X has the largest coefficient.
    cp.set(-D / normal[0], 0.0f, 0.0f);
    p1 = LPoint3f(-(normal[1] + normal[2] + D)/normal[0], 1.0f, 1.0f) - cp;

  } else if (fabs(normal[1]) > fabs(normal[2])) {
    // Y has the largest coefficient.
    cp.set(0.0f, -D / normal[1], 0.0f);
    p1 = LPoint3f(1.0f, -(normal[0] + normal[2] + D)/normal[1], 1.0f) - cp;

  } else {
    // Z has the largest coefficient.
    cp.set(0.0f, 0.0f, -D / normal[2]);
    p1 = LPoint3f(1.0f, 1.0f, -(normal[0] + normal[1] + D)/normal[2]) - cp;
  }

  p1.normalize();
  p2 = cross(normal, p1);
  p3 = cross(normal, p2);
  p4 = cross(normal, p3);

  static const double plane_scale = 10.0;

  PT(GeomVertexData) vdata = new GeomVertexData
    ("collision", GeomVertexFormat::get_v3(),
     Geom::UH_static);
  GeomVertexWriter vertex(vdata, InternalName::get_vertex());

  vertex.add_data3f(cp + p1 * plane_scale);
  vertex.add_data3f(cp + p2 * plane_scale);
  vertex.add_data3f(cp + p3 * plane_scale);
  vertex.add_data3f(cp + p4 * plane_scale);

  PT(GeomTrifans) body = new GeomTrifans(Geom::UH_static);
  body->add_consecutive_vertices(0, 4);
  body->close_primitive();

  PT(GeomLinestrips) border = new GeomLinestrips(Geom::UH_static);
  border->add_consecutive_vertices(0, 4);
  border->add_vertex(0);
  border->close_primitive();

  PT(Geom) geom1 = new Geom(vdata);
  geom1->add_primitive(body);

  PT(Geom) geom2 = new Geom(vdata);
  geom2->add_primitive(border);

  _viz_geom->add_geom(geom1, get_solid_viz_state());
  _viz_geom->add_geom(geom2, get_wireframe_viz_state());

  _bounds_viz_geom->add_geom(geom1, get_solid_bounds_viz_state());
  _bounds_viz_geom->add_geom(geom2, get_wireframe_bounds_viz_state());
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::write_datagram
//       Access: Public
//  Description: Function to write the important information in
//               the particular object to a Datagram
////////////////////////////////////////////////////////////////////
void CollisionPlane::
write_datagram(BamWriter *manager, Datagram &me)
{
  CollisionSolid::write_datagram(manager, me);
  _plane.write_datagram(me);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::fillin
//       Access: Protected
//  Description: Function that reads out of the datagram (or asks
//               manager to read) all of the data that is needed to
//               re-create this object and stores it in the appropiate
//               place
////////////////////////////////////////////////////////////////////
void CollisionPlane::
fillin(DatagramIterator& scan, BamReader* manager)
{
  CollisionSolid::fillin(scan, manager);
  _plane.read_datagram(scan);
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::make_CollisionPlane
//       Access: Protected
//  Description: Factory method to generate a CollisionPlane object
////////////////////////////////////////////////////////////////////
TypedWritable* CollisionPlane::
make_CollisionPlane(const FactoryParams &params)
{
  CollisionPlane *me = new CollisionPlane;
  DatagramIterator scan;
  BamReader *manager;

  parse_params(params, scan, manager);
  me->fillin(scan, manager);
  return me;
}

////////////////////////////////////////////////////////////////////
//     Function: CollisionPlane::register_with_factory
//       Access: Public, Static
//  Description: Factory method to generate a CollisionPlane object
////////////////////////////////////////////////////////////////////
void CollisionPlane::
register_with_read_factory()
{
  BamReader::get_factory()->register_factory(get_class_type(), make_CollisionPlane);
}
<|endoftext|>


<commit_before>/*
 * This file is a part of Xpiks - cross platform application for
 * keywording and uploading images for microstocks
 * Copyright (C) 2014-2017 Taras Kushnir <kushnirTV@gmail.com>
 *
 * Xpiks is distributed under the GNU General Public License, version 3.0
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "translationmanager.h"
#include <QDir>
#include <QSet>
#include <QFile>
#include <QTextStream>
#include <QDirIterator>
#include <QtConcurrent>
#include "../Common/defines.h"
#include "translationquery.h"
#include "translationservice.h"
#include "../Commands/commandmanager.h"
#include "../Models/settingsmodel.h"
#include "../Helpers/constants.h"

#define SHORT_TRANSLATION_SYMBOLS 500
#define BOOKNAME QLatin1String("bookname")

namespace Translation {
    bool parseIfoFile(const QString &fullIfoPath, DictionaryInfo &info) {
        LOG_INFO << fullIfoPath;
        QFile file(fullIfoPath);
        bool success = false;

        if (file.open(QIODevice::ReadOnly)) {
            QTextStream in(&file);

            QString line;

            do {
                if (!in.atEnd()) {
                    line = in.readLine();
                    if ((line != "StarDict's treedict ifo file") &&
                            (line != "StarDict's dict ifo file")) {
                        LOG_WARNING << "Wrong header" << line;
                        break;
                    }
                }

                while (!in.atEnd()) {
                    line = in.readLine();
                    if (line.startsWith(BOOKNAME)) {
                        info.m_Description = line.mid(BOOKNAME.size() + 1).simplified();
                        info.m_FullIfoPath = fullIfoPath;
                        success = true;
                        break;
                    }
                }
            } while (false);
        }

        return success;
    }

    TranslationManager::TranslationManager(QObject *parent) :
        QObject(parent),
        Common::BaseEntity(),
        m_AllowedSuffixes({"idx", "idx.dz", "idx.oft", "dict.dz", "dict", "ifo"}),
        m_SelectedDictionaryIndex(-1),
        m_IsBusy(false),
        m_HasMore(false)
    {
        m_TranslateTimer.setSingleShot(true);
        QObject::connect(&m_TranslateTimer, SIGNAL(timeout()), this, SLOT(updateTranslationTimer()));

        QObject::connect(&m_InitializationWatcher, SIGNAL(finished()), this, SLOT(initializationFinished()));
    }

    void TranslationManager::initializeDictionaries() {
        LOG_DEBUG << "#";
        QFuture<void> initializeFuture = QtConcurrent::run(this, &TranslationManager::doInitializeDictionaries);
        m_InitializationWatcher.setFuture(initializeFuture);
    }

    void TranslationManager::setQuery(const QString &value) {
        bool anySignificantDifference = value.simplified() != m_Query;

        if (m_Query != value) {
            m_Query = value;
            emit queryChanged();
        }

        if (anySignificantDifference) {
            m_TranslateTimer.start(1000);
        }
    }

    void TranslationManager::setSelectedDictionaryIndex(int value) {
        LOG_INFO << value;

        if (m_SelectedDictionaryIndex != value) {
            m_SelectedDictionaryIndex = value;
            emit selectedDictionaryIndexChanged();

            if ((0 <= value) && (value < m_DictionariesList.length())) {
                auto *translationService = m_CommandManager->getTranslationService();
                auto &dictInfo = m_DictionariesList[value];
                LOG_INFO << "Selecting" << dictInfo.m_Description;
                translationService->selectDictionary(dictInfo.m_FullIfoPath);
            } else {
                LOG_WARNING << "Cannot select dictionary path: indices to not match";
            }

            if (!m_Query.isEmpty()) {
                m_TranslateTimer.start(1000);
                setIsBusy(true);
            }

            auto *settingsModel = m_CommandManager->getSettingsModel();
            settingsModel->setSelectedDictIndex(value);
            settingsModel->saveSelectedDictionaryIndex();
        }
    }

    QStringList TranslationManager::getDictionariesDescriptions() const {
        QStringList descriptions;
        descriptions.reserve(m_DictionariesList.length());

        foreach (const DictionaryInfo &dict, m_DictionariesList) {
            descriptions.append(dict.m_Description);
        }

        return descriptions;
    }

    void TranslationManager::doInitializeDictionaries() {
        QString appDataPath = XPIKS_USERDATA_PATH;

        if (!appDataPath.isEmpty()) {
            m_DictionariesDirPath = QDir::cleanPath(appDataPath + QDir::separator() + Constants::TRANSLATOR_DIR);

            QDir dictionariesDir(m_DictionariesDirPath);
            if (!dictionariesDir.exists()) {
                LOG_INFO << "Creating dictionaries dir" << m_DictionariesDirPath;
                QDir().mkpath(m_DictionariesDirPath);
            }
        } else {
            m_DictionariesDirPath = QDir::currentPath();
        }

        QDirIterator it(m_DictionariesDirPath, QStringList() << "*.ifo", QDir::Files, QDirIterator::NoIteratorFlags);

        while (it.hasNext()) {
            QString ifoFullPath = it.next();

            DictionaryInfo di;
            if (parseIfoFile(ifoFullPath, di) &&
                    hasAllNeededComponents(ifoFullPath)) {
                m_DictionariesList.append(di);
                LOG_INFO << "Parsed" << di.m_Description;
            } else {
                LOG_WARNING << "Failed to parse IFO:" << ifoFullPath;
            }
        }
    }

    bool TranslationManager::acquireDictionary(const QString &anyDictFilePath) {
        QFileInfo fi(anyDictFilePath);
        Q_ASSERT(fi.exists());
        QString longPrefix = QDir::cleanPath(fi.absolutePath() + QDir::separator() + fi.baseName());
        QString shortPrefix = fi.baseName();

        bool anyError = false, anyFileCopied = false,
                indexFound = false, dictFound = false, ifoFound = false;

        foreach (const QString &suffix, m_AllowedSuffixes) {
            QString probablePath = longPrefix + "." + suffix;

            if (QFileInfo(probablePath).exists()) {
                LOG_INFO << "File found:" << probablePath;
                QString localDict = QDir::cleanPath(m_DictionariesDirPath + QDir::separator() + shortPrefix + "." + suffix);

                if (QFile::copy(probablePath, localDict)) {
                    LOG_INFO << "Copied to" << localDict;
                    anyFileCopied = true;

                    // TODO: refactor this someday to check just needed
                    // extensions instead of looping all of them
                    indexFound = indexFound || suffix.contains("idx");
                    dictFound = dictFound || suffix.contains("dict");
                    ifoFound = ifoFound || suffix.contains("ifo");
                } else {
                    LOG_WARNING << "Failed to copy as:" << localDict;
                    anyError = true;
                    break;
                }
            } else {
                LOG_INFO << "File NOT found:" << probablePath;
            }
        }

        LOG_INFO << "IFO found" << ifoFound << "Index found" << indexFound << "Dictionary found" << dictFound;

        bool success = (anyFileCopied) && (!anyError) && (ifoFound && dictFound && indexFound);
        return success;
    }

    bool TranslationManager::hasAllNeededComponents(const QString &anyDictFilePath) const {
        QFileInfo fi(anyDictFilePath);
        Q_ASSERT(fi.exists());

        QString longPrefix = QDir::cleanPath(fi.absolutePath() + QDir::separator() + fi.baseName());
        bool indexFound = false, dictFound = false, ifoFound = false;

        foreach (const QString &suffix, m_AllowedSuffixes) {
            QString probablePath = longPrefix + "." + suffix;

            if (QFileInfo(probablePath).exists()) {
                // TODO: refactor this someday to check just needed
                // extensions instead of looping all of them
                indexFound = indexFound || suffix.contains("idx");
                dictFound = dictFound || suffix.contains("dict");
                ifoFound = ifoFound || suffix.contains("ifo");
            }
        }

        bool success = ifoFound && dictFound && indexFound;
        return success;
    }

    void TranslationManager::clear() {
        m_Query.clear();
        m_FullTranslation.clear();
        m_ShortenedTranslation.clear();
        m_HasMore = false;
        emit queryChanged();
        emit fullTranslationChanged();
        emit shortTranslationChanged();
        emit hasMoreChanged();
    }

    bool TranslationManager::addDictionary(const QUrl &url) {
        Q_ASSERT(url.isLocalFile());
        QString anyDictFilePath = url.toLocalFile();
        LOG_INFO << anyDictFilePath;
        QFileInfo fi(anyDictFilePath);
        bool success = false;

        QSet<QString> allowedSuffixes = m_AllowedSuffixes.toSet();

        do {
            if (!fi.exists()) {
                LOG_WARNING << "File does not exist:" << anyDictFilePath;
                break;
            }

            if (!fi.isFile()) {
                LOG_WARNING << "Path is not a file:" << anyDictFilePath;
                break;
            }

            if (!allowedSuffixes.contains(fi.completeSuffix().toLower())) {
                LOG_WARNING << "File" << fi.completeSuffix() << "is unsupported";
                break;
            }

            QString probableIfoPath = QDir::cleanPath(fi.absolutePath() + QDir::separator() +
                                                      fi.baseName() + ".ifo");
            DictionaryInfo di;
            if (parseIfoFile(probableIfoPath, di)) {
                LOG_INFO << "Parsed" << di.m_Description;
                if (acquireDictionary(probableIfoPath)) {
                    m_DictionariesList.append(di);
                    success = true;
                } else {
                    LOG_WARNING << "Failed to acquire dictionary" << probableIfoPath;
                }
            } else {
                LOG_WARNING << "Failed to parse IFO:" << probableIfoPath;
            }
        } while (false);

        if (success && (m_SelectedDictionaryIndex == -1)) {
            setSelectedDictionaryIndex(0);
        }

        emit dictionariesChanged();

        return success;
    }

    void TranslationManager::translationArrived() {
        LOG_DEBUG << "#";
        TranslationQuery *query = qobject_cast<TranslationQuery *>(sender());
        Q_ASSERT(query != nullptr);

        if (query->hasTranslation()) {
            QString translation = query->getTranslation();

            m_FullTranslation = translation;
            m_ShortenedTranslation = translation.left(SHORT_TRANSLATION_SYMBOLS);
        } else {
            QString nothingFound = QObject::tr("No results");

            m_FullTranslation = nothingFound;
            m_ShortenedTranslation = nothingFound;
        }

        m_HasMore = m_FullTranslation.length() != m_ShortenedTranslation.length();
        emit hasMoreChanged();

        if (m_HasMore) {
            m_ShortenedTranslation.append("...");
        }

        emit fullTranslationChanged();
        emit shortTranslationChanged();

        setIsBusy(false);
    }

    void TranslationManager::updateTranslationTimer() {
        TranslationService *translationService = m_CommandManager->getTranslationService();
        translationService->translate(m_Query);
        setIsBusy(true);
    }

    void TranslationManager::initializationFinished() {
        LOG_DEBUG << "#";

        auto *settingsModel = m_CommandManager->getSettingsModel();
        int selectedDictIndex = settingsModel->getSelectedDictIndex();
        if ((0 <= selectedDictIndex) && (selectedDictIndex < m_DictionariesList.length())) {
            setSelectedDictionaryIndex(selectedDictIndex);
        } else {
            setSelectedDictionaryIndex(0);
        }

        emit dictionariesChanged();
        emit selectedDictionaryIndexChanged();
    }
}
<commit_msg>Fix for integration tests<commit_after>/*
 * This file is a part of Xpiks - cross platform application for
 * keywording and uploading images for microstocks
 * Copyright (C) 2014-2017 Taras Kushnir <kushnirTV@gmail.com>
 *
 * Xpiks is distributed under the GNU General Public License, version 3.0
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "translationmanager.h"
#include <QDir>
#include <QSet>
#include <QFile>
#include <QTextStream>
#include <QDirIterator>
#include <QtConcurrent>
#include "../Common/defines.h"
#include "translationquery.h"
#include "translationservice.h"
#include "../Commands/commandmanager.h"
#include "../Models/settingsmodel.h"
#include "../Helpers/constants.h"

#define SHORT_TRANSLATION_SYMBOLS 500
#define BOOKNAME QLatin1String("bookname")

namespace Translation {
    bool parseIfoFile(const QString &fullIfoPath, DictionaryInfo &info) {
        LOG_INFO << fullIfoPath;
        QFile file(fullIfoPath);
        bool success = false;

        if (file.open(QIODevice::ReadOnly)) {
            QTextStream in(&file);

            QString line;

            do {
                if (!in.atEnd()) {
                    line = in.readLine();
                    if ((line != "StarDict's treedict ifo file") &&
                            (line != "StarDict's dict ifo file")) {
                        LOG_WARNING << "Wrong header" << line;
                        break;
                    }
                }

                while (!in.atEnd()) {
                    line = in.readLine();
                    if (line.startsWith(BOOKNAME)) {
                        info.m_Description = line.mid(BOOKNAME.size() + 1).simplified();
                        info.m_FullIfoPath = fullIfoPath;
                        success = true;
                        break;
                    }
                }
            } while (false);
        }

        return success;
    }

    TranslationManager::TranslationManager(QObject *parent) :
        QObject(parent),
        Common::BaseEntity(),
        m_AllowedSuffixes({"idx", "idx.dz", "idx.oft", "dict.dz", "dict", "ifo"}),
        m_SelectedDictionaryIndex(-1),
        m_IsBusy(false),
        m_HasMore(false)
    {
        m_TranslateTimer.setSingleShot(true);
        QObject::connect(&m_TranslateTimer, SIGNAL(timeout()), this, SLOT(updateTranslationTimer()));

        QObject::connect(&m_InitializationWatcher, SIGNAL(finished()), this, SLOT(initializationFinished()));
    }

    void TranslationManager::initializeDictionaries() {
        LOG_DEBUG << "#";
        QFuture<void> initializeFuture = QtConcurrent::run(this, &TranslationManager::doInitializeDictionaries);
        m_InitializationWatcher.setFuture(initializeFuture);
    }

    void TranslationManager::setQuery(const QString &value) {
        bool anySignificantDifference = value.simplified() != m_Query;

        if (m_Query != value) {
            m_Query = value;
            emit queryChanged();
        }

        if (anySignificantDifference) {
            m_TranslateTimer.start(1000);
        }
    }

    void TranslationManager::setSelectedDictionaryIndex(int value) {
        LOG_INFO << value;
        LOG_INTEGRATION_TESTS << "Current index" << m_SelectedDictionaryIndex;
        LOG_INTEGRATION_TESTS << "Overall" << m_DictionariesList.length() << "dictionaries";

        if (m_SelectedDictionaryIndex != value) {
            LOG_INFO << "Changing current dictionary";
            m_SelectedDictionaryIndex = value;
            emit selectedDictionaryIndexChanged();

            if ((0 <= value) && (value < m_DictionariesList.length())) {
                auto *translationService = m_CommandManager->getTranslationService();
                auto &dictInfo = m_DictionariesList[value];
                LOG_INFO << "Selecting" << dictInfo.m_Description;
                translationService->selectDictionary(dictInfo.m_FullIfoPath);
            } else {
                LOG_WARNING << "Cannot select dictionary path: indices to not match";
            }

            if (!m_Query.isEmpty()) {
                m_TranslateTimer.start(1000);
                setIsBusy(true);
            }

            auto *settingsModel = m_CommandManager->getSettingsModel();
            settingsModel->setSelectedDictIndex(value);
            settingsModel->saveSelectedDictionaryIndex();
        }
    }

    QStringList TranslationManager::getDictionariesDescriptions() const {
        QStringList descriptions;
        descriptions.reserve(m_DictionariesList.length());

        foreach (const DictionaryInfo &dict, m_DictionariesList) {
            descriptions.append(dict.m_Description);
        }

        return descriptions;
    }

    void TranslationManager::doInitializeDictionaries() {
        QString appDataPath = XPIKS_USERDATA_PATH;

        if (!appDataPath.isEmpty()) {
            m_DictionariesDirPath = QDir::cleanPath(appDataPath + QDir::separator() + Constants::TRANSLATOR_DIR);

            QDir dictionariesDir(m_DictionariesDirPath);
            if (!dictionariesDir.exists()) {
                LOG_INFO << "Creating dictionaries dir" << m_DictionariesDirPath;
                QDir().mkpath(m_DictionariesDirPath);
            }
        } else {
            m_DictionariesDirPath = QDir::currentPath();
        }

        QDirIterator it(m_DictionariesDirPath, QStringList() << "*.ifo", QDir::Files, QDirIterator::NoIteratorFlags);

        while (it.hasNext()) {
            QString ifoFullPath = it.next();

            DictionaryInfo di;
            if (parseIfoFile(ifoFullPath, di) &&
                    hasAllNeededComponents(ifoFullPath)) {
                m_DictionariesList.append(di);
                LOG_INFO << "Parsed" << di.m_Description;
            } else {
                LOG_WARNING << "Failed to parse IFO:" << ifoFullPath;
            }
        }
    }

    bool TranslationManager::acquireDictionary(const QString &anyDictFilePath) {
        QFileInfo fi(anyDictFilePath);
        Q_ASSERT(fi.exists());
        QString longPrefix = QDir::cleanPath(fi.absolutePath() + QDir::separator() + fi.baseName());
        QString shortPrefix = fi.baseName();

        bool anyError = false, anyFileCopied = false,
                indexFound = false, dictFound = false, ifoFound = false;

        foreach (const QString &suffix, m_AllowedSuffixes) {
            QString probablePath = longPrefix + "." + suffix;

            if (QFileInfo(probablePath).exists()) {
                LOG_INFO << "File found:" << probablePath;
                QString localDict = QDir::cleanPath(m_DictionariesDirPath + QDir::separator() + shortPrefix + "." + suffix);

                if (QFile::copy(probablePath, localDict)) {
                    LOG_INFO << "Copied to" << localDict;
                    anyFileCopied = true;

                    // TODO: refactor this someday to check just needed
                    // extensions instead of looping all of them
                    indexFound = indexFound || suffix.contains("idx");
                    dictFound = dictFound || suffix.contains("dict");
                    ifoFound = ifoFound || suffix.contains("ifo");
                } else {
                    LOG_WARNING << "Failed to copy as:" << localDict;
                    anyError = true;
                    break;
                }
            } else {
                LOG_INFO << "File NOT found:" << probablePath;
            }
        }

        LOG_INFO << "IFO found" << ifoFound << "Index found" << indexFound << "Dictionary found" << dictFound;

        bool success = (anyFileCopied) && (!anyError) && (ifoFound && dictFound && indexFound);
        return success;
    }

    bool TranslationManager::hasAllNeededComponents(const QString &anyDictFilePath) const {
        QFileInfo fi(anyDictFilePath);
        Q_ASSERT(fi.exists());

        QString longPrefix = QDir::cleanPath(fi.absolutePath() + QDir::separator() + fi.baseName());
        bool indexFound = false, dictFound = false, ifoFound = false;

        foreach (const QString &suffix, m_AllowedSuffixes) {
            QString probablePath = longPrefix + "." + suffix;

            if (QFileInfo(probablePath).exists()) {
                // TODO: refactor this someday to check just needed
                // extensions instead of looping all of them
                indexFound = indexFound || suffix.contains("idx");
                dictFound = dictFound || suffix.contains("dict");
                ifoFound = ifoFound || suffix.contains("ifo");
            }
        }

        bool success = ifoFound && dictFound && indexFound;
        return success;
    }

    void TranslationManager::clear() {
        m_Query.clear();
        m_FullTranslation.clear();
        m_ShortenedTranslation.clear();
        m_HasMore = false;
        emit queryChanged();
        emit fullTranslationChanged();
        emit shortTranslationChanged();
        emit hasMoreChanged();
    }

    bool TranslationManager::addDictionary(const QUrl &url) {
        Q_ASSERT(url.isLocalFile());
        QString anyDictFilePath = url.toLocalFile();
        LOG_INFO << anyDictFilePath;
        QFileInfo fi(anyDictFilePath);
        bool success = false;

        QSet<QString> allowedSuffixes = m_AllowedSuffixes.toSet();

        do {
            if (!fi.exists()) {
                LOG_WARNING << "File does not exist:" << anyDictFilePath;
                break;
            }

            if (!fi.isFile()) {
                LOG_WARNING << "Path is not a file:" << anyDictFilePath;
                break;
            }

            if (!allowedSuffixes.contains(fi.completeSuffix().toLower())) {
                LOG_WARNING << "File" << fi.completeSuffix() << "is unsupported";
                break;
            }

            QString probableIfoPath = QDir::cleanPath(fi.absolutePath() + QDir::separator() +
                                                      fi.baseName() + ".ifo");
            DictionaryInfo di;
            if (parseIfoFile(probableIfoPath, di)) {
                LOG_INFO << "Parsed" << di.m_Description;
                if (acquireDictionary(probableIfoPath)) {
                    m_DictionariesList.append(di);
                    success = true;
                } else {
                    LOG_WARNING << "Failed to acquire dictionary" << probableIfoPath;
                }
            } else {
                LOG_WARNING << "Failed to parse IFO:" << probableIfoPath;
            }
        } while (false);

        if (success && (m_SelectedDictionaryIndex == -1)) {
            setSelectedDictionaryIndex(0);
        }

        emit dictionariesChanged();

        return success;
    }

    void TranslationManager::translationArrived() {
        LOG_DEBUG << "#";
        TranslationQuery *query = qobject_cast<TranslationQuery *>(sender());
        Q_ASSERT(query != nullptr);

        if (query->hasTranslation()) {
            QString translation = query->getTranslation();

            m_FullTranslation = translation;
            m_ShortenedTranslation = translation.left(SHORT_TRANSLATION_SYMBOLS);
        } else {
            QString nothingFound = QObject::tr("No results");

            m_FullTranslation = nothingFound;
            m_ShortenedTranslation = nothingFound;
        }

        m_HasMore = m_FullTranslation.length() != m_ShortenedTranslation.length();
        emit hasMoreChanged();

        if (m_HasMore) {
            m_ShortenedTranslation.append("...");
        }

        emit fullTranslationChanged();
        emit shortTranslationChanged();

        setIsBusy(false);
    }

    void TranslationManager::updateTranslationTimer() {
        TranslationService *translationService = m_CommandManager->getTranslationService();
        translationService->translate(m_Query);
        setIsBusy(true);
    }

    void TranslationManager::initializationFinished() {
        LOG_DEBUG << "#";

        auto *settingsModel = m_CommandManager->getSettingsModel();
        int selectedDictIndex = settingsModel->getSelectedDictIndex();
        if ((0 <= selectedDictIndex) && (selectedDictIndex < m_DictionariesList.length())) {
            setSelectedDictionaryIndex(selectedDictIndex);
        } else {
            if (!m_DictionariesList.empty()) {
                setSelectedDictionaryIndex(0);
            }
        }

        emit dictionariesChanged();
        emit selectedDictionaryIndexChanged();
    }
}
<|endoftext|>


<commit_before>#ifndef STAN_MATH_FWD_MAT_FUN_UNIT_VECTOR_CONSTRAIN_HPP
#define STAN_MATH_FWD_MAT_FUN_UNIT_VECTOR_CONSTRAIN_HPP

#include <stan/math/fwd/core.hpp>
#include <stan/math/fwd/mat/fun/dot_self.hpp>
#include <stan/math/fwd/scal/fun/sqrt.hpp>
#include <stan/math/prim/mat/fun/Eigen.hpp>
#include <stan/math/prim/mat/fun/tcrossprod.hpp>
#include <stan/math/prim/mat/fun/unit_vector_constrain.hpp>
#include <stan/math/prim/scal/fun/inv.hpp>

namespace stan {
  namespace math {

    template <typename T, int R, int C>
    inline Eigen::Matrix<fvar<T>, R, C>
    unit_vector_constrain(const Eigen::Matrix<fvar<T>, R, C>& y) {
      using std::sqrt;
      using Eigen::Matrix;

      Matrix<T, R, C> y_t(y.size());
      for (int k = 0; k < y.size(); ++k)
        y_t.coeffRef(k) = y.coeff(k).val_;

      Matrix<T, R, C> unit_vector_y_t
        = unit_vector_constrain(y_t);
      Matrix<fvar<T>, R, C> unit_vector_y(y.size());
      for (int k = 0; k < y.size(); ++k)
        unit_vector_y.coeffRef(k).val_ = unit_vector_y_t.coeff(k);

      const T squared_norm = dot_self(y_t);
      const T norm = sqrt(squared_norm);
      const T inv_norm = inv(norm);
      Matrix<T, Eigen::Dynamic, Eigen::Dynamic> J
        = tcrossprod(y_t) / (-norm * squared_norm);

      // for each input position
      for (int m = 0; m < y.size(); ++m) {
        J.coeffRef(m, m) += inv_norm;
        // for each output position
        for (int k = 0; k < y.size(); ++k) {
          // chain from input to output
          unit_vector_y.coeffRef(k).d_ = J.coeff(k, m);
        }
      }
      return unit_vector_y;
    }

    template <typename T, int R, int C>
    inline Eigen::Matrix<fvar<T>, R, C>
    unit_vector_constrain(const Eigen::Matrix<fvar<T>, R, C>& y, fvar<T>& lp) {
      const fvar<T> squared_norm = dot_self(y);
      lp -= 0.5 * squared_norm;
      return unit_vector_constrain(y);
    }

  }
}
#endif
<commit_msg>includes and divide for unit vector constrain<commit_after>#ifndef STAN_MATH_FWD_MAT_FUN_UNIT_VECTOR_CONSTRAIN_HPP
#define STAN_MATH_FWD_MAT_FUN_UNIT_VECTOR_CONSTRAIN_HPP

#include <stan/math/fwd/core.hpp>
#include <stan/math/fwd/mat/fun/divide.hpp>
#include <stan/math/fwd/mat/fun/dot_self.hpp>
#include <stan/math/fwd/mat/fun/tcrossprod.hpp>
#include <stan/math/fwd/scal/fun/sqrt.hpp>
#include <stan/math/prim/mat/fun/divide.hpp>
#include <stan/math/prim/mat/fun/Eigen.hpp>
#include <stan/math/prim/mat/fun/tcrossprod.hpp>
#include <stan/math/prim/mat/fun/unit_vector_constrain.hpp>
#include <stan/math/prim/scal/fun/inv.hpp>

namespace stan {
  namespace math {

    template <typename T, int R, int C>
    inline Eigen::Matrix<fvar<T>, R, C>
    unit_vector_constrain(const Eigen::Matrix<fvar<T>, R, C>& y) {
      using std::sqrt;
      using Eigen::Matrix;

      Matrix<T, R, C> y_t(y.size());
      for (int k = 0; k < y.size(); ++k)
        y_t.coeffRef(k) = y.coeff(k).val_;

      Matrix<T, R, C> unit_vector_y_t
        = unit_vector_constrain(y_t);
      Matrix<fvar<T>, R, C> unit_vector_y(y.size());
      for (int k = 0; k < y.size(); ++k)
        unit_vector_y.coeffRef(k).val_ = unit_vector_y_t.coeff(k);

      const T squared_norm = dot_self(y_t);
      const T norm = sqrt(squared_norm);
      const T inv_norm = inv(norm);
      Matrix<T, Eigen::Dynamic, Eigen::Dynamic> J
        = divide(tcrossprod(y_t),  -norm * squared_norm);

      // for each input position
      for (int m = 0; m < y.size(); ++m) {
        J.coeffRef(m, m) += inv_norm;
        // for each output position
        for (int k = 0; k < y.size(); ++k) {
          // chain from input to output
          unit_vector_y.coeffRef(k).d_ = J.coeff(k, m);
        }
      }
      return unit_vector_y;
    }

    template <typename T, int R, int C>
    inline Eigen::Matrix<fvar<T>, R, C>
    unit_vector_constrain(const Eigen::Matrix<fvar<T>, R, C>& y, fvar<T>& lp) {
      const fvar<T> squared_norm = dot_self(y);
      lp -= 0.5 * squared_norm;
      return unit_vector_constrain(y);
    }

  }
}
#endif
<|endoftext|>


<commit_before>//                       MFEM
//
// Compile with: make ex1p
//
// Sample runs:
// mpirun -np 1 ex1p -m ../../data/inline-quad.mesh  -rs 0 -vis -o 2

#include "../../mfem.hpp"
#include <fstream>
#include <iostream>

using namespace std;
using namespace mfem;

double dist_value(const Vector &x, const int type)
{
    double ring_radius = 0.2;
    if (type == 1 || type == 2) { // circle of radius 0.2 - centered at 0.5, 0.5
        double dx = x(0) - 0.5,
           dy = x(1) - 0.5,
           rv = dx*dx + dy*dy;
        rv = rv > 0 ? pow(rv, 0.5) : 0;
        return rv - ring_radius; // +ve is the domain
    }
    else if (type == 3) { // walls at y = 0.0 and y = 1.0
        if (x(1) > 0.5) {
            return 1. - x(1);
        }
        else {
            return x(1);
        }
    }
    else {
        MFEM_ABORT(" Function type not implement yet.");
    }
    return 0.;
}

/// Returns distance from 0 level set. If dist +ve, point is inside the domain,
/// otherwise outside.
class Dist_Value_Coefficient : public Coefficient
{
private:
   int type;

public:
   Dist_Value_Coefficient(int type_)
      : Coefficient(), type(type_) { }

   virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip)
   {
      Vector x(3);
      T.Transform(ip, x);
      return dist_value(x, type);
   }
};

/// Level set coefficient - 1 inside the domain, -1 outside, 0 at the boundary.
class Dist_Level_Set_Coefficient : public Coefficient
{
private:
   int type;

public:
   Dist_Level_Set_Coefficient(int type_)
      : Coefficient(), type(type_) { }

   virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip)
   {
      Vector x(3);
      T.Transform(ip, x);
      double dist = dist_value(x, type);
      if (dist >= 0.) { return 1.; }
      else { return -1.; }
   }
};

/// Distance vector to the zero level-set.
class Dist_Vector_Coefficient : public VectorCoefficient
{
private:
   int type;

public:
   Dist_Vector_Coefficient(int dim_, int type_)
      : VectorCoefficient(dim_), type(type_) { }

   virtual void Eval(Vector &p, ElementTransformation &T,
                     const IntegrationPoint &ip)
   {
      Vector x(3);
      T.Transform(ip, x);
      p.SetSize(x.Size());
      if (type == 1 || type == 2) {
          double dist0 = dist_value(x, type);
          double theta = std::atan2(x(1)-0.5, x(0)-0.5);
          p(0) = -dist0*std::cos(theta);
          p(1) = -dist0*std::sin(theta);
      }
      else if (type == 3) {
          double dist0 = dist_value(x, type);
          p(0) = 0.;
          if (x(1) > 1. || x(1) < 0.5) {
              p(1) = -dist0;
          }
          else {
              p(1) = dist0;
          }
      }
   }
};

#define xy_p 2.
/// Boundary conditions
double dirichlet_velocity(const Vector &x, int type)
{
    if (type == 1) { // u = 0. on the boundaries.
        return 0.;
    }
    else if (type == 2) { // u = x^p+y^p
        return pow(x(0), xy_p) + pow(x(1), xy_p);
    }
    else if (type == 3) { // u = (1/pi^2)sin(pi*x*y),
        return 1./(M_PI*M_PI)*std::sin(M_PI*x(0)*x(1));
    }
    else {
        MFEM_ABORT(" Function type not implement yet.");
    }
    return 0.;
}

/// `f` for the Poisson problem (-nabla^2 u = f).
double rhs_fun(const Vector &x, int type)
{
    if (type == 1) {
        return 1.;
    }
    else if (type == 2) {
        double coeff = std::max(xy_p*(xy_p-1), 1.);
        double expon = std::max(0., xy_p-2);
        if (xy_p == 1) {
            return 0.;
        }
        else {
            return -coeff*std::pow(x(0), expon) - coeff*std::pow(x(1), expon);
        }
    }
    else if (type == 3) {
        return std::sin(M_PI*x(0)*x(1))*(x(0)*x(0)+x(1)*x(1));
    }
    else {
        MFEM_ABORT(" Function type not implement yet.");
    }
    return 0.;
}
<commit_msg>level set type 1 for 3D case<commit_after>//                       MFEM
//
// Compile with: make ex1p
//
// Sample runs:
// mpirun -np 1 ex1p -m ../../data/inline-quad.mesh  -rs 0 -vis -o 2

#include "../../mfem.hpp"
#include <fstream>
#include <iostream>

using namespace std;
using namespace mfem;

double dist_value(const Vector &x, const int type)
{
    double ring_radius = 0.2;
    if (type == 1 || type == 2) { // circle of radius 0.2 - centered at 0.5, 0.5
        double dx = x(0) - 0.5,
           dy = x(1) - 0.5,
           rv = dx*dx + dy*dy;
        if (x.Size() == 3) {
            double dz = x(2) - 0.5;
            rv += dz*dz;
        }
        rv = rv > 0 ? pow(rv, 0.5) : 0;
        return rv - ring_radius; // +ve is the domain
    }
    else if (type == 3) { // walls at y = 0.0 and y = 1.0
        if (x(1) > 0.5) {
            return 1. - x(1);
        }
        else {
            return x(1);
        }
    }
    else {
        MFEM_ABORT(" Function type not implement yet.");
    }
    return 0.;
}

/// Returns distance from 0 level set. If dist +ve, point is inside the domain,
/// otherwise outside.
class Dist_Value_Coefficient : public Coefficient
{
private:
   int type;

public:
   Dist_Value_Coefficient(int type_)
      : Coefficient(), type(type_) { }

   virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip)
   {
      Vector x(3);
      T.Transform(ip, x);
      return dist_value(x, type);
   }
};

/// Level set coefficient - 1 inside the domain, -1 outside, 0 at the boundary.
class Dist_Level_Set_Coefficient : public Coefficient
{
private:
   int type;

public:
   Dist_Level_Set_Coefficient(int type_)
      : Coefficient(), type(type_) { }

   virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip)
   {
      Vector x(3);
      T.Transform(ip, x);
      double dist = dist_value(x, type);
      if (dist >= 0.) { return 1.; }
      else { return -1.; }
   }
};

/// Distance vector to the zero level-set.
class Dist_Vector_Coefficient : public VectorCoefficient
{
private:
   int type;

public:
   Dist_Vector_Coefficient(int dim_, int type_)
      : VectorCoefficient(dim_), type(type_) { }

   virtual void Eval(Vector &p, ElementTransformation &T,
                     const IntegrationPoint &ip)
   {
      Vector x;
      T.Transform(ip, x);
      const int dim = x.Size();
      p.SetSize(dim);
      if (type == 1 || type == 2) {
          double dist0 = dist_value(x, type);
          for (int i = 0; i < dim; i++) { p(i) = 0.5 - x(i); }
          double length = p.Norml2();
          p *= dist0/length;
      }
      else if (type == 3) {
          double dist0 = dist_value(x, type);
          p(0) = 0.;
          if (x(1) > 1. || x(1) < 0.5) {
              p(1) = -dist0;
          }
          else {
              p(1) = dist0;
          }
      }
   }
};

#define xy_p 2.
/// Boundary conditions
double dirichlet_velocity(const Vector &x, int type)
{
    if (type == 1) { // u = 0. on the boundaries.
        return 0.;
    }
    else if (type == 2) { // u = x^p+y^p
        return pow(x(0), xy_p) + pow(x(1), xy_p);
    }
    else if (type == 3) { // u = (1/pi^2)sin(pi*x*y),
        return 1./(M_PI*M_PI)*std::sin(M_PI*x(0)*x(1));
    }
    else {
        MFEM_ABORT(" Function type not implement yet.");
    }
    return 0.;
}

/// `f` for the Poisson problem (-nabla^2 u = f).
double rhs_fun(const Vector &x, int type)
{
    if (type == 1) {
        return 1.;
    }
    else if (type == 2) {
        double coeff = std::max(xy_p*(xy_p-1), 1.);
        double expon = std::max(0., xy_p-2);
        if (xy_p == 1) {
            return 0.;
        }
        else {
            return -coeff*std::pow(x(0), expon) - coeff*std::pow(x(1), expon);
        }
    }
    else if (type == 3) {
        return std::sin(M_PI*x(0)*x(1))*(x(0)*x(0)+x(1)*x(1));
    }
    else {
        MFEM_ABORT(" Function type not implement yet.");
    }
    return 0.;
}
<|endoftext|>


<commit_before><commit_msg>We should use the same settings in the HLT Wrapper for TPC Offline Preprocessor as offline uses. Offline settings are set in CPass0 makeOCDB.C macro. Unfortunatelly I currently see no better solution but copying.<commit_after><|endoftext|>


<commit_before>#include "itkImage.h"

int main()
{

  typedef itk::Image< unsigned short, 3 > ImageType;

  ImageType::Pointer image = ImageType::New();

  ImageType::IndexType start;
  ImageType::SizeType  size;

  size[0]  = 200;  // size along X
  size[1]  = 200;  // size along Y
  size[2]  = 200;  // size along Z

  start[0] =   0;  // first index on X
  start[1] =   0;  // first index on Y
  start[2] =   0;  // first index on Z

  ImageType::RegionType region;

  image->SetRegions( region );
  image->Allocate();

  image->FillBuffer( 0 );

  double spacing[ 3 ];

  spacing[0] = 0.33; // spacing in millimeters
  spacing[1] = 0.33; // spacing in millimeters
  spacing[2] = 1.20; // spacing in millimeters

  image->SetSpacing( spacing );

  double origin[ 3 ];

  origin[0] = 0.0;  // coordinates of the
  origin[1] = 0.0;  // first pixel in N-D
  origin[2] = 0.0;

  image->SetOrigin( origin );

  return 0;

}

<commit_msg>ENH: Use of the PhysicalPointToIndex method added.<commit_after>#include "itkImage.h"
#include "itkPoint.h"

int main()
{

  typedef itk::Image< unsigned short, 3 > ImageType;

  ImageType::Pointer image = ImageType::New();

  ImageType::IndexType start;
  ImageType::SizeType  size;

  size[0]  = 200;  // size along X
  size[1]  = 200;  // size along Y
  size[2]  = 200;  // size along Z

  start[0] =   0;  // first index on X
  start[1] =   0;  // first index on Y
  start[2] =   0;  // first index on Z

  ImageType::RegionType region;

  image->SetRegions( region );
  image->Allocate();

  image->FillBuffer( 0 );

  double spacing[ 3 ];

  spacing[0] = 0.33; // spacing in millimeters
  spacing[1] = 0.33; // spacing in millimeters
  spacing[2] = 1.20; // spacing in millimeters

  image->SetSpacing( spacing );

  double origin[ 3 ];

  origin[0] = 0.0;  // coordinates of the
  origin[1] = 0.0;  // first pixel in N-D
  origin[2] = 0.0;

  image->SetOrigin( origin );

  typedef itk::Point<double,3> PointType;

  PointType point;

  point[0] = 1.45;
  point[1] = 7.21;
  point[2] = 9.28;

  ImageType::IndexType index;

  image->TransformPhysicalPointToIndex( point, index );

  return 0;

}

<|endoftext|>


<commit_before><commit_msg>Fix a race condition in async_loader_stdlib.cpp.<commit_after><|endoftext|>


<commit_before>#include "catch.hpp"

#include "libirc/wilson.h"

#include "libirc/atom.h"
#include "libirc/connectivity.h"
#include "libirc/conversion.h"
#include "libirc/io.h"
#include "libirc/molecule.h"

#include "config.h"

#include <cmath>
#include <iostream>

#ifdef HAVE_ARMA
#include <armadillo>
using vec3 = arma::vec3;
using vec = arma::vec;
using mat = arma::mat;
#elif HAVE_EIGEN3
#include <Eigen3/Eigen/Dense>
using vec3 = Eigen::Vector3d;
using vec = Eigen::VectorXd;
using mat = Eigen::MatrixXd;
#else
#error
#endif

using namespace irc;

TEST_CASE("Wilson B matrix", "[wilson]") {

  bool verbose{true};

  using namespace std;

  using namespace connectivity;
  using namespace molecule;
  using namespace wilson;
  using namespace tools;

  SECTION("H2 stretching") {

    // Define molecule
    Molecule<vec3> mol{{"H", {0., 0., 0.}}, {"H", {1., 0., 0.}}};

    // Compute interatomic distances
    mat dd{distances<vec3, mat>(mol)};

    // Compute adjacency matrix (graph)
    UGraph adj{adjacency_matrix(dd, mol)};

    // Compute distance matrix and predecessor matrix
    mat dist, pred;
    tie(dist, pred) = distance_matrix<mat>(adj);

    // Compute bonds
    vector<Bond> B{bonds(dist, mol)};

    REQUIRE(B.size() == 1);

    // Compute Wilson B matrix for H2 analytically
    mat Bwilson =
        wilson_matrix<vec3, vec, mat>(to_cartesian<vec3, vec>(mol), B);

    // Check Wilson B matrix size
    REQUIRE(linalg::size(Bwilson) == 6);

    if (verbose) {
      cout << "Wilson B matrix (analytical):" << endl;
      cout << Bwilson << endl;
    }

    // Compute Wilson B matrix for H2 numerically
    mat BwilsonN = wilson_matrix_numerical<vec3, vec, mat>(
        to_cartesian<vec3, vec>(mol), B);

    // Check Wilson B matrix size
    REQUIRE(linalg::size(BwilsonN) == 6);

    if (verbose) {
      cout << "Wilson B matrix (numerical):" << endl;
      cout << BwilsonN << endl;
    }

    // Check analytical and numerical Wilson matrices are the same
    SECTION("Analytical vs Numerical") {
      for (size_t i{0}; i < 6; i++) {
        Approx target{BwilsonN(i)};
        target.margin(1e-6);

        REQUIRE(Bwilson(i) == target);
      }
    }

    // Set bond stretching
    double d{0.01};
    vec dx{-d, 0.00, 0.00, d, 0.00, 0.00};

    SECTION("Bond change (analytical)") {
      // Compute transformation in IRC
      vec transformation{Bwilson * dx};

      if (verbose) {
        cout << "\nTransformation (analytical)" << endl;
        cout << Bwilson * dx << endl;
      }

      Approx target{2 * d};
      target.margin(1e-5);

      REQUIRE(transformation(0) == target);
    }

    SECTION("Bond change (numerical)") {
      // Compute transformation in IRC
      vec transformation{BwilsonN * dx};

      if (verbose) {
        cout << "\nTransformation (numerical)" << endl;
        cout << Bwilson * dx << endl;
      }

      Approx target{2 * d};
      target.margin(1e-5);

      REQUIRE(transformation(0) == target);
    }
  } // H2 stretching

  SECTION("H2O bending") {

    double angle(0.5);
    double angle_rad(angle / 180. * constants::pi);

    std::vector<mat> R{
        {// Rotation for H1
         {cos(angle_rad), -sin(angle_rad), 0},
         {sin(angle_rad), cos(angle_rad), 0},
         {0, 0, 1}},
        {// Rotation for O
         {1, 0, 0},
         {0, 1, 0},
         {0, 0, 1}},
        {// Rotation for H2
         {cos(-angle_rad), -sin(-angle_rad), 0},
         {sin(-angle_rad), cos(-angle_rad), 0},
         {0, 0, 1}},
    };

    Molecule<vec3> mol{
        {"H", {1.43, -1.10, 0.00}}, // H1
        {"O", {0.00, 0.00, 0.00}},  // O
        {"H", {-1.43, -1.10, 0.00}} // H2
    };

    // Allocate displacements in cartesian coordinates
    vec dx{linalg::zeros<vec>(3 * mol.size())};

    // Compute displacements
    for (size_t i{0}; i < 3; i++) {
      vec3 v{R[i] * mol[i].position - mol[i].position};

      dx(3 * i + 0) = v(0);
      dx(3 * i + 1) = v(1);
      dx(3 * i + 2) = v(2);
    }

    // Compute interatomic distances
    mat dd{distances<vec3, mat>(mol)};

    // Compute adjacency matrix (graph)
    UGraph adj{adjacency_matrix(dd, mol)};

    // Compute distance matrix and predecessor matrix
    mat dist, pred;
    tie(dist, pred) = distance_matrix<mat>(adj);

    // Compute bonds
    vector<Bond> B{bonds(dist, mol)};

    // Check number of bonds
    REQUIRE(B.size() == 2);

    // Compute bonds
    vector<Angle> A{angles(dist, pred, mol)};

    REQUIRE(A.size() == 1);

    // Compute Wilson B matrix for H2O analytically
    mat Bwilson =
        wilson_matrix<vec3, vec, mat>(to_cartesian<vec3, vec>(mol), B, A);

    // Check Wilson B matrix size
    REQUIRE(linalg::size(Bwilson) == 27);

    if (verbose) {
      // Print Wilson B matrix
      cout << "\nWilson B matrix (analytical):" << endl;
      cout << Bwilson << endl;
    }

    // Compute Wilson B matrix for H2O numerically
    mat BwilsonN = wilson_matrix_numerical<vec3, vec, mat>(
        to_cartesian<vec3, vec>(mol), B, A);

    // Check Wilson B matrix size
    REQUIRE(linalg::size(BwilsonN) == 27);

    if (verbose) {
      // Print Wilson B matrix
      cout << "\nWilson B matrix (numerical):" << endl;
      cout << BwilsonN << endl;
    }

    // Check analytical and numerical Wilson matrices are the same
    SECTION("Analytical vs Numerical") {
      for (size_t i{0}; i < 27; i++) {
        Approx target{BwilsonN(i)};
        target.margin(1e-6);

        REQUIRE(Bwilson(i) == target);
      }
    }

    SECTION("Analytical displacement") {
      // Compute displacement in internal coordinates
      vec displacement{Bwilson * dx};
      cout << "\nDisplacement:" << endl;
      cout << displacement << endl;

      // Check that bonds do not change
      SECTION("Bond change") {
        Approx target{0};

        target.margin(1e-4);

        REQUIRE(displacement(0) == target);
        REQUIRE(displacement(1) == target);
      }

      // Check change in angle
      SECTION("Angle change") {
        Approx target{2 * angle};

        target.margin(1e-3);

        REQUIRE(displacement(2) * 180 / constants::pi == target);
      }
    }
  }

  SECTION("H2O2 torsion") {

    double angle(1.0);
    double angle_rad(angle / 180. * constants::pi);

    mat R{{cos(angle_rad), -sin(angle_rad), 0},
          {sin(angle_rad), cos(angle_rad), 0},
          {0, 0, 1}};

    molecule::Molecule<vec3> molecule{
        {"H", {0.000, 0.947, -0.079}}, // H1
        {"O", {0.000, 0.000, 0.000}},  // O1
        {"O", {0.000, 0.000, 1.474}},  // O2
        {"H", {-0.854, -0.407, 1.553}} // H2
    };

    // Transform molecular coordinates from angstrom to bohr
    molecule::multiply_positions(molecule, conversion::angstrom_to_bohr);

    // Allocate displacements in cartesian coordinates
    vec dx{linalg::zeros<vec>(3 * molecule.size())};

    // Compute transformation for H1 rotation
    vec3 v{R * molecule[0].position - molecule[0].position};
    dx(0) = v(0);
    dx(1) = v(1);
    dx(2) = v(2);

    // Compute old dihedral (before rotation)
    double d_old{dihedral<vec3>(molecule[0].position,
                                molecule[1].position,
                                molecule[2].position,
                                molecule[3].position)};

    // Compute new dihedral angle (after rotation)
    double d_new{dihedral<vec3>(R * molecule[0].position,
                                molecule[1].position,
                                molecule[2].position,
                                molecule[3].position)};

    // Compute dihedral variation
    double d_diff{d_new - d_old};

    // Compute interatomic distances
    mat dd{distances<vec3, mat>(molecule)};

    // Compute adjacency matrix (graph)
    UGraph adj{adjacency_matrix(dd, molecule)};

    // Compute distance matrix and predecessor matrix
    mat dist, predecessors;
    tie(dist, predecessors) = distance_matrix<mat>(adj);

    // Compute bonds
    vector<Bond> B{bonds(dist, molecule)};

    // Check number of bonds
    REQUIRE(B.size() == 3);

    // Compute angles
    vector<Angle> A{angles(dist, predecessors, molecule)};

    // Check number of angles
    REQUIRE(A.size() == 2);

    // Compute dihedrals
    vector<Dihedral> D{dihedrals(dist, predecessors, molecule)};

    // Check number of dihedrals
    REQUIRE(D.size() == 1);

    // Compute Wilson's B matrix
    mat Bwilson = wilson_matrix<vec3, vec, mat>(
        to_cartesian<vec3, vec>(molecule), B, A, D);

    REQUIRE(linalg::size(Bwilson) == 72);

    if (verbose) {
      // Print Wilson B matrix
      cout << "\nWilson B matrix (analytical):" << endl;
      cout << Bwilson << endl;
    }

    // Compute Wilson's B matrix
    mat BwilsonN = wilson_matrix_numerical<vec3, vec, mat>(
        to_cartesian<vec3, vec>(molecule), B, A, D);

    REQUIRE(linalg::size(BwilsonN) == 72);

    if (verbose) {
      // Print Wilson B matrix
      cout << "\nWilson B matrix (numerical):" << endl;
      cout << BwilsonN << endl;
    }

    // Check analytical and numerical Wilson matrices are the same
    SECTION("Analytical vs Numerical") {
      for (size_t i{0}; i < 72; i++) {
        Approx target{BwilsonN(i)};
        target.margin(1e-6);

        REQUIRE(Bwilson(i) == target);
      }
    }

    SECTION("Analytical displacements") {
      // Compute displacement in internal coordinates
      vec displacement{Bwilson * dx};

      // Print displacements
      if (verbose) {
        cout << "\nDisplacement (analytical):" << endl;
        cout << displacement << endl;
      }

      // Check that bonds do not change
      SECTION("Bonds change") {
        Approx target{0};

        target.margin(1e-3);

        REQUIRE(displacement(0) == target);
        REQUIRE(displacement(1) == target);
        REQUIRE(displacement(2) == target);
      }

      // Check change in angle
      SECTION("Angles change") {
        Approx target{0};

        target.margin(1e-4);

        REQUIRE(displacement(3) == target);
        REQUIRE(displacement(4) == target);
      }

      // Check change in angle
      SECTION("Dihedral change") {
        Approx target{d_diff};

        target.margin(1e-4);

        REQUIRE(displacement(5) == target);
      }
    }

    SECTION("Numerical displacements") {
      // Compute displacement in internal coordinates
      vec displacement{BwilsonN * dx};

      // Print displacements
      if (verbose) {
        cout << "\nDisplacement (numerical):" << endl;
        cout << displacement << endl;
      }

      // Check that bonds do not change
      SECTION("Bonds change") {
        Approx target{0};

        target.margin(1e-3);

        REQUIRE(displacement(0) == target);
        REQUIRE(displacement(1) == target);
        REQUIRE(displacement(2) == target);
      }

      // Check change in angle
      SECTION("Angles change") {
        Approx target{0};

        target.margin(1e-4);

        REQUIRE(displacement(3) == target);
        REQUIRE(displacement(4) == target);
      }

      // Check change in angle
      SECTION("Dihedral change") {
        Approx target{d_diff};

        target.margin(1e-4);

        REQUIRE(displacement(5) == target);
      }
    }
  }
}

TEST_CASE("Wilson") {
  using namespace std;

  using namespace connectivity;
  using namespace molecule;
  using namespace tools;
  using namespace wilson;

  bool verbose{true};

  Molecule<vec3> mol{
      io::load_xyz<vec3>(config::molecules_dir + "water_dimer_2.xyz")};

  // Transform molecule to bohr
  multiply_positions(mol, conversion::angstrom_to_bohr);

  // Compute interatomic distances
  mat dd{distances<vec3, mat>(mol)};

  // Compute adjacency matrix (graph)
  UGraph adj{adjacency_matrix(dd, mol)};

  // Compute distance matrix and predecessor matrix
  mat dist, predecessors;
  tie(dist, predecessors) = distance_matrix<mat>(adj);

  // Compute bonds
  vector<Bond> B{bonds(dist, mol)};

  // Compute angles
  vector<Angle> A{angles(dist, predecessors, mol)};

  // Compute dihedrals
  vector<Dihedral> D{dihedrals(dist, predecessors, mol)};

  cout << "q =\n"
       << connectivity::cartesian_to_irc<vec3, vec>(
              to_cartesian<vec3, vec>(mol), B, A, D)
       << endl;

  // Return Wilson's B matrix (analytical)
  mat WB{wilson_matrix<vec3, vec, mat>(to_cartesian<vec3, vec>(mol), B, A, D)};

  if (verbose) {
    cout << "B (analytical) =\n" << WB << endl;
  }

  // Return Wilson's B matrix (numerical)
  mat WBN{wilson_matrix_numerical<vec3, vec, mat>(
      to_cartesian<vec3, vec>(mol), B, A, D)};

  if (verbose) {
    cout << "B (numerical) =\n" << WBN << endl;
  }

  size_t n{linalg::size(WB)};
  for (size_t i{0}; i < n; i++) {
    Approx target(WBN(i));
    target.margin(1e-5);

    REQUIRE(WB(i) == target);
  }
}<commit_msg>Reduce LOC in wilson test<commit_after>#include "catch.hpp"

#include "libirc/wilson.h"

#include "libirc/atom.h"
#include "libirc/connectivity.h"
#include "libirc/conversion.h"
#include "libirc/io.h"
#include "libirc/molecule.h"

#include "config.h"

#include <cmath>

#ifdef HAVE_ARMA
#include <armadillo>
using vec3 = arma::vec3;
using vec = arma::vec;
using mat = arma::mat;
#elif HAVE_EIGEN3
#include <Eigen3/Eigen/Dense>
using vec3 = Eigen::Vector3d;
using vec = Eigen::VectorXd;
using mat = Eigen::MatrixXd;
#else
#error
#endif

using namespace irc;

TEST_CASE("Wilson B matrix for single fragments", "[wilson]") {
  using namespace connectivity;
  using namespace molecule;
  using namespace wilson;
  using namespace tools;

  SECTION("H2 stretching") {
    // Define molecule
    Molecule<vec3> mol{{"H", {0., 0., 0.}}, {"H", {1., 0., 0.}}};
    // Molecular connectivity
    mat dd{distances<vec3, mat>(mol)};
    UGraph adj{adjacency_matrix(dd, mol)};
    mat dist, pred;
    std::tie(dist, pred) = distance_matrix<mat>(adj);

    // Compute bonds
    std::vector<Bond> B{bonds(dist, mol)};
    REQUIRE(B.size() == 1);

    // Compute Wilson B matrix for H2 analytically
    mat Bwilson =
        wilson_matrix<vec3, vec, mat>(to_cartesian<vec3, vec>(mol), B);

    // Check Wilson B matrix size
    INFO("Wilson B matrix (analytical):\n" << Bwilson);
    REQUIRE(linalg::size(Bwilson) == 6);

    // Compute Wilson B matrix for H2 numerically
    mat BwilsonN = wilson_matrix_numerical<vec3, vec, mat>(
        to_cartesian<vec3, vec>(mol), B);

    // Check Wilson B matrix size
    INFO("Wilson B matrix (numerical):\n" << BwilsonN);
    REQUIRE(linalg::size(BwilsonN) == 6);

    // Check analytical and numerical Wilson matrices are the same
    INFO("Analytical vs Numerical");
    for (size_t i{0}; i < 6; i++)
        REQUIRE(Bwilson(i) == Approx(BwilsonN(i)).margin(1e-6));

    INFO("Transformation with bond stretch");
    const double d{0.01};
    vec dx{-d, 0.00, 0.00, d, 0.00, 0.00};

    INFO("Analytical transformation");
    vec analytical_transformation = Bwilson * dx;
    CAPTURE(analytical_transformation);
    REQUIRE(analytical_transformation(0) == Approx(2*d).margin(1e-5));

    INFO("Numerical transformation");
    vec numerical_transformation = BwilsonN * dx;
    CAPTURE(numerical_transformation);
    REQUIRE(numerical_transformation(0) == Approx(2*d).margin(1e-5));
  } // H2 stretching

  SECTION("H2O bending") {

    double angle(0.5);
    double angle_rad(angle / 180. * constants::pi);

    const std::vector<mat> R{
        {// Rotation for H1
         {cos(angle_rad), -sin(angle_rad), 0},
         {sin(angle_rad), cos(angle_rad), 0},
         {0, 0, 1}},
        {// Rotation for O
         {1, 0, 0},
         {0, 1, 0},
         {0, 0, 1}},
        {// Rotation for H2
         {cos(-angle_rad), -sin(-angle_rad), 0},
         {sin(-angle_rad), cos(-angle_rad), 0},
         {0, 0, 1}},
    };

    const Molecule<vec3> mol{
        {"H", {1.43, -1.10, 0.00}}, // H1
        {"O", {0.00, 0.00, 0.00}},  // O
        {"H", {-1.43, -1.10, 0.00}} // H2
    };

    // Allocate displacements in cartesian coordinates
    vec dx{linalg::zeros<vec>(3 * mol.size())};

    // Compute displacements
    for (size_t i{0}; i < 3; i++) {
      vec3 v{R[i] * mol[i].position - mol[i].position};

      dx(3 * i + 0) = v(0);
      dx(3 * i + 1) = v(1);
      dx(3 * i + 2) = v(2);
    }

    // Molecular connectivity
    mat dd{distances<vec3, mat>(mol)};
    UGraph adj{adjacency_matrix(dd, mol)};
    mat dist, pred;
    std::tie(dist, pred) = distance_matrix<mat>(adj);

    // Compute bonds
    const auto B = bonds(dist, mol);
    REQUIRE(B.size() == 2);

    // Compute angles
    const auto A = angles(dist, pred, mol);
    REQUIRE(A.size() == 1);

    // Compute Wilson B matrix for H2O analytically
    mat Bwilson =
        wilson_matrix<vec3, vec, mat>(to_cartesian<vec3, vec>(mol), B, A);
    REQUIRE(linalg::size(Bwilson) == 27);
    INFO("Wilson B matrix (analytical):\n" << Bwilson);

    // Compute Wilson B matrix for H2O numerically
    const mat BwilsonN = wilson_matrix_numerical<vec3, vec, mat>(
                           to_cartesian<vec3, vec>(mol), B, A);
    REQUIRE(linalg::size(BwilsonN) == 27);
    INFO("Wilson B matrix (numerical):\n" << BwilsonN);

    // Check analytical and numerical Wilson matrices are the same
    for (size_t i{0}; i < 27; i++)
      REQUIRE(Bwilson(i) == Approx(BwilsonN(i)).margin(1e-6));

    INFO("Compute displacements in internal coordinates");
    const vec displacement = Bwilson * dx;
    CAPTURE(displacement);

    INFO("Check bond change");
    REQUIRE(displacement(0) == Approx(0).margin(1e-4));
    REQUIRE(displacement(1) == Approx(0).margin(1e-4));

    INFO("Check angle change");
    REQUIRE(displacement(2) == Approx(2*angle_rad).margin(1e-3));
  }

  SECTION("H2O2 torsion") {

    const double angle(1.0);
    const double angle_rad(angle / 180. * constants::pi);

    const mat R{{cos(angle_rad), -sin(angle_rad), 0},
          {sin(angle_rad), cos(angle_rad), 0},
          {0, 0, 1}};

    molecule::Molecule<vec3> molecule{
        {"H", {0.000, 0.947, -0.079}}, // H1
        {"O", {0.000, 0.000, 0.000}},  // O1
        {"O", {0.000, 0.000, 1.474}},  // O2
        {"H", {-0.854, -0.407, 1.553}} // H2
    };
    molecule::multiply_positions(molecule, conversion::angstrom_to_bohr);

    // Allocate displacements in cartesian coordinates
    vec dx{linalg::zeros<vec>(3 * molecule.size())};

    // Compute transformation for H1 rotation
    vec3 v{R * molecule[0].position - molecule[0].position};
    dx(0) = v(0);
    dx(1) = v(1);
    dx(2) = v(2);

    // Compute old dihedral (before rotation)
    double d_old{dihedral<vec3>(molecule[0].position,
                                molecule[1].position,
                                molecule[2].position,
                                molecule[3].position)};

    // Compute new dihedral angle (after rotation)
    double d_new{dihedral<vec3>(R * molecule[0].position,
                                molecule[1].position,
                                molecule[2].position,
                                molecule[3].position)};

    // Compute dihedral variation
    double d_diff{d_new - d_old};

    // Compute interatomic distances
    mat dd{distances<vec3, mat>(molecule)};
    UGraph adj{adjacency_matrix(dd, molecule)};
    mat dist, predecessors;
    std::tie(dist, predecessors) = distance_matrix<mat>(adj);

    // Compute bonds, angles and dihedrals
    const std::vector<Bond> B{bonds(dist, molecule)};
    REQUIRE(B.size() == 3);
    const std::vector<Angle> A{angles(dist, predecessors, molecule)};
    REQUIRE(A.size() == 2);
    const std::vector<Dihedral> D{dihedrals(dist, predecessors, molecule)};
    REQUIRE(D.size() == 1);

    // Compute Wilson's B matrix
    const mat Bwilson = wilson_matrix<vec3, vec, mat>(
        to_cartesian<vec3, vec>(molecule), B, A, D);
    REQUIRE(linalg::size(Bwilson) == 72);
    INFO("Wilson B matrix (analytical):\n" << Bwilson);

    // Compute Wilson's B matrix
    const mat BwilsonN = wilson_matrix_numerical<vec3, vec, mat>(
        to_cartesian<vec3, vec>(molecule), B, A, D);
    REQUIRE(linalg::size(BwilsonN) == 72);
    INFO("Wilson B matrix (numerical):\n" << BwilsonN);

    // Check analytical and numerical Wilson matrices are the same
    INFO("Check Analytical vs Numerical Wilson B matrix");
    for (size_t i{0}; i < 72; i++) {
      REQUIRE(Bwilson(i) == Approx(BwilsonN(i)).margin(1e-6));
    }

    SECTION("Analytical displacements") {
      // Compute displacement in internal coordinates
      const vec displacement{Bwilson * dx};
      INFO("Displacement (analytical):\n" << displacement);

      INFO("Bonds do not change");
      REQUIRE(displacement(0) == Approx(0).margin(1e-3));
      REQUIRE(displacement(1) == Approx(0).margin(1e-3));
      REQUIRE(displacement(2) == Approx(0).margin(1e-3));

      INFO("Angles do not change");
      REQUIRE(displacement(3) == Approx(0).margin(1e-4));
      REQUIRE(displacement(4) == Approx(0).margin(1e-4));

      INFO("Dihedral should change");
      REQUIRE(displacement(5) == Approx(d_diff).margin(1e-4));
    }

    SECTION("Numerical displacements") {
      // Compute displacement in internal coordinates
      const vec displacement{BwilsonN * dx};
      INFO("Displacement (numerical):\n" << displacement);

      INFO("Bonds do not change");
      REQUIRE(displacement(0) == Approx(0).margin(1e-3));
      REQUIRE(displacement(1) == Approx(0).margin(1e-3));
      REQUIRE(displacement(2) == Approx(0).margin(1e-3));

      INFO("Angles do not change");
      REQUIRE(displacement(3) == Approx(0).margin(1e-4));
      REQUIRE(displacement(4) == Approx(0).margin(1e-4));

      INFO("Dihedral should change");
      REQUIRE(displacement(5) == Approx(d_diff).margin(1e-4));
    }
  }
}

TEST_CASE("Wilson B matrix for water dimer", "[wilson]") {
  using namespace connectivity;
  using namespace molecule;
  using namespace tools;
  using namespace wilson;
  using namespace io;

  Molecule<vec3> mol = load_xyz<vec3>(config::molecules_dir + "water_dimer_2.xyz");
  multiply_positions(mol, conversion::angstrom_to_bohr);

  // Compute interatomic distances
  mat dd{distances<vec3, mat>(mol)};
  UGraph adj{adjacency_matrix(dd, mol)};
  mat dist, predecessors;
  std::tie(dist, predecessors) = distance_matrix<mat>(adj);

  // Compute bonds
  std::vector<Bond> B{bonds(dist, mol)};
  std::vector<Angle> A{angles(dist, predecessors, mol)};
  std::vector<Dihedral> D{dihedrals(dist, predecessors, mol)};

  const auto q = connectivity::cartesian_to_irc<vec3, vec>(
              to_cartesian<vec3, vec>(mol), B, A, D);
  CAPTURE(q);

  const mat wilson_b_analytical =
    wilson_matrix<vec3, vec, mat>(to_cartesian<vec3, vec>(mol), B, A, D);
  CAPTURE(wilson_b_analytical);

  const mat wilson_b_numerical =
    wilson_matrix_numerical<vec3, vec, mat>(to_cartesian<vec3, vec>(mol), B, A, D);
  CAPTURE(wilson_b_numerical);

  REQUIRE(linalg::size(wilson_b_analytical) == linalg::size(wilson_b_numerical));

  const size_t n = linalg::size(wilson_b_analytical);
  for (size_t i{0}; i < n; i++) {
    REQUIRE(wilson_b_analytical(i) == Approx(wilson_b_numerical(i)).margin(1e-5));
  }
}
<|endoftext|>


<commit_before><commit_msg> -- Remove the setup for the telemetry service.<commit_after><|endoftext|>