<commit_before>// =============================================================================
// PROJECT CHRONO - http://projectchrono.org
//
// Copyright (c) 2014 projectchrono.org
// All right 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.
//
// =============================================================================
// Authors: Justin Madsen
// =============================================================================
//
// Tracked vehicle model built from subsystems.
//  Location of subsystems hard-coded for M113 vehicle
//  TODO: specify this w/ JSON input data file
//
// =============================================================================

#include <cstdio>

#include "assets/ChCylinderShape.h"
#include "assets/ChTriangleMeshShape.h"
#include "assets/ChTexture.h"
#include "assets/ChColorAsset.h"
#include "assets/ChAssetLevel.h"
#include "physics/ChGlobal.h"

#include "TrackVehicle.h"

#include "subsys/trackSystem/TrackSystem.h"
#include "subsys/driveline/TrackDriveline.h"

#include "utils/ChUtilsInputOutput.h"
#include "utils/ChUtilsData.h"

// collision mesh
#include "geometry/ChCTriangleMeshSoup.h"

namespace chrono {

// -----------------------------------------------------------------------------
// Static variables
const size_t TrackVehicle::m_num_tracks = 2;    // number of trackSystems to create
const size_t TrackVehicle::m_num_engines = 1;   // number of powertrains (and drivelines) to create

const ChVector<> TrackVehicle::m_trackPos_Right(0.23644, -0.4780, 0.83475); // relative to chassis COG
const ChVector<> TrackVehicle::m_trackPos_Left(0.23644, -0.4780, -0.83475); // relative to chassis COG

const double     TrackVehicle::m_mass = 5489.2;   // chassis sprung mass
const ChVector<> TrackVehicle::m_COM = ChVector<>(0., 0.0, 0.);  // COM location, relative to body Csys REF frame
const ChVector<> TrackVehicle::m_inertia(1786.9, 10449.7, 10721.2);  // chassis inertia (roll,yaw,pitch)

const std::string TrackVehicle::m_meshName("M113_chassis");
const std::string TrackVehicle::m_meshFile = utils::GetModelDataFile("M113/Chassis_XforwardYup.obj");
const ChCoordsys<> TrackVehicle::m_driverCsys(ChVector<>(0.0, 0.5, 1.2), ChQuaternion<>(1, 0, 0, 0));
const ChVector<> TrackVehicle::m_chassisBoxSize(2.0, 0.6, 0.75);  // length, height, width HALF DIMS

/// constructor sets the basic integrator settings for this ChSystem, as well as the usual stuff
TrackVehicle::TrackVehicle(const std::string& name, VisualizationType chassisVis, CollisionType chassisCollide)
  : m_vis(chassisVis),
  m_collide(chassisCollide),
  m_ownsSystem(true),
  m_stepsize(1e-3)
{
  // Integration and Solver settings
  SetLcpSolverType(ChSystem::LCP_ITERATIVE_SOR);
  SetIterLCPmaxItersSpeed(150);
  SetIterLCPmaxItersStab(150);
  SetMaxPenetrationRecoverySpeed(2.0);
  // SetIterLCPomega(2.0);
  // SetIterLCPsharpnessLambda(2.0);

  // create the chassis body
  m_chassis = ChSharedPtr<ChBodyAuxRef>(new ChBodyAuxRef);
  m_chassis->SetIdentifier(0);
  m_chassis->SetNameString(name);
  m_chassis->SetFrame_COG_to_REF(ChFrame<>(m_COM, ChQuaternion<>(1, 0, 0, 0)));
  // basic body info
  m_chassis->SetMass(m_mass);
  m_chassis->SetInertiaXX(m_inertia);

  // add visualization assets to the chassis
  AddVisualization();

  // m_chassis->SetBodyFixed(true);
  // add the chassis body to the system
  Add(m_chassis);

  // resize all vectors for the number of track systems
  m_TrackSystems.resize(m_num_tracks);
  m_TrackSystem_locs.resize(m_num_tracks);
  // Right and Left track System relative locations, respectively
  m_TrackSystem_locs[0] = m_trackPos_Right;
  m_TrackSystem_locs[1] = m_trackPos_Left;

  // Each trackSystem has its own driveline and powertrain, so the left and right
  // sides can have power applied independently
  m_drivelines.resize(m_num_engines);
  m_ptrains.resize(m_num_engines);

  // create track systems
  for (int i = 0; i < m_num_tracks; i++) {
    m_TrackSystems[i] = ChSharedPtr<TrackSystem>(new TrackSystem("track chain "+std::to_string(i), i) );
  }

  // create the powertrain and drivelines
  for (int j = 0; j < m_num_engines; j++)
  {
    m_drivelines[j] = ChSharedPtr<TrackDriveline>(new TrackDriveline("driveline "+std::to_string(j)) );
    m_ptrains[j] = ChSharedPtr<TrackPowertrain>(new TrackPowertrain("powertrain "+std::to_string(j)) );

  }

  // TODO: add brakes. Perhaps they are a part of the suspension subsystem?

}

TrackVehicle::~TrackVehicle()
{
  if(m_ownsSystem)
    delete m_system;
}

// Set any collision geometry on the hull, then Initialize() all subsystems
void TrackVehicle::Initialize(const ChCoordsys<>& chassis_Csys)
{
  // move the chassis REF frame to the specified initial position/orientation
  m_chassis->SetFrame_REF_to_abs(ChFrame<>(chassis_Csys));

  // add collision geometry to the chassis
  AddCollisionGeometry();

  // initialize the subsystems with the initial c-sys and specified offsets
  for (int i = 0; i < m_num_tracks; i++)
  {
    m_TrackSystems[i]->Initialize(m_chassis, m_TrackSystem_locs[i]);
  }

  // initialize the powertrain, drivelines
  for (int j = 0; j < m_num_engines; j++)
  {
    size_t driveGear_R_idx = 2*j;
    size_t driveGear_L_idx = 2*j + 1;
    m_drivelines[j]->Initialize(m_chassis,
      m_TrackSystems[driveGear_R_idx]->GetDriveGear(),
      m_TrackSystems[driveGear_L_idx]->GetDriveGear());
    m_ptrains[j]->Initialize(m_chassis, m_drivelines[j]->GetDriveshaft());
  }

}

void TrackVehicle::Update(double	time,
                          const std::vector<double>&  throttle,
                          const std::vector<double>&  braking)
{
  assert( throttle.size() >= m_num_tracks);
  assert( braking.size() >= m_num_tracks );
  // update left and right powertrains, with the new left and right throttle/shaftspeed
  for(int i = 0; i < m_num_engines; i++)
  {
    m_ptrains[i]->Update(time, throttle[i], m_drivelines[0]->GetDriveshaftSpeed() );
  }

}

void TrackVehicle::AddVisualization()
{
  // add visual geometry asset to the chassis, if enabled
  switch (m_vis) {
  case VisualizationType::NONE:
  {
    // put a sphere at the chassis COM and at the REF point
    ChSharedPtr<ChSphereShape> COMsphere(new ChSphereShape);
    COMsphere->GetSphereGeometry().rad = 0.1;
    COMsphere->Pos = m_COM;
    m_chassis->AddAsset(COMsphere);
    // make the COM sphere blue
    ChSharedPtr<ChColorAsset> blue(new ChColorAsset(0.1f, 0.2f, 0.8f));
    m_chassis->AddAsset(blue);

    // to give the REF sphere a different color, add it to the level first.
    ChSharedPtr<ChAssetLevel> ref_level(new ChAssetLevel);
    ChSharedPtr<ChSphereShape> REFsphere(new ChSphereShape);
    REFsphere->GetSphereGeometry().rad = 0.1;
    REFsphere->Pos = ChVector<>(0,0,0); // REF should be at the body c-sys origin
    ref_level->AddAsset(REFsphere);
    // make the REF sphere red
    ChSharedPtr<ChColorAsset> red(new ChColorAsset(0.8f, 0.2f, 0.1f) );
    ref_level->AddAsset(red);
    // add the level to the body
    m_chassis->AddAsset(ref_level);

    break;
  }
  case VisualizationType::PRIMITIVES:
  {
    ChSharedPtr<ChBoxShape> box(new ChBoxShape);
    box->GetBoxGeometry().SetLengths(m_chassisBoxSize );
    m_chassis->AddAsset(box);
    break;
  }
  case VisualizationType::MESH:
  {
    geometry::ChTriangleMeshConnected trimesh;
    trimesh.LoadWavefrontMesh(m_meshFile, true, true);

    ChSharedPtr<ChTriangleMeshShape> trimesh_shape(new ChTriangleMeshShape);
    trimesh_shape->SetMesh(trimesh);
    trimesh_shape->SetName("chassis triMesh");
    m_chassis->AddAsset(trimesh_shape);

    break;
  }
  } // end switch

}

void TrackVehicle::AddCollisionGeometry()
{
  // add collision geometrey to the chassis, if enabled
  if( m_collide == CollisionType::NONE)
  {
    m_chassis->SetCollide(false);
    return;
  }
  m_chassis->SetCollide(true);
  m_chassis->GetCollisionModel()->ClearModel();

  // 1 cm outwards, 0.5 inwards for envelope and margin, respectfully.
  m_chassis->GetCollisionModel()->SetSafeMargin(0.005);	// inward safe margin
	m_chassis->GetCollisionModel()->SetEnvelope(0.010);		// distance of the outward "collision envelope"

  switch (m_collide) {
  case CollisionType::PRIMITIVES:
  {
    // use a simple box
    m_chassis->GetCollisionModel()->AddBox(m_chassisBoxSize.x, m_chassisBoxSize.y, m_chassisBoxSize.z);

    break;
  }
  case CollisionType::MESH:
  {
    // use a triangle mesh

		geometry::ChTriangleMeshSoup temp_trianglemesh;

    // TODO: fill the triangleMesh here with some track shoe geometry

    // is there an offset??
    double shoelength = 0.2;
    ChVector<> mesh_displacement(shoelength*0.5,0,0);  // since mesh origin is not in body center of mass
    m_chassis->GetCollisionModel()->AddTriangleMesh(temp_trianglemesh, false, false, mesh_displacement);

    break;
  }
  case CollisionType::CONVEXHULL:
  {
    // use convex hulls, loaded from file
    ChStreamInAsciiFile chull_file(GetChronoDataFile("track_shoe.chulls").c_str());
    // transform the collision geometry as needed
    double mangle = 45.0; // guess
    ChQuaternion<>rot;
    rot.Q_from_AngAxis(mangle*(CH_C_PI/180.),VECT_X);
    ChMatrix33<> rot_offset(rot);
    ChVector<> disp_offset(0,0,0);  // no displacement offset
    m_chassis->GetCollisionModel()->AddConvexHullsFromFile(chull_file, disp_offset, rot_offset);
    break;
  }
  default:
    // no collision geometry
    GetLog() << "not recognized CollisionType: " << (int)m_collide <<" for chassis \n";
    m_chassis->SetCollide(false);
    return;
  } // end switch

  // set the collision family
  m_chassis->GetCollisionModel()->SetFamily( (int)CollisionFam::HULL );
  // don't collide with rolling elements or tracks
  // m_chassis->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily( (int)(CollisionFam::WHEELS) );
  m_chassis->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily( (int)(CollisionFam::SHOES) );

  m_chassis->GetCollisionModel()->BuildModel();
}

void TrackVehicle::Advance(double step)
{
  double t = 0;
  double settlePhaseA = 0.001;
  double settlePhaseB = 0.01;
  while (t < step) {
    double h = std::min<>(m_stepsize, step - t);
    if( GetChTime() < settlePhaseA )
    {
      h = 1e-5;
    } else if ( GetChTime() < settlePhaseB )
    {
      h = 1e-4;
    }
    DoStepDynamics(h);
    t += h;
  }
}

double TrackVehicle::GetIdlerForce(size_t side)
{
  assert(side < m_num_tracks);
  ChVector<> out_force = m_TrackSystems[side]->Get_idler_spring_react();

  return out_force.Length();
}

} // end namespace chrono
<commit_msg>remove things already in the abstract class functions. Add the pure virtual function defs<commit_after>// =============================================================================
// PROJECT CHRONO - http://projectchrono.org
//
// Copyright (c) 2014 projectchrono.org
// All right 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.
//
// =============================================================================
// Authors: Justin Madsen
// =============================================================================
//
// Tracked vehicle model built from subsystems.
//  Location of subsystems hard-coded for M113 vehicle
//  TODO: specify this w/ JSON input data file
//
// =============================================================================

