<commit_before>#include <babylon/gamepads/controllers/pose_enabled_controller.h>

#include <babylon/culling/ray.h>
#include <babylon/engines/scene.h>
#include <babylon/interfaces/ibrowser_gamepad.h>
#include <babylon/maths/tmp_vectors.h>
#include <babylon/meshes/abstract_mesh.h>

namespace BABYLON {

PoseEnabledController::PoseEnabledController(const IBrowserGamepadPtr& iBrowserGamepad)
    : Gamepad(iBrowserGamepad->id, iBrowserGamepad->index, iBrowserGamepad)
    , isXR{false}
    , _mesh{nullptr}
    , _deviceToWorld{Matrix::Identity()}
    , _pointingPoseNode{nullptr}
    , mesh{this, &PoseEnabledController::get_mesh}
    , _deviceRoomPosition{Vector3::Zero()}
    , _poseControlledCamera{nullptr}
    , _workingMatrix{Matrix::Identity()}
{
  type              = Gamepad::POSE_ENABLED;
  controllerType    = PoseEnabledControllerType::GENERIC;
  devicePosition    = Vector3::Zero();
  deviceScaleFactor = 1.f;
  position          = Vector3::Zero();

  // Used to convert 6dof controllers to 3dof
  _trackPosition              = true;
  _maxRotationDistFromHeadset = Math::PI / 5.f;
  _draggedRoomRotation        = 0.f;

  _calculatedPosition = Vector3::Zero();
  Quaternion::RotationYawPitchRollToRef(Math::PI, 0, 0, _leftHandSystemQuaternion);
}

PoseEnabledController::~PoseEnabledController() = default;

void PoseEnabledController::_disableTrackPosition(const Vector3& fixedPosition)
{
  if (_trackPosition) {
    _calculatedPosition.copyFrom(fixedPosition);
    _trackPosition = false;
  }
}

void PoseEnabledController::update()
{
  Gamepad::update();
  _updatePoseAndMesh();
}

void PoseEnabledController::_updatePoseAndMesh()
{
  if (isXR) {
    return;
  }
  const auto& pose = browserGamepad->pose;
  if (pose) {
    updateFromDevice(*pose);
  }

  Vector3::TransformCoordinatesToRef(_calculatedPosition, _deviceToWorld, devicePosition);
  _deviceToWorld.getRotationMatrixToRef(_workingMatrix);
  Quaternion::FromRotationMatrixToRef(_workingMatrix, deviceRotationQuaternion);
  deviceRotationQuaternion.multiplyInPlace(_calculatedRotation);

  if (_mesh) {
    _mesh->position().copyFrom(devicePosition);
    if (_mesh->rotationQuaternion()) {
      _mesh->rotationQuaternion()->copyFrom(deviceRotationQuaternion);
    }
  }
}

void PoseEnabledController::updateFromDevice(const DevicePose& poseData)
{
  if (isXR) {
    return;
  }
  rawPose = poseData;
  if (!poseData.position.empty()) {
    _deviceRoomPosition.copyFromFloats(poseData.position[0], poseData.position[1],
                                       -poseData.position[2]);
    if (_mesh && _mesh->getScene()->useRightHandedSystem()) {
      _deviceRoomPosition.z *= -1.f;
    }

    _deviceRoomPosition.scaleToRef(deviceScaleFactor, _calculatedPosition);
    _calculatedPosition.addInPlace(position);
  }
  auto& pose = rawPose;
  if (!poseData.orientation.empty() && !pose.orientation.empty() && pose.orientation.size() == 4) {
    _deviceRoomRotationQuaternion.copyFromFloats(pose.orientation[0], pose.orientation[1],
                                                 -pose.orientation[2], -pose.orientation[3]);
    if (_mesh) {
      if (_mesh->getScene()->useRightHandedSystem()) {
        _deviceRoomRotationQuaternion.z *= -1.f;
        _deviceRoomRotationQuaternion.w *= -1.f;
      }
      else {
        _deviceRoomRotationQuaternion.multiplyToRef(_leftHandSystemQuaternion,
                                                    deviceRotationQuaternion);
      }
    }

    // if the camera is set, rotate to the camera's rotation
    _deviceRoomRotationQuaternion.multiplyToRef(rotationQuaternion, _calculatedRotation);
  }
}

void PoseEnabledController::attachToMesh(const AbstractMeshPtr& iMesh)
{
  if (_mesh) {
    _mesh->setParent(nullptr);
  }
  _mesh = iMesh;
  if (_poseControlledCamera) {
    // _mesh->setParent(_poseControlledCamera);
  }
  if (!_mesh->rotationQuaternion()) {
    _mesh->rotationQuaternion = Quaternion();
  }

  // Sync controller mesh and pointing pose node's state with controller, this
  // is done to avoid a frame where position is 0,0,0 when attaching mesh
  if (!isXR) {
    _updatePoseAndMesh();
    if (_pointingPoseNode) {
      std::vector<Node*> parents;
      auto obj = static_cast<Node*>(_pointingPoseNode);
      while (obj && obj->parent()) {
        parents.emplace_back(obj->parent());
        obj = obj->parent();
      }
      std::reverse(parents.begin(), parents.end());
      for (auto& p : parents) {
        p->computeWorldMatrix(true);
      }
    }
  }

  _meshAttachedObservable.notifyObservers(iMesh.get());
}

void PoseEnabledController::attachToPoseControlledCamera(TargetCamera* camera)
{
  _poseControlledCamera = camera;
  if (_mesh) {
    //  _mesh->parent = _poseControlledCamera;
  }
}

void PoseEnabledController::dispose()
{
  if (_mesh) {
    _mesh->dispose();
  }
  _mesh = nullptr;

  Gamepad::dispose();
}

AbstractMeshPtr& PoseEnabledController::get_mesh()
{
  return _mesh;
}

Ray PoseEnabledController::getForwardRay(float length)
{
  if (!mesh()) {
    return Ray(Vector3::Zero(), Vector3{0.f, 0.f, 1.f}, length);
  }

  auto m      = _pointingPoseNode ? _pointingPoseNode->getWorldMatrix() : mesh()->getWorldMatrix();
  auto origin = m.getTranslation();

  Vector3 forward{0.f, 0.f, -1.f};
  auto forwardWorld = Vector3::TransformNormal(forward, m);

  auto direction = Vector3::Normalize(forwardWorld);

  return Ray(origin, direction, length);
}

} // end of namespace BABYLON
<commit_msg>Formatted doc<commit_after>#include <babylon/gamepads/controllers/pose_enabled_controller.h>

#include <babylon/culling/ray.h>
#include <babylon/engines/scene.h>
#include <babylon/interfaces/ibrowser_gamepad.h>
#include <babylon/maths/tmp_vectors.h>
#include <babylon/meshes/abstract_mesh.h>

namespace BABYLON {

PoseEnabledController::PoseEnabledController(const IBrowserGamepadPtr& iBrowserGamepad)
    : Gamepad(iBrowserGamepad->id, iBrowserGamepad->index, iBrowserGamepad)
    , isXR{false}
    , _mesh{nullptr}
    , _deviceToWorld{Matrix::Identity()}
    , _pointingPoseNode{nullptr}
    , mesh{this, &PoseEnabledController::get_mesh}
    , _deviceRoomPosition{Vector3::Zero()}
    , _poseControlledCamera{nullptr}
    , _workingMatrix{Matrix::Identity()}
{
  type              = Gamepad::POSE_ENABLED;
  controllerType    = PoseEnabledControllerType::GENERIC;
  devicePosition    = Vector3::Zero();
  deviceScaleFactor = 1.f;
  position          = Vector3::Zero();

  // Used to convert 6dof controllers to 3dof
  _trackPosition              = true;
  _maxRotationDistFromHeadset = Math::PI / 5.f;
  _draggedRoomRotation        = 0.f;

  _calculatedPosition = Vector3::Zero();
  Quaternion::RotationYawPitchRollToRef(Math::PI, 0, 0, _leftHandSystemQuaternion);
}

PoseEnabledController::~PoseEnabledController() = default;

void PoseEnabledController::_disableTrackPosition(const Vector3& fixedPosition)
{
  if (_trackPosition) {
    _calculatedPosition.copyFrom(fixedPosition);
    _trackPosition = false;
  }
}

void PoseEnabledController::update()
{
  Gamepad::update();
  _updatePoseAndMesh();
}

void PoseEnabledController::_updatePoseAndMesh()
{
  if (isXR) {
    return;
  }
  const auto& pose = browserGamepad->pose;
  if (pose) {
    updateFromDevice(*pose);
  }

  Vector3::TransformCoordinatesToRef(_calculatedPosition, _deviceToWorld, devicePosition);
  _deviceToWorld.getRotationMatrixToRef(_workingMatrix);
  Quaternion::FromRotationMatrixToRef(_workingMatrix, deviceRotationQuaternion);
  deviceRotationQuaternion.multiplyInPlace(_calculatedRotation);

  if (_mesh) {
    _mesh->position().copyFrom(devicePosition);
    if (_mesh->rotationQuaternion()) {
      _mesh->rotationQuaternion()->copyFrom(deviceRotationQuaternion);
    }
  }
}

void PoseEnabledController::updateFromDevice(const DevicePose& poseData)
{
  if (isXR) {
    return;
  }
  rawPose = poseData;
  if (!poseData.position.empty()) {
    _deviceRoomPosition.copyFromFloats(poseData.position[0], poseData.position[1],
                                       -poseData.position[2]);
    if (_mesh && _mesh->getScene()->useRightHandedSystem()) {
      _deviceRoomPosition.z *= -1.f;
    }

    _deviceRoomPosition.scaleToRef(deviceScaleFactor, _calculatedPosition);
    _calculatedPosition.addInPlace(position);
  }
  auto& pose = rawPose;
  if (!poseData.orientation.empty() && !pose.orientation.empty() && pose.orientation.size() == 4) {
    _deviceRoomRotationQuaternion.copyFromFloats(pose.orientation[0], pose.orientation[1],
                                                 -pose.orientation[2], -pose.orientation[3]);
    if (_mesh) {
      if (_mesh->getScene()->useRightHandedSystem()) {
        _deviceRoomRotationQuaternion.z *= -1.f;
        _deviceRoomRotationQuaternion.w *= -1.f;
      }
      else {
        _deviceRoomRotationQuaternion.multiplyToRef(_leftHandSystemQuaternion,
                                                    deviceRotationQuaternion);
      }
    }

    // if the camera is set, rotate to the camera's rotation
    _deviceRoomRotationQuaternion.multiplyToRef(rotationQuaternion, _calculatedRotation);
  }
}

void PoseEnabledController::attachToMesh(const AbstractMeshPtr& iMesh)
{
  if (_mesh) {
    _mesh->setParent(nullptr);
  }
  _mesh = iMesh;
  if (_poseControlledCamera) {
    // _mesh->setParent(_poseControlledCamera);
  }
  if (!_mesh->rotationQuaternion()) {
    _mesh->rotationQuaternion = Quaternion();
  }

  // Sync controller mesh and pointing pose node's state with controller, this is done to avoid a
  // frame where position is 0,0,0 when attaching mesh
  if (!isXR) {
    _updatePoseAndMesh();
    if (_pointingPoseNode) {
      std::vector<Node*> parents;
      auto obj = static_cast<Node*>(_pointingPoseNode);
      while (obj && obj->parent()) {
        parents.emplace_back(obj->parent());
        obj = obj->parent();
      }
      std::reverse(parents.begin(), parents.end());
      for (auto& p : parents) {
        p->computeWorldMatrix(true);
      }
    }
  }

  _meshAttachedObservable.notifyObservers(iMesh.get());
}

void PoseEnabledController::attachToPoseControlledCamera(TargetCamera* camera)
{
  _poseControlledCamera = camera;
  if (_mesh) {
    //  _mesh->parent = _poseControlledCamera;
  }
}

void PoseEnabledController::dispose()
{
  if (_mesh) {
    _mesh->dispose();
  }
  _mesh = nullptr;

  Gamepad::dispose();
}

AbstractMeshPtr& PoseEnabledController::get_mesh()
{
  return _mesh;
}

Ray PoseEnabledController::getForwardRay(float length)
{
  if (!mesh()) {
    return Ray(Vector3::Zero(), Vector3{0.f, 0.f, 1.f}, length);
  }

  auto m      = _pointingPoseNode ? _pointingPoseNode->getWorldMatrix() : mesh()->getWorldMatrix();
  auto origin = m.getTranslation();

  Vector3 forward{0.f, 0.f, -1.f};
  auto forwardWorld = Vector3::TransformNormal(forward, m);

  auto direction = Vector3::Normalize(forwardWorld);

  return Ray(origin, direction, length);
}

} // end of namespace BABYLON
<|endoftext|>


