<commit_before>#include "routing/index_graph.hpp"

#include "routing/restrictions_serialization.hpp"
#include "routing/routing_options.hpp"
#include "routing/world_graph.hpp"

#include "platform/settings.hpp"

#include "base/assert.hpp"
#include "base/checked_cast.hpp"
#include "base/exception.hpp"

#include <algorithm>
#include <cstdlib>
#include <limits>

namespace
{
using namespace base;
using namespace routing;
using namespace std;

bool IsUTurn(Segment const & u, Segment const & v)
{
  return u.GetFeatureId() == v.GetFeatureId() && u.GetSegmentIdx() == v.GetSegmentIdx() &&
         u.IsForward() != v.IsForward();
}

bool IsRestricted(RestrictionVec const & restrictions, Segment const & u, Segment const & v,
                  bool isOutgoing)
{
  uint32_t const featureIdFrom = isOutgoing ? u.GetFeatureId() : v.GetFeatureId();
  uint32_t const featureIdTo = isOutgoing ? v.GetFeatureId() : u.GetFeatureId();

  if (!binary_search(restrictions.cbegin(), restrictions.cend(),
                     Restriction(Restriction::Type::No, {featureIdFrom, featureIdTo})))
  {
    return false;
  }

  if (featureIdFrom != featureIdTo)
    return true;

  // @TODO(bykoianko) According to current code if a feature id is marked as a feature with
  // restrictricted U-turn it's restricted to make a U-turn on the both ends of the feature.
  // Generally speaking it's wrong. In osm there's information about the end of the feature
  // where the U-turn is restricted. It's necessary to pass the data to mwm and to use it here.
  // Please see test LineGraph_RestrictionF1F1No for details.
  //
  // Another example when it's necessary to be aware about feature end participated in restriction
  // is
  //        *---F1---*
  //        |        |
  // *--F3--A        B--F4--*
  //        |        |
  //        *---F2---*
  // In case of restriction F1-A-F2 or F1-B-F2 of any type (No, Only) the important information
  // is lost.
  return IsUTurn(u, v);
}
}  // namespace

namespace routing
{
IndexGraph::IndexGraph(shared_ptr<Geometry> geometry, shared_ptr<EdgeEstimator> estimator,
                       RoutingOptions routingOptions)
  : m_geometry(geometry),
    m_estimator(move(estimator)),
    m_avoidRoutingOptions(routingOptions)
{
  CHECK(m_geometry, ());
  CHECK(m_estimator, ());
}

bool IndexGraph::IsJoint(RoadPoint const & roadPoint) const
{
  return m_roadIndex.GetJointId(roadPoint) != Joint::kInvalidId;
}

void IndexGraph::GetEdgeList(Segment const & segment, bool isOutgoing, vector<SegmentEdge> & edges)
{
  RoadPoint const roadPoint = segment.GetRoadPoint(isOutgoing);
  Joint::Id const jointId = m_roadIndex.GetJointId(roadPoint);

  if (jointId != Joint::kInvalidId)
  {
    m_jointIndex.ForEachPoint(jointId, [&](RoadPoint const & rp) {
      GetNeighboringEdges(segment, rp, isOutgoing, edges);
    });
  }
  else
  {
    GetNeighboringEdges(segment, roadPoint, isOutgoing, edges);
  }
}

void IndexGraph::GetLastPointsForJoint(vector<Segment> const & children,
                                       bool isOutgoing,
                                       vector<uint32_t> & lastPoints)
{
  CHECK(lastPoints.empty(), ());

  lastPoints.reserve(children.size());
  for (auto const & child : children)
  {
    uint32_t const startPointId = child.GetPointId(!isOutgoing /* front */);
    uint32_t const pointsNumber = m_geometry->GetRoad(child.GetFeatureId()).GetPointsCount();
    CHECK_LESS(startPointId, pointsNumber, ());

    uint32_t endPointId;
    // child.IsForward() == isOutgoing
    //   This is the direction, indicating the end of the road,
    //   where we should go for building JointSegment.
    // You can retrieve such result if bust possible options of |child.IsForward()| and |isOutgoing|.
    bool forward = child.IsForward() == isOutgoing;
    if (IsRoad(child.GetFeatureId()))
    {
      std::tie(std::ignore, endPointId) =
        GetRoad(child.GetFeatureId()).FindNeighbor(startPointId, forward,
                                                   pointsNumber);
    }
    else
    {
      // child.GetFeatureId() can be not road in this case:
      // -->--> { -->-->--> } -->
      // Where { ... } - borders of mwm
      // Here one feature, which enter from mwm A to mwm B and then exit from B to A.
      // And in mwm B no other feature cross this one, those feature is in geometry
      // and absent in roadIndex.
      // In this case we just return endPointNumber.
      endPointId = forward ? pointsNumber - 1 : 0;
    }

    lastPoints.push_back(endPointId);
  }
}

void IndexGraph::GetEdgeList(Segment const & parent, bool isOutgoing, vector<JointEdge> & edges,
                             vector<RouteWeight> & parentWeights)
{
  vector<Segment> possibleChildren;
  GetSegmentCandidateForJoint(parent, isOutgoing, possibleChildren);

  vector<uint32_t> lastPoints;
  GetLastPointsForJoint(possibleChildren, isOutgoing, lastPoints);

  ReconstructJointSegment(parent, possibleChildren, lastPoints,
                          isOutgoing, edges, parentWeights);
}

boost::optional<JointEdge>
IndexGraph::GetJointEdgeByLastPoint(Segment const & parent, Segment const & firstChild,
                                    bool isOutgoing, uint32_t lastPoint)
{
  vector<Segment> const possibleChilds = {firstChild};
  vector<uint32_t> const lastPoints = {lastPoint};

  vector<JointEdge> edges;
  vector<RouteWeight> parentWeights;
  ReconstructJointSegment(parent, possibleChilds, lastPoints,
                          isOutgoing, edges, parentWeights);

  CHECK_LESS_OR_EQUAL(edges.size(), 1, ());
  if (edges.size() == 1)
    return {edges.back()};

  return {};
}

void IndexGraph::Build(uint32_t numJoints)
{
  m_jointIndex.Build(m_roadIndex, numJoints);
}

void IndexGraph::Import(vector<Joint> const & joints)
{
  m_roadIndex.Import(joints);
  CHECK_LESS_OR_EQUAL(joints.size(), numeric_limits<uint32_t>::max(), ());
  Build(checked_cast<uint32_t>(joints.size()));
}

void IndexGraph::SetRestrictions(RestrictionVec && restrictions)
{
  ASSERT(is_sorted(restrictions.cbegin(), restrictions.cend()), ());
  m_restrictions = move(restrictions);
}

void IndexGraph::SetRoadAccess(RoadAccess && roadAccess) { m_roadAccess = move(roadAccess); }

void IndexGraph::GetOutgoingEdgesList(Segment const & segment, vector<SegmentEdge> & edges)
{
  edges.clear();
  GetEdgeList(segment, true /* isOutgoing */, edges);
}

void IndexGraph::GetIngoingEdgesList(Segment const & segment, vector<SegmentEdge> & edges)
{
  edges.clear();
  GetEdgeList(segment, false /* isOutgoing */, edges);
}

RouteWeight IndexGraph::CalcSegmentWeight(Segment const & segment)
{
  return RouteWeight(
      m_estimator->CalcSegmentWeight(segment, m_geometry->GetRoad(segment.GetFeatureId())));
}

void IndexGraph::GetNeighboringEdges(Segment const & from, RoadPoint const & rp, bool isOutgoing,
                                     vector<SegmentEdge> & edges)
{
  RoadGeometry const & road = m_geometry->GetRoad(rp.GetFeatureId());

  if (!road.IsValid())
    return;

  if (!road.SuitableForOptions(m_avoidRoutingOptions))
    return;

  bool const bidirectional = !road.IsOneWay();

  if ((isOutgoing || bidirectional) && rp.GetPointId() + 1 < road.GetPointsCount())
  {
    GetNeighboringEdge(from,
                       Segment(from.GetMwmId(), rp.GetFeatureId(), rp.GetPointId(), isOutgoing),
                       isOutgoing, edges);
  }

  if ((!isOutgoing || bidirectional) && rp.GetPointId() > 0)
  {
    GetNeighboringEdge(
        from, Segment(from.GetMwmId(), rp.GetFeatureId(), rp.GetPointId() - 1, !isOutgoing),
        isOutgoing, edges);
  }
}

void IndexGraph::GetSegmentCandidateForJoint(Segment const & parent, bool isOutgoing,
                                             vector<Segment> & children)
{
  RoadPoint const roadPoint = parent.GetRoadPoint(isOutgoing);
  Joint::Id const jointId = m_roadIndex.GetJointId(roadPoint);

  if (jointId == Joint::kInvalidId)
    return;

  m_jointIndex.ForEachPoint(jointId, [&](RoadPoint const & rp)
  {
    RoadGeometry const & road = m_geometry->GetRoad(rp.GetFeatureId());
    if (!road.IsValid())
      return;

    if (!road.SuitableForOptions(m_avoidRoutingOptions))
      return;

    bool const bidirectional = !road.IsOneWay();
    auto const pointId = rp.GetPointId();

    if ((isOutgoing || bidirectional) && pointId + 1 < road.GetPointsCount())
      children.emplace_back(parent.GetMwmId(), rp.GetFeatureId(), pointId, isOutgoing);

    if ((!isOutgoing || bidirectional) && pointId > 0)
      children.emplace_back(parent.GetMwmId(), rp.GetFeatureId(), pointId - 1, !isOutgoing);
  });
}

/// \brief Prolongs segments from |parent| to |firstChildren| directions in order to
///        create JointSegments.
/// \param |firstChildren| - vecotor of neigbouring segments from parent.
/// \param |lastPointIds| - vector of the end numbers of road points for |firstChildren|.
/// \param |jointEdges| - the result vector with JointEdges.
/// \param |parentWeights| - see |IndexGraphStarterJoints::GetEdgeList| method about this argument.
///                          Shotly - in case of |isOutgoing| == false, method saves here the weights
///                                   from parent to firstChildren.
void IndexGraph::ReconstructJointSegment(Segment const & parent,
                                         vector<Segment> const & firstChildren,
                                         vector<uint32_t> const & lastPointIds,
                                         bool isOutgoing,
                                         vector<JointEdge> & jointEdges,
                                         vector<RouteWeight> & parentWeights)
{
  CHECK_EQUAL(firstChildren.size(), lastPointIds.size(), ());

  for (size_t i = 0; i < firstChildren.size(); ++i)
  {
    auto const & firstChild = firstChildren[i];
    auto const lastPointId = lastPointIds[i];

    uint32_t currentPointId = firstChild.GetPointId(!isOutgoing /* front */);
    CHECK_NOT_EQUAL(currentPointId, lastPointId,
                    ("Invariant violated, can not build JointSegment,"
                     "started and ended in the same point."));

    auto const increment = [currentPointId, lastPointId](uint32_t pointId) {
      return currentPointId < lastPointId ? pointId + 1 : pointId - 1;
    };

    if (m_roadAccess.GetFeatureType(firstChild.GetFeatureId()) == RoadAccess::Type::No)
      continue;

    if (m_roadAccess.GetPointType(parent.GetRoadPoint(isOutgoing)) == RoadAccess::Type::No)
      continue;

    // Check firstChild for UTurn.
    RoadPoint rp = parent.GetRoadPoint(isOutgoing);
    if (IsUTurn(parent, firstChild) && m_roadIndex.GetJointId(rp) == Joint::kInvalidId
        && !m_geometry->GetRoad(parent.GetFeatureId()).IsEndPointId(rp.GetPointId()))
    {
      continue;
    }

    if (parent.GetFeatureId() != firstChild.GetFeatureId() &&
        IsRestricted(m_restrictions, parent, firstChild, isOutgoing))
    {
      continue;
    }

    // Check current JointSegment for bad road access between segments.
    rp = firstChild.GetRoadPoint(isOutgoing);
    uint32_t start = currentPointId;
    bool noRoadAccess = false;
    do
    {
      if (m_roadAccess.GetPointType(rp) == RoadAccess::Type::No)
      {
        noRoadAccess = true;
        break;
      }

      start = increment(start);
      rp.SetPointId(start);
    } while (start != lastPointId);

    if (noRoadAccess)
      continue;

    bool forward = currentPointId < lastPointId;
    Segment current = firstChild;
    Segment prev = parent;
    RouteWeight summaryWeight;

    do
    {
      RouteWeight const weight = CalcSegmentWeight(isOutgoing ? current : prev) +
                                 GetPenalties(isOutgoing ? prev : current, isOutgoing ? current : prev);

      if (isOutgoing || prev != parent)
        summaryWeight += weight;

      if (prev == parent)
        parentWeights.emplace_back(weight);

      prev = current;
      current.Next(forward);
      currentPointId = increment(currentPointId);
    } while (currentPointId != lastPointId);

    jointEdges.emplace_back(isOutgoing ? JointSegment(firstChild, prev) :
                                         JointSegment(prev, firstChild),
                            summaryWeight);
  }
}

void IndexGraph::GetNeighboringEdge(Segment const & from, Segment const & to, bool isOutgoing,
                                    vector<SegmentEdge> & edges)
{
  // Blocking U-turns on internal feature points.
  RoadPoint const rp = from.GetRoadPoint(isOutgoing);
  if (IsUTurn(from, to) && m_roadIndex.GetJointId(rp) == Joint::kInvalidId
      && !m_geometry->GetRoad(from.GetFeatureId()).IsEndPointId(rp.GetPointId()))
  {
    return;
  }

  if (IsRestricted(m_restrictions, from, to, isOutgoing))
    return;

  if (m_roadAccess.GetFeatureType(to.GetFeatureId()) == RoadAccess::Type::No)
    return;

  if (m_roadAccess.GetPointType(rp) == RoadAccess::Type::No)
    return;

  RouteWeight const weight = CalcSegmentWeight(isOutgoing ? to : from) +
                             GetPenalties(isOutgoing ? from : to, isOutgoing ? to : from);
  edges.emplace_back(to, weight);
}

RouteWeight IndexGraph::GetPenalties(Segment const & u, Segment const & v)
{
  bool const fromPassThroughAllowed = m_geometry->GetRoad(u.GetFeatureId()).IsPassThroughAllowed();
  bool const toPassThroughAllowed = m_geometry->GetRoad(v.GetFeatureId()).IsPassThroughAllowed();
  // Route crosses border of pass-through/non-pass-through area if |u| and |v| have different
  // pass through restrictions.
  int32_t const passThroughPenalty = fromPassThroughAllowed == toPassThroughAllowed ? 0 : 1;

  // We do not distinguish between RoadAccess::Type::Private and RoadAccess::Type::Destination for now.
  bool const fromAccessAllowed = m_roadAccess.GetFeatureType(u.GetFeatureId()) == RoadAccess::Type::Yes;
  bool const toAccessAllowed = m_roadAccess.GetFeatureType(v.GetFeatureId()) == RoadAccess::Type::Yes;
  // Route crosses border of access=yes/access={private, destination} area if |u| and |v| have different
  // access restrictions.
  int32_t accessPenalty = fromAccessAllowed == toAccessAllowed ? 0 : 1;

  // RoadPoint between u and v is front of u.
  auto const rp = u.GetRoadPoint(true /* front */);
  // No double penalty for barriers on the border of access=yes/access={private, destination} area.
  if (m_roadAccess.GetPointType(rp) != RoadAccess::Type::Yes)
    accessPenalty = 1;

  auto const uTurnPenalty = IsUTurn(u, v) ? m_estimator->GetUTurnPenalty() : 0.0;

  return RouteWeight(uTurnPenalty /* weight */, passThroughPenalty, accessPenalty, 0.0 /* transitTime */);
}

WorldGraphMode IndexGraph::GetMode() const { return WorldGraphMode::Undefined; }
}  // namespace routing
<commit_msg>[routing] review fixes<commit_after>#include "routing/index_graph.hpp"