#include <cstdio>

#include "assets/ChCylinderShape.h"
#include "assets/ChTriangleMeshShape.h"
#include "assets/ChTexture.h"
#include "assets/ChColorAsset.h"
#include "assets/ChAssetLevel.h"
#include "physics/ChGlobal.h"

#include "TrackVehicle.h"

#include "subsys/trackSystem/TrackSystem.h"
#include "subsys/driveline/TrackDriveline.h"

#include "utils/ChUtilsInputOutput.h"
#include "utils/ChUtilsData.h"

// collision mesh
#include "geometry/ChCTriangleMeshSoup.h"

namespace chrono {

// -----------------------------------------------------------------------------
// Static variables
const ChVector<> TrackVehicle::m_trackPos_Right(0.23644, -0.4780, 0.83475); // relative to chassis COG
const ChVector<> TrackVehicle::m_trackPos_Left(0.23644, -0.4780, -0.83475); // relative to chassis COG

const double     TrackVehicle::m_mass = 5489.2;   // chassis sprung mass
const ChVector<> TrackVehicle::m_COM = ChVector<>(0., 0.0, 0.);  // COM location, relative to body Csys REF frame
const ChVector<> TrackVehicle::m_inertia(1786.9, 10449.7, 10721.2);  // chassis inertia (roll,yaw,pitch)

const std::string TrackVehicle::m_meshName("M113_chassis");
const std::string TrackVehicle::m_meshFile = utils::GetModelDataFile("M113/Chassis_XforwardYup.obj");
const ChCoordsys<> TrackVehicle::m_driverCsys(ChVector<>(0.0, 0.5, 1.2), ChQuaternion<>(1, 0, 0, 0));
const ChVector<> TrackVehicle::m_chassisBoxSize(2.0, 0.6, 0.75);  // length, height, width HALF DIMS

/// constructor sets the basic integrator settings for this ChSystem, as well as the usual stuff
TrackVehicle::TrackVehicle(const std::string& name, VisualizationType chassisVis, CollisionType chassisCollide)
  : m_vis(chassisVis),
  m_collide(chassisCollide),
  m_num_tracks(2)
{
  // Integration and Solver settings, set in ChTrackVehicle

  // create the chassis body
  m_chassis = ChSharedPtr<ChBodyAuxRef>(new ChBodyAuxRef);
  m_chassis->SetIdentifier(0);
  m_chassis->SetNameString(name);
  m_chassis->SetFrame_COG_to_REF(ChFrame<>(m_COM, ChQuaternion<>(1, 0, 0, 0)));
  // basic body info
  m_chassis->SetMass(m_mass);
  m_chassis->SetInertiaXX(m_inertia);

  // add visualization assets to the chassis
  AddVisualization();

  // m_chassis->SetBodyFixed(true);
  // add the chassis body to the system
  m_system->Add(m_chassis);

  // resize all vectors for the number of track systems
  m_TrackSystems.resize(m_num_tracks);
  m_TrackSystem_locs.resize(m_num_tracks);
  // Right and Left track System relative locations, respectively
  m_TrackSystem_locs[0] = m_trackPos_Right;
  m_TrackSystem_locs[1] = m_trackPos_Left;

  // two drive Gears, like a 2WD driven vehicle.
  m_num_engines = 1;
  m_drivelines.resize(m_num_engines);
  m_ptrains.resize(m_num_engines);

  // create track systems
  for (int i = 0; i < m_num_tracks; i++) {
    m_TrackSystems[i] = ChSharedPtr<TrackSystem>(new TrackSystem("track chain "+std::to_string(i), i) );
  }

  // create the powertrain and drivelines
  for (int j = 0; j < m_num_engines; j++)
  {
    m_drivelines[j] = ChSharedPtr<TrackDriveline>(new TrackDriveline("driveline "+std::to_string(j)) );
    m_ptrains[j] = ChSharedPtr<TrackPowertrain>(new TrackPowertrain("powertrain "+std::to_string(j)) );

  }

  // TODO: add brakes. Perhaps they are a part of the suspension subsystem?

}

TrackVehicle::~TrackVehicle()
{
  if(m_ownsSystem)
    delete m_system;
}

// Set any collision geometry on the hull, then Initialize() all subsystems
void TrackVehicle::Initialize(const ChCoordsys<>& chassis_Csys)
{
  // move the chassis REF frame to the specified initial position/orientation
  m_chassis->SetFrame_REF_to_abs(ChFrame<>(chassis_Csys));

  // add collision geometry to the chassis
  AddCollisionGeometry();

  // initialize the subsystems with the initial c-sys and specified offsets
  for (int i = 0; i < m_num_tracks; i++)
  {
    m_TrackSystems[i]->Initialize(m_chassis, m_TrackSystem_locs[i]);
  }

  // initialize the powertrain, drivelines
  for (int j = 0; j < m_num_engines; j++)
  {
    size_t driveGear_R_idx = 2*j;
    size_t driveGear_L_idx = 2*j + 1;
    m_drivelines[j]->Initialize(m_chassis,
      m_TrackSystems[driveGear_R_idx]->GetDriveGear(),
      m_TrackSystems[driveGear_L_idx]->GetDriveGear());
    m_ptrains[j]->Initialize(m_chassis, m_drivelines[j]->GetDriveshaft());
  }

}

void TrackVehicle::Update(double	time,
                          const std::vector<double>&  throttle,
                          const std::vector<double>&  braking)
{
  assert( throttle.size() >= m_num_tracks);
  assert( braking.size() >= m_num_tracks );
  // update left and right powertrains, with the new left and right throttle/shaftspeed
  for(int i = 0; i < m_num_engines; i++)
  {
    m_ptrains[i]->Update(time, throttle[i], m_drivelines[0]->GetDriveshaftSpeed() );
  }

}

void TrackVehicle::AddVisualization()
{
  // add visual geometry asset to the chassis, if enabled
  switch (m_vis) {
  case VisualizationType::NONE:
  {
    // put a sphere at the chassis COM and at the REF point
    ChSharedPtr<ChSphereShape> COMsphere(new ChSphereShape);
    COMsphere->GetSphereGeometry().rad = 0.1;
    COMsphere->Pos = m_COM;
    m_chassis->AddAsset(COMsphere);
    // make the COM sphere blue
    ChSharedPtr<ChColorAsset> blue(new ChColorAsset(0.1f, 0.2f, 0.8f));
    m_chassis->AddAsset(blue);

    // to give the REF sphere a different color, add it to the level first.
    ChSharedPtr<ChAssetLevel> ref_level(new ChAssetLevel);
    ChSharedPtr<ChSphereShape> REFsphere(new ChSphereShape);
    REFsphere->GetSphereGeometry().rad = 0.1;
    REFsphere->Pos = ChVector<>(0,0,0); // REF should be at the body c-sys origin
    ref_level->AddAsset(REFsphere);
    // make the REF sphere red
    ChSharedPtr<ChColorAsset> red(new ChColorAsset(0.8f, 0.2f, 0.1f) );
    ref_level->AddAsset(red);
    // add the level to the body
    m_chassis->AddAsset(ref_level);

    break;
  }
  case VisualizationType::PRIMITIVES:
  {
    ChSharedPtr<ChBoxShape> box(new ChBoxShape);
    box->GetBoxGeometry().SetLengths(m_chassisBoxSize );
    m_chassis->AddAsset(box);
    break;
  }
  case VisualizationType::MESH:
  {
    geometry::ChTriangleMeshConnected trimesh;
    trimesh.LoadWavefrontMesh(m_meshFile, true, true);

    ChSharedPtr<ChTriangleMeshShape> trimesh_shape(new ChTriangleMeshShape);
    trimesh_shape->SetMesh(trimesh);
    trimesh_shape->SetName("chassis triMesh");
    m_chassis->AddAsset(trimesh_shape);

    break;
  }
  } // end switch

}

void TrackVehicle::AddCollisionGeometry()
{
  // add collision geometrey to the chassis, if enabled
  if( m_collide == CollisionType::NONE)
  {
    m_chassis->SetCollide(false);
    return;
  }
  m_chassis->SetCollide(true);
  m_chassis->GetCollisionModel()->ClearModel();

  // 1 cm outwards, 0.5 inwards for envelope and margin, respectfully.
  m_chassis->GetCollisionModel()->SetSafeMargin(0.005);	// inward safe margin
	m_chassis->GetCollisionModel()->SetEnvelope(0.010);		// distance of the outward "collision envelope"

  switch (m_collide) {
  case CollisionType::PRIMITIVES:
  {
    // use a simple box
    m_chassis->GetCollisionModel()->AddBox(m_chassisBoxSize.x, m_chassisBoxSize.y, m_chassisBoxSize.z);

    break;
  }
  case CollisionType::MESH:
  {
    // use a triangle mesh

		geometry::ChTriangleMeshSoup temp_trianglemesh;

    // TODO: fill the triangleMesh here with some track shoe geometry

    // is there an offset??
    double shoelength = 0.2;
    ChVector<> mesh_displacement(shoelength*0.5,0,0);  // since mesh origin is not in body center of mass
    m_chassis->GetCollisionModel()->AddTriangleMesh(temp_trianglemesh, false, false, mesh_displacement);

    break;
  }
  case CollisionType::CONVEXHULL:
  {
    // use convex hulls, loaded from file
    ChStreamInAsciiFile chull_file(GetChronoDataFile("track_shoe.chulls").c_str());
    // transform the collision geometry as needed
    double mangle = 45.0; // guess
    ChQuaternion<>rot;
    rot.Q_from_AngAxis(mangle*(CH_C_PI/180.),VECT_X);
    ChMatrix33<> rot_offset(rot);
    ChVector<> disp_offset(0,0,0);  // no displacement offset
    m_chassis->GetCollisionModel()->AddConvexHullsFromFile(chull_file, disp_offset, rot_offset);
    break;
  }
  default:
    // no collision geometry
    GetLog() << "not recognized CollisionType: " << (int)m_collide <<" for chassis \n";
    m_chassis->SetCollide(false);
    return;
  } // end switch

  // set the collision family
  m_chassis->GetCollisionModel()->SetFamily( (int)CollisionFam::HULL );
  // don't collide with rolling elements or tracks
  // m_chassis->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily( (int)(CollisionFam::WHEELS) );
  m_chassis->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily( (int)(CollisionFam::SHOES) );

  m_chassis->GetCollisionModel()->BuildModel();
}

void TrackVehicle::Advance(double step)
{
  double t = 0;
  double settlePhaseA = 0.001;
  double settlePhaseB = 0.01;
  while (t < step) {
    double h = std::min<>(m_stepsize, step - t);
    if( m_system->GetChTime() < settlePhaseA )
    {
      h = 1e-5;
    } else if ( m_system->GetChTime() < settlePhaseB )
    {
      h = 1e-4;
    }
    m_system->DoStepDynamics(h);
    t += h;
  }
}

double TrackVehicle::GetIdlerForce(size_t side)
{
  assert(side < m_num_tracks);
  ChVector<> out_force = m_TrackSystems[side]->Get_idler_spring_react();

  return out_force.Length();
}

double TrackVehicle::GetDriveshaftSpeed(size_t idx) const
{
  assert(idx < m_drivelines.size() );
  return m_drivelines[idx]->GetDriveshaftSpeed();
}

} // end namespace chrono
<|endoftext|>