<commit_before>
#include <cmath>     // for std::abs, std::pow, std::fabs
#include <cstdint>   // I dont use int, long, long long etc b/c they are ambiguous
#include <iostream>  // for std::cin
#include <fstream>   // for std::ifstream
#include <sstream>   // for std::istringstream
#include <algorithm> // for std::swap, std::copy

// Represents a ring
struct Ring {
    int32_t elem[4];
};

// This should in fact be inside a class but this is just an example
std::vector<Ring> rings;
double bestpw[4];

// Computes the stacked power of a set of rings - for a single element
static inline double calc_ring_power( int32_t pw0, int32_t pw1, int32_t pw2, int32_t pw3, int32_t pw4 )
{
    double s1 = 0 + std::pow(pw0-2,2);
    double s2 = (s1-30) + 5*std::abs(pw1-5);
    double s3 = -s2 + pw2%3;
    double s4 = std::floor(std::fabs(s3)/2) + std::pow((pw3-7),2);
    double s5 = (100-s4) + (10-pw4);
    return s5;
}

// Receives the callback from gen_permutations() for each new permutation
void new_permutation( uint32_t pm_count, std::vector<uint32_t>& perm )
{
    double pw[4];
    for ( uint32_t k = 0; k<4; ++k ) {
        pw[k] = calc_ring_power(rings[ perm[0] ].elem[k],
                                rings[ perm[1] ].elem[k],
                                rings[ perm[2] ].elem[k],
                                rings[ perm[3] ].elem[k],
                                rings[ perm[4] ].elem[k] );

        // No good if the power is less than the minimum
        if ( pw[k]<80 ) return;
    }

    // Print the ring IDs
    printf( ">> Perm %d:  %d %d %d %d %d  ",
            pm_count, perm[0], perm[1], perm[2], perm[3], perm[4]  );

    // For each element, compare the current solution to the best solution so far
    for ( uint32_t k=0; k<4; ++k ) {
        if ( pw[k]>bestpw[k] ) {
            bestpw[k] = pw[k];
            // print in a highlighted way for fanciness - these are just comments
            printf( " [%2.0f] ", pw[k] );
        } else {
            printf( "  %2.0f  ", pw[k] );
        }
    }
    printf( "\n" );
}

// Generate all permutations (no order) - this should be N!/(N-R)! permutations
// Taken from https://docs.python.org/2/library/itertools.html#itertools.permutations
void gen_permutations( const uint32_t N, const uint32_t R )
{
    // sanity test
    if ( (N<1) or (R>N) ) return;

    // Create an index of rings and initialize with 1,2,3,4,5...,N
    std::vector<uint32_t> idx(N);
    for ( uint32_t j=0; j<N; ++j ) idx[j] = j;

    // Create a cyclic counter - part of the algorithm
    std::vector<uint32_t> cyc(R);
    for ( uint32_t j=0; j<R; ++j ) cyc[j] = N-j;

    std::vector<uint32_t> cur(R);
    std::copy( &idx[0], &idx[R], &cur[0] );

    // output the first trivial solution
    new_permutation( 0, cur );

    // Counts the number of permutations generated so far
    uint32_t count = 0;

    // We will stop when we could not create anymore permutations
    bool gotit;

    do {
        // assume the worst
        gotit = false;

        // Initialize index
        uint32_t i = R;
        while ( i>0 ) {
            --i;
            cyc[i]--;
            if ( cyc[i] == 0 ) {
                uint32_t first = idx[i];
                for ( uint32_t j=i; j<N-1; ++j ) idx[j] = idx[j+1];
                idx[N-1] = first;
                cyc[i] = N-i;
            }
            else {
                uint32_t j = cyc[i];

                // Swap two elements in the index array
                std::swap( idx[i], idx[N-j] );

                // copy the first R elements from the index array to the actual solution array
                std::copy( &idx[0], &idx[R], &cur[0] );
                new_permutation( ++count, cur );
                gotit = true;
                break;
            }
        }
    } while( gotit );
}

// Processes a file (or stdin)
void process( std::istream& ifs ) {
    std::string line;
    uint32_t numrings;

    // first line has to be the number of rings
    std::getline( ifs, line );
    std::istringstream iheader( line );
    iheader >> numrings;

    // Initialize the best solution with zeros
    for ( uint32_t j=0; j<4; ++j ) bestpw[j] = 0;

    // Resize the vector of rings b/c I hate to be doing push_back()
    // If you know the size up front just resize and use it
    // It avoids calling malloc() internally
    rings.resize( numrings );

    // Loop for every ring
    for ( uint32_t j=0; j<numrings; ++j ) {

        // Read line
        std::getline( ifs, line );

        // We use a second istream to parse the line
        // This is slower but much more convenient
        std::istringstream iis( line );
        iis >> rings[j].elem[0]
            >> rings[j].elem[1]
            >> rings[j].elem[2]
            >> rings[j].elem[3];
    }

    // Now that we read all the rings, proceed to create all
    // possible permutations and compute the stacked power of
    // each of them, keeping the element's best
    gen_permutations( numrings, 5 );

    // Print the solution
    for ( uint32_t k=0; k<4; ++k ) {
        printf( "%.0f\n", bestpw[k] );
    }
    // say yay
}

int main( int argc, char* argv[] )
{
    if ( argc>1 ) {
        // If we have command line arguments, they should be file names
        // We process them one by one
        // Notice that argv[0] should be the program name (rings)
        for ( uint32_t j=1; j<argc; ++j ) {
            // Open the file
            std::ifstream ifs( argv[j] );
            process( ifs );
        }
    }
    else {
        // Otherwise we expect the data to be piped into stdin
        // as in `rings < rings.txt`
        process( std::cin );
    }
}
<commit_msg>Added simpler permutation algorithm<commit_after>
#include <cmath>     // for std::abs, std::pow, std::fabs
#include <cstdint>   // I dont use int, long, long long etc b/c they are ambiguous
#include <iostream>  // for std::cin
#include <fstream>   // for std::ifstream
#include <sstream>   // for std::istringstream
#include <algorithm> // for std::swap, std::copy

// Represents a ring
struct Ring {
    int32_t elem[4];
};

// This should in fact be inside a class but this is just an example
std::vector<Ring> rings;
double bestpw[4];

// Computes the stacked power of a set of rings - for a single element
static inline double calc_ring_power( int32_t pw0, int32_t pw1, int32_t pw2, int32_t pw3, int32_t pw4 )
{
    double s1 = 0 + std::pow(pw0-2,2);
    double s2 = (s1-30) + 5*std::abs(pw1-5);
    double s3 = -s2 + pw2%3;
    double s4 = std::floor(std::fabs(s3)/2) + std::pow((pw3-7),2);
    double s5 = (100-s4) + (10-pw4);
    return s5;
}

// Receives the callback from gen_permutations() for each new permutation
void new_permutation( uint32_t pm_count, std::vector<uint32_t>& perm )
{
    double pw[4];
    for ( uint32_t k = 0; k<4; ++k ) {
        pw[k] = calc_ring_power(rings[ perm[0] ].elem[k],
                                rings[ perm[1] ].elem[k],
                                rings[ perm[2] ].elem[k],
                                rings[ perm[3] ].elem[k],
                                rings[ perm[4] ].elem[k] );

        // No good if the power is less than the minimum
        if ( pw[k]<80 ) return;
    }

    // Print the ring IDs
    printf( ">> Perm %d:  %d %d %d %d %d  ",
            pm_count, perm[0], perm[1], perm[2], perm[3], perm[4]  );

    // For each element, compare the current solution to the best solution so far
    for ( uint32_t k=0; k<4; ++k ) {
        if ( pw[k]>bestpw[k] ) {
            bestpw[k] = pw[k];
            // print in a highlighted way for fanciness - these are just comments
            printf( " [%2.0f] ", pw[k] );
        } else {
            printf( "  %2.0f  ", pw[k] );
        }
    }
    printf( "\n" );
}

// Generate all permutations - this should be N! permutations
// This is VERY wasteful if N is much greater than R
void gen_permutations_wasteful( const uint32_t N, const uint32_t R )
{
    // Create an index of rings and initialize with 1,2,3,4,5...,N
    std::vector<uint32_t> idx(N);
    for ( uint32_t j=0; j<N; ++j ) idx[j] = j;

    // output the first trivial solution
    std::vector<uint32_t> cur(R);
    std::copy( &idx[0], &idx[R], &cur[0] );
    new_permutation( 0, cur );

    while ( std::next_permutation( &idx[0], &idx[N] ) ) {
        std::copy( &idx[0], &idx[R], &cur[0] );
        new_permutation( 0, cur );
    }
}

// Generate all permutations (no order) - this should be N!/(N-R)! permutations
// Taken from https://docs.python.org/2/library/itertools.html#itertools.permutations
void gen_permutations( const uint32_t N, const uint32_t R )
{
    // sanity test
    if ( (N<1) or (R>N) ) return;

    // Create an index of rings and initialize with 1,2,3,4,5...,N
    std::vector<uint32_t> idx(N);
    for ( uint32_t j=0; j<N; ++j ) idx[j] = j;

    // Create a cyclic counter - part of the algorithm
    std::vector<uint32_t> cyc(R);
    for ( uint32_t j=0; j<R; ++j ) cyc[j] = N-j;

    std::vector<uint32_t> cur(R);
    std::copy( &idx[0], &idx[R], &cur[0] );

    // output the first trivial solution
    new_permutation( 0, cur );

    // Counts the number of permutations generated so far
    uint32_t count = 0;

    // We will stop when we could not create anymore permutations
    bool gotit;

    do {
        // assume the worst
        gotit = false;

        // Initialize index
        uint32_t i = R;
        while ( i>0 ) {
            --i;
            cyc[i]--;
            if ( cyc[i] == 0 ) {
                uint32_t first = idx[i];
                for ( uint32_t j=i; j<N-1; ++j ) idx[j] = idx[j+1];
                idx[N-1] = first;
                cyc[i] = N-i;
            }
            else {
                uint32_t j = cyc[i];

                // Swap two elements in the index array
                std::swap( idx[i], idx[N-j] );

                // copy the first R elements from the index array to the actual solution array
                std::copy( &idx[0], &idx[R], &cur[0] );
                new_permutation( ++count, cur );
                gotit = true;
                break;
            }
        }
    } while( gotit );
}