#include "routing/restrictions_serialization.hpp"
#include "routing/routing_options.hpp"
#include "routing/world_graph.hpp"

#include "platform/settings.hpp"

#include "base/assert.hpp"
#include "base/checked_cast.hpp"
#include "base/exception.hpp"

#include <algorithm>
#include <cstdlib>
#include <limits>

namespace
{
using namespace base;
using namespace routing;
using namespace std;

bool IsUTurn(Segment const & u, Segment const & v)
{
  return u.GetFeatureId() == v.GetFeatureId() && u.GetSegmentIdx() == v.GetSegmentIdx() &&
         u.IsForward() != v.IsForward();
}

bool IsRestricted(RestrictionVec const & restrictions, Segment const & u, Segment const & v,
                  bool isOutgoing)
{
  uint32_t const featureIdFrom = isOutgoing ? u.GetFeatureId() : v.GetFeatureId();
  uint32_t const featureIdTo = isOutgoing ? v.GetFeatureId() : u.GetFeatureId();

  if (!binary_search(restrictions.cbegin(), restrictions.cend(),
                     Restriction(Restriction::Type::No, {featureIdFrom, featureIdTo})))
  {
    return false;
  }

  if (featureIdFrom != featureIdTo)
    return true;

  // @TODO(bykoianko) According to current code if a feature id is marked as a feature with
  // restrictricted U-turn it's restricted to make a U-turn on the both ends of the feature.
  // Generally speaking it's wrong. In osm there's information about the end of the feature
  // where the U-turn is restricted. It's necessary to pass the data to mwm and to use it here.
  // Please see test LineGraph_RestrictionF1F1No for details.
  //
  // Another example when it's necessary to be aware about feature end participated in restriction
  // is
  //        *---F1---*
  //        |        |
  // *--F3--A        B--F4--*
  //        |        |
  //        *---F2---*
  // In case of restriction F1-A-F2 or F1-B-F2 of any type (No, Only) the important information
  // is lost.
  return IsUTurn(u, v);
}
}  // namespace

namespace routing
{
IndexGraph::IndexGraph(shared_ptr<Geometry> geometry, shared_ptr<EdgeEstimator> estimator,
                       RoutingOptions routingOptions)
  : m_geometry(geometry),
    m_estimator(move(estimator)),
    m_avoidRoutingOptions(routingOptions)
{
  CHECK(m_geometry, ());
  CHECK(m_estimator, ());
}

bool IndexGraph::IsJoint(RoadPoint const & roadPoint) const
{
  return m_roadIndex.GetJointId(roadPoint) != Joint::kInvalidId;
}

void IndexGraph::GetEdgeList(Segment const & segment, bool isOutgoing, vector<SegmentEdge> & edges)
{
  RoadPoint const roadPoint = segment.GetRoadPoint(isOutgoing);
  Joint::Id const jointId = m_roadIndex.GetJointId(roadPoint);

  if (jointId != Joint::kInvalidId)
  {
    m_jointIndex.ForEachPoint(jointId, [&](RoadPoint const & rp) {
      GetNeighboringEdges(segment, rp, isOutgoing, edges);
    });
  }
  else
  {
    GetNeighboringEdges(segment, roadPoint, isOutgoing, edges);
  }
}

void IndexGraph::GetLastPointsForJoint(vector<Segment> const & children,
                                       bool isOutgoing,
                                       vector<uint32_t> & lastPoints)
{
  CHECK(lastPoints.empty(), ());

  lastPoints.reserve(children.size());
  for (auto const & child : children)
  {
    uint32_t const startPointId = child.GetPointId(!isOutgoing /* front */);
    uint32_t const pointsNumber = m_geometry->GetRoad(child.GetFeatureId()).GetPointsCount();
    CHECK_LESS(startPointId, pointsNumber, ());

    uint32_t endPointId;
    // child.IsForward() == isOutgoing
    //   This is the direction, indicating the end of the road,
    //   where we should go for building JointSegment.
    // You can retrieve such result if bust possible options of |child.IsForward()| and |isOutgoing|.
    bool forward = child.IsForward() == isOutgoing;
    if (IsRoad(child.GetFeatureId()))
    {
      std::tie(std::ignore, endPointId) =
        GetRoad(child.GetFeatureId()).FindNeighbor(startPointId, forward,
                                                   pointsNumber);
    }
    else
    {
      // child.GetFeatureId() can be not road in this case:
      // -->--> { -->-->--> } -->
      // Where { ... } - borders of mwm
      // Here one feature, which enter from mwm A to mwm B and then exit from B to A.
      // And in mwm B no other feature cross this one, those feature is in geometry
      // and absent in roadIndex.
      // In this case we just return endPointNumber.
      endPointId = forward ? pointsNumber - 1 : 0;
    }

    lastPoints.push_back(endPointId);
  }
}

void IndexGraph::GetEdgeList(Segment const & parent, bool isOutgoing, vector<JointEdge> & edges,
                             vector<RouteWeight> & parentWeights)
{
  vector<Segment> possibleChildren;
  GetSegmentCandidateForJoint(parent, isOutgoing, possibleChildren);

  vector<uint32_t> lastPoints;
  GetLastPointsForJoint(possibleChildren, isOutgoing, lastPoints);

  ReconstructJointSegment(parent, possibleChildren, lastPoints,
                          isOutgoing, edges, parentWeights);
}

boost::optional<JointEdge>
IndexGraph::GetJointEdgeByLastPoint(Segment const & parent, Segment const & firstChild,
                                    bool isOutgoing, uint32_t lastPoint)
{
  vector<Segment> const possibleChilds = {firstChild};
  vector<uint32_t> const lastPoints = {lastPoint};

  vector<JointEdge> edges;
  vector<RouteWeight> parentWeights;
  ReconstructJointSegment(parent, possibleChilds, lastPoints,
                          isOutgoing, edges, parentWeights);

  CHECK_LESS_OR_EQUAL(edges.size(), 1, ());
  if (edges.size() == 1)
    return {edges.back()};

  return {};
}

void IndexGraph::Build(uint32_t numJoints)
{
  m_jointIndex.Build(m_roadIndex, numJoints);
}

void IndexGraph::Import(vector<Joint> const & joints)
{
  m_roadIndex.Import(joints);
  CHECK_LESS_OR_EQUAL(joints.size(), numeric_limits<uint32_t>::max(), ());
  Build(checked_cast<uint32_t>(joints.size()));
}

void IndexGraph::SetRestrictions(RestrictionVec && restrictions)
{
  ASSERT(is_sorted(restrictions.cbegin(), restrictions.cend()), ());
  m_restrictions = move(restrictions);
}

void IndexGraph::SetRoadAccess(RoadAccess && roadAccess) { m_roadAccess = move(roadAccess); }

void IndexGraph::GetOutgoingEdgesList(Segment const & segment, vector<SegmentEdge> & edges)
{
  edges.clear();
  GetEdgeList(segment, true /* isOutgoing */, edges);
}

void IndexGraph::GetIngoingEdgesList(Segment const & segment, vector<SegmentEdge> & edges)
{
  edges.clear();
  GetEdgeList(segment, false /* isOutgoing */, edges);
}

RouteWeight IndexGraph::CalcSegmentWeight(Segment const & segment)
{
  return RouteWeight(
      m_estimator->CalcSegmentWeight(segment, m_geometry->GetRoad(segment.GetFeatureId())));
}

void IndexGraph::GetNeighboringEdges(Segment const & from, RoadPoint const & rp, bool isOutgoing,
                                     vector<SegmentEdge> & edges)
{
  RoadGeometry const & road = m_geometry->GetRoad(rp.GetFeatureId());

  if (!road.IsValid())
    return;

  if (!road.SuitableForOptions(m_avoidRoutingOptions))
    return;

  bool const bidirectional = !road.IsOneWay();

  if ((isOutgoing || bidirectional) && rp.GetPointId() + 1 < road.GetPointsCount())
  {
    GetNeighboringEdge(from,
                       Segment(from.GetMwmId(), rp.GetFeatureId(), rp.GetPointId(), isOutgoing),
                       isOutgoing, edges);
  }

  if ((!isOutgoing || bidirectional) && rp.GetPointId() > 0)
  {
    GetNeighboringEdge(
        from, Segment(from.GetMwmId(), rp.GetFeatureId(), rp.GetPointId() - 1, !isOutgoing),
        isOutgoing, edges);
  }
}

void IndexGraph::GetSegmentCandidateForJoint(Segment const & parent, bool isOutgoing,
                                             vector<Segment> & children)
{
  RoadPoint const roadPoint = parent.GetRoadPoint(isOutgoing);
  Joint::Id const jointId = m_roadIndex.GetJointId(roadPoint);

  if (jointId == Joint::kInvalidId)
    return;

  m_jointIndex.ForEachPoint(jointId, [&](RoadPoint const & rp)
  {
    RoadGeometry const & road = m_geometry->GetRoad(rp.GetFeatureId());
    if (!road.IsValid())
      return;

    if (!road.SuitableForOptions(m_avoidRoutingOptions))
      return;

    bool const bidirectional = !road.IsOneWay();
    auto const pointId = rp.GetPointId();

    if ((isOutgoing || bidirectional) && pointId + 1 < road.GetPointsCount())
      children.emplace_back(parent.GetMwmId(), rp.GetFeatureId(), pointId, isOutgoing);

    if ((!isOutgoing || bidirectional) && pointId > 0)
      children.emplace_back(parent.GetMwmId(), rp.GetFeatureId(), pointId - 1, !isOutgoing);
  });
}

/// \brief Prolongs segments from |parent| to |firstChildren| directions in order to
///        create JointSegments.
/// \param |firstChildren| - vecotor of neigbouring segments from parent.
/// \param |lastPointIds| - vector of the end numbers of road points for |firstChildren|.
/// \param |jointEdges| - the result vector with JointEdges.
/// \param |parentWeights| - see |IndexGraphStarterJoints::GetEdgeList| method about this argument.
///                          Shotly - in case of |isOutgoing| == false, method saves here the weights
///                                   from parent to firstChildren.
void IndexGraph::ReconstructJointSegment(Segment const & parent,
                                         vector<Segment> const & firstChildren,
                                         vector<uint32_t> const & lastPointIds,
                                         bool isOutgoing,
                                         vector<JointEdge> & jointEdges,
                                         vector<RouteWeight> & parentWeights)
{
  CHECK_EQUAL(firstChildren.size(), lastPointIds.size(), ());

  for (size_t i = 0; i < firstChildren.size(); ++i)
  {
    auto const & firstChild = firstChildren[i];
    auto const lastPointId = lastPointIds[i];

    uint32_t currentPointId = firstChild.GetPointId(!isOutgoing /* front */);
    CHECK_NOT_EQUAL(currentPointId, lastPointId,
                    ("Invariant violated, can not build JointSegment,"
                     "started and ended in the same point."));

    auto const increment = [currentPointId, lastPointId](uint32_t pointId) {
      return currentPointId < lastPointId ? pointId + 1 : pointId - 1;
    };

    if (m_roadAccess.GetFeatureType(firstChild.GetFeatureId()) == RoadAccess::Type::No)
      continue;

    if (m_roadAccess.GetPointType(parent.GetRoadPoint(isOutgoing)) == RoadAccess::Type::No)
      continue;

    // Check firstChild for UTurn.
    RoadPoint rp = parent.GetRoadPoint(isOutgoing);
    if (IsUTurn(parent, firstChild) && m_roadIndex.GetJointId(rp) == Joint::kInvalidId
        && !m_geometry->GetRoad(parent.GetFeatureId()).IsEndPointId(rp.GetPointId()))
    {
      continue;
    }

    if (parent.GetFeatureId() != firstChild.GetFeatureId() &&
        IsRestricted(m_restrictions, parent, firstChild, isOutgoing))
    {
      continue;
    }

    // Check current JointSegment for bad road access between segments.
    rp = firstChild.GetRoadPoint(isOutgoing);
    uint32_t start = currentPointId;
    bool noRoadAccess = false;
    do
    {
      if (m_roadAccess.GetPointType(rp) == RoadAccess::Type::No)
      {
        noRoadAccess = true;
        break;
      }

      start = increment(start);
      rp.SetPointId(start);
    } while (start != lastPointId);

    if (noRoadAccess)
      continue;

    bool forward = currentPointId < lastPointId;
    Segment current = firstChild;
    Segment prev = parent;
    RouteWeight summaryWeight;

    do
    {
      RouteWeight const weight = CalcSegmentWeight(isOutgoing ? current : prev) +
                                 GetPenalties(isOutgoing ? prev : current, isOutgoing ? current : prev);

      if (isOutgoing || prev != parent)
        summaryWeight += weight;

      if (prev == parent)
        parentWeights.emplace_back(weight);

      prev = current;
      current.Next(forward);
      currentPointId = increment(currentPointId);
    } while (currentPointId != lastPointId);

    jointEdges.emplace_back(isOutgoing ? JointSegment(firstChild, prev) :
                                         JointSegment(prev, firstChild),
                            summaryWeight);
  }
}

void IndexGraph::GetNeighboringEdge(Segment const & from, Segment const & to, bool isOutgoing,
                                    vector<SegmentEdge> & edges)
{
  // Blocking U-turns on internal feature points.
  RoadPoint const rp = from.GetRoadPoint(isOutgoing);
  if (IsUTurn(from, to) && m_roadIndex.GetJointId(rp) == Joint::kInvalidId
      && !m_geometry->GetRoad(from.GetFeatureId()).IsEndPointId(rp.GetPointId()))
  {
    return;
  }

  if (IsRestricted(m_restrictions, from, to, isOutgoing))
    return;

  if (m_roadAccess.GetFeatureType(to.GetFeatureId()) == RoadAccess::Type::No)
    return;

  if (m_roadAccess.GetPointType(rp) == RoadAccess::Type::No)
    return;

  RouteWeight const weight = CalcSegmentWeight(isOutgoing ? to : from) +
                             GetPenalties(isOutgoing ? from : to, isOutgoing ? to : from);
  edges.emplace_back(to, weight);
}

RouteWeight IndexGraph::GetPenalties(Segment const & u, Segment const & v)
{
  bool const fromPassThroughAllowed = m_geometry->GetRoad(u.GetFeatureId()).IsPassThroughAllowed();
  bool const toPassThroughAllowed = m_geometry->GetRoad(v.GetFeatureId()).IsPassThroughAllowed();
  // Route crosses border of pass-through/non-pass-through area if |u| and |v| have different
  // pass through restrictions.
  int32_t const passThroughPenalty = fromPassThroughAllowed == toPassThroughAllowed ? 0 : 1;

  // We do not distinguish between RoadAccess::Type::Private and RoadAccess::Type::Destination for now.
  bool const fromAccessAllowed = m_roadAccess.GetFeatureType(u.GetFeatureId()) == RoadAccess::Type::Yes;
  bool const toAccessAllowed = m_roadAccess.GetFeatureType(v.GetFeatureId()) == RoadAccess::Type::Yes;
  // Route crosses border of access=yes/access={private, destination} area if |u| and |v| have different
  // access restrictions.
  int32_t accessPenalty = fromAccessAllowed == toAccessAllowed ? 0 : 1;

  // RoadPoint between u and v is front of u.
  auto const rp = u.GetRoadPoint(true /* front */);
  // No double penalty for barriers on the border of access=yes/access={private, destination} area.
  if (m_roadAccess.GetPointType(rp) != RoadAccess::Type::Yes)
    accessPenalty = 1;

  auto const uTurnPenalty = IsUTurn(u, v) ? m_estimator->GetUTurnPenalty() : 0.0;

  return RouteWeight(uTurnPenalty /* weight */, passThroughPenalty, accessPenalty, 0.0 /* transitTime */);
}

WorldGraphMode IndexGraph::GetMode() const { return WorldGraphMode::Undefined; }
}  // namespace routing
<|endoftext|>