<commit_before>#include "optimizer.h"

TLANG_NAMESPACE_BEGIN

class AddressAnalyzer : Visitor {
 public:
  int lane_a, lane_b;
  using result_type = std::pair<bool, int>;
  std::map<Expr, result_type> memo;

  AddressAnalyzer(int lane_a, int lane_b)
      : Visitor(Visitor::Order::parent_first), lane_a(lane_a), lane_b(lane_b) {
  }

  result_type run(Expr &expr) {
    expr.accept(*this);
    return memo[expr];
  }

  void visit(Expr &expr) override {
    if (memo.find(expr) != memo.end()) {
      return;
    }

    std::vector<result_type> ch_results;

    for (int i = 0; i < (int)expr->ch.size(); i++) {
      ch_results.push_back(memo[expr->ch[i]]);
    }

    result_type ret{false, 0};
    auto t = expr->type;
    if (t == NodeType::binary) {
      // TODO: sub
      // TODO: imm + val
      if (expr->binary_type == BinaryType::add) {
        if (expr[1]->type == NodeType::imm) {
          if (ch_results[0].first) {
            ret = {true, ch_results[0].second + expr[1]->value<int>(lane_b) -
                             expr[1]->value<int>(lane_a)};
          }
        }
      }
    } else if (t == NodeType::adapter_load) {
      // adapter_id
      if (expr[0]->value<int>(lane_a) == expr[0]->value<int>(lane_b)) {
        // adapter offset
        if (expr[1]->value<int>(lane_a) == expr[1]->value<int>(lane_b)) {
          ret = {true, 0};
        }
      }
    }
    memo[expr] = ret;
  }
};

bool Optimizer::search_and_replace(Expr &expr) {
  if (visited.find(expr) == visited.end()) {
    visited.insert(expr);
  } else {
    return false;
  }
  for (auto &ch : expr->ch) {
    bool ret = search_and_replace(ch);
    if (ret)
      return true;
  }
  return optimize(expr);
}

class ContinuousMemOptimizer : public Optimizer {
 public:
  bool optimize(Expr &expr) override {
    if (expr->type == NodeType::load || expr->type == NodeType::store) {
      auto &ptr = expr._pointer();
      auto &addr_node = expr._pointer()._address();
      bool all_same = true;

      for (int i = 0; i < addr_node->lanes; i++) {
        if (addr_node->snode_ptr(i) != addr_node->snode_ptr(0))
          all_same = false;
      }

      bool incremental = true;
      bool regular_elements = true;
      int offset_start;
      int offset_inc;

      // only consider the last one.
      auto &index_node = expr._pointer()->ch.back();

      if (index_node->type != NodeType::index) {
        return false;
      }
      bool indirect = false;
      if (kernel->program.current_snode->type == SNodeType::indirect &&
          index_node->index_id(0) == kernel->program.current_snode->index_id) {
        TC_INFO("Skipped optimization since it is indirect.");
        indirect = true;
      }

      // TODO: check non-last indices are uniform
      if (index_node->type == NodeType::index) {
        offset_start = index_node->index_offset(0);
        offset_inc = index_node->index_offset(1) - offset_start;
        for (int i = 0; i + 1 < addr_node->lanes; i++) {
          if (index_node->index_offset(i) + offset_inc !=
              index_node->index_offset(i + 1)) {
            incremental = false;
          }
        }
      } else {
        // computed indices
        // case I: adapter load
        incremental = true;
        offset_start = 0;  // useless
        offset_inc = 0;

        // second DFS to match two lanes
        if (all_same) {
          for (int i = 0; i + 1 < index_node->lanes; i++) {
            auto ret = AddressAnalyzer(i, i + 1).run(index_node);
            if (!ret.first) {
              incremental = false;
            } else {
              if (i == 0) {
                offset_inc = ret.second;
              } else if (offset_inc != ret.second) {
                incremental = false;
                break;
              }
            }
          }
        }
      }

      for (int i = 0; i < addr_node->lanes; i++) {
        auto p = addr_node->snode_ptr(i)->parent;
        if (p != addr_node->snode_ptr(0)->parent)
          regular_elements = false;
        if (p->child_id(addr_node->snode_ptr(i)) != i)
          regular_elements = false;
      }

      auto snode = addr_node->snode_ptr(0);
      // continuous index, same element
      bool vpointer_case_1 =
          incremental && offset_start == 0 && offset_inc == 1 &&
          snode->parent->type == SNodeType::fixed && all_same;
      // continuous element, same index
      bool vpointer_case_2 = regular_elements && incremental && offset_inc == 0;
      bool vpointer = !indirect && (vpointer_case_1 || vpointer_case_2);

      if (vpointer) {
        // replace load with vload
        if (expr->type == NodeType::load) {
          TC_INFO("Optimized load to vloadu");
          auto vload = Expr::create(NodeType::vload, addr_node);
          vload->ch.resize(ptr->ch.size());
          for (int i = 1; i < (int)ptr->ch.size(); i++) {
            auto c = Expr::copy_from(ptr->ch[i]);
            TC_ASSERT(c->lanes == 8);
            if (c->type == NodeType::index)
              c->set_lanes(1);
            vload->ch[i] = c;
          }
          vload->set_similar(expr);
          expr.set(vload);
          return true;
        } else {
          TC_INFO("Optimized store to vstoreu");
          auto vstore = Expr::create(NodeType::vstore, addr_node, expr->ch[1]);
          vstore->ch.resize(ptr->ch.size() + 1);
          for (int i = 1; i < (int)ptr->ch.size(); i++) {
            auto c = Expr::copy_from(ptr->ch[i]);
            TC_ASSERT(c->lanes == 8);
            if (c->type == NodeType::index)
              c->set_lanes(1);
            vstore->ch[i + 1] = c;
          }
          vstore->set_similar(expr);
          expr.set(vstore);
          return true;
        }
      }
    }
    return false;
  }
};

class GatherMemOptimizer : public Optimizer {
 public:
  bool optimize(Expr &expr) override {
    if (expr->type != NodeType::load)
      return false;
    auto &ptr = expr._pointer();
    auto &addr_node = expr._pointer()._address();

    for (int i = 0; i < addr_node->lanes; i++) {
      if (addr_node->snode_ptr(i) != addr_node->snode_ptr(0))
        return false;
    }

    // only consider the last one.
    auto &index_node = expr._pointer()->ch.back();

    if (index_node->type != NodeType::index) {
      // return false;
    }
    /*
    if (kernel->program.current_snode->type == SNodeType::indirect &&
        index_node->index_id(0) == kernel->program.current_snode->index_id) {
      TC_INFO("Skipped optimization since it is indirect.");
    }
    */

    // TODO: check non-last indices are uniform
    auto snode = addr_node->snode_ptr(0);
    // continuous index, same element

    if (snode->parent->type == SNodeType::fixed) {
      TC_INFO("Optimized store to gather");  // gather
      Expr gather = Expr::create(NodeType::gather, addr_node);
      gather->ch.resize(ptr->ch.size());
      for (int i = 1; i < (int)ptr->ch.size(); i++) {
        auto c = Expr::copy_from(ptr->ch[i]);
        gather->ch[i] = c;
      }
      gather->set_similar(expr);
      expr.set(gather);
      return true;
    }
    return false;
  }
};
void apply_optimizers(Kernel &ker, Expr &expr) {
  ContinuousMemOptimizer().run(ker, expr);
  GatherMemOptimizer().run(ker, expr);
}

TLANG_NAMESPACE_END
<commit_msg>vectorized load for dynamic<commit_after>#include "optimizer.h"

TLANG_NAMESPACE_BEGIN

class AddressAnalyzer : Visitor {
 public:
  int lane_a, lane_b;
  using result_type = std::pair<bool, int>;
  std::map<Expr, result_type> memo;

  AddressAnalyzer(int lane_a, int lane_b)
      : Visitor(Visitor::Order::parent_first), lane_a(lane_a), lane_b(lane_b) {
  }

  result_type run(Expr &expr) {
    expr.accept(*this);
    return memo[expr];
  }

  void visit(Expr &expr) override {
    if (memo.find(expr) != memo.end()) {
      return;
    }

    std::vector<result_type> ch_results;

    for (int i = 0; i < (int)expr->ch.size(); i++) {
      ch_results.push_back(memo[expr->ch[i]]);
    }

    result_type ret{false, 0};
    auto t = expr->type;
    if (t == NodeType::binary) {
      // TODO: sub
      // TODO: imm + val
      if (expr->binary_type == BinaryType::add) {
        if (expr[1]->type == NodeType::imm) {
          if (ch_results[0].first) {
            ret = {true, ch_results[0].second + expr[1]->value<int>(lane_b) -
                             expr[1]->value<int>(lane_a)};
          }
        }
      }
    } else if (t == NodeType::adapter_load) {
      // adapter_id
      if (expr[0]->value<int>(lane_a) == expr[0]->value<int>(lane_b)) {
        // adapter offset
        if (expr[1]->value<int>(lane_a) == expr[1]->value<int>(lane_b)) {
          ret = {true, 0};
        }
      }
    }
    memo[expr] = ret;
  }
};

bool Optimizer::search_and_replace(Expr &expr) {
  if (visited.find(expr) == visited.end()) {
    visited.insert(expr);
  } else {
    return false;
  }
  for (auto &ch : expr->ch) {
    bool ret = search_and_replace(ch);
    if (ret)
      return true;
  }
  return optimize(expr);
}