// Processes a file (or stdin)
void process( std::istream& ifs ) {
    std::string line;
    uint32_t numrings;

    // first line has to be the number of rings
    std::getline( ifs, line );
    std::istringstream iheader( line );
    iheader >> numrings;

    // Initialize the best solution with zeros
    for ( uint32_t j=0; j<4; ++j ) bestpw[j] = 0;

    // Resize the vector of rings b/c I hate to be doing push_back()
    // If you know the size up front just resize and use it
    // It avoids calling malloc() internally
    rings.resize( numrings );

    // Loop for every ring
    for ( uint32_t j=0; j<numrings; ++j ) {

        // Read line
        std::getline( ifs, line );

        // We use a second istream to parse the line
        // This is slower but much more convenient
        std::istringstream iis( line );
        iis >> rings[j].elem[0]
            >> rings[j].elem[1]
            >> rings[j].elem[2]
            >> rings[j].elem[3];
    }

    // Now that we read all the rings, proceed to create all
    // possible permutations and compute the stacked power of
    // each of them, keeping the element's best
    gen_permutations_wasteful( numrings, 5 );

    // Print the solution
    for ( uint32_t k=0; k<4; ++k ) {
        printf( "%.0f\n", bestpw[k] );
    }
    // say yay
}

int main( int argc, char* argv[] )
{
    if ( argc>1 ) {
        // If we have command line arguments, they should be file names
        // We process them one by one
        // Notice that argv[0] should be the program name (rings)
        for ( uint32_t j=1; j<argc; ++j ) {
            // Open the file
            std::ifstream ifs( argv[j] );
            process( ifs );
        }
    }
    else {
        // Otherwise we expect the data to be piped into stdin
        // as in `rings < rings.txt`
        process( std::cin );
    }
}
<|endoftext|>