<commit_before>#include "HexagonOffload.h"
#include "IRMutator.h"
#include "Substitute.h"
#include "Closure.h"
#include "Param.h"
#include "Image.h"
#include "Output.h"
#include "LLVM_Headers.h"
#include "RemoveTrivialForLoops.h"

#include <iostream>
#include <fstream>

namespace Halide {
namespace Internal {

using std::string;
using std::vector;

namespace {

Target hexagon_remote_target(Target::NoOS, Target::Hexagon, 32);

// Replace the parameter objects of loads/stores witha new parameter
// object.
class ReplaceParams : public IRMutator {
    const std::map<std::string, Parameter> &replacements;

    using IRMutator::visit;

    void visit(const Load *op) {
        auto i = replacements.find(op->name);
        if (i != replacements.end()) {
            expr = Load::make(op->type, op->name, mutate(op->index), op->image, i->second);
        } else {
            IRMutator::visit(op);
        }
    }

    void visit(const Store *op) {
        auto i = replacements.find(op->name);
        if (i != replacements.end()) {
            stmt = Store::make(op->name, mutate(op->value), mutate(op->index), i->second);
        } else {
            IRMutator::visit(op);
        }
    }

public:
    ReplaceParams(const std::map<std::string, Parameter> &replacements)
        : replacements(replacements) {}
};

Stmt replace_params(Stmt s, const std::map<std::string, Parameter> &replacements) {
    return ReplaceParams(replacements).mutate(s);
}

class InjectHexagonRpc : public IRMutator {
    std::map<std::string, Expr> state_vars;

    Module device_code;

    /** Alignment info for Int(32) variables in scope, so we don't
     * lose the information when creating Hexagon kernels. */
    Scope<ModulusRemainder> alignment_info;

    Expr state_var(const std::string& name, Type type) {
        Expr& var = state_vars[name];
        if (!var.defined()) {
            Buffer storage(type, {}, nullptr, name + "_buf");
            *(void **)storage.host_ptr() = nullptr;
            var = Load::make(type_of<void*>(), name + "_buf", 0, storage, Parameter());
        }
        return var;
    }

    Expr state_var_ptr(const std::string& name, Type type) {
        Expr var = state_var(name, type);
        return Call::make(Handle(), Call::address_of, {var}, Call::Intrinsic);
    }

    Expr module_state() {
        return state_var("hexagon_module_state", type_of<void*>());
    }

    Expr module_state_ptr() {
        return state_var_ptr("hexagon_module_state", type_of<void*>());
    }

    // Create a Buffer containing the given buffer/size, and return an
    // expression for a pointer to the first element.
    Expr buffer_ptr(const uint8_t* buffer, size_t size, const char* name) {
        Buffer code(type_of<uint8_t>(), {(int)size}, nullptr, name);
        memcpy(code.host_ptr(), buffer, (int)size);

        Expr ptr_0 = Load::make(type_of<uint8_t>(), name, 0, code, Parameter());
        return Call::make(Handle(), Call::address_of, {ptr_0}, Call::Intrinsic);
    }

    Stmt call_extern_and_assert(const std::string& name, const std::vector<Expr>& args) {
        Expr call = Call::make(Int(32), name, args, Call::Extern);
        string call_result_name = unique_name(name + "_result");
        Expr call_result_var = Variable::make(Int(32), call_result_name);
        return LetStmt::make(call_result_name, call,
                             AssertStmt::make(EQ::make(call_result_var, 0), call_result_var));
    }

    using IRMutator::visit;

    void visit(const For *loop) {
        if (loop->device_api == DeviceAPI::Hexagon) {
            // Unrolling or loop partitioning might generate multiple
            // loops with the same name, so we need to unique them.
            std::string hex_name = "hex_" + loop->name;

            Stmt body = remove_trivial_for_loops(loop, true /*device_loops*/);

            // Build a closure for the device code.
            // TODO: Should this move the body of the loop to Hexagon,
            // or the loop itself? Currently, this moves the loop itself.
            Closure c(body);

            // Make an argument list, and generate a function in the
            // device_code module. The hexagon runtime code expects
            // the arguments to appear in the order of (input buffers,
            // output buffers, input scalars).  There's a huge hack
            // here, in that the scalars must be last for the scalar
            // arguments to shadow the symbols of the buffer.
            std::vector<LoweredArgument> input_buffers, output_buffers;
            std::map<std::string, Parameter> replacement_params;
            for (const auto& i : c.buffers) {
                if (i.second.write) {
                    Argument::Kind kind = Argument::OutputBuffer;
                    output_buffers.push_back(LoweredArgument(i.first, kind, i.second.type, i.second.dimensions));
                } else {
                    Argument::Kind kind = Argument::InputBuffer;
                    input_buffers.push_back(LoweredArgument(i.first, kind, i.second.type, i.second.dimensions));
                }

                // Build a parameter to replace.
                Parameter p(i.second.type, true, i.second.dimensions);
                // Assert that buffers are aligned to one HVX
                // vector. Generally, they should be page aligned ION
                // buffers, so this should be a reasonable
                // requirement.
                const int alignment = 128;
                p.set_host_alignment(alignment);
                // The other parameter constraints are already
                // accounted for by the closure grabbing those
                // arguments, so we only need to provide the host
                // alignment.
                replacement_params[i.first] = p;

                // Add an assert to the body that validates the
                // alignment of the buffer.
                if (!device_code.target().has_feature(Target::NoAsserts)) {
                    Expr host_ptr = reinterpret<uint64_t>(Variable::make(Handle(), i.first + ".host"));
                    Expr error = Call::make(Int(32), "halide_error_unaligned_host_ptr",
                                            {i.first, alignment}, Call::Extern);
                    body = Block::make(AssertStmt::make(host_ptr % alignment == 0, error), body);
                }
            }
            body = replace_params(body, replacement_params);

            std::vector<LoweredArgument> args;
            args.insert(args.end(), input_buffers.begin(), input_buffers.end());
            args.insert(args.end(), output_buffers.begin(), output_buffers.end());
            for (const auto& i : c.vars) {
                LoweredArgument arg(i.first, Argument::InputScalar, i.second, 0);
                if (alignment_info.contains(i.first)) {
                    arg.alignment = alignment_info.get(i.first);
                }
                args.push_back(arg);
            }
            device_code.append(LoweredFunc(hex_name, args, body, LoweredFunc::External));

            // Generate a call to hexagon_device_run.
            std::vector<Expr> arg_sizes;
            std::vector<Expr> arg_ptrs;
            std::vector<Expr> arg_flags;

            for (const auto& i : c.buffers) {
                arg_sizes.push_back(Expr((size_t)sizeof(buffer_t*)));
                arg_ptrs.push_back(Variable::make(type_of<buffer_t *>(), i.first + ".buffer"));
                int flags = 0;
                if (i.second.read) flags |= 0x1;
                if (i.second.write) flags |= 0x2;
                arg_flags.push_back(flags);
            }
            for (const auto& i : c.vars) {
                Expr arg = Variable::make(i.second, i.first);
                Expr arg_ptr = Call::make(type_of<void *>(), Call::make_struct, {arg}, Call::Intrinsic);

                arg_sizes.push_back(Expr((size_t)i.second.bytes()));
                arg_ptrs.push_back(arg_ptr);
                arg_flags.push_back(0x0);
            }

            // The argument list is terminated with an argument of size 0.
            arg_sizes.push_back(Expr((size_t)0));

            std::string pipeline_name = hex_name + "_argv";
            std::vector<Expr> params;
            params.push_back(module_state());
            params.push_back(pipeline_name);
            params.push_back(state_var_ptr(hex_name, type_of<int>()));
            params.push_back(Call::make(type_of<size_t*>(), Call::make_struct, arg_sizes, Call::Intrinsic));
            params.push_back(Call::make(type_of<void**>(), Call::make_struct, arg_ptrs, Call::Intrinsic));
            params.push_back(Call::make(type_of<int*>(), Call::make_struct, arg_flags, Call::Intrinsic));

            stmt = call_extern_and_assert("halide_hexagon_run", params);

        } else {
            IRMutator::visit(loop);
        }
    }