class ContinuousMemOptimizer : public Optimizer {
 public:
  bool optimize(Expr &expr) override {
    if (expr->type == NodeType::load || expr->type == NodeType::store) {
      auto &ptr = expr._pointer();
      auto &addr_node = expr._pointer()._address();
      bool all_same = true;

      for (int i = 0; i < addr_node->lanes; i++) {
        if (addr_node->snode_ptr(i) != addr_node->snode_ptr(0))
          all_same = false;
      }

      bool incremental = true;
      bool regular_elements = true;
      int offset_start;
      int offset_inc;

      // only consider the last one.
      auto &index_node = expr._pointer()->ch.back();

      if (index_node->type != NodeType::index) {
        return false;
      }
      bool indirect = false;
      if (kernel->program.current_snode->type == SNodeType::indirect &&
          index_node->index_id(0) == kernel->program.current_snode->index_id) {
        TC_INFO("Skipped optimization since it is indirect.");
        indirect = true;
      }

      // TODO: check non-last indices are uniform
      if (index_node->type == NodeType::index) {
        offset_start = index_node->index_offset(0);
        offset_inc = index_node->index_offset(1) - offset_start;
        for (int i = 0; i + 1 < addr_node->lanes; i++) {
          if (index_node->index_offset(i) + offset_inc !=
              index_node->index_offset(i + 1)) {
            incremental = false;
          }
        }
      } else {
        // computed indices
        // case I: adapter load
        incremental = true;
        offset_start = 0;  // useless
        offset_inc = 0;

        // second DFS to match two lanes
        if (all_same) {
          for (int i = 0; i + 1 < index_node->lanes; i++) {
            auto ret = AddressAnalyzer(i, i + 1).run(index_node);
            if (!ret.first) {
              incremental = false;
            } else {
              if (i == 0) {
                offset_inc = ret.second;
              } else if (offset_inc != ret.second) {
                incremental = false;
                break;
              }
            }
          }
        }
      }

      for (int i = 0; i < addr_node->lanes; i++) {
        auto p = addr_node->snode_ptr(i)->parent;
        if (p != addr_node->snode_ptr(0)->parent)
          regular_elements = false;
        if (p->child_id(addr_node->snode_ptr(i)) != i)
          regular_elements = false;
      }

      auto snode = addr_node->snode_ptr(0);
      // continuous index, same element
      bool vpointer_case_1 = incremental && offset_start == 0 &&
                             offset_inc == 1 &&
                             (snode->parent->type == SNodeType::fixed ||
                              snode->parent->type == SNodeType::dynamic) &&
                             all_same;
      // continuous element, same index
      bool vpointer_case_2 = regular_elements && incremental && offset_inc == 0;
      bool vpointer = !indirect && (vpointer_case_1 || vpointer_case_2);

      if (vpointer) {
        // replace load with vload
        if (expr->type == NodeType::load) {
          TC_INFO("Optimized load to vloadu");
          auto vload = Expr::create(NodeType::vload, addr_node);
          vload->ch.resize(ptr->ch.size());
          for (int i = 1; i < (int)ptr->ch.size(); i++) {
            auto c = Expr::copy_from(ptr->ch[i]);
            TC_ASSERT(c->lanes == kernel->program.config.simd_width);
            if (c->type == NodeType::index)
              c->set_lanes(1);
            vload->ch[i] = c;
          }
          vload->set_similar(expr);
          expr.set(vload);
          return true;
        } else {
          TC_INFO("Optimized store to vstoreu");
          auto vstore = Expr::create(NodeType::vstore, addr_node, expr->ch[1]);
          vstore->ch.resize(ptr->ch.size() + 1);
          for (int i = 1; i < (int)ptr->ch.size(); i++) {
            auto c = Expr::copy_from(ptr->ch[i]);
            TC_ASSERT(c->lanes == kernel->program.config.simd_width);
            if (c->type == NodeType::index)
              c->set_lanes(1);
            vstore->ch[i + 1] = c;
          }
          vstore->set_similar(expr);
          expr.set(vstore);
          return true;
        }
      }
    }
    return false;
  }
};

class GatherMemOptimizer : public Optimizer {
 public:
  bool optimize(Expr &expr) override {
    if (expr->type != NodeType::load)
      return false;
    auto &ptr = expr._pointer();
    auto &addr_node = expr._pointer()._address();

    for (int i = 0; i < addr_node->lanes; i++) {
      if (addr_node->snode_ptr(i) != addr_node->snode_ptr(0))
        return false;
    }

    // only consider the last one.
    auto &index_node = expr._pointer()->ch.back();

    if (index_node->type != NodeType::index) {
      // return false;
    }
    /*
    if (kernel->program.current_snode->type == SNodeType::indirect &&
        index_node->index_id(0) == kernel->program.current_snode->index_id) {
      TC_INFO("Skipped optimization since it is indirect.");
    }
    */

    // TODO: check non-last indices are uniform
    auto snode = addr_node->snode_ptr(0);
    // continuous index, same element

    if (snode->parent->type == SNodeType::fixed) {
      TC_INFO("Optimized store to gather");  // gather
      Expr gather = Expr::create(NodeType::gather, addr_node);
      gather->ch.resize(ptr->ch.size());
      for (int i = 1; i < (int)ptr->ch.size(); i++) {
        auto c = Expr::copy_from(ptr->ch[i]);
        gather->ch[i] = c;
      }
      gather->set_similar(expr);
      expr.set(gather);
      return true;
    }
    return false;
  }
};
void apply_optimizers(Kernel &ker, Expr &expr) {
  ContinuousMemOptimizer().run(ker, expr);
  GatherMemOptimizer().run(ker, expr);
}

TLANG_NAMESPACE_END
<|endoftext|>


<commit_before>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/util/slaveproc.h>

using vespalib::SlaveProc;

TEST("simple run, ignore output") {
    EXPECT_TRUE(SlaveProc::run("echo foo"));
}

TEST("simple run, ignore output, failure") {
    EXPECT_TRUE(!SlaveProc::run("false"));
}

TEST("simple run, ignore output, timeout") {
    EXPECT_TRUE(!SlaveProc::run("sleep 60", 10));
}

TEST("simple run") {
    std::string out;
    EXPECT_TRUE(SlaveProc::run("echo -n foo", out));
    EXPECT_EQUAL(out, "foo");
}

TEST("simple run, strip single-line trailing newline") {
    std::string out;
    EXPECT_TRUE(SlaveProc::run("echo foo", out));
    EXPECT_EQUAL(out, "foo");
}

TEST("simple run, don't strip multi-line output") {
    std::string out;
    EXPECT_TRUE(SlaveProc::run("echo -e \"foo\\n\"", out));
    EXPECT_EQUAL(out, "foo\n\n");
}

TEST("simple run with input") {
    std::string in = "bar";
    std::string out;
    EXPECT_TRUE(SlaveProc::run(in, "cat", out));
    EXPECT_EQUAL(out, "bar");
}

TEST("simple run with input, strip single-line trailing newline") {
    std::string in = "bar\n";
    std::string out;
    EXPECT_TRUE(SlaveProc::run(in, "cat", out));
    EXPECT_EQUAL(out, "bar");
}

TEST("simple run with input, don't strip multi-line output") {
    std::string in = "bar\n\n";
    std::string out;
    EXPECT_TRUE(SlaveProc::run(in, "cat", out));
    EXPECT_EQUAL("bar\n\n", out);
}

TEST_MT("simple run, partial output due to timeout", 2) {
    std::string out;
    std::vector<size_t> timeouts({150, 300, 3000, 6000, 60000});
    for (size_t timeout: timeouts) {
        fprintf(stderr, "... verifying partial output with%s input (timeout = %zu)\n",
                (thread_id == 0) ? "out" : "", timeout);
        if (thread_id == 0) {
            out.clear();
            EXPECT_TRUE(!SlaveProc::run("echo foo; sleep 600; echo bar", out, timeout));
        } else {
            out.clear();
            std::string in = "ignored\n";
            EXPECT_TRUE(!SlaveProc::run(in, "echo foo; sleep 600; echo bar", out, timeout));
        }
        if (out == "foo") {
            break;
        }
    }
    EXPECT_EQUAL(out, "foo");
}

TEST("proc failure") {
    SlaveProc proc("false");
    // read with length 0 will wait for output
    EXPECT_TRUE(proc.read(NULL, 0) == 0);
    EXPECT_TRUE(proc.wait(60000));
    EXPECT_TRUE(!proc.running());
    EXPECT_TRUE(proc.failed());
}

TEST("basic read/write") {
    int x;
    int read;
    char buf[64];
    SlaveProc proc("cat");

    EXPECT_TRUE(proc.running());
    EXPECT_TRUE(!proc.failed());
    EXPECT_TRUE(proc.write("foo", 3));
    for (x = 0, read = 0; x < 10 && read < 3; ++x) {
        read += proc.read(buf + read, sizeof(buf) - read);
    }
    EXPECT_TRUE(read == 3 && memcmp(buf, "foo", 3) == 0);
    EXPECT_TRUE(proc.write("bar!", 4));
    for (x = 0, read = 0; x < 10 && read < 4; ++x) {
        read += proc.read(buf + read, sizeof(buf) - read);
    }
    EXPECT_TRUE(read == 4 && memcmp(buf, "bar!", 4) == 0);
    EXPECT_TRUE(!proc.eof());  // not eof yet
    EXPECT_TRUE(proc.close()); // close stdin
    EXPECT_TRUE(!proc.eof());  // eof not detected yet
    EXPECT_TRUE(proc.read(buf, sizeof(buf)) == 0);
    EXPECT_TRUE(proc.eof());
    EXPECT_TRUE(proc.read(buf, sizeof(buf)) == 0);
    EXPECT_TRUE(proc.wait(60000));
    EXPECT_TRUE(!proc.running());
    EXPECT_TRUE(!proc.failed());
}

TEST("continuos run, readLine") {
    std::string str;
    SlaveProc proc("cat");

    EXPECT_TRUE(proc.running());
    EXPECT_TRUE(!proc.failed());
    EXPECT_TRUE(proc.write("foo\n", 4));
    EXPECT_TRUE(proc.readLine(str));
    EXPECT_EQUAL(str, "foo");
    EXPECT_TRUE(proc.write("bar!\n", 5));
    EXPECT_TRUE(proc.readLine(str));
    EXPECT_EQUAL(str, "bar!");
    EXPECT_TRUE(!proc.eof());  // not eof yet
    EXPECT_TRUE(proc.close()); // close stdin
    EXPECT_TRUE(!proc.eof());  // eof not detected yet
    EXPECT_TRUE(!proc.readLine(str));
    EXPECT_EQUAL(str, "");
    EXPECT_TRUE(proc.eof());
    EXPECT_TRUE(!proc.readLine(str));
    EXPECT_EQUAL(str, "");
    EXPECT_TRUE(proc.wait(60000));
    EXPECT_TRUE(!proc.running());
    EXPECT_TRUE(!proc.failed());
}

TEST("readLine, eof flushes last line") {
    std::string str;
    SlaveProc proc("cat");

    EXPECT_TRUE(proc.running());
    EXPECT_TRUE(!proc.failed());
    EXPECT_TRUE(proc.write("foo\n", 4));
    EXPECT_TRUE(proc.readLine(str));
    EXPECT_EQUAL(str, "foo");
    EXPECT_TRUE(proc.write("bar!", 4));
    EXPECT_TRUE(!proc.eof());  // not eof yet
    EXPECT_TRUE(proc.close()); // close stdin
    EXPECT_TRUE(!proc.eof());  // eof not detected yet
    EXPECT_TRUE(proc.readLine(str));
    EXPECT_EQUAL(str, "bar!");
    EXPECT_TRUE(proc.eof());
    EXPECT_TRUE(!proc.readLine(str));
    EXPECT_EQUAL(str, "");
    EXPECT_TRUE(proc.wait(60000));
    EXPECT_TRUE(!proc.running());
    EXPECT_TRUE(!proc.failed());
}

TEST("long continuos run, readLine") {
    std::string in;
    std::string out;
    SlaveProc proc("cat");

    EXPECT_TRUE(proc.running());
    EXPECT_TRUE(!proc.failed());
    for (uint32_t i = 0; i < 10000; ++i) {
        char num[32];
        sprintf(num, "%d", i);
        in.assign("long continous run, line ");
        in.append(num).append("\n");
        EXPECT_TRUE(proc.write(in.data(), in.length()));
        in.erase(in.size() - 1, 1);
        EXPECT_TRUE(proc.readLine(out));
        EXPECT_EQUAL(in, out);
    }
    EXPECT_TRUE(proc.running());
    EXPECT_TRUE(!proc.failed());
}

TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); }
<commit_msg>avoid multiple levels of sub-processes<commit_after>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/util/slaveproc.h>

using vespalib::SlaveProc;

TEST("simple run, ignore output") {
    EXPECT_TRUE(SlaveProc::run("echo foo"));
}

TEST("simple run, ignore output, failure") {
    EXPECT_TRUE(!SlaveProc::run("false"));
}

TEST("simple run, ignore output, timeout") {
    EXPECT_TRUE(!SlaveProc::run("exec sleep 60", 10));
}

TEST("simple run") {
    std::string out;
    EXPECT_TRUE(SlaveProc::run("echo -n foo", out));
    EXPECT_EQUAL(out, "foo");
}