<commit_before>/*
 *
 *    Copyright (c) 2021 Project CHIP Authors
 *    All rights reserved.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

#include <controller/ExampleOperationalCredentialsIssuer.h>
#include <credentials/CHIPCert.h>

namespace chip {
namespace Controller {

constexpr const char kOperationalCredentialsIssuerKeypairStorage[] = "ExampleOpCredsCAKey";

using namespace Credentials;
using namespace Crypto;

CHIP_ERROR ExampleOperationalCredentialsIssuer::Initialize(PersistentStorageDelegate & storage)
{
    Crypto::P256SerializedKeypair serializedKey;
    uint16_t keySize = static_cast<uint16_t>(serializedKey.Capacity());

    if (storage.SyncGetKeyValue(kOperationalCredentialsIssuerKeypairStorage, serializedKey, keySize) != CHIP_NO_ERROR)
    {
        // Storage doesn't have an existing keypair. Let's create one and add it to the storage.
        ReturnErrorOnFailure(mIssuer.Initialize());
        ReturnErrorOnFailure(mIssuer.Serialize(serializedKey));

        keySize = static_cast<uint16_t>(serializedKey.Length());
        ReturnErrorOnFailure(storage.SyncSetKeyValue(kOperationalCredentialsIssuerKeypairStorage, serializedKey, keySize));
    }
    else
    {
        // Use the keypair from the storage
        ReturnErrorOnFailure(mIssuer.Deserialize(serializedKey));
    }

    mInitialized = true;
    return CHIP_NO_ERROR;
}

CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNodeOperationalCertificate(const PeerId & peerId, const ByteSpan & csr,
                                                                                int64_t serialNumber, uint8_t * certBuf,
                                                                                uint32_t certBufSize, uint32_t & outCertLen)
{
    VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE);
    X509CertRequestParams request = { serialNumber, mIssuerId,         mNow, mNow + mValidity, true, peerId.GetFabricId(),
                                      true,         peerId.GetNodeId() };

    P256PublicKey pubkey;
    ReturnErrorOnFailure(VerifyCertificateSigningRequest(csr.data(), csr.size(), pubkey));
    return NewNodeOperationalX509Cert(request, CertificateIssuerLevel::kIssuerIsRootCA, pubkey, mIssuer, certBuf, certBufSize,
                                      outCertLen);
}

CHIP_ERROR ExampleOperationalCredentialsIssuer::GetRootCACertificate(FabricId fabricId, uint8_t * certBuf, uint32_t certBufSize,
                                                                     uint32_t & outCertLen)
{
    VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE);
    X509CertRequestParams request = { 0, mIssuerId, mNow, mNow + mValidity, true, fabricId, false, 0 };
    return NewRootX509Cert(request, mIssuer, certBuf, certBufSize, outCertLen);
}

} // namespace Controller
} // namespace chip
<commit_msg>Include length of key when it's stored in persistent storage (#6853)<commit_after>/*
 *
 *    Copyright (c) 2021 Project CHIP Authors
 *    All rights reserved.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

#include <controller/ExampleOperationalCredentialsIssuer.h>
#include <credentials/CHIPCert.h>

namespace chip {
namespace Controller {

constexpr const char kOperationalCredentialsIssuerKeypairStorage[] = "ExampleOpCredsCAKey";

using namespace Credentials;
using namespace Crypto;

CHIP_ERROR ExampleOperationalCredentialsIssuer::Initialize(PersistentStorageDelegate & storage)
{
    Crypto::P256SerializedKeypair serializedKey;
    uint16_t keySize = static_cast<uint16_t>(sizeof(serializedKey));

    if (storage.SyncGetKeyValue(kOperationalCredentialsIssuerKeypairStorage, &serializedKey, keySize) != CHIP_NO_ERROR)
    {
        // Storage doesn't have an existing keypair. Let's create one and add it to the storage.
        ReturnErrorOnFailure(mIssuer.Initialize());
        ReturnErrorOnFailure(mIssuer.Serialize(serializedKey));
        ReturnErrorOnFailure(storage.SyncSetKeyValue(kOperationalCredentialsIssuerKeypairStorage, &serializedKey, keySize));
    }
    else
    {
        // Use the keypair from the storage
        ReturnErrorOnFailure(mIssuer.Deserialize(serializedKey));
    }

    mInitialized = true;
    return CHIP_NO_ERROR;
}

CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNodeOperationalCertificate(const PeerId & peerId, const ByteSpan & csr,
                                                                                int64_t serialNumber, uint8_t * certBuf,
                                                                                uint32_t certBufSize, uint32_t & outCertLen)
{
    VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE);
    X509CertRequestParams request = { serialNumber, mIssuerId,         mNow, mNow + mValidity, true, peerId.GetFabricId(),
                                      true,         peerId.GetNodeId() };

    P256PublicKey pubkey;
    ReturnErrorOnFailure(VerifyCertificateSigningRequest(csr.data(), csr.size(), pubkey));
    return NewNodeOperationalX509Cert(request, CertificateIssuerLevel::kIssuerIsRootCA, pubkey, mIssuer, certBuf, certBufSize,
                                      outCertLen);
}

CHIP_ERROR ExampleOperationalCredentialsIssuer::GetRootCACertificate(FabricId fabricId, uint8_t * certBuf, uint32_t certBufSize,
                                                                     uint32_t & outCertLen)
{
    VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE);
    X509CertRequestParams request = { 0, mIssuerId, mNow, mNow + mValidity, true, fabricId, false, 0 };
    return NewRootX509Cert(request, mIssuer, certBuf, certBufSize, outCertLen);
}

} // namespace Controller
} // namespace chip
<|endoftext|>


<commit_before>#include "CommonImport.h"

#include "CommonUtilities.h"
#include "CommonMeshUtilities.h"
#include "CommonAlembic.h"

#include <boost/algorithm/string.hpp>
#include <sstream>

bool parseBool(std::string value){
	//std::istringstream(valuePair[1]) >> bExportSelected;

	if( value.find("true") != std::string::npos || value.find("1") != std::string::npos ){
		return true;
	}
	else{
		return false;
	}
}

bool IJobStringParser::parse(const std::string& jobString)
{

	std::vector<std::string> tokens;
	boost::split(tokens, jobString, boost::is_any_of(";"));

   //if(tokens.empty()){
   //   return false;
   //}

	for(int j=0; j<tokens.size(); j++){

		std::vector<std::string> valuePair;
		boost::split(valuePair, tokens[j], boost::is_any_of("="));
		if(valuePair.size() != 2){
			ESS_LOG_WARNING("Skipping invalid token: "<<tokens[j]);
			continue;
		}

		if(boost::iequals(valuePair[0], "filename")){
			filename = valuePair[1];
		}
		else if(boost::iequals(valuePair[0], "normals")){
			importNormals = parseBool(valuePair[1]);
		}
		else if(boost::iequals(valuePair[0], "uvs")){
			importUVs = parseBool(valuePair[1]);
		}
		else if(boost::iequals(valuePair[0], "facesets")){
         importFacesets = parseBool(valuePair[1]);
		}
		else if(boost::iequals(valuePair[0], "materialIds")){
         importMaterialIds = parseBool(valuePair[1]);
		}
      else if(boost::iequals(valuePair[0], "attachToExisting")){
         attachToExisting = parseBool(valuePair[1]);
      }
      else if(boost::iequals(valuePair[0], "importStandinProperties")){
         importStandinProperties = parseBool(valuePair[1]);
      }
      else if(boost::iequals(valuePair[0], "importBoundingBoxes")){
         importBoundingBoxes = parseBool(valuePair[1]);
      }
      else if(boost::iequals(valuePair[0], "importVisibilityControllers")){
         importVisibilityControllers = parseBool(valuePair[1]);
		}
	   else if(boost::iequals(valuePair[0], "failOnUnsupported")){
         failOnUnsupported = parseBool(valuePair[1]);
		}
      else if(boost::iequals(valuePair[0], "filters") || boost::iequals(valuePair[0], "identifiers")){
	      boost::split(nodesToImport, valuePair[1], boost::is_any_of(","));
		}
	   else if(boost::iequals(valuePair[0], "includeChildren")){
         includeChildren = parseBool(valuePair[1]);
		}
		else
		{
			ESS_LOG_WARNING("Skipping invalid token: "<<tokens[j]);
			continue;
		}
	}

   return true;
}

std::string IJobStringParser::buildJobString()
{
   ////Note: there are currently some minor differences between the apps. This function is somewhat XSI specific.
   std::stringstream stream;

   if(!filename.empty()){
      stream<<"filename="<<filename<<";";
   }

   stream<<"normals="<<importNormals<<";uvs="<<importUVs<<";facesets="<<importFacesets;
   stream<<";importVisibilityControllers="<<importVisibilityControllers<<";importStandinProperties="<<importStandinProperties;
   stream<<";importBoundingBoxes="<<importBoundingBoxes<<";attachToExisting="<<attachToExisting<<";failOnUnsupported="<<failOnUnsupported;

   if(!nodesToImport.empty()){
      stream<<";identifiers=";
      for(int i=0; i<nodesToImport.size(); i++){
         stream<<nodesToImport[i];
         if(i != nodesToImport.size()-1){
            stream<<",";
         }
      }
   }

   return stream.str();
}

SceneNode::nodeTypeE getNodeType(Alembic::Abc::IObject iObj)
{
   AbcG::MetaData metadata = iObj.getMetaData();
   if(AbcG::IXform::matches(metadata)){
      return SceneNode::ITRANSFORM;
      //Note: this method assumes everything is an ITRANSFORM, this is corrected later on in the the method that builds the common scene graph
   }
	else if(AbcG::IPolyMesh::matches(metadata) ||
		AbcG::ISubD::matches(metadata) ){
      return SceneNode::POLYMESH;
	}
	else if(AbcG::ICamera::matches(metadata)){
      return SceneNode::CAMERA;
	}
	else if(AbcG::IPoints::matches(metadata)){
      return SceneNode::PARTICLES;
	}
	else if(AbcG::ICurves::matches(metadata)){
      return SceneNode::CURVES;
	}
	else if(AbcG::ILight::matches(metadata)){
      return SceneNode::LIGHT;
	}
   else if(AbcG::INuPatch::matches(metadata)){
      return SceneNode::SURFACE;
	}
   return SceneNode::UNKNOWN;
}

struct AlembicISceneBuildElement
{
   AbcObjectCache *pObjectCache;
   SceneNodePtr parentNode;

   AlembicISceneBuildElement(AbcObjectCache *pMyObjectCache, SceneNodePtr node):pObjectCache(pMyObjectCache), parentNode(node)
   {}
};

SceneNodeAlembicPtr buildAlembicSceneGraph(AbcArchiveCache *pArchiveCache, AbcObjectCache *pRootObjectCache, int& nNumNodes)
{
   std::list<AlembicISceneBuildElement> sceneStack;

   Alembic::Abc::IObject rootObj = pRootObjectCache->obj;

   SceneNodeAlembicPtr sceneRoot(new SceneNodeAlembic(pRootObjectCache));
   sceneRoot->name = rootObj.getName();
   sceneRoot->dccIdentifier = rootObj.getFullName();
   sceneRoot->type = SceneNode::SCENE_ROOT;

   for(size_t j=0; j<pRootObjectCache->childIdentifiers.size(); j++)
   {
      AbcObjectCache *pChildObjectCache = &( pArchiveCache->find( pRootObjectCache->childIdentifiers[j] )->second );
      Alembic::AbcGeom::IObject childObj = pChildObjectCache->obj;
      NodeCategory::type childCat = NodeCategory::get(childObj);
      //we should change this to explicity check which node types are not support (e.g. facesets), so that we can still give out warnings
      if( childCat == NodeCategory::UNSUPPORTED ) continue;// skip over unsupported types

      sceneStack.push_back(AlembicISceneBuildElement(pChildObjectCache, sceneRoot));
   }

   //sceneStack.push_back(AlembicISceneBuildElement(pRootObjectCache, NULL));

   int numNodes = 0;

   while( !sceneStack.empty() )
   {
      AlembicISceneBuildElement sElement = sceneStack.back();
      Alembic::Abc::IObject iObj = sElement.pObjectCache->obj;
      SceneNodePtr parentNode = sElement.parentNode;
      sceneStack.pop_back();

      numNodes++;

      SceneNodeAlembicPtr newNode(new SceneNodeAlembic(sElement.pObjectCache));

      newNode->name = iObj.getName();
      newNode->dccIdentifier = iObj.getFullName();
      newNode->type = getNodeType(iObj);
      //select every node by default
      newNode->selected = true;

      if(parentNode){ //create bi-direction link if there is a parent
         newNode->parent = parentNode.get();
         parentNode->children.push_back(newNode);

         //the parent transforms of geometry nodes should be to be external transforms
         //(we don't a transform's type until we have seen what type(s) of child it has)
         if( NodeCategory::get(iObj) == NodeCategory::GEOMETRY ){
			 if(parentNode->type == SceneNode::ITRANSFORM){
               parentNode->type = SceneNode::ETRANSFORM;
            }
            else{
               ESS_LOG_WARNING("node "<<iObj.getFullName()<<" does not have a parent transform.");
            }
         }
      }

      //push the children as the last step, since we need to who the parent is first (we may have merged)
      for(size_t j=0; j<sElement.pObjectCache->childIdentifiers.size(); j++)
      {
         AbcObjectCache *pChildObjectCache = &( pArchiveCache->find( sElement.pObjectCache->childIdentifiers[j] )->second );
         Alembic::AbcGeom::IObject childObj = pChildObjectCache->obj;
         NodeCategory::type childCat = NodeCategory::get(childObj);
         //we should change this to explicity check which node types are not support (e.g. facesets), so that we can still give out warnings
         if( childCat == NodeCategory::UNSUPPORTED ) continue;// skip over unsupported types

         sceneStack.push_back(AlembicISceneBuildElement(pChildObjectCache, newNode));
      }
   }

   nNumNodes = numNodes;

   return sceneRoot;
}

struct AttachStackElement
{
   SceneNodeAppPtr currAppNode;
   SceneNodeAlembicPtr currFileNode;

   AttachStackElement(SceneNodeAppPtr appNode, SceneNodeAlembicPtr fileNode): currAppNode(appNode), currFileNode(fileNode)
   {}

};

bool AttachSceneFile(SceneNodeAlembicPtr fileRoot, SceneNodeAppPtr appRoot, const IJobStringParser& jobParams)
{
   //TODO: how to account for filtering?
   //it would break the sibling namespace assumption. Perhaps we should require that all parent nodes of selected are imported.
   //We would then not traverse unselected children

   std::list<AttachStackElement> sceneStack;

   for(SceneChildIterator it = appRoot->children.begin(); it != appRoot->children.end(); it++){
      SceneNodeAppPtr appNode = reinterpret<SceneNode, SceneNodeApp>(*it);
      sceneStack.push_back(AttachStackElement(appNode, fileRoot));
   }

   //TODO: abstract progress

   //int intermittentUpdateInterval = std::max( (int)(nNumNodes / 100), (int)1 );
   //int i = 0;
   while( !sceneStack.empty() )
   {
      AttachStackElement sElement = sceneStack.back();
      SceneNodeAppPtr currAppNode = sElement.currAppNode;
      SceneNodeAlembicPtr currFileNode = sElement.currFileNode;
      sceneStack.pop_back();

      //if( i % intermittentUpdateInterval == 0 ) {
      //   prog.PutCaption(L"Importing "+CString(iObj.getFullName().c_str())+L" ...");
      //}
      //i++;

      SceneNodeAlembicPtr newFileNode = currFileNode;
      //Each set of siblings names in an Alembic file exist within a namespace
      //This is not true for 3DS Max scene graphs, so we check for such conflicts using the "attached appNode flag"
      bool bChildAttached = false;
      for(SceneChildIterator it = currFileNode->children.begin(); it != currFileNode->children.end(); it++){
         SceneNodeAlembicPtr fileNode = reinterpret<SceneNode, SceneNodeAlembic>(*it);
         if(currAppNode->name == fileNode->name){
            ESS_LOG_WARNING("nodeMatch: "<<(*it)->name<<" = "<<fileNode->name);
            if(fileNode->isAttached()){
               ESS_LOG_ERROR("More than one match for node "<<(*it)->name);
               return false;
            }
            else{
               bChildAttached = currAppNode->replaceData(fileNode, jobParams, newFileNode);
            }
         }
      }

      //push the children as the last step, since we need to who the parent is first (we may have merged)
      for(SceneChildIterator it = currAppNode->children.begin(); it != currAppNode->children.end(); it++){
         SceneNodeAppPtr appNode = reinterpret<SceneNode, SceneNodeApp>(*it);
         sceneStack.push_back( AttachStackElement( appNode, newFileNode ) );
      }

      //if(prog.IsCancelPressed()){
      //   break;
      //}
      //prog.Increment();
   }

   return true;
}

struct ImportStackElement
{
   SceneNodeAlembicPtr currFileNode;
   SceneNodeAppPtr parentAppNode;

   ImportStackElement(SceneNodeAlembicPtr node, SceneNodeAppPtr parent):currFileNode(node), parentAppNode(parent)
   {}

};

bool ImportSceneFile(SceneNodeAlembicPtr fileRoot, SceneNodeAppPtr appRoot, const IJobStringParser& jobParams)
{
   //TODO skip unselected children, if thats we how we do filtering.

   //compare to application scene graph to see if we need to rename nodes (or maybe we might throw an error)

   std::list<ImportStackElement> sceneStack;

   for(SceneChildIterator it = fileRoot->children.begin(); it != fileRoot->children.end(); it++){
      SceneNodeAlembicPtr fileNode = reinterpret<SceneNode, SceneNodeAlembic>(*it);
      sceneStack.push_back(ImportStackElement(fileNode, appRoot));
   }

   //TODO: abstract progress

   //int intermittentUpdateInterval = std::max( (int)(nNumNodes / 100), (int)1 );
   //int i = 0;
   while( !sceneStack.empty() )
   {
      ImportStackElement sElement = sceneStack.back();
      SceneNodeAlembicPtr currFileNode = sElement.currFileNode;
      SceneNodeAppPtr parentAppNode = sElement.parentAppNode;
      sceneStack.pop_back();

      //if( i % intermittentUpdateInterval == 0 ) {
      //   prog.PutCaption(L"Importing "+CString(iObj.getFullName().c_str())+L" ...");
      //}
      //i++;

      SceneNodeAppPtr newAppNode;
      bool bContinue = parentAppNode->addChild(currFileNode, jobParams, newAppNode);

      if(!bContinue){
         return false;
      }

      if(newAppNode){
         ESS_LOG_WARNING("newAppNode: "<<newAppNode->name<<" useCount: "<<newAppNode.use_count());

         //push the children as the last step, since we need to who the parent is first (we may have merged)
         for(SceneChildIterator it = currFileNode->children.begin(); it != currFileNode->children.end(); it++){
            SceneNodeAlembicPtr fileNode = reinterpret<SceneNode, SceneNodeAlembic>(*it);
            if(!fileNode->isSupported()) continue;

            if( fileNode->isMerged() ){
               //The child node was merged with its parent, so skip this child, and add its children
               //(Although this case is technically possible, I think it will not be common)
               SceneNodePtr& mergedChild = *it;

               for(SceneChildIterator cit = mergedChild->children.begin(); cit != mergedChild->children.end(); cit++){
                  SceneNodeAlembicPtr cfileNode = reinterpret<SceneNode, SceneNodeAlembic>(*cit);
                  sceneStack.push_back( ImportStackElement( cfileNode, newAppNode ) );
               }
            }
            else{
               sceneStack.push_back( ImportStackElement( fileNode, newAppNode ) );
            }
         }

      }
      else{
         ESS_LOG_WARNING("newAppNode useCount: "<<newAppNode.use_count());
	      if( currFileNode->children.empty() == false ) {
		      EC_LOG_WARNING("Unsupported node: " << currFileNode->name << " has children that have not been imported." );
	      }
      }

      //if(prog.IsCancelPressed()){
      //   break;
      //}
      //prog.Increment();
   }

   return true;
}
<commit_msg>add profiling, remove some debug messages!<commit_after>#include "CommonImport.h"

#include "CommonUtilities.h"
#include "CommonMeshUtilities.h"
#include "CommonAlembic.h"

#include <boost/algorithm/string.hpp>
#include <sstream>

bool parseBool(std::string value){
	//std::istringstream(valuePair[1]) >> bExportSelected;

	if( value.find("true") != std::string::npos || value.find("1") != std::string::npos ){
		return true;
	}
	else{
		return false;
	}
}

bool IJobStringParser::parse(const std::string& jobString)
{

	std::vector<std::string> tokens;
	boost::split(tokens, jobString, boost::is_any_of(";"));

   //if(tokens.empty()){
   //   return false;
   //}

	for(int j=0; j<tokens.size(); j++){

		std::vector<std::string> valuePair;
		boost::split(valuePair, tokens[j], boost::is_any_of("="));
		if(valuePair.size() != 2){
			ESS_LOG_WARNING("Skipping invalid token: "<<tokens[j]);
			continue;
		}

		if(boost::iequals(valuePair[0], "filename")){
			filename = valuePair[1];
		}
		else if(boost::iequals(valuePair[0], "normals")){
			importNormals = parseBool(valuePair[1]);
		}
		else if(boost::iequals(valuePair[0], "uvs")){
			importUVs = parseBool(valuePair[1]);
		}
		else if(boost::iequals(valuePair[0], "facesets")){
         importFacesets = parseBool(valuePair[1]);
		}
		else if(boost::iequals(valuePair[0], "materialIds")){
         importMaterialIds = parseBool(valuePair[1]);
		}
      else if(boost::iequals(valuePair[0], "attachToExisting")){
         attachToExisting = parseBool(valuePair[1]);
      }
      else if(boost::iequals(valuePair[0], "importStandinProperties")){
         importStandinProperties = parseBool(valuePair[1]);
      }
      else if(boost::iequals(valuePair[0], "importBoundingBoxes")){
         importBoundingBoxes = parseBool(valuePair[1]);
      }
      else if(boost::iequals(valuePair[0], "importVisibilityControllers")){
         importVisibilityControllers = parseBool(valuePair[1]);
		}
	   else if(boost::iequals(valuePair[0], "failOnUnsupported")){
         failOnUnsupported = parseBool(valuePair[1]);
		}
      else if(boost::iequals(valuePair[0], "filters") || boost::iequals(valuePair[0], "identifiers")){
	      boost::split(nodesToImport, valuePair[1], boost::is_any_of(","));
		}
	   else if(boost::iequals(valuePair[0], "includeChildren")){
         includeChildren = parseBool(valuePair[1]);
		}
		else
		{
			ESS_LOG_WARNING("Skipping invalid token: "<<tokens[j]);
			continue;
		}
	}

   return true;
}

std::string IJobStringParser::buildJobString()
{
   ////Note: there are currently some minor differences between the apps. This function is somewhat XSI specific.
   std::stringstream stream;

   if(!filename.empty()){
      stream<<"filename="<<filename<<";";
   }

   stream<<"normals="<<importNormals<<";uvs="<<importUVs<<";facesets="<<importFacesets;
   stream<<";importVisibilityControllers="<<importVisibilityControllers<<";importStandinProperties="<<importStandinProperties;
   stream<<";importBoundingBoxes="<<importBoundingBoxes<<";attachToExisting="<<attachToExisting<<";failOnUnsupported="<<failOnUnsupported;

   if(!nodesToImport.empty()){
      stream<<";identifiers=";
      for(int i=0; i<nodesToImport.size(); i++){
         stream<<nodesToImport[i];
         if(i != nodesToImport.size()-1){
            stream<<",";
         }
      }
   }

   return stream.str();
}

SceneNode::nodeTypeE getNodeType(Alembic::Abc::IObject iObj)
{
   AbcG::MetaData metadata = iObj.getMetaData();
   if(AbcG::IXform::matches(metadata)){
      return SceneNode::ITRANSFORM;
      //Note: this method assumes everything is an ITRANSFORM, this is corrected later on in the the method that builds the common scene graph
   }
	else if(AbcG::IPolyMesh::matches(metadata) ||
		AbcG::ISubD::matches(metadata) ){
      return SceneNode::POLYMESH;
	}
	else if(AbcG::ICamera::matches(metadata)){
      return SceneNode::CAMERA;
	}
	else if(AbcG::IPoints::matches(metadata)){
      return SceneNode::PARTICLES;
	}
	else if(AbcG::ICurves::matches(metadata)){
      return SceneNode::CURVES;
	}
	else if(AbcG::ILight::matches(metadata)){
      return SceneNode::LIGHT;
	}
   else if(AbcG::INuPatch::matches(metadata)){
      return SceneNode::SURFACE;
	}
   return SceneNode::UNKNOWN;
}

struct AlembicISceneBuildElement
{
   AbcObjectCache *pObjectCache;
   SceneNodePtr parentNode;

   AlembicISceneBuildElement(AbcObjectCache *pMyObjectCache, SceneNodePtr node):pObjectCache(pMyObjectCache), parentNode(node)
   {}
};

SceneNodeAlembicPtr buildAlembicSceneGraph(AbcArchiveCache *pArchiveCache, AbcObjectCache *pRootObjectCache, int& nNumNodes)
{
	ESS_PROFILE_SCOPE("buildAlembicSceneGraph");
   std::list<AlembicISceneBuildElement> sceneStack;

   Alembic::Abc::IObject rootObj = pRootObjectCache->obj;

   SceneNodeAlembicPtr sceneRoot(new SceneNodeAlembic(pRootObjectCache));
   sceneRoot->name = rootObj.getName();
   sceneRoot->dccIdentifier = rootObj.getFullName();
   sceneRoot->type = SceneNode::SCENE_ROOT;

   for(size_t j=0; j<pRootObjectCache->childIdentifiers.size(); j++)
   {
      AbcObjectCache *pChildObjectCache = &( pArchiveCache->find( pRootObjectCache->childIdentifiers[j] )->second );
      Alembic::AbcGeom::IObject childObj = pChildObjectCache->obj;
      NodeCategory::type childCat = NodeCategory::get(childObj);
      //we should change this to explicity check which node types are not support (e.g. facesets), so that we can still give out warnings
      if( childCat == NodeCategory::UNSUPPORTED ) continue;// skip over unsupported types

      sceneStack.push_back(AlembicISceneBuildElement(pChildObjectCache, sceneRoot));
   }

   //sceneStack.push_back(AlembicISceneBuildElement(pRootObjectCache, NULL));

   int numNodes = 0;

   while( !sceneStack.empty() )
   {
      AlembicISceneBuildElement sElement = sceneStack.back();
      Alembic::Abc::IObject iObj = sElement.pObjectCache->obj;
      SceneNodePtr parentNode = sElement.parentNode;
      sceneStack.pop_back();

      numNodes++;

      SceneNodeAlembicPtr newNode(new SceneNodeAlembic(sElement.pObjectCache));

      newNode->name = iObj.getName();
      newNode->dccIdentifier = iObj.getFullName();
      newNode->type = getNodeType(iObj);
      //select every node by default
      newNode->selected = true;

      if(parentNode){ //create bi-direction link if there is a parent
         newNode->parent = parentNode.get();
         parentNode->children.push_back(newNode);

         //the parent transforms of geometry nodes should be to be external transforms
         //(we don't a transform's type until we have seen what type(s) of child it has)
         if( NodeCategory::get(iObj) == NodeCategory::GEOMETRY ){
			 if(parentNode->type == SceneNode::ITRANSFORM){
               parentNode->type = SceneNode::ETRANSFORM;
            }
            else{
               ESS_LOG_WARNING("node "<<iObj.getFullName()<<" does not have a parent transform.");
            }
         }
      }

      //push the children as the last step, since we need to who the parent is first (we may have merged)
      for(size_t j=0; j<sElement.pObjectCache->childIdentifiers.size(); j++)
      {
         AbcObjectCache *pChildObjectCache = &( pArchiveCache->find( sElement.pObjectCache->childIdentifiers[j] )->second );
         Alembic::AbcGeom::IObject childObj = pChildObjectCache->obj;
         NodeCategory::type childCat = NodeCategory::get(childObj);
         //we should change this to explicity check which node types are not support (e.g. facesets), so that we can still give out warnings
         if( childCat == NodeCategory::UNSUPPORTED ) continue;// skip over unsupported types

         sceneStack.push_back(AlembicISceneBuildElement(pChildObjectCache, newNode));
      }
   }

   nNumNodes = numNodes;

   return sceneRoot;
}

struct AttachStackElement
{
   SceneNodeAppPtr currAppNode;
   SceneNodeAlembicPtr currFileNode;

   AttachStackElement(SceneNodeAppPtr appNode, SceneNodeAlembicPtr fileNode): currAppNode(appNode), currFileNode(fileNode)
   {}

};

bool AttachSceneFile(SceneNodeAlembicPtr fileRoot, SceneNodeAppPtr appRoot, const IJobStringParser& jobParams)
{
   //TODO: how to account for filtering?
   //it would break the sibling namespace assumption. Perhaps we should require that all parent nodes of selected are imported.
   //We would then not traverse unselected children

   std::list<AttachStackElement> sceneStack;

   for(SceneChildIterator it = appRoot->children.begin(); it != appRoot->children.end(); it++){
      SceneNodeAppPtr appNode = reinterpret<SceneNode, SceneNodeApp>(*it);
      sceneStack.push_back(AttachStackElement(appNode, fileRoot));
   }

   //TODO: abstract progress

   //int intermittentUpdateInterval = std::max( (int)(nNumNodes / 100), (int)1 );
   //int i = 0;
   while( !sceneStack.empty() )
   {
      AttachStackElement sElement = sceneStack.back();
      SceneNodeAppPtr currAppNode = sElement.currAppNode;
      SceneNodeAlembicPtr currFileNode = sElement.currFileNode;
      sceneStack.pop_back();

      //if( i % intermittentUpdateInterval == 0 ) {
      //   prog.PutCaption(L"Importing "+CString(iObj.getFullName().c_str())+L" ...");
      //}
      //i++;

      SceneNodeAlembicPtr newFileNode = currFileNode;
      //Each set of siblings names in an Alembic file exist within a namespace
      //This is not true for 3DS Max scene graphs, so we check for such conflicts using the "attached appNode flag"
      bool bChildAttached = false;
      for(SceneChildIterator it = currFileNode->children.begin(); it != currFileNode->children.end(); it++){
         SceneNodeAlembicPtr fileNode = reinterpret<SceneNode, SceneNodeAlembic>(*it);
         if(currAppNode->name == fileNode->name){
            ESS_LOG_WARNING("nodeMatch: "<<(*it)->name<<" = "<<fileNode->name);
            if(fileNode->isAttached()){
               ESS_LOG_ERROR("More than one match for node "<<(*it)->name);
               return false;
            }
            else{
               bChildAttached = currAppNode->replaceData(fileNode, jobParams, newFileNode);
            }
         }
      }

      //push the children as the last step, since we need to who the parent is first (we may have merged)
      for(SceneChildIterator it = currAppNode->children.begin(); it != currAppNode->children.end(); it++){
         SceneNodeAppPtr appNode = reinterpret<SceneNode, SceneNodeApp>(*it);
         sceneStack.push_back( AttachStackElement( appNode, newFileNode ) );
      }

      //if(prog.IsCancelPressed()){
      //   break;
      //}
      //prog.Increment();
   }

   return true;
}

struct ImportStackElement
{
   SceneNodeAlembicPtr currFileNode;
   SceneNodeAppPtr parentAppNode;

   ImportStackElement(SceneNodeAlembicPtr node, SceneNodeAppPtr parent):currFileNode(node), parentAppNode(parent)
   {}

};

bool ImportSceneFile(SceneNodeAlembicPtr fileRoot, SceneNodeAppPtr appRoot, const IJobStringParser& jobParams)
{
	ESS_PROFILE_SCOPE("ImportSceneFile");
   //TODO skip unselected children, if thats we how we do filtering.

   //compare to application scene graph to see if we need to rename nodes (or maybe we might throw an error)

   std::list<ImportStackElement> sceneStack;

   for(SceneChildIterator it = fileRoot->children.begin(); it != fileRoot->children.end(); it++){
      SceneNodeAlembicPtr fileNode = reinterpret<SceneNode, SceneNodeAlembic>(*it);
      sceneStack.push_back(ImportStackElement(fileNode, appRoot));
   }

   //TODO: abstract progress

   //int intermittentUpdateInterval = std::max( (int)(nNumNodes / 100), (int)1 );
   //int i = 0;
   while( !sceneStack.empty() )
   {
      ImportStackElement sElement = sceneStack.back();
      SceneNodeAlembicPtr currFileNode = sElement.currFileNode;
      SceneNodeAppPtr parentAppNode = sElement.parentAppNode;
      sceneStack.pop_back();

      //if( i % intermittentUpdateInterval == 0 ) {
      //   prog.PutCaption(L"Importing "+CString(iObj.getFullName().c_str())+L" ...");
      //}
      //i++;

      SceneNodeAppPtr newAppNode;
      bool bContinue = parentAppNode->addChild(currFileNode, jobParams, newAppNode);

      if(!bContinue){
         return false;
      }

      if(newAppNode){
         //ESS_LOG_WARNING("newAppNode: "<<newAppNode->name<<" useCount: "<<newAppNode.use_count());

         //push the children as the last step, since we need to who the parent is first (we may have merged)
         for(SceneChildIterator it = currFileNode->children.begin(); it != currFileNode->children.end(); it++){
            SceneNodeAlembicPtr fileNode = reinterpret<SceneNode, SceneNodeAlembic>(*it);
            if(!fileNode->isSupported()) continue;

            if( fileNode->isMerged() ){
               //The child node was merged with its parent, so skip this child, and add its children
               //(Although this case is technically possible, I think it will not be common)
               SceneNodePtr& mergedChild = *it;

               for(SceneChildIterator cit = mergedChild->children.begin(); cit != mergedChild->children.end(); cit++){
                  SceneNodeAlembicPtr cfileNode = reinterpret<SceneNode, SceneNodeAlembic>(*cit);
                  sceneStack.push_back( ImportStackElement( cfileNode, newAppNode ) );
               }
            }
            else{
               sceneStack.push_back( ImportStackElement( fileNode, newAppNode ) );
            }
         }

      }
      else{
         //ESS_LOG_WARNING("newAppNode useCount: "<<newAppNode.use_count());
	      if( currFileNode->children.empty() == false ) {
		      EC_LOG_WARNING("Unsupported node: " << currFileNode->name << " has children that have not been imported." );
	      }
      }

      //if(prog.IsCancelPressed()){
      //   break;
      //}
      //prog.Increment();
   }

   return true;
}
<|endoftext|>


<commit_before>#pragma once

#include "jansson_handle.hpp"

#include "base/exception.hpp"
#include "base/string_utils.hpp"

#include <cstdint>
#include <memory>
#include <string>
#include <vector>

#include <boost/optional.hpp>

#include "3party/jansson/src/jansson.h"

namespace base
{
struct JSONDecRef
{
  void operator()(json_t * root) const
  {
    if (root)
      json_decref(root);
  }
};

using JSONPtr = std::unique_ptr<json_t, JSONDecRef>;

inline JSONPtr NewJSONObject() { return JSONPtr(json_object()); }
inline JSONPtr NewJSONArray() { return JSONPtr(json_array()); }
inline JSONPtr NewJSONString(std::string const & s) { return JSONPtr(json_string(s.c_str())); }
inline JSONPtr NewJSONInt(json_int_t value) { return JSONPtr(json_integer(value)); }
inline JSONPtr NewJSONReal(double value) { return JSONPtr(json_real(value)); }
inline JSONPtr NewJSONBool(bool value) { return JSONPtr(value ? json_true() : json_false()); }
inline JSONPtr NewJSONNull() { return JSONPtr(json_null()); }

class Json
{
public:
  DECLARE_EXCEPTION(Exception, RootException);

  Json() = default;
  explicit Json(std::string const & s) { ParseFrom(s); }
  explicit Json(char const * s) { ParseFrom(s); }
  explicit Json(JSONPtr && json) { m_handle.AttachNew(json.release()); }

  Json GetDeepCopy() const
  {
    Json copy;
    copy.m_handle.AttachNew(get_deep_copy());
    return copy;
  }
  void ParseFrom(std::string const & s) { ParseFrom(s.c_str()); }
  void ParseFrom(char const * s)
  {
    json_error_t jsonError;
    m_handle.AttachNew(json_loads(s, 0, &jsonError));
    if (!m_handle)
      MYTHROW(Exception, (jsonError.line, jsonError.text));
  }

  json_t * get() const { return m_handle.get(); }
  json_t * get_deep_copy() const { return json_deep_copy(get()); }

private:
  JsonHandle m_handle;
};

JSONPtr LoadFromString(std::string const & str);
std::string DumpToString(JSONPtr const & json, size_t flags = 0);

json_t * GetJSONObligatoryField(json_t * root, std::string const & field);
json_t const * GetJSONObligatoryField(json_t const * root, std::string const & field);
json_t * GetJSONObligatoryField(json_t * root, char const * field);
json_t const * GetJSONObligatoryField(json_t const * root, char const * field);
json_t * GetJSONOptionalField(json_t * root, std::string const & field);
json_t const * GetJSONOptionalField(json_t const * root, std::string const & field);
json_t * GetJSONOptionalField(json_t * root, char const * field);
json_t const * GetJSONOptionalField(json_t const * root, char const * field);

template <class First>
inline json_t const * GetJSONObligatoryFieldByPath(json_t const * root, First && path)
{
  return GetJSONObligatoryField(root, std::forward<First>(path));
}

template <class First, class... Paths>
inline json_t const * GetJSONObligatoryFieldByPath(json_t const * root, First && path,
                                                   Paths &&... paths)
{
  json_t const * newRoot = GetJSONObligatoryFieldByPath(root, std::forward<First>(path));
  return GetJSONObligatoryFieldByPath(newRoot, std::forward<Paths>(paths)...);
}

bool JSONIsNull(json_t const * root);
}  // namespace base

template <typename T>
T FromJSON(json_t const * root)
{
  T result{};
  FromJSON(root, result);
  return result;
}

inline void FromJSON(json_t * root, json_t *& value) { value = root; }
inline void FromJSON(json_t const * root, json_t const *& value) { value = root; }

void FromJSON(json_t const * root, double & result);
void FromJSON(json_t const * root, bool & result);

template <typename T, typename std::enable_if<std::is_integral<T>::value, void>::type * = nullptr>
void FromJSON(json_t const * root, T & result)
{
  if (!json_is_number(root))
    MYTHROW(base::Json::Exception, ("Object must contain a json number."));
  result = static_cast<T>(json_integer_value(root));
}

std::string FromJSONToString(json_t const * root);

template <typename T>
T FromJSONObject(json_t const * root, char const * field)
{
  auto const * json = base::GetJSONObligatoryField(root, field);
  try
  {
    return FromJSON<T>(json);
  }
  catch (base::Json::Exception const & e)
  {
    MYTHROW(base::Json::Exception, ("An error occured while parsing field", field, e.Msg()));
  }
}

template <typename T>
void FromJSONObject(json_t * root, std::string const & field, T & result)
{
  auto * json = base::GetJSONObligatoryField(root, field);
  try
  {
    FromJSON(json, result);
  }
  catch (base::Json::Exception const & e)
  {
    MYTHROW(base::Json::Exception, ("An error occured while parsing field", field, e.Msg()));
  }
}

template <typename T>
boost::optional<T> FromJSONObjectOptional(json_t const * root, char const * field)
{
  auto * json = base::GetJSONOptionalField(root, field);
  if (!json)
    return {};

  boost::optional<T> result{T{}};
  FromJSON(json, *result);
  return result;
}

template <typename T>
void FromJSONObjectOptionalField(json_t const * root, std::string const & field, T & result)
{
  auto * json = base::GetJSONOptionalField(root, field);
  if (!json)
  {
    result = T{};
    return;
  }
  FromJSON(json, result);
}

template <typename T, typename std::enable_if<std::is_integral<T>::value, void>::type * = nullptr>
inline base::JSONPtr ToJSON(T value)
{
  return base::NewJSONInt(value);
}
inline base::JSONPtr ToJSON(double value) { return base::NewJSONReal(value); }
inline base::JSONPtr ToJSON(bool value) { return base::NewJSONBool(value); }
inline base::JSONPtr ToJSON(char const * s) { return base::NewJSONString(s); }

template <typename T>
void ToJSONArray(json_t & root, T const & value)
{
  json_array_append_new(&root, ToJSON(value).release());
}

inline void ToJSONArray(json_t & parent, base::JSONPtr & child)
{
  json_array_append_new(&parent, child.release());
}

inline void ToJSONArray(json_t & parent, json_t & child) { json_array_append_new(&parent, &child); }

template <typename T>
void ToJSONObject(json_t & root, char const * field, T const & value)
{
  json_object_set_new(&root, field, ToJSON(value).release());
}

inline void ToJSONObject(json_t & parent, char const * field, base::JSONPtr && child)
{
  json_object_set_new(&parent, field, child.release());
}

inline void ToJSONObject(json_t & parent, char const * field, base::JSONPtr & child)
{
  json_object_set_new(&parent, field, child.release());
}

inline void ToJSONObject(json_t & parent, char const * field, json_t & child)
{
  json_object_set_new(&parent, field, &child);
}

template <typename T>
void ToJSONObject(json_t & root, std::string const & field, T const & value)
{
  ToJSONObject(root, field.c_str(), value);
}

inline void ToJSONObject(json_t & parent, std::string const & field, base::JSONPtr && child)
{
  ToJSONObject(parent, field.c_str(), std::move(child));
}

inline void ToJSONObject(json_t & parent, std::string const & field, base::JSONPtr & child)
{
  ToJSONObject(parent, field.c_str(), child);
}

inline void ToJSONObject(json_t & parent, std::string const & field, json_t & child)
{
  ToJSONObject(parent, field.c_str(), child);
}

template <typename T>
void FromJSONObject(json_t * root, std::string const & field, std::vector<T> & result)
{
  auto * arr = base::GetJSONObligatoryField(root, field);
  if (!json_is_array(arr))
    MYTHROW(base::Json::Exception, ("The field", field, "must contain a json array."));
  size_t sz = json_array_size(arr);
  result.resize(sz);
  for (size_t i = 0; i < sz; ++i)
    FromJSON(json_array_get(arr, i), result[i]);
}

// The function tries to parse array of values from a value
// corresponding to |field| in a json object corresponding to |root|.
// Returns true when the value is non-null and array is successfully
// parsed.  Returns false when there is no such |field| in the |root|
// or the value is null.  Also, the method may throw an exception in
// case of json parsing errors.
template <typename T>
bool FromJSONObjectOptional(json_t * root, std::string const & field, std::vector<T> & result)
{
  auto * arr = base::GetJSONOptionalField(root, field);
  if (!arr || base::JSONIsNull(arr))
  {
    result.clear();
    return false;
  }
  if (!json_is_array(arr))
    MYTHROW(base::Json::Exception, ("The field", field, "must contain a json array."));
  size_t const sz = json_array_size(arr);
  result.resize(sz);
  for (size_t i = 0; i < sz; ++i)
    FromJSON(json_array_get(arr, i), result[i]);
  return true;
}

template <typename T>
void ToJSONObject(json_t & root, char const * field, std::vector<T> const & values)
{
  auto arr = base::NewJSONArray();
  for (auto const & value : values)
    json_array_append_new(arr.get(), ToJSON(value).release());
  json_object_set_new(&root, field, arr.release());
}

template <typename T>
void ToJSONObject(json_t & root, std::string const & field, std::vector<T> const & values)
{
  ToJSONObject(root, field.c_str(), values);
}

template <typename T>
void FromJSONObjectOptionalField(json_t * root, std::string const & field, std::vector<T> & result)
{
  FromJSONObjectOptionalField(const_cast<json_t *>(root), field, result);
}

template <typename T>
void FromJSONObjectOptionalField(json_t const * root, std::string const & field, std::vector<T> & result)
{
  json_t const * arr = base::GetJSONOptionalField(root, field);
  if (!arr)
  {
    result.clear();
    return;
  }
  if (!json_is_array(arr))
    MYTHROW(base::Json::Exception, ("The field", field, "must contain a json array."));
  size_t sz = json_array_size(arr);
  result.resize(sz);
  for (size_t i = 0; i < sz; ++i)
    FromJSON(json_array_get(arr, i), result[i]);
}

struct JSONFreeDeleter
{
  void operator()(char * buffer) const { free(buffer); }
};

namespace std
{
void FromJSON(json_t const * root, std::string & result);
inline base::JSONPtr ToJSON(std::string const & s) { return base::NewJSONString(s); }
}  // namespace std

namespace strings
{
void FromJSON(json_t const * root, UniString & result);
base::JSONPtr ToJSON(UniString const & s);
}  // namespace strings
<commit_msg>[myjansson] Fix const_cast<commit_after>#pragma once

#include "jansson_handle.hpp"

#include "base/exception.hpp"
#include "base/string_utils.hpp"

#include <cstdint>
#include <memory>
#include <string>
#include <vector>

#include <boost/optional.hpp>

#include "3party/jansson/src/jansson.h"

namespace base
{
struct JSONDecRef
{
  void operator()(json_t * root) const
  {
    if (root)
      json_decref(root);
  }
};

using JSONPtr = std::unique_ptr<json_t, JSONDecRef>;

inline JSONPtr NewJSONObject() { return JSONPtr(json_object()); }
inline JSONPtr NewJSONArray() { return JSONPtr(json_array()); }
inline JSONPtr NewJSONString(std::string const & s) { return JSONPtr(json_string(s.c_str())); }
inline JSONPtr NewJSONInt(json_int_t value) { return JSONPtr(json_integer(value)); }
inline JSONPtr NewJSONReal(double value) { return JSONPtr(json_real(value)); }
inline JSONPtr NewJSONBool(bool value) { return JSONPtr(value ? json_true() : json_false()); }
inline JSONPtr NewJSONNull() { return JSONPtr(json_null()); }

class Json
{
public:
  DECLARE_EXCEPTION(Exception, RootException);

  Json() = default;
  explicit Json(std::string const & s) { ParseFrom(s); }
  explicit Json(char const * s) { ParseFrom(s); }
  explicit Json(JSONPtr && json) { m_handle.AttachNew(json.release()); }

  Json GetDeepCopy() const
  {
    Json copy;
    copy.m_handle.AttachNew(get_deep_copy());
    return copy;
  }
  void ParseFrom(std::string const & s) { ParseFrom(s.c_str()); }
  void ParseFrom(char const * s)
  {
    json_error_t jsonError;
    m_handle.AttachNew(json_loads(s, 0, &jsonError));
    if (!m_handle)
      MYTHROW(Exception, (jsonError.line, jsonError.text));
  }

  json_t * get() const { return m_handle.get(); }
  json_t * get_deep_copy() const { return json_deep_copy(get()); }

private:
  JsonHandle m_handle;
};

JSONPtr LoadFromString(std::string const & str);
std::string DumpToString(JSONPtr const & json, size_t flags = 0);

json_t * GetJSONObligatoryField(json_t * root, std::string const & field);
json_t const * GetJSONObligatoryField(json_t const * root, std::string const & field);
json_t * GetJSONObligatoryField(json_t * root, char const * field);
json_t const * GetJSONObligatoryField(json_t const * root, char const * field);
json_t * GetJSONOptionalField(json_t * root, std::string const & field);
json_t const * GetJSONOptionalField(json_t const * root, std::string const & field);
json_t * GetJSONOptionalField(json_t * root, char const * field);
json_t const * GetJSONOptionalField(json_t const * root, char const * field);

template <class First>
inline json_t const * GetJSONObligatoryFieldByPath(json_t const * root, First && path)
{
  return GetJSONObligatoryField(root, std::forward<First>(path));
}

template <class First, class... Paths>
inline json_t const * GetJSONObligatoryFieldByPath(json_t const * root, First && path,
                                                   Paths &&... paths)
{
  json_t const * newRoot = GetJSONObligatoryFieldByPath(root, std::forward<First>(path));
  return GetJSONObligatoryFieldByPath(newRoot, std::forward<Paths>(paths)...);
}

bool JSONIsNull(json_t const * root);
}  // namespace base

template <typename T>
T FromJSON(json_t const * root)
{
  T result{};
  FromJSON(root, result);
  return result;
}

inline void FromJSON(json_t * root, json_t *& value) { value = root; }
inline void FromJSON(json_t const * root, json_t const *& value) { value = root; }

void FromJSON(json_t const * root, double & result);
void FromJSON(json_t const * root, bool & result);

template <typename T, typename std::enable_if<std::is_integral<T>::value, void>::type * = nullptr>
void FromJSON(json_t const * root, T & result)
{
  if (!json_is_number(root))
    MYTHROW(base::Json::Exception, ("Object must contain a json number."));
  result = static_cast<T>(json_integer_value(root));
}

std::string FromJSONToString(json_t const * root);

template <typename T>
T FromJSONObject(json_t const * root, char const * field)
{
  auto const * json = base::GetJSONObligatoryField(root, field);
  try
  {
    return FromJSON<T>(json);
  }
  catch (base::Json::Exception const & e)
  {
    MYTHROW(base::Json::Exception, ("An error occured while parsing field", field, e.Msg()));
  }
}

template <typename T>
void FromJSONObject(json_t * root, std::string const & field, T & result)
{
  auto * json = base::GetJSONObligatoryField(root, field);
  try
  {
    FromJSON(json, result);
  }
  catch (base::Json::Exception const & e)
  {
    MYTHROW(base::Json::Exception, ("An error occured while parsing field", field, e.Msg()));
  }
}

template <typename T>
boost::optional<T> FromJSONObjectOptional(json_t const * root, char const * field)
{
  auto * json = base::GetJSONOptionalField(root, field);
  if (!json)
    return {};

  boost::optional<T> result{T{}};
  FromJSON(json, *result);
  return result;
}

template <typename T>
void FromJSONObjectOptionalField(json_t const * root, std::string const & field, T & result)
{
  auto * json = base::GetJSONOptionalField(root, field);
  if (!json)
  {
    result = T{};
    return;
  }
  FromJSON(json, result);
}

template <typename T, typename std::enable_if<std::is_integral<T>::value, void>::type * = nullptr>
inline base::JSONPtr ToJSON(T value)
{
  return base::NewJSONInt(value);
}
inline base::JSONPtr ToJSON(double value) { return base::NewJSONReal(value); }
inline base::JSONPtr ToJSON(bool value) { return base::NewJSONBool(value); }
inline base::JSONPtr ToJSON(char const * s) { return base::NewJSONString(s); }

template <typename T>
void ToJSONArray(json_t & root, T const & value)
{
  json_array_append_new(&root, ToJSON(value).release());
}

inline void ToJSONArray(json_t & parent, base::JSONPtr & child)
{
  json_array_append_new(&parent, child.release());
}

inline void ToJSONArray(json_t & parent, json_t & child) { json_array_append_new(&parent, &child); }

template <typename T>
void ToJSONObject(json_t & root, char const * field, T const & value)
{
  json_object_set_new(&root, field, ToJSON(value).release());
}

inline void ToJSONObject(json_t & parent, char const * field, base::JSONPtr && child)
{
  json_object_set_new(&parent, field, child.release());
}

inline void ToJSONObject(json_t & parent, char const * field, base::JSONPtr & child)
{
  json_object_set_new(&parent, field, child.release());
}

inline void ToJSONObject(json_t & parent, char const * field, json_t & child)
{
  json_object_set_new(&parent, field, &child);
}

template <typename T>
void ToJSONObject(json_t & root, std::string const & field, T const & value)
{
  ToJSONObject(root, field.c_str(), value);
}

inline void ToJSONObject(json_t & parent, std::string const & field, base::JSONPtr && child)
{
  ToJSONObject(parent, field.c_str(), std::move(child));
}

inline void ToJSONObject(json_t & parent, std::string const & field, base::JSONPtr & child)
{
  ToJSONObject(parent, field.c_str(), child);
}

inline void ToJSONObject(json_t & parent, std::string const & field, json_t & child)
{
  ToJSONObject(parent, field.c_str(), child);
}

template <typename T>
void FromJSONObject(json_t * root, std::string const & field, std::vector<T> & result)
{
  auto * arr = base::GetJSONObligatoryField(root, field);
  if (!json_is_array(arr))
    MYTHROW(base::Json::Exception, ("The field", field, "must contain a json array."));
  size_t sz = json_array_size(arr);
  result.resize(sz);
  for (size_t i = 0; i < sz; ++i)
    FromJSON(json_array_get(arr, i), result[i]);
}

// The function tries to parse array of values from a value
// corresponding to |field| in a json object corresponding to |root|.
// Returns true when the value is non-null and array is successfully
// parsed.  Returns false when there is no such |field| in the |root|
// or the value is null.  Also, the method may throw an exception in
// case of json parsing errors.
template <typename T>
bool FromJSONObjectOptional(json_t * root, std::string const & field, std::vector<T> & result)
{
  auto * arr = base::GetJSONOptionalField(root, field);
  if (!arr || base::JSONIsNull(arr))
  {
    result.clear();
    return false;
  }
  if (!json_is_array(arr))
    MYTHROW(base::Json::Exception, ("The field", field, "must contain a json array."));
  size_t const sz = json_array_size(arr);
  result.resize(sz);
  for (size_t i = 0; i < sz; ++i)
    FromJSON(json_array_get(arr, i), result[i]);
  return true;
}

template <typename T>
void ToJSONObject(json_t & root, char const * field, std::vector<T> const & values)
{
  auto arr = base::NewJSONArray();
  for (auto const & value : values)
    json_array_append_new(arr.get(), ToJSON(value).release());
  json_object_set_new(&root, field, arr.release());
}

template <typename T>
void ToJSONObject(json_t & root, std::string const & field, std::vector<T> const & values)
{
  ToJSONObject(root, field.c_str(), values);
}

template <typename T>
void FromJSONObjectOptionalField(json_t * root, std::string const & field, std::vector<T> & result)
{
  FromJSONObjectOptionalField(const_cast<json_t const *>(root), field, result);
}

template <typename T>
void FromJSONObjectOptionalField(json_t const * root, std::string const & field, std::vector<T> & result)
{
  json_t const * arr = base::GetJSONOptionalField(root, field);
  if (!arr)
  {
    result.clear();
    return;
  }
  if (!json_is_array(arr))
    MYTHROW(base::Json::Exception, ("The field", field, "must contain a json array."));
  size_t sz = json_array_size(arr);
  result.resize(sz);
  for (size_t i = 0; i < sz; ++i)
    FromJSON(json_array_get(arr, i), result[i]);
}

struct JSONFreeDeleter
{
  void operator()(char * buffer) const { free(buffer); }
};

namespace std
{
void FromJSON(json_t const * root, std::string & result);
inline base::JSONPtr ToJSON(std::string const & s) { return base::NewJSONString(s); }
}  // namespace std

namespace strings
{
void FromJSON(json_t const * root, UniString & result);
base::JSONPtr ToJSON(UniString const & s);
}  // namespace strings
<|endoftext|>


<commit_before>
#include <cstdlib>

#include <boost/test/unit_test.hpp>
#include <cucumber-cpp/defs.hpp>

#include <QApplication>
#include <QTest>

#include <CalculatorWidget.h>

static int argc = 0;
static QApplication app(argc, 0);
static int milliseconds = -1;

int millisecondsToWait() {
    if (milliseconds < 0)
    {
        char* envVariable = getenv("CUKE_CALCQT_WAIT");
        milliseconds = (0 != envVariable) ? atoi(envVariable) : 0;
    }
    return milliseconds;
}

std::istream& operator>> (std::istream& in, QString& val) { std::string s; in >> s; val = s.c_str(); return in; }
std::ostream& operator<< (std::ostream& out, const QString& val) { out << val.toAscii().data(); return out; }

GIVEN("^I just turned on the calculator$") {
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    calculator->move(0, 0);
    calculator->show();
    QTest::qWaitForWindowShown(calculator.get());
    QTest::qWait(millisecondsToWait());
}

WHEN("^I press (\\d+)$") {
    REGEX_PARAM(unsigned int, n);
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    QTest::keyClick(calculator.get(), Qt::Key_0 + n, Qt::NoModifier, millisecondsToWait());
}

WHEN("^I press add") {
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    QTest::keyClick(calculator.get(), Qt::Key_Plus, Qt::NoModifier, millisecondsToWait());
}

WHEN("^I press calculate") {
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    QTest::keyClick(calculator.get(), Qt::Key_Return, Qt::NoModifier, millisecondsToWait());
}

WHEN("^I press clear") {
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    QTest::keyClick(calculator.get(), Qt::Key_Escape, Qt::NoModifier, millisecondsToWait());
}

WHEN("^I press subtract") {
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    QTest::keyClick(calculator.get(), Qt::Key_Minus, Qt::NoModifier, millisecondsToWait());
}

THEN("^the display should be empty$") {
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    BOOST_CHECK_EQUAL(calculator->display().size(), 0);
    QTest::qWait(millisecondsToWait());
}

THEN("^the display should show (.*)$") {
    REGEX_PARAM(QString, expected);
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    BOOST_CHECK_EQUAL(expected, calculator->display());
    QTest::qWait(millisecondsToWait());
}
<commit_msg>renamed delay env variable to CALCQT_STEP_DELAY<commit_after>
#include <cstdlib>

#include <boost/test/unit_test.hpp>
#include <cucumber-cpp/defs.hpp>

#include <QApplication>
#include <QTest>

#include <CalculatorWidget.h>

static int argc = 0;
static QApplication app(argc, 0);
static int milliseconds = -1;

int millisecondsToWait() {
    if (milliseconds < 0)
    {
        char* envVariable = getenv("CALCQT_STEP_DELAY");
        milliseconds = (0 != envVariable) ? atoi(envVariable) : 0;
    }
    return milliseconds;
}

std::istream& operator>> (std::istream& in, QString& val) { std::string s; in >> s; val = s.c_str(); return in; }
std::ostream& operator<< (std::ostream& out, const QString& val) { out << val.toAscii().data(); return out; }

GIVEN("^I just turned on the calculator$") {
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    calculator->move(0, 0);
    calculator->show();
    QTest::qWaitForWindowShown(calculator.get());
    QTest::qWait(millisecondsToWait());
}

WHEN("^I press (\\d+)$") {
    REGEX_PARAM(unsigned int, n);
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    QTest::keyClick(calculator.get(), Qt::Key_0 + n, Qt::NoModifier, millisecondsToWait());
}

WHEN("^I press add") {
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    QTest::keyClick(calculator.get(), Qt::Key_Plus, Qt::NoModifier, millisecondsToWait());
}

WHEN("^I press calculate") {
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    QTest::keyClick(calculator.get(), Qt::Key_Return, Qt::NoModifier, millisecondsToWait());
}

WHEN("^I press clear") {
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    QTest::keyClick(calculator.get(), Qt::Key_Escape, Qt::NoModifier, millisecondsToWait());
}

WHEN("^I press subtract") {
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    QTest::keyClick(calculator.get(), Qt::Key_Minus, Qt::NoModifier, millisecondsToWait());
}

THEN("^the display should be empty$") {
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    BOOST_CHECK_EQUAL(calculator->display().size(), 0);
    QTest::qWait(millisecondsToWait());
}

THEN("^the display should show (.*)$") {
    REGEX_PARAM(QString, expected);
    cucumber::ScenarioScope<CalculatorWidget> calculator;
    BOOST_CHECK_EQUAL(expected, calculator->display());
    QTest::qWait(millisecondsToWait());
}
<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Ruben Van Boxem
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 **/