    void visit(const Let *op) {
        if (op->value.type() == Int(32)) {
            alignment_info.push(op->name, modulus_remainder(op->value, alignment_info));
        }

        IRMutator::visit(op);

        if (op->value.type() == Int(32)) {
            alignment_info.pop(op->name);
        }
    }

    void visit(const LetStmt *op) {
        if (op->value.type() == Int(32)) {
            alignment_info.push(op->name, modulus_remainder(op->value, alignment_info));
        }

        IRMutator::visit(op);

        if (op->value.type() == Int(32)) {
            alignment_info.pop(op->name);
        }
    }

public:
    InjectHexagonRpc(const Target &target) : device_code("hexagon", target) {}

    Stmt inject(Stmt s) {
        s = mutate(s);

        // Skip if there are no device kernels.
        if (device_code.functions.empty()) {
            return s;
        }

        // Compile the device code.
        std::vector<uint8_t> object;
#if 0
        compile_module_to_shared_object(device_code, object);
        //compile_module_to_shared_object(device_code, "/tmp/hex.so");
#else
        debug(1) << "Hexagon device code module: " << device_code << "\n";
        compile_module_to_llvm_bitcode(device_code, "hex.bc");
        int result = system("${HEX_TOOLS}/bin/hexagon-clang hex.bc -fPIC -O3 -Wno-override-module -shared -o hex.so");
        internal_assert(result == 0) << "hexagon-clang failed\n";

        std::ifstream so("hex.so", std::ios::binary | std::ios::ate);
        object.resize(so.tellg());
        so.seekg(0, std::ios::beg);
        so.read(reinterpret_cast<char*>(&object[0]), object.size());
#endif

        // Put the compiled object into a buffer.
        size_t code_size = object.size();
        Expr code_ptr = buffer_ptr(&object[0], code_size, "hexagon_code");

        // Wrap the statement in calls to halide_initialize_kernels.
        Stmt init_kernels = call_extern_and_assert("halide_hexagon_initialize_kernels",
                                                   {module_state_ptr(), code_ptr, Expr(code_size)});
        s = Block::make(init_kernels, s);

        return s;
    }
};

}

Stmt inject_hexagon_rpc(Stmt s, const Target &host_target) {
    Target target = hexagon_remote_target;
    for (Target::Feature i : {Target::Debug, Target::NoAsserts, Target::HVX_64, Target::HVX_128}) {
        if (host_target.has_feature(i)) {
            target = target.with_feature(i);
        }
    }
    InjectHexagonRpc injector(target);
    s = injector.inject(s);
    return s;
}

}  // namespace Internal
}  // namespace Halide
<commit_msg>Remove global remote target.<commit_after>#include "HexagonOffload.h"
#include "IRMutator.h"
#include "Substitute.h"
#include "Closure.h"
#include "Param.h"
#include "Image.h"
#include "Output.h"
#include "LLVM_Headers.h"
#include "RemoveTrivialForLoops.h"

#include <iostream>
#include <fstream>

namespace Halide {
namespace Internal {

using std::string;
using std::vector;

namespace {

// Replace the parameter objects of loads/stores witha new parameter
// object.
class ReplaceParams : public IRMutator {
    const std::map<std::string, Parameter> &replacements;

    using IRMutator::visit;

    void visit(const Load *op) {
        auto i = replacements.find(op->name);
        if (i != replacements.end()) {
            expr = Load::make(op->type, op->name, mutate(op->index), op->image, i->second);
        } else {
            IRMutator::visit(op);
        }
    }

    void visit(const Store *op) {
        auto i = replacements.find(op->name);
        if (i != replacements.end()) {
            stmt = Store::make(op->name, mutate(op->value), mutate(op->index), i->second);
        } else {
            IRMutator::visit(op);
        }
    }

public:
    ReplaceParams(const std::map<std::string, Parameter> &replacements)
        : replacements(replacements) {}
};

Stmt replace_params(Stmt s, const std::map<std::string, Parameter> &replacements) {
    return ReplaceParams(replacements).mutate(s);
}

class InjectHexagonRpc : public IRMutator {
    std::map<std::string, Expr> state_vars;

    Module device_code;

    /** Alignment info for Int(32) variables in scope, so we don't
     * lose the information when creating Hexagon kernels. */
    Scope<ModulusRemainder> alignment_info;

    Expr state_var(const std::string& name, Type type) {
        Expr& var = state_vars[name];
        if (!var.defined()) {
            Buffer storage(type, {}, nullptr, name + "_buf");
            *(void **)storage.host_ptr() = nullptr;
            var = Load::make(type_of<void*>(), name + "_buf", 0, storage, Parameter());
        }
        return var;
    }

    Expr state_var_ptr(const std::string& name, Type type) {
        Expr var = state_var(name, type);
        return Call::make(Handle(), Call::address_of, {var}, Call::Intrinsic);
    }

    Expr module_state() {
        return state_var("hexagon_module_state", type_of<void*>());
    }

    Expr module_state_ptr() {
        return state_var_ptr("hexagon_module_state", type_of<void*>());
    }

    // Create a Buffer containing the given buffer/size, and return an
    // expression for a pointer to the first element.
    Expr buffer_ptr(const uint8_t* buffer, size_t size, const char* name) {
        Buffer code(type_of<uint8_t>(), {(int)size}, nullptr, name);
        memcpy(code.host_ptr(), buffer, (int)size);

        Expr ptr_0 = Load::make(type_of<uint8_t>(), name, 0, code, Parameter());
        return Call::make(Handle(), Call::address_of, {ptr_0}, Call::Intrinsic);
    }

    Stmt call_extern_and_assert(const std::string& name, const std::vector<Expr>& args) {
        Expr call = Call::make(Int(32), name, args, Call::Extern);
        string call_result_name = unique_name(name + "_result");
        Expr call_result_var = Variable::make(Int(32), call_result_name);
        return LetStmt::make(call_result_name, call,
                             AssertStmt::make(EQ::make(call_result_var, 0), call_result_var));
    }

    using IRMutator::visit;

    void visit(const For *loop) {
        if (loop->device_api == DeviceAPI::Hexagon) {
            // Unrolling or loop partitioning might generate multiple
            // loops with the same name, so we need to unique them.
            std::string hex_name = "hex_" + loop->name;

            Stmt body = remove_trivial_for_loops(loop, true /*device_loops*/);

            // Build a closure for the device code.
            // TODO: Should this move the body of the loop to Hexagon,
            // or the loop itself? Currently, this moves the loop itself.
            Closure c(body);

            // Make an argument list, and generate a function in the
            // device_code module. The hexagon runtime code expects
            // the arguments to appear in the order of (input buffers,
            // output buffers, input scalars).  There's a huge hack
            // here, in that the scalars must be last for the scalar
            // arguments to shadow the symbols of the buffer.
            std::vector<LoweredArgument> input_buffers, output_buffers;
            std::map<std::string, Parameter> replacement_params;
            for (const auto& i : c.buffers) {
                if (i.second.write) {
                    Argument::Kind kind = Argument::OutputBuffer;
                    output_buffers.push_back(LoweredArgument(i.first, kind, i.second.type, i.second.dimensions));
                } else {
                    Argument::Kind kind = Argument::InputBuffer;
                    input_buffers.push_back(LoweredArgument(i.first, kind, i.second.type, i.second.dimensions));
                }

                // Build a parameter to replace.
                Parameter p(i.second.type, true, i.second.dimensions);
                // Assert that buffers are aligned to one HVX
                // vector. Generally, they should be page aligned ION
                // buffers, so this should be a reasonable
                // requirement.
                const int alignment = 128;
                p.set_host_alignment(alignment);
                // The other parameter constraints are already
                // accounted for by the closure grabbing those
                // arguments, so we only need to provide the host
                // alignment.
                replacement_params[i.first] = p;

                // Add an assert to the body that validates the
                // alignment of the buffer.
                if (!device_code.target().has_feature(Target::NoAsserts)) {
                    Expr host_ptr = reinterpret<uint64_t>(Variable::make(Handle(), i.first + ".host"));
                    Expr error = Call::make(Int(32), "halide_error_unaligned_host_ptr",
                                            {i.first, alignment}, Call::Extern);
                    body = Block::make(AssertStmt::make(host_ptr % alignment == 0, error), body);
                }
            }
            body = replace_params(body, replacement_params);

            std::vector<LoweredArgument> args;
            args.insert(args.end(), input_buffers.begin(), input_buffers.end());
            args.insert(args.end(), output_buffers.begin(), output_buffers.end());
            for (const auto& i : c.vars) {
                LoweredArgument arg(i.first, Argument::InputScalar, i.second, 0);
                if (alignment_info.contains(i.first)) {
                    arg.alignment = alignment_info.get(i.first);
                }
                args.push_back(arg);
            }
            device_code.append(LoweredFunc(hex_name, args, body, LoweredFunc::External));

            // Generate a call to hexagon_device_run.
            std::vector<Expr> arg_sizes;
            std::vector<Expr> arg_ptrs;
            std::vector<Expr> arg_flags;

            for (const auto& i : c.buffers) {
                arg_sizes.push_back(Expr((size_t)sizeof(buffer_t*)));
                arg_ptrs.push_back(Variable::make(type_of<buffer_t *>(), i.first + ".buffer"));
                int flags = 0;
                if (i.second.read) flags |= 0x1;
                if (i.second.write) flags |= 0x2;
                arg_flags.push_back(flags);
            }
            for (const auto& i : c.vars) {
                Expr arg = Variable::make(i.second, i.first);
                Expr arg_ptr = Call::make(type_of<void *>(), Call::make_struct, {arg}, Call::Intrinsic);

                arg_sizes.push_back(Expr((size_t)i.second.bytes()));
                arg_ptrs.push_back(arg_ptr);
                arg_flags.push_back(0x0);
            }

            // The argument list is terminated with an argument of size 0.
            arg_sizes.push_back(Expr((size_t)0));

            std::string pipeline_name = hex_name + "_argv";
            std::vector<Expr> params;
            params.push_back(module_state());
            params.push_back(pipeline_name);
            params.push_back(state_var_ptr(hex_name, type_of<int>()));
            params.push_back(Call::make(type_of<size_t*>(), Call::make_struct, arg_sizes, Call::Intrinsic));
            params.push_back(Call::make(type_of<void**>(), Call::make_struct, arg_ptrs, Call::Intrinsic));
            params.push_back(Call::make(type_of<int*>(), Call::make_struct, arg_flags, Call::Intrinsic));

            stmt = call_extern_and_assert("halide_hexagon_run", params);

        } else {
            IRMutator::visit(loop);
        }
    }

    void visit(const Let *op) {
        if (op->value.type() == Int(32)) {
            alignment_info.push(op->name, modulus_remainder(op->value, alignment_info));
        }

        IRMutator::visit(op);

        if (op->value.type() == Int(32)) {
            alignment_info.pop(op->name);
        }
    }

    void visit(const LetStmt *op) {
        if (op->value.type() == Int(32)) {
            alignment_info.push(op->name, modulus_remainder(op->value, alignment_info));
        }

        IRMutator::visit(op);

        if (op->value.type() == Int(32)) {
            alignment_info.pop(op->name);
        }
    }

public:
    InjectHexagonRpc(const Target &target) : device_code("hexagon", target) {}