TEST("simple run, strip single-line trailing newline") {
    std::string out;
    EXPECT_TRUE(SlaveProc::run("echo foo", out));
    EXPECT_EQUAL(out, "foo");
}

TEST("simple run, don't strip multi-line output") {
    std::string out;
    EXPECT_TRUE(SlaveProc::run("echo -e \"foo\\n\"", out));
    EXPECT_EQUAL(out, "foo\n\n");
}

TEST("simple run with input") {
    std::string in = "bar";
    std::string out;
    EXPECT_TRUE(SlaveProc::run(in, "cat", out));
    EXPECT_EQUAL(out, "bar");
}

TEST("simple run with input, strip single-line trailing newline") {
    std::string in = "bar\n";
    std::string out;
    EXPECT_TRUE(SlaveProc::run(in, "cat", out));
    EXPECT_EQUAL(out, "bar");
}

TEST("simple run with input, don't strip multi-line output") {
    std::string in = "bar\n\n";
    std::string out;
    EXPECT_TRUE(SlaveProc::run(in, "cat", out));
    EXPECT_EQUAL("bar\n\n", out);
}

TEST_MT("simple run, partial output due to timeout", 2) {
    std::string out;
    std::vector<size_t> timeouts({150, 300, 3000, 6000, 60000});
    const char *my_cmd = "exec perl -e '$| = 1; print \"foo\\\n\"; sleep(600); print \"bar\\\n\"'";
    for (size_t timeout: timeouts) {
        fprintf(stderr, "... verifying partial output with%s input (timeout = %zu)\n",
                (thread_id == 0) ? "out" : "", timeout);
        if (thread_id == 0) {
            out.clear();
            EXPECT_TRUE(!SlaveProc::run(my_cmd, out, timeout));
        } else {
            out.clear();
            std::string in = "ignored\n";
            EXPECT_TRUE(!SlaveProc::run(in, my_cmd, out, timeout));
        }
        if (out == "foo") {
            break;
        }
    }
    EXPECT_EQUAL(out, "foo");
}

TEST("proc failure") {
    SlaveProc proc("false");
    // read with length 0 will wait for output
    EXPECT_TRUE(proc.read(NULL, 0) == 0);
    EXPECT_TRUE(proc.wait(60000));
    EXPECT_TRUE(!proc.running());
    EXPECT_TRUE(proc.failed());
}

TEST("basic read/write") {
    int x;
    int read;
    char buf[64];
    SlaveProc proc("cat");

    EXPECT_TRUE(proc.running());
    EXPECT_TRUE(!proc.failed());
    EXPECT_TRUE(proc.write("foo", 3));
    for (x = 0, read = 0; x < 10 && read < 3; ++x) {
        read += proc.read(buf + read, sizeof(buf) - read);
    }
    EXPECT_TRUE(read == 3 && memcmp(buf, "foo", 3) == 0);
    EXPECT_TRUE(proc.write("bar!", 4));
    for (x = 0, read = 0; x < 10 && read < 4; ++x) {
        read += proc.read(buf + read, sizeof(buf) - read);
    }
    EXPECT_TRUE(read == 4 && memcmp(buf, "bar!", 4) == 0);
    EXPECT_TRUE(!proc.eof());  // not eof yet
    EXPECT_TRUE(proc.close()); // close stdin
    EXPECT_TRUE(!proc.eof());  // eof not detected yet
    EXPECT_TRUE(proc.read(buf, sizeof(buf)) == 0);
    EXPECT_TRUE(proc.eof());
    EXPECT_TRUE(proc.read(buf, sizeof(buf)) == 0);
    EXPECT_TRUE(proc.wait(60000));
    EXPECT_TRUE(!proc.running());
    EXPECT_TRUE(!proc.failed());
}

TEST("continuos run, readLine") {
    std::string str;
    SlaveProc proc("cat");

    EXPECT_TRUE(proc.running());
    EXPECT_TRUE(!proc.failed());
    EXPECT_TRUE(proc.write("foo\n", 4));
    EXPECT_TRUE(proc.readLine(str));
    EXPECT_EQUAL(str, "foo");
    EXPECT_TRUE(proc.write("bar!\n", 5));
    EXPECT_TRUE(proc.readLine(str));
    EXPECT_EQUAL(str, "bar!");
    EXPECT_TRUE(!proc.eof());  // not eof yet
    EXPECT_TRUE(proc.close()); // close stdin
    EXPECT_TRUE(!proc.eof());  // eof not detected yet
    EXPECT_TRUE(!proc.readLine(str));
    EXPECT_EQUAL(str, "");
    EXPECT_TRUE(proc.eof());
    EXPECT_TRUE(!proc.readLine(str));
    EXPECT_EQUAL(str, "");
    EXPECT_TRUE(proc.wait(60000));
    EXPECT_TRUE(!proc.running());
    EXPECT_TRUE(!proc.failed());
}

TEST("readLine, eof flushes last line") {
    std::string str;
    SlaveProc proc("cat");

    EXPECT_TRUE(proc.running());
    EXPECT_TRUE(!proc.failed());
    EXPECT_TRUE(proc.write("foo\n", 4));
    EXPECT_TRUE(proc.readLine(str));
    EXPECT_EQUAL(str, "foo");
    EXPECT_TRUE(proc.write("bar!", 4));
    EXPECT_TRUE(!proc.eof());  // not eof yet
    EXPECT_TRUE(proc.close()); // close stdin
    EXPECT_TRUE(!proc.eof());  // eof not detected yet
    EXPECT_TRUE(proc.readLine(str));
    EXPECT_EQUAL(str, "bar!");
    EXPECT_TRUE(proc.eof());
    EXPECT_TRUE(!proc.readLine(str));
    EXPECT_EQUAL(str, "");
    EXPECT_TRUE(proc.wait(60000));
    EXPECT_TRUE(!proc.running());
    EXPECT_TRUE(!proc.failed());
}

TEST("long continuos run, readLine") {
    std::string in;
    std::string out;
    SlaveProc proc("cat");

    EXPECT_TRUE(proc.running());
    EXPECT_TRUE(!proc.failed());
    for (uint32_t i = 0; i < 10000; ++i) {
        char num[32];
        sprintf(num, "%d", i);
        in.assign("long continous run, line ");
        in.append(num).append("\n");
        EXPECT_TRUE(proc.write(in.data(), in.length()));
        in.erase(in.size() - 1, 1);
        EXPECT_TRUE(proc.readLine(out));
        EXPECT_EQUAL(in, out);
    }
    EXPECT_TRUE(proc.running());
    EXPECT_TRUE(!proc.failed());
}

TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); }
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "subcomponentmanager.h"
#include "model.h"
#include "metainfo.h"

#include <utils/hostosinfo.h>

#include <QDir>
#include <QMetaType>
#include <QUrl>

enum { debug = false };

QT_BEGIN_NAMESPACE

// Allow usage of QFileInfo in qSort

static bool operator<(const QFileInfo &file1, const QFileInfo &file2)
{
    return file1.filePath() < file2.filePath();
}

QT_END_NAMESPACE

static inline QStringList importPaths() {
    QStringList paths;

    // env import paths
    QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
    if (!envImportPath.isEmpty()) {
        paths = QString::fromLatin1(envImportPath)
                .split(Utils::HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
    }

    return paths;
}

namespace QmlDesigner {
static const QString s_qmlFilePattern = QString(QLatin1String("*.qml"));

SubComponentManager::SubComponentManager(Model *model, QObject *parent)
    : QObject(parent),
      m_model(model)
{
    connect(&m_watcher, SIGNAL(directoryChanged(QString)), this, SLOT(parseDirectory(QString)));
}

void SubComponentManager::addImport(int pos, const Import &import)
{
    if (debug)
        qDebug() << Q_FUNC_INFO << pos << import.file().toLatin1();

    if (import.isFileImport()) {
        QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.file()).toLocalFile());
        if (dirInfo.exists() && dirInfo.isDir()) {
            const QString canonicalDirPath = dirInfo.canonicalFilePath();
            m_watcher.addPath(canonicalDirPath);
            //m_dirToQualifier.insertMulti(canonicalDirPath, import.qualifier()); ### todo: proper support for import as
        }
    } else {
        QString url = import.url();

        url.replace(QLatin1Char('.'), QLatin1Char('/'));

        foreach (const QString &path, importPaths()) {
            url  = path + QLatin1Char('/') + url;
            QFileInfo dirInfo = QFileInfo(url);
            if (dirInfo.exists() && dirInfo.isDir()) {
                const QString canonicalDirPath = dirInfo.canonicalFilePath();
                m_watcher.addPath(canonicalDirPath);
                //m_dirToQualifier.insertMulti(canonicalDirPath, import.qualifier()); ### todo: proper support for import as
            }
        }
        // TODO: QDeclarativeDomImport::Library
    }

    m_imports.insert(pos, import);
}

void SubComponentManager::removeImport(int pos)
{
    const Import import = m_imports.takeAt(pos);

    if (import.isFileImport()) {
        const QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.file()).toLocalFile());
        const QString canonicalDirPath = dirInfo.canonicalFilePath();

        //m_dirToQualifier.remove(canonicalDirPath, import.qualifier()); ### todo: proper support for import as

        if (!m_dirToQualifier.contains(canonicalDirPath))
            m_watcher.removePath(canonicalDirPath);

//        foreach (const QFileInfo &monitoredFile, watchedFiles(canonicalDirPath)) { ### todo: proper support for import as
//            if (!m_dirToQualifier.contains(canonicalDirPath))
//                unregisterQmlFile(monitoredFile, import.qualifier());
//        }
    } else {
            // TODO: QDeclarativeDomImport::Library
    }
}

void SubComponentManager::parseDirectories()
{
    if (!m_filePath.isEmpty()) {
        const QString file = m_filePath.toLocalFile();
        QFileInfo dirInfo = QFileInfo(QFileInfo(file).path());
        if (dirInfo.exists() && dirInfo.isDir())
            parseDirectory(dirInfo.canonicalFilePath());
    }

    foreach (const Import &import, m_imports) {
        if (import.isFileImport()) {
            QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.file()).toLocalFile());
            if (dirInfo.exists() && dirInfo.isDir()) {
                parseDirectory(dirInfo.canonicalFilePath());
            }
        } else {
            QString url = import.url();
            foreach (const QString &path, importPaths()) {
                url.replace(QLatin1Char('.'), QLatin1Char('/'));
                url  = path + QLatin1Char('/') + url;
                QFileInfo dirInfo = QFileInfo(url);
                if (dirInfo.exists() && dirInfo.isDir()) {
                    //### todo full qualified names QString nameSpace = import.uri();
                    parseDirectory(dirInfo.canonicalFilePath(), false);
                }
            }
        }
    }
}