/*
 * Signal
 * Registers observers.
 * Notifies registered observers.
 */

#include <algorithm>
#include <functional>
#include <mutex>
#include <vector>

namespace skui
{
  namespace core
  {
    namespace implementation
    {
      template<typename... ArgTypes>
      class signal_base
      {
      public:
        using function_type = void(*)(ArgTypes...);
        using slot_type = std::function<void(ArgTypes...)>;

        signal_base() = default;

        signal_base(const signal_base& other) : slots(other.slots) {}
        signal_base(signal_base&& other) : slots(std::move(other.slots)) {}
        signal_base& operator=(signal_base other) { swap(slots, other.slots); return *this; }

        void connect(slot_type&& slot)
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          slots.emplace_back(slot);
        }

        bool disconnect(slot_type&& slot)
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          auto function_pointer = slot.template target<function_type>();
          const auto result_it = std::find_if(begin(slots), end(slots),
                                              [&function_pointer](slot_type& connected_slot)
                                              {
                                                return function_pointer == connected_slot.template target<function_type>();
                                              });
          if(result_it != end(slots))
          {
            slots.erase(result_it);
            return true;
          }
          return false;
        }

        void disconnect_all()
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          slots.clear();
        }

      protected:
        // mutable here allows to connect to a const object's signals
        mutable std::vector<slot_type> slots;
        mutable std::mutex slots_mutex;
      };
    }
    template<typename... ArgTypes>
    class signal : public implementation::signal_base<ArgTypes...>
    {
    public:
      signal() = default;

      void emit(ArgTypes... arguments) const
      {
        std::lock_guard<decltype(this->slots_mutex)> lock(this->slots_mutex);
        for(auto&& slot : this->slots)
        {
          slot(arguments...);
        }
      }
    };

    // Parameterless specialization
    template<> class signal<> : public implementation::signal_base<>
    {
    public:
      signal() = default;

      void emit() const
      {
        std::lock_guard<decltype(this->slots_mutex)> lock(this->slots_mutex);
        for(auto&& slot : this->slots)
        {
          slot();
        }
      }
    };
  }
}