    Stmt inject(Stmt s) {
        s = mutate(s);

        // Skip if there are no device kernels.
        if (device_code.functions.empty()) {
            return s;
        }

        // Compile the device code.
        std::vector<uint8_t> object;
#if 0
        compile_module_to_shared_object(device_code, object);
        //compile_module_to_shared_object(device_code, "/tmp/hex.so");
#else
        debug(1) << "Hexagon device code module: " << device_code << "\n";
        compile_module_to_llvm_bitcode(device_code, "hex.bc");
        int result = system("${HEX_TOOLS}/bin/hexagon-clang hex.bc -fPIC -O3 -Wno-override-module -shared -o hex.so");
        internal_assert(result == 0) << "hexagon-clang failed\n";

        std::ifstream so("hex.so", std::ios::binary | std::ios::ate);
        object.resize(so.tellg());
        so.seekg(0, std::ios::beg);
        so.read(reinterpret_cast<char*>(&object[0]), object.size());
#endif

        // Put the compiled object into a buffer.
        size_t code_size = object.size();
        Expr code_ptr = buffer_ptr(&object[0], code_size, "hexagon_code");

        // Wrap the statement in calls to halide_initialize_kernels.
        Stmt init_kernels = call_extern_and_assert("halide_hexagon_initialize_kernels",
                                                   {module_state_ptr(), code_ptr, Expr(code_size)});
        s = Block::make(init_kernels, s);

        return s;
    }
};

}

Stmt inject_hexagon_rpc(Stmt s, const Target &host_target) {
    Target target(Target::NoOS, Target::Hexagon, 32);
    for (Target::Feature i : {Target::Debug, Target::NoAsserts, Target::HVX_64, Target::HVX_128}) {
        if (host_target.has_feature(i)) {
            target = target.with_feature(i);
        }
    }
    InjectHexagonRpc injector(target);
    s = injector.inject(s);
    return s;
}

}  // namespace Internal
}  // namespace Halide
<|endoftext|>


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

#include "OrbitSshQt/Tunnel.h"

#include <QHostAddress>
#include <QTimer>

#include "OrbitBase/Logging.h"
#include "OrbitSshQt/Error.h"

/**
 * Schedules a task in the currently running event loop.
 *
 * This task checks first whether parent still exists and afterwards destructs
 * the content of opt (which is supposed to be a member of parent.)
 *
 * parent needs to be derived from QObject.
 **/
template <typename P, typename T>
static void deleteByEventLoop(P* parent, std::optional<T>* opt) {
  if (opt) {
    QTimer::singleShot(0, [opt, parent = QPointer<P>(parent)]() {
      if (parent && opt) {
        *opt = std::nullopt;
      }
    });
  }
}

namespace OrbitSshQt {
Tunnel::Tunnel(Session* session, std::string remote_host, uint16_t remote_port, QObject* parent)
    : StateMachineHelper(parent),
      session_(session),
      remote_host_(std::move(remote_host)),
      remote_port_(remote_port) {
  about_to_shutdown_connection_.emplace(
      QObject::connect(session_, &Session::aboutToShutdown, this, &Tunnel::HandleSessionShutdown));
}

void Tunnel::Start() {
  if (CurrentState() == State::kInitial) {
    SetState(State::kNoChannel);
    OnEvent();
  }
}

void Tunnel::Stop() {
  if (CurrentState() == State::kError) {
    return;
  }

  if (CurrentState() < State::kStarted) {
    SetState(State::kDone);
    deleteByEventLoop(this, &local_server_);
    channel_ = std::nullopt;
  }

  if (CurrentState() == State::kServerListening) {
    SetState(State::kFlushing);
    OnEvent();
  }
}

outcome::result<void> Tunnel::startup() {
  if (!data_event_connection_) {
    data_event_connection_.emplace(
        QObject::connect(session_, &Session::dataEvent, this, &Tunnel::OnEvent));
  }

  switch (CurrentState()) {
    case State::kInitial:
    case State::kNoChannel: {
      OUTCOME_TRY(channel, OrbitSsh::Channel::OpenTcpIpTunnel(session_->GetRawSession(),
                                                              remote_host_, remote_port_));
      channel_ = std::move(channel);
      SetState(State::kChannelInitialized);
      ABSL_FALLTHROUGH_INTENDED;
    }
    case State::kChannelInitialized: {
      local_server_.emplace(this);
      const auto result = local_server_->listen(QHostAddress{QHostAddress::LocalHost});

      if (!result) {
        return Error::kCouldNotListen;
      }

      QObject::connect(&local_server_.value(), &QTcpServer::newConnection, this, [&]() {
        if (!local_socket_) {
          local_socket_ = local_server_->nextPendingConnection();
          local_server_->pauseAccepting();
          QObject::connect(local_socket_, &QTcpSocket::readyRead, this,
                           &Tunnel::HandleIncomingDataLocalSocket);
          QObject::connect(local_socket_, &QTcpSocket::disconnected, this, [&]() { Stop(); });
        }
      });

      SetState(State::kServerListening);
      emit tunnelOpened(GetListenPort());
      break;
    }
    case State::kStarted:
    case State::kServerListening:
    case State::kShutdown:
    case State::kFlushing:
    case State::kSendEOF:
    case State::kClosingChannel:
    case State::kWaitRemoteClosed:
    case State::kDone:
    case State::kError:
      UNREACHABLE();
  }
  return outcome::success();
}

outcome::result<void> Tunnel::shutdown() {
  switch (CurrentState()) {
    case State::kInitial:
    case State::kNoChannel:
    case State::kChannelInitialized:
    case State::kStarted:
    case State::kServerListening:
      UNREACHABLE();
    case State::kShutdown:
    case State::kFlushing: {
      OUTCOME_TRY(writeToChannel());
      SetState(State::kSendEOF);
      // local_server_ might have triggered this shutdown iteration and we can't
      // delete it when it's still somewhere up in the callstack.
      deleteByEventLoop(this, &local_server_);
      ABSL_FALLTHROUGH_INTENDED;
    }
    case State::kSendEOF: {
      OUTCOME_TRY(channel_->SendEOF());
      SetState(State::kClosingChannel);
      ABSL_FALLTHROUGH_INTENDED;
    }
    case State::kClosingChannel: {
      OUTCOME_TRY(channel_->Close());
      SetState(State::kWaitRemoteClosed);
      ABSL_FALLTHROUGH_INTENDED;
    }
    case State::kWaitRemoteClosed: {
      OUTCOME_TRY(channel_->WaitClosed());
      SetState(State::kDone);
      data_event_connection_ = std::nullopt;
      about_to_shutdown_connection_ = std::nullopt;
      channel_ = std::nullopt;
      ABSL_FALLTHROUGH_INTENDED;
    }
    case State::kDone:
      break;
    case State::kError:
      UNREACHABLE();
  }

  return outcome::success();
}

outcome::result<void> Tunnel::readFromChannel() {
  while (true) {
    const size_t kChunkSize = 1024 * 1024;
    const auto result = channel_->ReadStdOut(kChunkSize);

    if (!result && !OrbitSsh::ShouldITryAgain(result)) {
      return outcome::failure(result.error());
    } else if (!result) {
      // That's the EAGAIN case
      HandleEagain();
      break;
    } else if (result && result.value().empty()) {
      // Empty result means remote socket was closed.
      return Error::kRemoteSocketClosed;
    } else if (result) {
      read_buffer_.append(result.value());
    }
  }

  if (local_socket_ && !read_buffer_.empty()) {
    const auto bytes_written = local_socket_->write(read_buffer_.data(), read_buffer_.size());

    if (bytes_written == -1) {
      SetError(Error::kLocalSocketClosed);
    } else {
      // TODO(b/172686811): avoid extra copy.
      read_buffer_ = read_buffer_.substr(bytes_written);
    }
  }

  return outcome::success();
}

outcome::result<void> Tunnel::writeToChannel() {
  if (!write_buffer_.empty()) {
    OUTCOME_TRY(bytes_written, channel_->Write(write_buffer_));
    write_buffer_ = write_buffer_.substr(bytes_written);
  }
  return outcome::success();
}

outcome::result<void> Tunnel::run() {
  OUTCOME_TRY(readFromChannel());
  OUTCOME_TRY(writeToChannel());
  return outcome::success();
}

void Tunnel::SetError(std::error_code e) {
  data_event_connection_ = std::nullopt;
  about_to_shutdown_connection_ = std::nullopt;
  StateMachineHelper::SetError(e);

  // local_server_ might have triggered this error and we can't delete it when
  // it's still somewhere up in the callstack.
  deleteByEventLoop(this, &local_server_);
  channel_ = std::nullopt;
}

// local_socket -> write_buffer_
void Tunnel::HandleIncomingDataLocalSocket() {
  const auto data = local_socket_->readAll();
  write_buffer_.append(data.toStdString());

  const auto result = writeToChannel();

  if (!result && !OrbitSsh::ShouldITryAgain(result)) {
    SetError(result.error());
    return;
  } else if (!result) {
    HandleEagain();
  }
}

void Tunnel::HandleSessionShutdown() {
  if (CurrentState() >= State::kChannelInitialized && CurrentState() < State::kDone) {
    SetError(Error::kUncleanSessionShutdown);
  }
}

void Tunnel::HandleEagain() {
  if (session_) {
    session_->HandleEagain();
  }
}

}  // namespace OrbitSshQt
<commit_msg>Replace std::string::substr by std::string::erase<commit_after>// Copyright (c) 2020 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "OrbitSshQt/Tunnel.h"

#include <QHostAddress>
#include <QTimer>

#include "OrbitBase/Logging.h"
#include "OrbitSshQt/Error.h"

/**
 * Schedules a task in the currently running event loop.
 *
 * This task checks first whether parent still exists and afterwards destructs
 * the content of opt (which is supposed to be a member of parent.)
 *
 * parent needs to be derived from QObject.
 **/
template <typename P, typename T>
static void deleteByEventLoop(P* parent, std::optional<T>* opt) {
  if (opt) {
    QTimer::singleShot(0, [opt, parent = QPointer<P>(parent)]() {
      if (parent && opt) {
        *opt = std::nullopt;
      }
    });
  }
}