void SubComponentManager::parseDirectory(const QString &canonicalDirPath, bool addToLibrary, const QString& qualification)
{
    if (debug)
        qDebug() << Q_FUNC_INFO << canonicalDirPath;

    QDir dir(canonicalDirPath);

    dir.setNameFilters(QStringList(s_qmlFilePattern));
    dir.setFilter(QDir::Files | QDir::Readable | QDir::CaseSensitive);

    QList<QFileInfo> monitoredList = watchedFiles(canonicalDirPath);
    QList<QFileInfo> newList;
    foreach (const QFileInfo &qmlFile, dir.entryInfoList()) {
        if (QFileInfo(m_filePath.toLocalFile()) == qmlFile) {
            // do not parse main file
            continue;
        }
        if (!qmlFile.fileName().at(0).isUpper()) {
            // QML sub components must be upper case
            continue;
        }
        newList << qmlFile;
    }

    qSort(monitoredList);
    qSort(newList);

    if (debug)
        qDebug() << "monitored list " << monitoredList.size() << "new list " << newList.size();
    QList<QFileInfo>::const_iterator oldIter = monitoredList.constBegin();
    QList<QFileInfo>::const_iterator newIter = newList.constBegin();

    while (oldIter != monitoredList.constEnd() && newIter != newList.constEnd()) {
        const QFileInfo oldFileInfo = *oldIter;
        const QFileInfo newFileInfo = *newIter;
        if (oldFileInfo == newFileInfo) {
            ++oldIter;
            ++newIter;
            continue;
        }
        if (oldFileInfo < newFileInfo) {
            foreach (const QString &qualifier, m_dirToQualifier.value(canonicalDirPath))
                unregisterQmlFile(oldFileInfo, qualifier);
            m_watcher.removePath(oldFileInfo.filePath());
            ++oldIter;
            continue;
        }
        // oldFileInfo > newFileInfo
        parseFile(newFileInfo.filePath(), addToLibrary, qualification);
        ++newIter;
    }

    while (oldIter != monitoredList.constEnd()) {
        foreach (const QString &qualifier, m_dirToQualifier.value(canonicalDirPath))
            unregisterQmlFile(*oldIter, qualifier);
        ++oldIter;
    }

    while (newIter != newList.constEnd()) {
        parseFile(newIter->filePath(), addToLibrary, qualification);
        if (debug)
            qDebug() << "m_watcher.addPath(" << newIter->filePath() << ')';
        ++newIter;
    }
}

void SubComponentManager::parseFile(const QString &canonicalFilePath, bool addToLibrary, const QString& /* qualification */)
{
    if (debug)
        qDebug() << Q_FUNC_INFO << canonicalFilePath;

    QFile file(canonicalFilePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return;
    }

    QString dir = QFileInfo(canonicalFilePath).path();
    foreach (const QString &qualifier, m_dirToQualifier.values(dir)) {
        registerQmlFile(canonicalFilePath, qualifier, addToLibrary);
    }
    registerQmlFile(canonicalFilePath, QString(), addToLibrary);
}

void SubComponentManager::parseFile(const QString &canonicalFilePath)
{
    parseFile(canonicalFilePath, true, QString());
}

// dirInfo must already contain a canonical path
QList<QFileInfo> SubComponentManager::watchedFiles(const QString &canonicalDirPath)
{
    QList<QFileInfo> files;

    foreach (const QString &monitoredFile, m_watcher.files()) {
        QFileInfo fileInfo(monitoredFile);
        if (fileInfo.dir().absolutePath() == canonicalDirPath) {
            files.append(fileInfo);
        }
    }
    return files;
}

void SubComponentManager::unregisterQmlFile(const QFileInfo &fileInfo, const QString &qualifier)
{
    QString componentName = fileInfo.baseName();
    if (!qualifier.isEmpty())
        componentName = qualifier + '.' + componentName;
}

static inline bool isDepricatedQtType(const QString &typeName)
{
    if (typeName.length() < 8)
        return false;

    return typeName.contains("Qt.");
}

void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QString &qualifier,
                                                 bool addToLibrary)
{
    if (!model())
        return;

    QString componentName = fileInfo.baseName();

    if (!qualifier.isEmpty()) {
        QString fixedQualifier = qualifier;
        if (qualifier.right(1) == QLatin1String("."))
            fixedQualifier.chop(1); //remove last char if it is a dot
        componentName = fixedQualifier + '.' + componentName;
    }

    if (debug)
        qDebug() << "SubComponentManager" << __FUNCTION__ << componentName;

    if (addToLibrary) {
        // Add file components to the library
        ItemLibraryEntry itemLibraryEntry;
        itemLibraryEntry.setType(componentName, -1, -1);
        itemLibraryEntry.setName(componentName);
        itemLibraryEntry.setCategory("QML Components");

        if (model()->metaInfo(componentName).isValid() && model()->metaInfo(componentName).isSubclassOf("QtQuick.Item", -1, -1) &&
            !model()->metaInfo().itemLibraryInfo()->containsEntry(itemLibraryEntry)) {

            model()->metaInfo().itemLibraryInfo()->addEntry(itemLibraryEntry);
        }
    }
}

Model *SubComponentManager::model() const
{
    return m_model.data();
}

/*!
  \class SubComponentManager

  Detects & monitors (potential) component files in a list of directories, and registers
  these in the metatype system.
*/

QStringList SubComponentManager::directories() const
{
    return m_watcher.directories();
}

QStringList SubComponentManager::qmlFiles() const
{
    return m_watcher.files();
}

void SubComponentManager::update(const QUrl &filePath, const QList<Import> &imports)
{
    if (debug)
        qDebug() << Q_FUNC_INFO << filePath << imports.size();

    QFileInfo oldDir, newDir;

    if (!m_filePath.isEmpty()) {
        const QString file = m_filePath.toLocalFile();
        oldDir = QFileInfo(QFileInfo(file).path());
    }
    if (!filePath.isEmpty()) {
        const QString file = filePath.toLocalFile();
        newDir = QFileInfo(QFileInfo(file).path());
    }

    m_filePath = filePath;

    //
    // (implicit) import of local directory
    //
    if (oldDir != newDir) {
        if (!oldDir.filePath().isEmpty()) {
            m_dirToQualifier.remove(oldDir.canonicalFilePath(), QString());
            if (!m_dirToQualifier.contains(oldDir.canonicalFilePath()))
                m_watcher.removePath(oldDir.filePath());
        }

        if (!newDir.filePath().isEmpty()) {
            m_dirToQualifier.insertMulti(newDir.canonicalFilePath(), QString());
        }
    }

    //
    // Imports
    //

    // skip first list items until the lists differ
    int i = 0;
    while (i < qMin(imports.size(), m_imports.size())) {
        if (!(imports.at(i) == m_imports.at(i)))
            break;
        ++i;
    }

    for (int ii = m_imports.size() - 1; ii >= i; --ii)
        removeImport(ii);

    for (int ii = i; ii < imports.size(); ++ii) {
        addImport(ii, imports.at(ii));
    }

    parseDirectories();
}

} // namespace QmlDesigner

<commit_msg>QmlDesigner.SubComponentManager: fix qualification of file imports<commit_after>/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "subcomponentmanager.h"
#include "model.h"
#include "metainfo.h"

#include <utils/hostosinfo.h>

#include <QDir>
#include <QMetaType>
#include <QUrl>

enum { debug = false };

QT_BEGIN_NAMESPACE

// Allow usage of QFileInfo in qSort

static bool operator<(const QFileInfo &file1, const QFileInfo &file2)
{
    return file1.filePath() < file2.filePath();
}

QT_END_NAMESPACE

static inline QStringList importPaths() {
    QStringList paths;

    // env import paths
    QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
    if (!envImportPath.isEmpty()) {
        paths = QString::fromLatin1(envImportPath)
                .split(Utils::HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
    }

    return paths;
}

namespace QmlDesigner {
static const QString s_qmlFilePattern = QString(QLatin1String("*.qml"));

SubComponentManager::SubComponentManager(Model *model, QObject *parent)
    : QObject(parent),
      m_model(model)
{
    connect(&m_watcher, SIGNAL(directoryChanged(QString)), this, SLOT(parseDirectory(QString)));
}

void SubComponentManager::addImport(int pos, const Import &import)
{
    if (debug)
        qDebug() << Q_FUNC_INFO << pos << import.file().toLatin1();

    if (import.isFileImport()) {
        QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.file()).toLocalFile());
        if (dirInfo.exists() && dirInfo.isDir()) {
            const QString canonicalDirPath = dirInfo.canonicalFilePath();
            m_watcher.addPath(canonicalDirPath);
            //m_dirToQualifier.insertMulti(canonicalDirPath, import.qualifier()); ### todo: proper support for import as
        }
    } else {
        QString url = import.url();

        url.replace(QLatin1Char('.'), QLatin1Char('/'));

        foreach (const QString &path, importPaths()) {
            url  = path + QLatin1Char('/') + url;
            QFileInfo dirInfo = QFileInfo(url);
            if (dirInfo.exists() && dirInfo.isDir()) {
                const QString canonicalDirPath = dirInfo.canonicalFilePath();
                m_watcher.addPath(canonicalDirPath);
                //m_dirToQualifier.insertMulti(canonicalDirPath, import.qualifier()); ### todo: proper support for import as
            }
        }
        // TODO: QDeclarativeDomImport::Library
    }

    m_imports.insert(pos, import);
}

void SubComponentManager::removeImport(int pos)
{
    const Import import = m_imports.takeAt(pos);

    if (import.isFileImport()) {
        const QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.file()).toLocalFile());
        const QString canonicalDirPath = dirInfo.canonicalFilePath();

        //m_dirToQualifier.remove(canonicalDirPath, import.qualifier()); ### todo: proper support for import as

        if (!m_dirToQualifier.contains(canonicalDirPath))
            m_watcher.removePath(canonicalDirPath);

//        foreach (const QFileInfo &monitoredFile, watchedFiles(canonicalDirPath)) { ### todo: proper support for import as
//            if (!m_dirToQualifier.contains(canonicalDirPath))
//                unregisterQmlFile(monitoredFile, import.qualifier());
//        }
    } else {
            // TODO: QDeclarativeDomImport::Library
    }
}

void SubComponentManager::parseDirectories()
{
    if (!m_filePath.isEmpty()) {
        const QString file = m_filePath.toLocalFile();
        QFileInfo dirInfo = QFileInfo(QFileInfo(file).path());
        if (dirInfo.exists() && dirInfo.isDir())
            parseDirectory(dirInfo.canonicalFilePath());
    }

    foreach (const Import &import, m_imports) {
        if (import.isFileImport()) {
            QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.file()).toLocalFile());
            if (dirInfo.exists() && dirInfo.isDir()) {
                parseDirectory(dirInfo.canonicalFilePath());
            }
        } else {
            QString url = import.url();
            foreach (const QString &path, importPaths()) {
                url.replace(QLatin1Char('.'), QLatin1Char('/'));
                url  = path + QLatin1Char('/') + url;
                QFileInfo dirInfo = QFileInfo(url);
                if (dirInfo.exists() && dirInfo.isDir()) {
                    //### todo full qualified names QString nameSpace = import.uri();
                    parseDirectory(dirInfo.canonicalFilePath(), false);
                }
            }
        }
    }
}

void SubComponentManager::parseDirectory(const QString &canonicalDirPath, bool addToLibrary, const QString& qualification)
{
    if (debug)
        qDebug() << Q_FUNC_INFO << canonicalDirPath;

    QDir dir(canonicalDirPath);

    dir.setNameFilters(QStringList(s_qmlFilePattern));
    dir.setFilter(QDir::Files | QDir::Readable | QDir::CaseSensitive);

    QList<QFileInfo> monitoredList = watchedFiles(canonicalDirPath);
    QList<QFileInfo> newList;
    foreach (const QFileInfo &qmlFile, dir.entryInfoList()) {
        if (QFileInfo(m_filePath.toLocalFile()) == qmlFile) {
            // do not parse main file
            continue;
        }
        if (!qmlFile.fileName().at(0).isUpper()) {
            // QML sub components must be upper case
            continue;
        }
        newList << qmlFile;
    }

    qSort(monitoredList);
    qSort(newList);

    if (debug)
        qDebug() << "monitored list " << monitoredList.size() << "new list " << newList.size();
    QList<QFileInfo>::const_iterator oldIter = monitoredList.constBegin();
    QList<QFileInfo>::const_iterator newIter = newList.constBegin();

    while (oldIter != monitoredList.constEnd() && newIter != newList.constEnd()) {
        const QFileInfo oldFileInfo = *oldIter;
        const QFileInfo newFileInfo = *newIter;
        if (oldFileInfo == newFileInfo) {
            ++oldIter;
            ++newIter;
            continue;
        }
        if (oldFileInfo < newFileInfo) {
            foreach (const QString &qualifier, m_dirToQualifier.value(canonicalDirPath))
                unregisterQmlFile(oldFileInfo, qualifier);
            m_watcher.removePath(oldFileInfo.filePath());
            ++oldIter;
            continue;
        }
        // oldFileInfo > newFileInfo
        parseFile(newFileInfo.filePath(), addToLibrary, qualification);
        ++newIter;
    }

    while (oldIter != monitoredList.constEnd()) {
        foreach (const QString &qualifier, m_dirToQualifier.value(canonicalDirPath))
            unregisterQmlFile(*oldIter, qualifier);
        ++oldIter;
    }

    while (newIter != newList.constEnd()) {
        parseFile(newIter->filePath(), addToLibrary, qualification);
        if (debug)
            qDebug() << "m_watcher.addPath(" << newIter->filePath() << ')';
        ++newIter;
    }
}