<commit_msg>Add object (void*) pointer in preparation to disconnecting member slots.<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Ruben Van Boxem
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 **/

/*
 * Signal
 * Registers observers.
 * Notifies registered observers.
 */

#include <algorithm>
#include <functional>
#include <mutex>
#include <utility>
#include <vector>

namespace skui
{
  namespace core
  {
    namespace implementation
    {
      template<typename... ArgTypes>
      class signal_base
      {
      public:
        using function_type = void(*)(ArgTypes...);
        using slot_type = std::function<void(ArgTypes...)>;

        signal_base() = default;

        signal_base(const signal_base& other) : slots(other.slots) {}
        signal_base(signal_base&& other) : slots(std::move(other.slots)) {}
        signal_base& operator=(signal_base other) { swap(slots, other.slots); return *this; }

        void connect(slot_type&& slot)
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          slots.emplace_back(nullptr, slot);
        }

        bool disconnect(slot_type&& slot)
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          auto function_pointer = slot.template target<function_type>();
          const auto result_it = std::find_if(begin(slots), end(slots),
                                              [&function_pointer](std::pair<void*, slot_type>& object_slot)
                                              {
                                                return function_pointer == object_slot.second.template target<function_type>();
                                              });
          if(result_it != end(slots))
          {
            slots.erase(result_it);
            return true;
          }
          return false;
        }

        void disconnect_all()
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          slots.clear();
        }

      protected:
        // mutable here allows to connect to a const object's signals
        mutable std::vector<std::pair<void*, slot_type>> slots;
        mutable std::mutex slots_mutex;
      };
    }

    template<typename... ArgTypes>
    class signal : public implementation::signal_base<ArgTypes...>
    {
    public:
      signal() = default;

      void emit(ArgTypes... arguments) const
      {
        std::lock_guard<decltype(this->slots_mutex)> lock(this->slots_mutex);
        for(auto&& object_slot : this->slots)
        {
          object_slot.second(arguments...);
        }
      }
    };

    // Parameterless specialization
    template<> class signal<> : public implementation::signal_base<>
    {
    public:
      signal() = default;

      void emit() const
      {
        std::lock_guard<decltype(this->slots_mutex)> lock(this->slots_mutex);
        for(auto&& object_slot_pair : this->slots)
        {
          object_slot_pair.second();
        }
      }
    };
  }
}

<|endoftext|>