namespace OrbitSshQt {
Tunnel::Tunnel(Session* session, std::string remote_host, uint16_t remote_port, QObject* parent)
    : StateMachineHelper(parent),
      session_(session),
      remote_host_(std::move(remote_host)),
      remote_port_(remote_port) {
  about_to_shutdown_connection_.emplace(
      QObject::connect(session_, &Session::aboutToShutdown, this, &Tunnel::HandleSessionShutdown));
}

void Tunnel::Start() {
  if (CurrentState() == State::kInitial) {
    SetState(State::kNoChannel);
    OnEvent();
  }
}

void Tunnel::Stop() {
  if (CurrentState() == State::kError) {
    return;
  }

  if (CurrentState() < State::kStarted) {
    SetState(State::kDone);
    deleteByEventLoop(this, &local_server_);
    channel_ = std::nullopt;
  }

  if (CurrentState() == State::kServerListening) {
    SetState(State::kFlushing);
    OnEvent();
  }
}

outcome::result<void> Tunnel::startup() {
  if (!data_event_connection_) {
    data_event_connection_.emplace(
        QObject::connect(session_, &Session::dataEvent, this, &Tunnel::OnEvent));
  }

  switch (CurrentState()) {
    case State::kInitial:
    case State::kNoChannel: {
      OUTCOME_TRY(channel, OrbitSsh::Channel::OpenTcpIpTunnel(session_->GetRawSession(),
                                                              remote_host_, remote_port_));
      channel_ = std::move(channel);
      SetState(State::kChannelInitialized);
      ABSL_FALLTHROUGH_INTENDED;
    }
    case State::kChannelInitialized: {
      local_server_.emplace(this);
      const auto result = local_server_->listen(QHostAddress{QHostAddress::LocalHost});

      if (!result) {
        return Error::kCouldNotListen;
      }

      QObject::connect(&local_server_.value(), &QTcpServer::newConnection, this, [&]() {
        if (!local_socket_) {
          local_socket_ = local_server_->nextPendingConnection();
          local_server_->pauseAccepting();
          QObject::connect(local_socket_, &QTcpSocket::readyRead, this,
                           &Tunnel::HandleIncomingDataLocalSocket);
          QObject::connect(local_socket_, &QTcpSocket::disconnected, this, [&]() { Stop(); });
        }
      });

      SetState(State::kServerListening);
      emit tunnelOpened(GetListenPort());
      break;
    }
    case State::kStarted:
    case State::kServerListening:
    case State::kShutdown:
    case State::kFlushing:
    case State::kSendEOF:
    case State::kClosingChannel:
    case State::kWaitRemoteClosed:
    case State::kDone:
    case State::kError:
      UNREACHABLE();
  }
  return outcome::success();
}

outcome::result<void> Tunnel::shutdown() {
  switch (CurrentState()) {
    case State::kInitial:
    case State::kNoChannel:
    case State::kChannelInitialized:
    case State::kStarted:
    case State::kServerListening:
      UNREACHABLE();
    case State::kShutdown:
    case State::kFlushing: {
      OUTCOME_TRY(writeToChannel());
      SetState(State::kSendEOF);
      // local_server_ might have triggered this shutdown iteration and we can't
      // delete it when it's still somewhere up in the callstack.
      deleteByEventLoop(this, &local_server_);
      ABSL_FALLTHROUGH_INTENDED;
    }
    case State::kSendEOF: {
      OUTCOME_TRY(channel_->SendEOF());
      SetState(State::kClosingChannel);
      ABSL_FALLTHROUGH_INTENDED;
    }
    case State::kClosingChannel: {
      OUTCOME_TRY(channel_->Close());
      SetState(State::kWaitRemoteClosed);
      ABSL_FALLTHROUGH_INTENDED;
    }
    case State::kWaitRemoteClosed: {
      OUTCOME_TRY(channel_->WaitClosed());
      SetState(State::kDone);
      data_event_connection_ = std::nullopt;
      about_to_shutdown_connection_ = std::nullopt;
      channel_ = std::nullopt;
      ABSL_FALLTHROUGH_INTENDED;
    }
    case State::kDone:
      break;
    case State::kError:
      UNREACHABLE();
  }

  return outcome::success();
}

outcome::result<void> Tunnel::readFromChannel() {
  while (true) {
    const size_t kChunkSize = 1024 * 1024;
    const auto result = channel_->ReadStdOut(kChunkSize);

    if (!result && !OrbitSsh::ShouldITryAgain(result)) {
      return outcome::failure(result.error());
    } else if (!result) {
      // That's the EAGAIN case
      HandleEagain();
      break;
    } else if (result && result.value().empty()) {
      // Empty result means remote socket was closed.
      return Error::kRemoteSocketClosed;
    } else if (result) {
      read_buffer_.append(result.value());
    }
  }

  if (local_socket_ && !read_buffer_.empty()) {
    const auto bytes_written = local_socket_->write(read_buffer_.data(), read_buffer_.size());

    if (bytes_written == -1) {
      SetError(Error::kLocalSocketClosed);
    } else {
      CHECK(static_cast<size_t>(bytes_written) <= read_buffer_.size());
      read_buffer_.erase(read_buffer_.begin(), read_buffer_.begin() + bytes_written);
    }
  }

  return outcome::success();
}

outcome::result<void> Tunnel::writeToChannel() {
  if (!write_buffer_.empty()) {
    const std::string_view buffer_view{write_buffer_.data(), write_buffer_.size()};
    OUTCOME_TRY(bytes_written, channel_->Write(buffer_view));
    CHECK(static_cast<size_t>(bytes_written) <= write_buffer_.size());
    write_buffer_.erase(write_buffer_.begin(), write_buffer_.begin() + bytes_written);
  }
  return outcome::success();
}

outcome::result<void> Tunnel::run() {
  OUTCOME_TRY(readFromChannel());
  OUTCOME_TRY(writeToChannel());
  return outcome::success();
}

void Tunnel::SetError(std::error_code e) {
  data_event_connection_ = std::nullopt;
  about_to_shutdown_connection_ = std::nullopt;
  StateMachineHelper::SetError(e);

  // local_server_ might have triggered this error and we can't delete it when
  // it's still somewhere up in the callstack.
  deleteByEventLoop(this, &local_server_);
  channel_ = std::nullopt;
}

// local_socket -> write_buffer_
void Tunnel::HandleIncomingDataLocalSocket() {
  const auto data = local_socket_->readAll();
  write_buffer_.append(data.toStdString());

  const auto result = writeToChannel();

  if (!result && !OrbitSsh::ShouldITryAgain(result)) {
    SetError(result.error());
    return;
  } else if (!result) {
    HandleEagain();
  }
}

void Tunnel::HandleSessionShutdown() {
  if (CurrentState() >= State::kChannelInitialized && CurrentState() < State::kDone) {
    SetError(Error::kUncleanSessionShutdown);
  }
}

void Tunnel::HandleEagain() {
  if (session_) {
    session_->HandleEagain();
  }
}

}  // namespace OrbitSshQt
<|endoftext|>


<commit_before>#include <reversed.hpp>

#include <vector>
#include <string>

#include "helpers.hpp"
#include "catch.hpp"

using iter::reversed;
using Vec = const std::vector<int>;

TEST_CASE("Reversing a vector", "[reversed]") {
    Vec ns = {10, 20, 30, 40};
    auto r = reversed(ns);

    Vec v(std::begin(r), std::end(r));
    Vec vc = {40, 30, 20, 10};

    REQUIRE( v == vc );
}
<commit_msg>tests reversed with array<commit_after>#include <reversed.hpp>

#include <vector>
#include <string>

#include "helpers.hpp"
#include "catch.hpp"

using iter::reversed;
using Vec = const std::vector<int>;

TEST_CASE("reversed: can reverse a vector", "[reversed]") {
    Vec ns = {10, 20, 30, 40};
    auto r = reversed(ns);

    Vec v(std::begin(r), std::end(r));
    Vec vc = {40, 30, 20, 10};

    REQUIRE( v == vc );
}

TEST_CASE("reversed: can reverse an array", "[reversed]") {
    int ns[] = {10, 20, 30, 40};
    auto r = reversed(ns);

    Vec v(std::begin(r), std::end(r));
    Vec vc = {40, 30, 20, 10};

    REQUIRE( v == vc );
}
<|endoftext|>


<commit_before>//
// PROJECT CHRONO - http://projectchrono.org
//
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file at the top level of the distribution
// and at http://projectchrono.org/license-chrono.txt.
//
///////////////////////////////////////////////////
//
//   A tracked vehicle, M113, built and simulated using the trackedVehicle library.
//   Build the vehicle using a hierarchy of subsystems.
//   Simulate by GUI input to an irrlicht EventReceiver.
//   Y-up, X-forward, Z-lateral global c-sys
//
//	 Author: Justin Madsen, 2014
///////////////////////////////////////////////////

#include "physics/ChSystem.h"
// #include "particlefactory/ChParticleEmitter.h"

#include "core/ChFileutils.h"
#include "core/ChStream.h"
#include "core/ChRealtimeStep.h"
#include "physics/ChSystem.h"
#include "physics/ChLinkDistance.h"
#include "physics/ChBodyEasy.h"

#include "utils/ChUtilsInputOutput.h"
#include "utils/ChUtilsData.h"
#include "assets/ChTexture.h"
#include "assets/ChColorAsset.h"
/*
#if IRRLICHT_ENABLED
*/
#include "unit_IRRLICHT/ChIrrApp.h"
#include "subsys/driver/ChIrrGuiTrack.h"
 /*
 # define USE_IRRLICHT
#endif
 */
#include "subsys/trackVehicle/trackVehicle.h"
#include "ModelDefs.h"
// Use the main namespace of Chrono
using namespace chrono;

// Use the main namespaces of Irrlicht
using namespace irr;
using namespace core;

// =============================================================================
// User Settings
// =============================================================================
// display the 1) system heirarchy, 2) a set of subsystem hardpoints, 3) constraint violations
#define DEBUG_LOG

// Initial vehicle position and heading. Defines the REF frame for the hull body
ChVector<> initLoc(0, 1.0, 0);
//ChQuaternion<> initRot = Q_from_AngAxis(CH_C_PI_4, VECT_Y);
ChQuaternion<> initRot(QUNIT);

// flat ground size and COG location
ChVector<> groundSize(60.0, 1.0, 100.0);
ChVector<> groundPos(0, -1.0, 0);
double mu = 0.8;  // dry friction coef.

// Simulation step size
double step_size = 0.001;

// Time interval between two render frames
int FPS = 40;
double render_step_size = 1.0 / FPS;   // FPS = 50
// Time interval between two output frames
double output_step_size = 1.0 / 1;    // once a second

// #ifdef USE_IRRLICHT
  // Point on chassis tracked by the camera
double chaseDist = 3.0;
double chaseHeight = 0.0;
ChVector<> trackPoint(0, 0, 0);
  /*
#else
  double tend = 20.0;

  const std::string out_dir = "../M113";
  const std::string pov_dir = out_dir + "/POVRAY";
#endif
  */

/// the ground body, visual assets and collision shape.
ChSharedPtr<ChBody> Add_FlatGround(TrackVehicle* vehicle,
                    const ChVector<>& size,
                    const ChVector<>& pos,
                    double friction_mu)
{
  // body, visual and collision all in one.
  ChSharedPtr<ChBody> ground(new ChBodyEasyBox(size.x, size.y, size.z, 1000.0, true, true));
  ground->SetBodyFixed(true);
  ground->SetPos(pos);
	// ground->SetIdentifier(-1);
  ground->SetName("ground");
  ground->SetFriction(friction_mu);

  // Don't forget to set this collision family!
  ground->GetCollisionModel()->SetFamily((int)CollisionFam::GROUND);

  // color asset for the ground
  ChSharedPtr<ChColorAsset> groundColor(new ChColorAsset);
  groundColor->SetColor(ChColor(0.2f, 0.4f, 0.6f));
  ground->AddAsset(groundColor);

  vehicle->GetSystem()->Add(ground);  // add this body to the system, which is the vehicle

  return ground;
}

int main(int argc, char* argv[])
{
  // NOTE: utils library built with this sets this statically
  // SetChronoDataPath(CHRONO_DATA_DIR);
  // --------------------------
  // Create the tracked vehicle and the ground/environment

  // The vehicle inherits ChSystem. Input chassis visual and collision type
	TrackVehicle vehicle("Justins M113 model",
    VisualizationType::MESH,
    CollisionType::PRIMITIVES);

  // set the chassis REF at the specified initial config.
  vehicle.Initialize(ChCoordsys<>(initLoc, initRot));

  // ground is a large flat plate
  // TODO: superimposed obstacles using ChParticleEmitter class.
  ChSharedPtr<ChBody> ground = Add_FlatGround(&vehicle, groundSize, groundPos, mu);

  // --------------------------
  // Setup the Irrlicht GUI

/*
#ifdef USE_IRRLICHT
*/
	// Create the Irrlicht visualization applicaiton
  bool do_shadows = false; // shadow map is experimental

  ChIrrApp application(vehicle.GetSystem(),
                      L"M113 tracked vehicle demo",
                      dimension2d<u32>(1000, 800),
                      false,
                      do_shadows);
  // assumes Y-up
  application.AddTypicalSky();

  irr::scene::ILightSceneNode* mlight = 0;

  if (do_shadows)
  {
    mlight = application.AddLightWithShadow(
      irr::core::vector3df(10.f, 60.f, 30.f),
      irr::core::vector3df(0.f, 0.f, 0.f),
      150, 60, 80, 15, 512, irr::video::SColorf(1, 1, 1), false, false);
  }
  else
  {
    application.AddTypicalLights(
      irr::core::vector3df(30.f, 100.f, 30.f),
      irr::core::vector3df(30.f, 100.f, 50.f),
      250, 130);
  }

  application.SetTimestep(step_size);

  // the GUI driver
  ChIrrGuiTrack driver(application, vehicle, trackPoint, chaseDist, chaseHeight);

  // Set the time response for steering and throttle keyboard inputs.
  // NOTE: this is not exact, since we do not render quite at the specified FPS.
  double steering_time = 1.0;  // time to go from 0 to +1 (or from 0 to -1)
  double throttle_time = 1.0;  // time to go from 0 to +1
  double braking_time = 0.3;  // time to go from 0 to +1
  driver.SetSteeringDelta(render_step_size / steering_time);
  driver.SetThrottleDelta(render_step_size / throttle_time);
  driver.SetBrakingDelta(render_step_size / braking_time);

  // Set up the assets for rendering
  application.AssetBindAll();
  application.AssetUpdateAll();
  if (do_shadows)
  {
    application.AddShadowAll();
  }
/*
#else
  Track_FuncDriver driver;
#endif
*/

  // ---------------------
  // GUI and render settings

  // GUI driver inputs
  std::vector<double> throttle_input;
  std::vector<double> braking_input;
  double steering_input;

  // Number of simulation steps between two 3D view render frames
  int render_steps = (int)std::ceil(render_step_size / step_size);

  // Number of simulation steps between two output frames
  int output_steps = (int)std::ceil(output_step_size / step_size);

  // ---------------------
  // Simulation loop
#ifdef DEBUG_LOG
  GetLog() << "\n\n============ System Configuration ============\n";
  vehicle.GetSystem()->ShowHierarchy(GetLog() );
#endif

  // Initialize simulation frame counter and simulation time
  int step_number = 0;
  double time = 0;
/*
#ifdef USE_IRRLICHT
*/

  ChRealtimeStepTimer realtime_timer;
  while (application.GetDevice()->run())
	{
		// keep track of the time spent calculating each sim step
    ChTimer<double> step_time;
    step_time.start();

    // Render scene
    if (step_number % render_steps == 0) {
      application.GetVideoDriver()->beginScene(true, true, irr::video::SColor(255, 140, 161, 192));
      driver.DrawAll();
      application.GetVideoDriver()->endScene();
    }

    // Collect output data from modules (for inter-module communication)
    throttle_input = driver.GetThrottle();
    steering_input = driver.GetSteering();
    braking_input = driver.GetBraking();

    // Update
    time = vehicle.GetSystem()->GetChTime();

    driver.Update(time);

    vehicle.Update(time, throttle_input, braking_input);

    // Advance simulation for one timestep for all modules
    // double step = realtime_timer.SuggestSimulationStep(step_size);

    driver.Advance(step_size);

    // SETTLING FOLLOWED BY NORMAL OPERATION STEP SIZES HARDCODED
    // 1e-5 and 1e-4, respectively
    vehicle.Advance(step_size);
    step_number++;
	}

  application.GetDevice()->drop();

/*
#else

  int render_frame = 0;

  if(ChFileutils::MakeDirectory(out_dir.c_str()) < 0) {
    std::cout << "Error creating directory " << out_dir << std::endl;
    return 1;
  }
  if(ChFileutils::MakeDirectory(pov_dir.c_str()) < 0) {
    std::cout << "Error creating directory " << pov_dir << std::endl;
    return 1;
  }

  vehicle.ExportMeshPovray(out_dir);

  char filename[100];

  while (time < tend)
  {
    if (step_number % render_steps == 0) {
      // Output render data
      sprintf(filename, "%s/data_%03d.dat", pov_dir.c_str(), render_frame + 1);
      utils::WriteShapesPovray(vehicle.GetSystem(), filename);
      std::cout << "Output frame:   " << render_frame << std::endl;
      std::cout << "Sim frame:      " << step_number << std::endl;
      std::cout << "Time:           " << time << std::endl;
      std::cout << " throttle: " << driver.GetThrottle() << " steering: " << driver.GetSteering() << std::endl;
      std::cout << std::endl;
      render_frame++;
    }

    // Collect output data from modules (for inter-module communication)
    throttle_input = driver.GetThrottle();
    steering_input = driver.GetSteering();
    braking_input = driver.GetBraking();

    // Update modules (process inputs from other modules)
    time = vehicle.GetSystem()->GetChTime();

    driver.Update(time);

    vehicle.Update(time, steering_input, braking_input, powertrain_torque, tire_forces);

    // Advance simulation for one timestep for all modules
    driver.Advance(step_size);

    vehicle.Advance(step_size);

    // Increment frame number
    step_number++;
  }

#endif

*/

	return 0;
}