void SubComponentManager::parseFile(const QString &canonicalFilePath, bool addToLibrary, const QString&  qualification)
{
    if (debug)
        qDebug() << Q_FUNC_INFO << canonicalFilePath;

    QFile file(canonicalFilePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return;
    }

    QString dir = QFileInfo(canonicalFilePath).path();
    foreach (const QString &qualifier, m_dirToQualifier.values(dir)) {
        registerQmlFile(canonicalFilePath, qualifier, addToLibrary);
    }
    registerQmlFile(canonicalFilePath, qualification, addToLibrary);
}

void SubComponentManager::parseFile(const QString &canonicalFilePath)
{
    parseFile(canonicalFilePath, true, QString());
}

// dirInfo must already contain a canonical path
QList<QFileInfo> SubComponentManager::watchedFiles(const QString &canonicalDirPath)
{
    QList<QFileInfo> files;

    foreach (const QString &monitoredFile, m_watcher.files()) {
        QFileInfo fileInfo(monitoredFile);
        if (fileInfo.dir().absolutePath() == canonicalDirPath) {
            files.append(fileInfo);
        }
    }
    return files;
}

void SubComponentManager::unregisterQmlFile(const QFileInfo &fileInfo, const QString &qualifier)
{
    QString componentName = fileInfo.baseName();
    if (!qualifier.isEmpty())
        componentName = qualifier + '.' + componentName;
}

static inline bool isDepricatedQtType(const QString &typeName)
{
    if (typeName.length() < 8)
        return false;

    return typeName.contains("Qt.");
}

void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QString &qualifier,
                                                 bool addToLibrary)
{
    if (!model())
        return;

    QString componentName = fileInfo.baseName();

    if (debug)
        qDebug() << "SubComponentManager" << __FUNCTION__ << componentName;

    if (addToLibrary) {
        // Add file components to the library
        ItemLibraryEntry itemLibraryEntry;
        itemLibraryEntry.setType(componentName, -1, -1);
        itemLibraryEntry.setName(componentName);
        itemLibraryEntry.setCategory("QML Components");
        if (!qualifier.isEmpty()) {
            itemLibraryEntry.setForceImport(true);
            itemLibraryEntry.setRequiredImport(qualifier);
        }

        if (model()->metaInfo(componentName).isValid() && model()->metaInfo(componentName).isSubclassOf("QtQuick.Item", -1, -1) &&
            !model()->metaInfo().itemLibraryInfo()->containsEntry(itemLibraryEntry)) {

            model()->metaInfo().itemLibraryInfo()->addEntry(itemLibraryEntry);
        }
    }
}

Model *SubComponentManager::model() const
{
    return m_model.data();
}

/*!
  \class SubComponentManager

  Detects & monitors (potential) component files in a list of directories, and registers
  these in the metatype system.
*/

QStringList SubComponentManager::directories() const
{
    return m_watcher.directories();
}

QStringList SubComponentManager::qmlFiles() const
{
    return m_watcher.files();
}

void SubComponentManager::update(const QUrl &filePath, const QList<Import> &imports)
{
    if (debug)
        qDebug() << Q_FUNC_INFO << filePath << imports.size();

    QFileInfo oldDir, newDir;

    if (!m_filePath.isEmpty()) {
        const QString file = m_filePath.toLocalFile();
        oldDir = QFileInfo(QFileInfo(file).path());
    }
    if (!filePath.isEmpty()) {
        const QString file = filePath.toLocalFile();
        newDir = QFileInfo(QFileInfo(file).path());
    }

    m_filePath = filePath;

    //
    // (implicit) import of local directory
    //
    if (oldDir != newDir) {
        if (!oldDir.filePath().isEmpty()) {
            m_dirToQualifier.remove(oldDir.canonicalFilePath(), QString());
            if (!m_dirToQualifier.contains(oldDir.canonicalFilePath()))
                m_watcher.removePath(oldDir.filePath());
        }

        if (!newDir.filePath().isEmpty()) {
            m_dirToQualifier.insertMulti(newDir.canonicalFilePath(), QString());
        }
    }

    //
    // Imports
    //

    // skip first list items until the lists differ
    int i = 0;
    while (i < qMin(imports.size(), m_imports.size())) {
        if (!(imports.at(i) == m_imports.at(i)))
            break;
        ++i;
    }

    for (int ii = m_imports.size() - 1; ii >= i; --ii)
        removeImport(ii);

    for (int ii = i; ii < imports.size(); ++ii) {
        addImport(ii, imports.at(ii));
    }

    parseDirectories();
}

} // namespace QmlDesigner

<|endoftext|>


<commit_before>#include "viewer/primitives/simple_geometry_primitives.hh"

#include <GL/glew.h>
#include "viewer/colors/material.hh"
#include "viewer/gl_aliases.hh"

#include "geometry/perp.hh"

namespace viewer {

using Vec3 = Eigen::Vector3d;
using Vec4 = Eigen::Vector4d;

void draw_axes(const Axes &axes) {
  glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT);
  glLineWidth(1.0);

  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glTransform(axes.world_from_axes);
  glScaled(axes.scale, axes.scale, axes.scale);

  if (axes.dotted) {
    glLineStipple(1.0, 0x1010);
  }
  glLineWidth(axes.line_width);

  glBegin(GL_LINES);

  glColor4d(1.0, 0.0, 0.0, 0.9);
  glVertex(Vec3::UnitX().eval());
  glVertex(Vec3::Zero().eval());

  glColor4d(0.0, 1.0, 0.0, 0.9);
  glVertex(Vec3::UnitY().eval());
  glVertex(Vec3::Zero().eval());

  glColor4d(0.0, 0.0, 1.0, 0.9);
  glVertex(Vec3::UnitZ().eval());
  glVertex(Vec3::Zero().eval());
  glEnd();

  glPopMatrix();
  glPopAttrib();
}

void draw_lines(const std::vector<Line> &lines) {
  glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT);

  for (const auto &line : lines) {
    glLineWidth(line.width);
    glBegin(GL_LINES);
    glColor(line.color);
    glVertex(line.start);
    glVertex(line.end);
    glEnd();
  }

  glPopAttrib();
}

void draw_polygon(const Polygon &polygon) {
  const int n_points = static_cast<int>(polygon.points.size());
  const Eigen::Vector3d offset(0.0, 0.0, polygon.height);

  glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT);
  glLineWidth(polygon.width);
  // Draw the height of the poly
  /*
  glBegin(GL_QUADS);
  glColor(polygon.color);
  for (int k = 0; k < n_points; ++k) {
    const auto &start = polygon.points[k];
    const auto &end = polygon.points[(k + 1) % n_points];
    glVertex(start);
    glVertex(end);

    glVertex(Vec3(end + offset));
    glVertex(Vec3(start + offset));
  }
  glEnd();
  */

  /*
  if (polygon.outline) {
    glLineWidth(0.8);
    glBegin(GL_LINE_LOOP);
    glColor(Vec4(Vec4::Ones()));
    for (int k = 0; k < n_points; ++k) {
      const auto &start = polygon.points[k];
      const auto &end = polygon.points[(k + 1) % n_points];
      glVertex(start);
      glVertex(end);

      glVertex(Vec3(end + offset));
      glVertex(Vec3(start + offset));
    }
    glEnd();
  }
  */

  glColor(polygon.color);
  glBegin(GL_TRIANGLE_FAN);
  for (int k = 0; k < n_points; ++k) {
    const auto &point = polygon.points[k];
    glVertex(point);
  }
  glEnd();

  if (polygon.outline) {
    glLineWidth(3.0);
    glColor(Vec4(Vec4::Ones()));
    glBegin(GL_LINE_LOOP);
    for (int k = 0; k < n_points; ++k) {
      const auto &point = polygon.points[k];
      glVertex(point);
    }
    glEnd();
  }

  glPopAttrib();
}

void draw_points(const Points &points) {
  glPushAttrib(GL_POINT_BIT | GL_CURRENT_BIT);
  glColor(points.color);
  glPointSize(points.size);
  glBegin(GL_POINTS);
  for (const auto &pt : points.points) {
    glVertex(pt);
  }
  glEnd();
  glPopAttrib();
}

void draw_colored_points(const ColoredPoints &points) {
  glPushAttrib(GL_POINT_BIT | GL_CURRENT_BIT);
  glPointSize(points.size);
  glBegin(GL_POINTS);
  for (std::size_t k = 0; k < points.points.size(); ++k) {
    glColor(points.colors[k]);
    glVertex(points.points[k]);
  }
  glEnd();
  glPopAttrib();
}

void draw_points2d(const Points2d &points) {
  glPushAttrib(GL_POINT_BIT | GL_CURRENT_BIT);
  glColor(points.color);
  glPointSize(points.size);
  glBegin(GL_POINTS);
  for (const auto &pt : points.points) {
    glVertex3d(pt.x(), pt.y(), points.z_offset);
  }
  glEnd();
  glPopAttrib();
}

void draw_circle(const Vec3 &center,
                 const Vec3 &normal,
                 const double radius,
                 const Vec4 &color) {
  constexpr double CIRCLE_RES = 0.05;

  const Vec3 x_u = geometry::perp(normal);
  const Vec3 y_u = -x_u.cross(normal);

  Eigen::Matrix3d world_from_circle_frame;
  world_from_circle_frame.col(0) = x_u;
  world_from_circle_frame.col(1) = y_u;
  world_from_circle_frame.col(2) = normal;

  glColor(color);
  glBegin(GL_LINE_LOOP);
  for (double t = 0.0; t < (2.0 * M_PI); t += CIRCLE_RES) {
    const Vec3 pt_circle_frm(radius * std::sin(t), radius * std::cos(t), 0.0);
    const Vec3 pt_world_frm = (world_from_circle_frame * pt_circle_frm) + center;
    glVertex(pt_world_frm);
  }
  glEnd();
}

void draw_sphere(const Sphere &sphere) {
  draw_circle(sphere.center, sphere.world_from_sphere * Vec3::UnitX(), sphere.radius,
              sphere.color);
  draw_circle(sphere.center, sphere.world_from_sphere * Vec3::UnitY(), sphere.radius,
              sphere.color);
  draw_circle(sphere.center, sphere.world_from_sphere * Vec3::UnitZ(), sphere.radius,
              sphere.color);
}