<commit_msg>better settings for the GUI<commit_after>//
// PROJECT CHRONO - http://projectchrono.org
//
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file at the top level of the distribution
// and at http://projectchrono.org/license-chrono.txt.
//
///////////////////////////////////////////////////
//
//   A tracked vehicle, M113, built and simulated using the trackedVehicle library.
//   Build the vehicle using a hierarchy of subsystems.
//   Simulate by GUI input to an irrlicht EventReceiver.
//   Y-up, X-forward, Z-lateral global c-sys
//
//	 Author: Justin Madsen, 2014
///////////////////////////////////////////////////

#include "physics/ChSystem.h"
// #include "particlefactory/ChParticleEmitter.h"

#include "core/ChFileutils.h"
#include "core/ChStream.h"
#include "core/ChRealtimeStep.h"
#include "physics/ChSystem.h"
#include "physics/ChLinkDistance.h"
#include "physics/ChBodyEasy.h"

#include "utils/ChUtilsInputOutput.h"
#include "utils/ChUtilsData.h"
#include "assets/ChTexture.h"
#include "assets/ChColorAsset.h"
/*
#if IRRLICHT_ENABLED
*/
#include "unit_IRRLICHT/ChIrrApp.h"
#include "subsys/driver/ChIrrGuiTrack.h"
 /*
 # define USE_IRRLICHT
#endif
 */
#include "subsys/trackVehicle/trackVehicle.h"
#include "ModelDefs.h"
// Use the main namespace of Chrono
using namespace chrono;

// Use the main namespaces of Irrlicht
using namespace irr;
using namespace core;

// =============================================================================
// User Settings
// =============================================================================
// display the 1) system heirarchy, 2) a set of subsystem hardpoints, 3) constraint violations
// #define DEBUG_LOG

// Initial vehicle position and heading. Defines the REF frame for the hull body
ChVector<> initLoc(0, 1.0, 0);
//ChQuaternion<> initRot = Q_from_AngAxis(CH_C_PI_4, VECT_Y);
ChQuaternion<> initRot(QUNIT);

// flat ground size and COG location
ChVector<> groundSize(60.0, 1.0, 100.0);
ChVector<> groundPos(0, -1.0, 0);
double mu = 0.8;  // dry friction coef.

// Simulation step size
double step_size = 0.001;

// Time interval between two render frames
int FPS = 20;
double render_step_size = 1.0 / FPS;   // FPS = 50
// Time interval between two output frames
double output_step_size = 1.0 / 1;    // once a second

// #ifdef USE_IRRLICHT
  // Point on chassis tracked by the camera
double chaseDist = 3.5;
double chaseHeight = 0.0;
ChVector<> trackPoint(0, 0, 0);
  /*
#else
  double tend = 20.0;

  const std::string out_dir = "../M113";
  const std::string pov_dir = out_dir + "/POVRAY";
#endif
  */

/// the ground body, visual assets and collision shape.
ChSharedPtr<ChBody> Add_FlatGround(TrackVehicle* vehicle,
                    const ChVector<>& size,
                    const ChVector<>& pos,
                    double friction_mu)
{
  // body, visual and collision all in one.
  ChSharedPtr<ChBody> ground(new ChBodyEasyBox(size.x, size.y, size.z, 1000.0, true, true));
  ground->SetBodyFixed(true);
  ground->SetPos(pos);
	// ground->SetIdentifier(-1);
  ground->SetName("ground");
  ground->SetFriction(friction_mu);

  // Don't forget to set this collision family!
  ground->GetCollisionModel()->SetFamily((int)CollisionFam::GROUND);

  // color asset for the ground
  ChSharedPtr<ChColorAsset> groundColor(new ChColorAsset);
  groundColor->SetColor(ChColor(0.2f, 0.4f, 0.6f));
  ground->AddAsset(groundColor);

  vehicle->GetSystem()->Add(ground);  // add this body to the system, which is the vehicle

  return ground;
}

int main(int argc, char* argv[])
{
  // NOTE: utils library built with this sets this statically
  // SetChronoDataPath(CHRONO_DATA_DIR);
  // --------------------------
  // Create the tracked vehicle and the ground/environment

  // The vehicle inherits ChSystem. Input chassis visual and collision type
	TrackVehicle vehicle("Justins M113 model",
    VisualizationType::NONE,
    CollisionType::PRIMITIVES);

  // set the chassis REF at the specified initial config.
  vehicle.Initialize(ChCoordsys<>(initLoc, initRot));

  // ground is a large flat plate
  // TODO: superimposed obstacles using ChParticleEmitter class.
  ChSharedPtr<ChBody> ground = Add_FlatGround(&vehicle, groundSize, groundPos, mu);

  // --------------------------
  // Setup the Irrlicht GUI

/*
#ifdef USE_IRRLICHT
*/
	// Create the Irrlicht visualization applicaiton
  bool do_shadows = false; // shadow map is experimental

  ChIrrApp application(vehicle.GetSystem(),
                      L"M113 tracked vehicle demo",
                      dimension2d<u32>(1400, 1000),
                      false,
                      do_shadows);
  // assumes Y-up
  application.AddTypicalSky();

  irr::scene::ILightSceneNode* mlight = 0;

  if (do_shadows)
  {
    mlight = application.AddLightWithShadow(
      irr::core::vector3df(10.f, 60.f, 30.f),
      irr::core::vector3df(0.f, 0.f, 0.f),
      150, 60, 80, 15, 512, irr::video::SColorf(1, 1, 1), false, false);
  }
  else
  {
    application.AddTypicalLights(
      irr::core::vector3df(30.f, 100.f, 30.f),
      irr::core::vector3df(30.f, 100.f, 50.f),
      250, 130);
  }

  application.SetTimestep(step_size);

  // the GUI driver
  ChIrrGuiTrack driver(application, vehicle, trackPoint, chaseDist, chaseHeight);

  // Set the time response for steering and throttle keyboard inputs.
  // NOTE: this is not exact, since we do not render quite at the specified FPS.
  double steering_time = 1.0;  // time to go from 0 to +1 (or from 0 to -1)
  double throttle_time = 1.0;  // time to go from 0 to +1
  double braking_time = 0.3;  // time to go from 0 to +1
  driver.SetSteeringDelta(render_step_size / steering_time);
  driver.SetThrottleDelta(render_step_size / throttle_time);
  driver.SetBrakingDelta(render_step_size / braking_time);

  // Set up the assets for rendering
  application.AssetBindAll();
  application.AssetUpdateAll();
  if (do_shadows)
  {
    application.AddShadowAll();
  }
/*
#else
  Track_FuncDriver driver;
#endif
*/

  // ---------------------
  // GUI and render settings

  // GUI driver inputs
  std::vector<double> throttle_input;
  std::vector<double> braking_input;
  double steering_input;

  // Number of simulation steps between two 3D view render frames
  int render_steps = (int)std::ceil(render_step_size / step_size);

  // Number of simulation steps between two output frames
  int output_steps = (int)std::ceil(output_step_size / step_size);

  // ---------------------
  // Simulation loop
#ifdef DEBUG_LOG
  GetLog() << "\n\n============ System Configuration ============\n";
  vehicle.GetSystem()->ShowHierarchy(GetLog() );
#endif

  // Initialize simulation frame counter and simulation time
  int step_number = 0;
  double time = 0;
/*
#ifdef USE_IRRLICHT
*/

  ChRealtimeStepTimer realtime_timer;
  while (application.GetDevice()->run())
	{
		// keep track of the time spent calculating each sim step
    ChTimer<double> step_time;
    step_time.start();

    // Render scene
    if (step_number % render_steps == 0) {
      application.GetVideoDriver()->beginScene(true, true, irr::video::SColor(255, 140, 161, 192));
      driver.DrawAll();
      application.GetVideoDriver()->endScene();
    }

    // Collect output data from modules (for inter-module communication)
    throttle_input = driver.GetThrottle();
    steering_input = driver.GetSteering();
    braking_input = driver.GetBraking();

    // Update
    time = vehicle.GetSystem()->GetChTime();

    driver.Update(time);

    vehicle.Update(time, throttle_input, braking_input);

    // Advance simulation for one timestep for all modules
    // double step = realtime_timer.SuggestSimulationStep(step_size);

    driver.Advance(step_size);

    // SETTLING FOLLOWED BY NORMAL OPERATION STEP SIZES HARDCODED
    // 1e-5 and 1e-4, respectively
    vehicle.Advance(step_size);
    step_number++;
	}

  application.GetDevice()->drop();

/*
#else

  int render_frame = 0;

  if(ChFileutils::MakeDirectory(out_dir.c_str()) < 0) {
    std::cout << "Error creating directory " << out_dir << std::endl;
    return 1;
  }
  if(ChFileutils::MakeDirectory(pov_dir.c_str()) < 0) {
    std::cout << "Error creating directory " << pov_dir << std::endl;
    return 1;
  }

  vehicle.ExportMeshPovray(out_dir);

  char filename[100];

  while (time < tend)
  {
    if (step_number % render_steps == 0) {
      // Output render data
      sprintf(filename, "%s/data_%03d.dat", pov_dir.c_str(), render_frame + 1);
      utils::WriteShapesPovray(vehicle.GetSystem(), filename);
      std::cout << "Output frame:   " << render_frame << std::endl;
      std::cout << "Sim frame:      " << step_number << std::endl;
      std::cout << "Time:           " << time << std::endl;
      std::cout << " throttle: " << driver.GetThrottle() << " steering: " << driver.GetSteering() << std::endl;
      std::cout << std::endl;
      render_frame++;
    }

    // Collect output data from modules (for inter-module communication)
    throttle_input = driver.GetThrottle();
    steering_input = driver.GetSteering();
    braking_input = driver.GetBraking();

    // Update modules (process inputs from other modules)
    time = vehicle.GetSystem()->GetChTime();

    driver.Update(time);

    vehicle.Update(time, steering_input, braking_input, powertrain_torque, tire_forces);

    // Advance simulation for one timestep for all modules
    driver.Advance(step_size);

    vehicle.Advance(step_size);

    // Increment frame number
    step_number++;
  }

#endif

*/

	return 0;
}

<|endoftext|>


<commit_before>#include "oglWidget.h"

//
// CONSTRUCTORS ////////////////////////////////////////////////////////////////
//

/**
 * @brief      Default constructor for OGLWidget.
 */
OGLWidget::OGLWidget()
{
    // Update the widget after a frameswap
    connect( this, SIGNAL( frameSwapped() ),
        this, SLOT( update() ) );

    // Allows keyboard input to fall through
    setFocusPolicy( Qt::ClickFocus );

    connect( this, SIGNAL( pause() ),
    this, SLOT( swapPause() ) );

    // Setup the Camera
    camera.rotate( -40.0f, 1.0f, 0.0f, 0.0f );
    camera.translate( 0.0f, 30.0f, 45.0f );

    renderables.push_back( new Sun() );
    renderables.push_back( new Skybox() );
}

/**
 * @brief      Destructor class to unallocate OpenGL information.
 */
OGLWidget::~OGLWidget()
{
    makeCurrent();
    teardownGL();
}

//
// OPENGL FUNCTIONS ////////////////////////////////////////////////////////////
//

void OGLWidget::initializeGL()
{
    // Init OpenGL Backend
    initializeOpenGLFunctions();
    printContextInfo();

    QVectorIterator<Renderable*> i_renderable( renderables );
    while( i_renderable.hasNext() )
    {
        ( i_renderable.next() )->initializeGL();
    }
}

/**
 * @brief      Sets the prespective whenever the window is resized.
 *
 * @param[in]  width   The width of the new window.
 * @param[in]  height  The height of the new window.
 */
void OGLWidget::resizeGL( int width, int height )
{
    projection.setToIdentity();
    projection.perspective( 55.0f,  // Field of view angle
                            float( width ) / float( height ),   // Aspect Ratio
                            0.01f,  // Near Plane (MUST BE GREATER THAN 0)
                            1000.0f );  // Far Plane
}

/**
 * @brief      OpenGL function to draw elements to the surface.
 */
void OGLWidget::paintGL()
{
    glEnable( GL_DEPTH_TEST );
    glDepthFunc( GL_LEQUAL );
    glDepthMask( GL_TRUE );
    glEnable( GL_CULL_FACE );
    glClearColor( 0.0f, 0.0f, 0.2f, 1.0f );

    // Clear the screen
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    QVectorIterator<Renderable*> i_renderable( renderables );
    while( i_renderable.hasNext() )
    {
        ( i_renderable.next() )->paintGL( camera, projection );
    }
}

/**
 * @brief      Destroys any OpenGL data.
 */
void OGLWidget::teardownGL()
{
    QVectorIterator<Renderable*> i_renderable( renderables );
    while( i_renderable.hasNext() )
    {
        ( i_renderable.next() )->teardownGL();
    }
}

//
// QT SLOTS ////////////////////////////////////////////////////////////////////
//

/**
 * @brief      Updates any user interactions and model transformations.
 */
void OGLWidget::update()
{
    Input::update();

    // Camera Transforms
    if( Input::buttonPressed( Qt::RightButton ) )
    {
        static const float cameraTranslationSpeed = 0.3f;
        static const float cameraRotationSpeed = 0.1f;

        camera.rotate( -cameraRotationSpeed * Input::mouseDelta().x(),
            Camera3D::LocalUp );
        camera.rotate( -cameraRotationSpeed * Input::mouseDelta().y(),
            camera.right() );

        QVector3D cameraTranslations;
        if( Input::keyPressed( Qt::Key_W ) )
            cameraTranslations += camera.forward();
        if( Input::keyPressed( Qt::Key_S ) )
            cameraTranslations -= camera.forward();
        if( Input::keyPressed( Qt::Key_A ) )
            cameraTranslations -= camera.right();
        if( Input::keyPressed( Qt::Key_D ) )
            cameraTranslations += camera.right();
        if( Input::keyPressed( Qt::Key_Q ) )
            cameraTranslations -= camera.up();
        if( Input::keyPressed( Qt::Key_E ) )
            cameraTranslations += camera.up();
        camera.translate( cameraTranslationSpeed * cameraTranslations );
    }

    if( Input::keyPressed( Qt::Key_P ))
        emit swapPause();

    if( !paused )
    {
        for( auto renderable : renderables )
        {
            renderable->update();
        }
    }

    QOpenGLWidget::update();
}

/**
* @brief      Slot for pausing/unpausing.
*/
void OGLWidget::swapPause()
{
    paused = !paused;
}

//
// INPUT EVENTS ////////////////////////////////////////////////////////////////
//

/**
 * @brief      Default slot for handling key press events.
 *
 * @param      event  The key event information.
 */
void OGLWidget::keyPressEvent( QKeyEvent* event )
{
    if( event->isAutoRepeat() )
        event->ignore();
    else
        Input::registerKeyPress( event->key() );
}

/**
 * @brief      Default slot for handling key release events.
 *
 * @param      event  The key event information.
 */
void OGLWidget::keyReleaseEvent( QKeyEvent* event )
{
    if( event->isAutoRepeat() )
        event->ignore();
    else
        Input::registerKeyRelease( event->key() );
}

/**
 * @brief      Default slot for handling mouse press events.
 *
 * @param      event  The mouse event information.
 */
void OGLWidget::mousePressEvent( QMouseEvent* event )
{
    Input::registerMousePress( event->button() );
}

/**
 * @brief      Default slot for handling mouse release events.
 *
 * @param      event  The mouse event information.
 */
void OGLWidget::mouseReleaseEvent( QMouseEvent* event )
{
    Input::registerMouseRelease( event->button() );
}

//
// PRIVATE HELPER FUNCTIONS ////////////////////////////////////////////////////
//

/**
 * @brief      Helper function to print OpenGL Context information to the debug.
 */
void OGLWidget::printContextInfo()
{
    QString glType;
    QString glVersion;
    QString glProfile;

    // Get Version Information
    glType = ( context()->isOpenGLES() ) ? "OpenGL ES" : "OpenGL";
    glVersion = reinterpret_cast<const char*>( glGetString( GL_VERSION ) );

    // Get Profile Information
    switch( format().profile() )
    {
        case QSurfaceFormat::NoProfile:
            glProfile = "No Profile";
            break;
        case QSurfaceFormat::CoreProfile:
            glProfile = "Core Profile";
            break;
        case QSurfaceFormat::CompatibilityProfile:
            glProfile = "Compatibility Profile";
            break;
    }

    qDebug() << qPrintable( glType ) << qPrintable( glVersion ) <<
        "(" << qPrintable( glProfile ) << ")";
}<commit_msg>Calls the signal and not the slow<commit_after>#include "oglWidget.h"

//
// CONSTRUCTORS ////////////////////////////////////////////////////////////////
//

/**
 * @brief      Default constructor for OGLWidget.
 */
OGLWidget::OGLWidget()
{
    // Update the widget after a frameswap
    connect( this, SIGNAL( frameSwapped() ),
        this, SLOT( update() ) );

    // Allows keyboard input to fall through
    setFocusPolicy( Qt::ClickFocus );

    connect( this, SIGNAL(pause()), this, SLOT(swapPause()) );

    // Setup the Camera
    camera.rotate( -40.0f, 1.0f, 0.0f, 0.0f );
    camera.translate( 0.0f, 30.0f, 45.0f );

    renderables.push_back( new Sun() );
    renderables.push_back( new Skybox() );
}

/**
 * @brief      Destructor class to unallocate OpenGL information.
 */
OGLWidget::~OGLWidget()
{
    makeCurrent();
    teardownGL();
}

//
// OPENGL FUNCTIONS ////////////////////////////////////////////////////////////
//

void OGLWidget::initializeGL()
{
    // Init OpenGL Backend
    initializeOpenGLFunctions();
    printContextInfo();

    QVectorIterator<Renderable*> i_renderable( renderables );
    while( i_renderable.hasNext() )
    {
        ( i_renderable.next() )->initializeGL();
    }
}

/**
 * @brief      Sets the prespective whenever the window is resized.
 *
 * @param[in]  width   The width of the new window.
 * @param[in]  height  The height of the new window.
 */
void OGLWidget::resizeGL( int width, int height )
{
    projection.setToIdentity();
    projection.perspective( 55.0f,  // Field of view angle
                            float( width ) / float( height ),   // Aspect Ratio
                            0.01f,  // Near Plane (MUST BE GREATER THAN 0)
                            1000.0f );  // Far Plane
}

/**
 * @brief      OpenGL function to draw elements to the surface.
 */
void OGLWidget::paintGL()
{
    glEnable( GL_DEPTH_TEST );
    glDepthFunc( GL_LEQUAL );
    glDepthMask( GL_TRUE );
    glEnable( GL_CULL_FACE );
    glClearColor( 0.0f, 0.0f, 0.2f, 1.0f );

    // Clear the screen
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    QVectorIterator<Renderable*> i_renderable( renderables );
    while( i_renderable.hasNext() )
    {
        ( i_renderable.next() )->paintGL( camera, projection );
    }
}

/**
 * @brief      Destroys any OpenGL data.
 */
void OGLWidget::teardownGL()
{
    QVectorIterator<Renderable*> i_renderable( renderables );
    while( i_renderable.hasNext() )
    {
        ( i_renderable.next() )->teardownGL();
    }
}

//
// QT SLOTS ////////////////////////////////////////////////////////////////////
//

/**
 * @brief      Updates any user interactions and model transformations.
 */
void OGLWidget::update()
{
    Input::update();

    // Camera Transforms
    if( Input::buttonPressed( Qt::RightButton ) )
    {
        static const float cameraTranslationSpeed = 0.3f;
        static const float cameraRotationSpeed = 0.1f;

        camera.rotate( -cameraRotationSpeed * Input::mouseDelta().x(),
            Camera3D::LocalUp );
        camera.rotate( -cameraRotationSpeed * Input::mouseDelta().y(),
            camera.right() );

        QVector3D cameraTranslations;
        if( Input::keyPressed( Qt::Key_W ) )
            cameraTranslations += camera.forward();
        if( Input::keyPressed( Qt::Key_S ) )
            cameraTranslations -= camera.forward();
        if( Input::keyPressed( Qt::Key_A ) )
            cameraTranslations -= camera.right();
        if( Input::keyPressed( Qt::Key_D ) )
            cameraTranslations += camera.right();
        if( Input::keyPressed( Qt::Key_Q ) )
            cameraTranslations -= camera.up();
        if( Input::keyPressed( Qt::Key_E ) )
            cameraTranslations += camera.up();
        camera.translate( cameraTranslationSpeed * cameraTranslations );
    }

    if( Input::keyPressed( Qt::Key_P ))
        emit pause();

    if( !paused )
    {
        for( auto renderable : renderables )
        {
            renderable->update();
        }
    }

    QOpenGLWidget::update();
}

/**
* @brief      Slot for pausing/unpausing.
*/
void OGLWidget::swapPause()
{
    paused = !paused;
}

//
// INPUT EVENTS ////////////////////////////////////////////////////////////////
//

/**
 * @brief      Default slot for handling key press events.
 *
 * @param      event  The key event information.
 */
void OGLWidget::keyPressEvent( QKeyEvent* event )
{
    if( event->isAutoRepeat() )
        event->ignore();
    else
        Input::registerKeyPress( event->key() );
}

/**
 * @brief      Default slot for handling key release events.
 *
 * @param      event  The key event information.
 */
void OGLWidget::keyReleaseEvent( QKeyEvent* event )
{
    if( event->isAutoRepeat() )
        event->ignore();
    else
        Input::registerKeyRelease( event->key() );
}

/**
 * @brief      Default slot for handling mouse press events.
 *
 * @param      event  The mouse event information.
 */
void OGLWidget::mousePressEvent( QMouseEvent* event )
{
    Input::registerMousePress( event->button() );
}

/**
 * @brief      Default slot for handling mouse release events.
 *
 * @param      event  The mouse event information.
 */
void OGLWidget::mouseReleaseEvent( QMouseEvent* event )
{
    Input::registerMouseRelease( event->button() );
}

//
// PRIVATE HELPER FUNCTIONS ////////////////////////////////////////////////////
//

/**
 * @brief      Helper function to print OpenGL Context information to the debug.
 */
void OGLWidget::printContextInfo()
{
    QString glType;
    QString glVersion;
    QString glProfile;

    // Get Version Information
    glType = ( context()->isOpenGLES() ) ? "OpenGL ES" : "OpenGL";
    glVersion = reinterpret_cast<const char*>( glGetString( GL_VERSION ) );

    // Get Profile Information
    switch( format().profile() )
    {
        case QSurfaceFormat::NoProfile:
            glProfile = "No Profile";
            break;
        case QSurfaceFormat::CoreProfile:
            glProfile = "Core Profile";
            break;
        case QSurfaceFormat::CompatibilityProfile:
            glProfile = "Compatibility Profile";
            break;
    }

    qDebug() << qPrintable( glType ) << qPrintable( glVersion ) <<
        "(" << qPrintable( glProfile ) << ")";
}<|endoftext|>