void draw_plane_grid(const Plane &plane) {
  const Vec3 &n = plane.plane.u_normal;
  const Vec3 x_dir = geometry::perp(n);
  const Vec3 y_dir = x_dir.cross(n).normalized();

  const int n_lines = 10;
  const double displacement = n_lines * plane.line_spacing;
  const Vec3 offset = plane.plane.u_normal * plane.plane.distance_from_origin;

  const Vec3 y_offset = y_dir * 10.0;
  const Vec3 x_offset = x_dir * 10.0;

  glPushAttrib(GL_CURRENT_BIT);
  glColor(plane.color);
  glBegin(GL_LINES);
  {
    for (double x = -displacement; x <= displacement; x += plane.line_spacing) {
      const Vec3 v = (x * x_dir) + offset;

      // Lines retreating off to infinity by homogeneousness
      // turns out, it doesn't look that good.

      /*glVertex4d(v.x(), v.y(), v.z(), 0.0);
      glVertex4d(y_dir.x(), y_dir.y(), y_dir.z(), 0.0);
      glVertex4d(v.x(), v.y(), v.z(), 1.0);*/

      glVertex(Vec3(v + y_offset));
      glVertex(Vec3(v - y_offset));
    }
    for (double y = -displacement; y <= displacement; y += plane.line_spacing) {
      const Vec3 v = (y * y_dir) + offset;

      glVertex(Vec3(v + x_offset));
      glVertex(Vec3(v - x_offset));
    }
  }
  glEnd();
  glPopAttrib();
}

void draw_point(const Point &point) {
  glPushAttrib(GL_POINT_BIT | GL_CURRENT_BIT);
  glPointSize(point.size);
  glColor(point.color);

  glBegin(GL_POINTS);
  glVertex(point.point);
  glEnd();

  glPopAttrib();
}

void draw_trimesh(const TriMesh &trimesh) {
  glPushAttrib(GL_CURRENT_BIT);

  glPushMatrix();
  glTransform(trimesh.world_from_mesh);

  glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  if (trimesh.outline) {
    glPushAttrib(GL_LINE_BIT);
    glLineWidth(trimesh.outline_width);
    glBegin(GL_LINES);
    for (const auto &tri : trimesh.mesh.triangles) {
      glVertex(tri.vertices[0]);
      glVertex(tri.vertices[1]);
      glVertex(tri.vertices[2]);
    }
    glEnd();
    glPopAttrib();
  }

  // glEnable(GL_LIGHTING);
  const auto material = colors::get_plastic(trimesh.color);
  colors::gl_material(material);
  glColor(trimesh.color);
  if (trimesh.filled) {
    glBegin(GL_TRIANGLES);
    for (const auto &tri : trimesh.mesh.triangles) {
      glVertex(tri.vertices[0]);
      glVertex(tri.vertices[1]);
      glVertex(tri.vertices[2]);
    }

    glEnd();
  }
  // glDisable(GL_LIGHTING);
  glPopMatrix();

  glPopAttrib();
  // draw the display list
}
}  // namespace viewer
<commit_msg>[Viewer] Fix stippling<commit_after>#include "viewer/primitives/simple_geometry_primitives.hh"

#include <GL/glew.h>
#include "viewer/colors/material.hh"
#include "viewer/gl_aliases.hh"

#include "geometry/perp.hh"

namespace viewer {

using Vec3 = Eigen::Vector3d;
using Vec4 = Eigen::Vector4d;

void draw_axes(const Axes &axes) {
  glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT);
  glLineWidth(1.0);

  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glTransform(axes.world_from_axes);
  glScaled(axes.scale, axes.scale, axes.scale);

  if (axes.dotted) {
    glEnable(GL_LINE_STIPPLE);
    glLineStipple(1.0, 0x00FF);
  }
  glLineWidth(axes.line_width);

  glBegin(GL_LINES);

  glColor4d(1.0, 0.0, 0.0, 0.9);
  glVertex(Vec3::UnitX().eval());
  glVertex(Vec3::Zero().eval());

  glColor4d(0.0, 1.0, 0.0, 0.9);
  glVertex(Vec3::UnitY().eval());
  glVertex(Vec3::Zero().eval());

  glColor4d(0.0, 0.0, 1.0, 0.9);
  glVertex(Vec3::UnitZ().eval());
  glVertex(Vec3::Zero().eval());
  glEnd();

  glPopMatrix();
  glPopAttrib();
}

void draw_lines(const std::vector<Line> &lines) {
  glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT);

  for (const auto &line : lines) {
    glLineWidth(line.width);
    glBegin(GL_LINES);
    glColor(line.color);
    glVertex(line.start);
    glVertex(line.end);
    glEnd();
  }

  glPopAttrib();
}

void draw_polygon(const Polygon &polygon) {
  const int n_points = static_cast<int>(polygon.points.size());
  const Eigen::Vector3d offset(0.0, 0.0, polygon.height);

  glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT);
  glLineWidth(polygon.width);
  // Draw the height of the poly
  /*
  glBegin(GL_QUADS);
  glColor(polygon.color);
  for (int k = 0; k < n_points; ++k) {
    const auto &start = polygon.points[k];
    const auto &end = polygon.points[(k + 1) % n_points];
    glVertex(start);
    glVertex(end);

    glVertex(Vec3(end + offset));
    glVertex(Vec3(start + offset));
  }
  glEnd();
  */

  /*
  if (polygon.outline) {
    glLineWidth(0.8);
    glBegin(GL_LINE_LOOP);
    glColor(Vec4(Vec4::Ones()));
    for (int k = 0; k < n_points; ++k) {
      const auto &start = polygon.points[k];
      const auto &end = polygon.points[(k + 1) % n_points];
      glVertex(start);
      glVertex(end);

      glVertex(Vec3(end + offset));
      glVertex(Vec3(start + offset));
    }
    glEnd();
  }
  */

  glColor(polygon.color);
  glBegin(GL_TRIANGLE_FAN);
  for (int k = 0; k < n_points; ++k) {
    const auto &point = polygon.points[k];
    glVertex(point);
  }
  glEnd();

  if (polygon.outline) {
    glLineWidth(3.0);
    glColor(Vec4(Vec4::Ones()));
    glBegin(GL_LINE_LOOP);
    for (int k = 0; k < n_points; ++k) {
      const auto &point = polygon.points[k];
      glVertex(point);
    }
    glEnd();
  }

  glPopAttrib();
}

void draw_points(const Points &points) {
  glPushAttrib(GL_POINT_BIT | GL_CURRENT_BIT);
  glColor(points.color);
  glPointSize(points.size);
  glBegin(GL_POINTS);
  for (const auto &pt : points.points) {
    glVertex(pt);
  }
  glEnd();
  glPopAttrib();
}

void draw_colored_points(const ColoredPoints &points) {
  glPushAttrib(GL_POINT_BIT | GL_CURRENT_BIT);
  glPointSize(points.size);
  glBegin(GL_POINTS);
  for (std::size_t k = 0; k < points.points.size(); ++k) {
    glColor(points.colors[k]);
    glVertex(points.points[k]);
  }
  glEnd();
  glPopAttrib();
}

void draw_points2d(const Points2d &points) {
  glPushAttrib(GL_POINT_BIT | GL_CURRENT_BIT);
  glColor(points.color);
  glPointSize(points.size);
  glBegin(GL_POINTS);
  for (const auto &pt : points.points) {
    glVertex3d(pt.x(), pt.y(), points.z_offset);
  }
  glEnd();
  glPopAttrib();
}

void draw_circle(const Vec3 &center,
                 const Vec3 &normal,
                 const double radius,
                 const Vec4 &color) {
  constexpr double CIRCLE_RES = 0.05;

  const Vec3 x_u = geometry::perp(normal);
  const Vec3 y_u = -x_u.cross(normal);

  Eigen::Matrix3d world_from_circle_frame;
  world_from_circle_frame.col(0) = x_u;
  world_from_circle_frame.col(1) = y_u;
  world_from_circle_frame.col(2) = normal;

  glColor(color);
  glBegin(GL_LINE_LOOP);
  for (double t = 0.0; t < (2.0 * M_PI); t += CIRCLE_RES) {
    const Vec3 pt_circle_frm(radius * std::sin(t), radius * std::cos(t), 0.0);
    const Vec3 pt_world_frm = (world_from_circle_frame * pt_circle_frm) + center;
    glVertex(pt_world_frm);
  }
  glEnd();
}

void draw_sphere(const Sphere &sphere) {
  draw_circle(sphere.center, sphere.world_from_sphere * Vec3::UnitX(), sphere.radius,
              sphere.color);
  draw_circle(sphere.center, sphere.world_from_sphere * Vec3::UnitY(), sphere.radius,
              sphere.color);
  draw_circle(sphere.center, sphere.world_from_sphere * Vec3::UnitZ(), sphere.radius,
              sphere.color);
}

void draw_plane_grid(const Plane &plane) {
  const Vec3 &n = plane.plane.u_normal;
  const Vec3 x_dir = geometry::perp(n);
  const Vec3 y_dir = x_dir.cross(n).normalized();

  const int n_lines = 10;
  const double displacement = n_lines * plane.line_spacing;
  const Vec3 offset = plane.plane.u_normal * plane.plane.distance_from_origin;

  const Vec3 y_offset = y_dir * 10.0;
  const Vec3 x_offset = x_dir * 10.0;

  glPushAttrib(GL_CURRENT_BIT);
  glColor(plane.color);
  glBegin(GL_LINES);
  {
    for (double x = -displacement; x <= displacement; x += plane.line_spacing) {
      const Vec3 v = (x * x_dir) + offset;

      // Lines retreating off to infinity by homogeneousness
      // turns out, it doesn't look that good.

      /*glVertex4d(v.x(), v.y(), v.z(), 0.0);
      glVertex4d(y_dir.x(), y_dir.y(), y_dir.z(), 0.0);
      glVertex4d(v.x(), v.y(), v.z(), 1.0);*/

      glVertex(Vec3(v + y_offset));
      glVertex(Vec3(v - y_offset));
    }
    for (double y = -displacement; y <= displacement; y += plane.line_spacing) {
      const Vec3 v = (y * y_dir) + offset;

      glVertex(Vec3(v + x_offset));
      glVertex(Vec3(v - x_offset));
    }
  }
  glEnd();
  glPopAttrib();
}

void draw_point(const Point &point) {
  glPushAttrib(GL_POINT_BIT | GL_CURRENT_BIT);
  glPointSize(point.size);
  glColor(point.color);

  glBegin(GL_POINTS);
  glVertex(point.point);
  glEnd();

  glPopAttrib();
}

void draw_trimesh(const TriMesh &trimesh) {
  glPushAttrib(GL_CURRENT_BIT);

  glPushMatrix();
  glTransform(trimesh.world_from_mesh);

  glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  if (trimesh.outline) {
    glPushAttrib(GL_LINE_BIT);
    glLineWidth(trimesh.outline_width);
    glBegin(GL_LINES);
    for (const auto &tri : trimesh.mesh.triangles) {
      glVertex(tri.vertices[0]);
      glVertex(tri.vertices[1]);
      glVertex(tri.vertices[2]);
    }
    glEnd();
    glPopAttrib();
  }

  // glEnable(GL_LIGHTING);
  const auto material = colors::get_plastic(trimesh.color);
  colors::gl_material(material);
  glColor(trimesh.color);
  if (trimesh.filled) {
    glBegin(GL_TRIANGLES);
    for (const auto &tri : trimesh.mesh.triangles) {
      glVertex(tri.vertices[0]);
      glVertex(tri.vertices[1]);
      glVertex(tri.vertices[2]);
    }

    glEnd();
  }
  // glDisable(GL_LIGHTING);
  glPopMatrix();

  glPopAttrib();
  // draw the display list
}
}  // namespace viewer
<|endoftext|>