<commit_before>/**
 * @file constrained_ik_plugin.cpp
 * @brief Constrained inverse kinematic plugin for moveit.
 * @author dsolomon
 * @date Sep 15, 2013
 * @version TODO
 * @bug No known bugs
 *
 * @copyright Copyright (c) 2013, Southwest Research Institute
 *
 * @license Software License Agreement (Apache License)\n
 * \n
 * 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\n
 * \n
 * http://www.apache.org/licenses/LICENSE-2.0\n
 * \n
 * 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 <constrained_ik/moveit_interface/constrained_ik_plugin.h>
#include <ros/ros.h>

#include <kdl_parser/kdl_parser.hpp>
#include <tf_conversions/tf_kdl.h>
#include <eigen_conversions/eigen_kdl.h>
#include <pluginlib/class_list_macros.h>
PLUGINLIB_EXPORT_CLASS(constrained_ik::ConstrainedIKPlugin, kinematics::KinematicsBase);

using namespace KDL;
using namespace tf;
using namespace std;
using namespace ros;

namespace constrained_ik
{

ConstrainedIKPlugin::ConstrainedIKPlugin():active_(false), dimension_(0)
{
}

bool ConstrainedIKPlugin::isActive()
{
  if(active_)
    return true;
  ROS_ERROR("kinematics not active");
  return false;
}

bool ConstrainedIKPlugin::isActive() const
{
  if(active_)
    return true;
  ROS_ERROR("kinematics not active");
  return false;
}

bool ConstrainedIKPlugin::initialize(const std::string& robot_description,
                                     const std::string& group_name,
                                     const std::string& base_name,
                                     const std::string& tip_name,
                                     double search_discretization)
{
  setValues(robot_description, group_name, base_name, tip_name, search_discretization);

  // init robot model
  rdf_loader::RDFLoader rdf_loader(robot_description_);
  const boost::shared_ptr<srdf::Model> &srdf = rdf_loader.getSRDF();
  const boost::shared_ptr<urdf::ModelInterface>& urdf_model = rdf_loader.getURDF();

  if (!urdf_model || !srdf)
  {
    ROS_ERROR_STREAM("URDF and SRDF must be loaded for Constrained Ik solver to work.");
    return false;
  }

  // instatiating a robot model
  robot_model_ptr_.reset(new robot_model::RobotModel(urdf_model,srdf));
  if(!robot_model_ptr_)
  {
    ROS_ERROR_STREAM("Could not load URDF model from " << robot_description);
    active_ = false;
    return false;
  }

  const robot_model::JointModelGroup* joint_model_group = robot_model_ptr_->getJointModelGroup(group_name);
  if (!joint_model_group)
    return false;

  if (!joint_model_group->isChain())
  {
    ROS_ERROR_NAMED("clik", "Group '%s' is not a chain.", group_name.c_str());
    return false;
  }

  robot_state_.reset(new moveit::core::RobotState(robot_model_ptr_));

  // initializing planning scene
  planning_scene_.reset(new planning_scene::PlanningScene(robot_model_ptr_));

  //initialize kinematic solver with robot info
  if (!kin_.init(joint_model_group))
  {
    ROS_ERROR("Could not initialize BasicIK");
    active_ = false;
  }
  else
  {
    dimension_ = kin_.numJoints();
    kin_.getJointNames(joint_names_);
    kin_.getLinkNames(link_names_);
    active_ = true;
  }

  try
  {
    solver_.init(kin_); // inside try because it has the potential to throw and error
  }
  catch (exception &e)
  {
    ROS_ERROR_STREAM("Caught exception from solver_: " << e.what());
    return false;
  }

  return active_;
}

bool ConstrainedIKPlugin::getPositionIK(const geometry_msgs::Pose &ik_pose,
                                        const std::vector<double> &ik_seed_state,
                                        std::vector<double> &solution,
                                        moveit_msgs::MoveItErrorCodes &error_code,
                                        const kinematics::KinematicsQueryOptions &options) const
{
  //check that solver is ready and properly configured
  if(!active_)
  {
    ROS_ERROR("kinematics not active");
    error_code.val = error_code.NO_IK_SOLUTION;
    return false;
  }
  if(ik_seed_state.size() != dimension_)
  {
    ROS_ERROR("ik_seed_state does not have same dimension as solver");
    error_code.val = error_code.NO_IK_SOLUTION;
    return false;
  }

  //convert input parameters to required types
  KDL::Frame pose_desired;
  tf::poseMsgToKDL(ik_pose, pose_desired);
  Eigen::Affine3d goal;
  tf::transformKDLToEigen(pose_desired, goal);

  Eigen::VectorXd seed(dimension_), joint_angles(dimension_);
  for(size_t ii=0; ii < dimension_; ++ii)
  {
    seed(ii) = ik_seed_state[ii];
  }

  //Do IK and report results
  try
  {
    if(!solver_.calcInvKin(goal, seed, planning_scene_, joint_angles))
    {
      ROS_ERROR_STREAM("Unable to find IK solution.");
      error_code.val = error_code.NO_IK_SOLUTION;
      return false;
    }
  }
  catch (exception &e)
  {
    ROS_ERROR_STREAM("Caught exception from IK: " << e.what());
    error_code.val = error_code.NO_IK_SOLUTION;
    return false;
  }
  solution.resize(dimension_);
  for(size_t ii=0; ii < dimension_; ++ii)
  {
    solution[ii] = joint_angles(ii);
  }

  return true;

}

bool ConstrainedIKPlugin::searchPositionIK( const geometry_msgs::Pose &ik_pose,
                                            const std::vector<double> &ik_seed_state,
                                            double timeout,
                                            std::vector<double> &solution,
                                            moveit_msgs::MoveItErrorCodes &error_code,
                                            const kinematics::KinematicsQueryOptions &options) const
{
    static IKCallbackFn solution_callback = 0;
    static std::vector<double> consistency_limits;
    return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, solution_callback, error_code);
}

bool ConstrainedIKPlugin::searchPositionIK( const geometry_msgs::Pose &ik_pose,
                                            const std::vector<double> &ik_seed_state,
                                            double timeout,
                                            const std::vector<double> &consistency_limits,
                                            std::vector<double> &solution,
                                            moveit_msgs::MoveItErrorCodes &error_code,
                                            const kinematics::KinematicsQueryOptions &options) const
{
    static IKCallbackFn solution_callback = 0;
    return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, solution_callback, error_code);
}

bool ConstrainedIKPlugin::searchPositionIK( const geometry_msgs::Pose &ik_pose,
                                            const std::vector<double> &ik_seed_state,
                                            double timeout,
                                            std::vector<double> &solution,
                                            const IKCallbackFn &solution_callback,
                                            moveit_msgs::MoveItErrorCodes &error_code,
                                            const kinematics::KinematicsQueryOptions &options) const
{
    static std::vector<double> consistency_limits;
    return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, solution_callback, error_code);
}

bool ConstrainedIKPlugin::searchPositionIK( const geometry_msgs::Pose &ik_pose,
                                            const std::vector<double> &ik_seed_state,
                                            double timeout,
                                            const std::vector<double> &consistency_limits,
                                            std::vector<double> &solution,
                                            const IKCallbackFn &solution_callback,
                                            moveit_msgs::MoveItErrorCodes &error_code,
                                            const kinematics::KinematicsQueryOptions &options) const
{

  if(!active_)
  {
    ROS_ERROR("kinematics not active");
    error_code.val = error_code.FAILURE;
    return false;
  }

  KDL::Frame pose_desired;
  tf::poseMsgToKDL(ik_pose, pose_desired);
  Eigen::Affine3d goal;
  tf::transformKDLToEigen(pose_desired, goal);

  if(dimension_ != ik_seed_state.size()){
    ROS_ERROR("dimension_ and ik_seed_state are of different sizes");
    return(false);
  }
  Eigen::VectorXd seed(dimension_), joint_angles;
  for(size_t ii=0; ii < dimension_; ii++)
  {
    seed(ii) = ik_seed_state[ii];
  }

  //Do the IK
  bool success(true);
  try
  {
    solver_.calcInvKin(goal, seed, planning_scene_,joint_angles);
  }
  catch (exception &e)
  {
    ROS_ERROR_STREAM("Caught exception in plugin from IK: " << e.what());
    error_code.val = error_code.NO_IK_SOLUTION;
    success &= false;
  }

  solution.resize(dimension_);
  for(size_t ii=0; ii < dimension_; ++ii)
  {
    solution[ii] = joint_angles(ii);
  }

  // If there is a solution callback registered, check before returning
  if (solution_callback)
  {
    solution_callback(ik_pose, solution, error_code);
    if(error_code.val != error_code.SUCCESS)
      success &= false;
  }
  // Default: return successfully
  if (success)
    error_code.val = error_code.SUCCESS;

  return success;

}

bool ConstrainedIKPlugin::getPositionFK(const std::vector<std::string> &link_names,
                                        const std::vector<double> &joint_angles,
                                        std::vector<geometry_msgs::Pose> &poses) const
{
  if(!active_)
  {
    ROS_ERROR("kinematics not active");
    return false;
  }

  Eigen::VectorXd jnt_pos_in;
  geometry_msgs::PoseStamped pose;
  tf::Stamped<tf::Pose> tf_pose;

  jnt_pos_in.resize(dimension_);
  for(int i=0; i < dimension_; i++)
  {
    jnt_pos_in(i) = joint_angles[i];
  }

  poses.resize(link_names.size());

  std::vector<KDL::Frame> kdl_poses;
  bool valid = kin_.linkTransforms(jnt_pos_in, kdl_poses, link_names);
  for(size_t ii=0; ii < kdl_poses.size(); ++ii)
  {
    tf::poseKDLToMsg(kdl_poses[ii],poses[ii]);
  }
  //TODO remove this printing
  ROS_INFO_STREAM("poses: ");
  for (size_t ii=0; ii<poses.size(); ++ii)
  {
    ROS_INFO_STREAM(poses[ii]);
  }
  return valid;
}

const std::vector<std::string>& ConstrainedIKPlugin::getJointNames() const
{
  isActive();
  return joint_names_;
}

const std::vector<std::string>& ConstrainedIKPlugin::getLinkNames() const
{
  isActive();
  return link_names_;
}

} //namespace constrained_ik_plugin
<commit_msg>Update missed changes<commit_after>/**
 * @file constrained_ik_plugin.cpp
 * @brief Constrained inverse kinematic plugin for moveit.
 * @author dsolomon
 * @date Sep 15, 2013
 * @version TODO
 * @bug No known bugs
 *
 * @copyright Copyright (c) 2013, Southwest Research Institute
 *
 * @license Software License Agreement (Apache License)\n
 * \n
 * 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\n
 * \n
 * http://www.apache.org/licenses/LICENSE-2.0\n
 * \n
 * 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 <constrained_ik/moveit_interface/constrained_ik_plugin.h>
#include <ros/ros.h>

#include <kdl_parser/kdl_parser.hpp>
#include <tf_conversions/tf_kdl.h>
#include <eigen_conversions/eigen_kdl.h>
#include <pluginlib/class_list_macros.h>
PLUGINLIB_EXPORT_CLASS(constrained_ik::ConstrainedIKPlugin, kinematics::KinematicsBase);

using namespace KDL;
using namespace tf;
using namespace std;
using namespace ros;

namespace constrained_ik
{

ConstrainedIKPlugin::ConstrainedIKPlugin():active_(false), dimension_(0)
{
}

bool ConstrainedIKPlugin::isActive()
{
  if(active_)
    return true;
  ROS_ERROR("kinematics not active");
  return false;
}

bool ConstrainedIKPlugin::isActive() const
{
  if(active_)
    return true;
  ROS_ERROR("kinematics not active");
  return false;
}

bool ConstrainedIKPlugin::initialize(const std::string& robot_description,
                                     const std::string& group_name,
                                     const std::string& base_name,
                                     const std::string& tip_name,
                                     double search_discretization)
{
  setValues(robot_description, group_name, base_name, tip_name, search_discretization);

  // init robot model
  rdf_loader::RDFLoader rdf_loader(robot_description_);
  const boost::shared_ptr<srdf::Model> &srdf = rdf_loader.getSRDF();
  const boost::shared_ptr<urdf::ModelInterface>& urdf_model = rdf_loader.getURDF();

  if (!urdf_model || !srdf)
  {
    ROS_ERROR_STREAM("URDF and SRDF must be loaded for Constrained Ik solver to work.");
    return false;
  }

  // instatiating a robot model
  robot_model_ptr_.reset(new robot_model::RobotModel(urdf_model,srdf));
  if(!robot_model_ptr_)
  {
    ROS_ERROR_STREAM("Could not load URDF model from " << robot_description);
    active_ = false;
    return false;
  }

  const robot_model::JointModelGroup* joint_model_group = robot_model_ptr_->getJointModelGroup(group_name);
  if (!joint_model_group)
    return false;

  if (!joint_model_group->isChain())
  {
    ROS_ERROR_NAMED("clik", "Group '%s' is not a chain.", group_name.c_str());
    return false;
  }

  robot_state_.reset(new moveit::core::RobotState(robot_model_ptr_));

  // initializing planning scene
  planning_scene_.reset(new planning_scene::PlanningScene(robot_model_ptr_));

  //initialize kinematic solver with robot info
  if (!kin_.init(joint_model_group))
  {
    ROS_ERROR("Could not initialize BasicIK");
    active_ = false;
  }
  else
  {
    dimension_ = kin_.numJoints();
    kin_.getJointNames(joint_names_);
    kin_.getLinkNames(link_names_);
    active_ = true;
  }

  try
  {
    solver_.init(kin_); // inside try because it has the potential to throw and error
  }
  catch (exception &e)
  {
    ROS_ERROR_STREAM("Caught exception from solver_: " << e.what());
    return false;
  }

  return active_;
}

bool ConstrainedIKPlugin::getPositionIK(const geometry_msgs::Pose &ik_pose,
                                        const std::vector<double> &ik_seed_state,
                                        std::vector<double> &solution,
                                        moveit_msgs::MoveItErrorCodes &error_code,
                                        const kinematics::KinematicsQueryOptions &options) const
{
  //check that solver is ready and properly configured
  if(!active_)
  {
    ROS_ERROR("kinematics not active");
    error_code.val = error_code.NO_IK_SOLUTION;
    return false;
  }
  if(ik_seed_state.size() != dimension_)
  {
    ROS_ERROR("ik_seed_state does not have same dimension as solver");
    error_code.val = error_code.NO_IK_SOLUTION;
    return false;
  }

  //convert input parameters to required types
  KDL::Frame pose_desired;
  tf::poseMsgToKDL(ik_pose, pose_desired);
  Eigen::Affine3d goal;
  tf::transformKDLToEigen(pose_desired, goal);

  Eigen::VectorXd seed(dimension_), joint_angles(dimension_);
  for(size_t ii=0; ii < dimension_; ++ii)
  {
    seed(ii) = ik_seed_state[ii];
  }

  //Do IK and report results
  try
  {
    if(!solver_.calcInvKin(goal, seed, planning_scene_, joint_angles))
    {
      ROS_ERROR_STREAM("Unable to find IK solution.");
      error_code.val = error_code.NO_IK_SOLUTION;
      return false;
    }
  }
  catch (exception &e)
  {
    ROS_ERROR_STREAM("Caught exception from IK: " << e.what());
    error_code.val = error_code.NO_IK_SOLUTION;
    return false;
  }
  solution.resize(dimension_);
  for(size_t ii=0; ii < dimension_; ++ii)
  {
    solution[ii] = joint_angles(ii);
  }

  return true;

}

bool ConstrainedIKPlugin::searchPositionIK( const geometry_msgs::Pose &ik_pose,
                                            const std::vector<double> &ik_seed_state,
                                            double timeout,
                                            std::vector<double> &solution,
                                            moveit_msgs::MoveItErrorCodes &error_code,
                                            const kinematics::KinematicsQueryOptions &options) const
{
    static IKCallbackFn solution_callback = 0;
    static std::vector<double> consistency_limits;
    return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, solution_callback, error_code);
}

bool ConstrainedIKPlugin::searchPositionIK( const geometry_msgs::Pose &ik_pose,
                                            const std::vector<double> &ik_seed_state,
                                            double timeout,
                                            const std::vector<double> &consistency_limits,
                                            std::vector<double> &solution,
                                            moveit_msgs::MoveItErrorCodes &error_code,
                                            const kinematics::KinematicsQueryOptions &options) const
{
    static IKCallbackFn solution_callback = 0;
    return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, solution_callback, error_code);
}

bool ConstrainedIKPlugin::searchPositionIK( const geometry_msgs::Pose &ik_pose,
                                            const std::vector<double> &ik_seed_state,
                                            double timeout,
                                            std::vector<double> &solution,
                                            const IKCallbackFn &solution_callback,
                                            moveit_msgs::MoveItErrorCodes &error_code,
                                            const kinematics::KinematicsQueryOptions &options) const
{
    static std::vector<double> consistency_limits;
    return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, solution_callback, error_code);
}

bool ConstrainedIKPlugin::searchPositionIK( const geometry_msgs::Pose &ik_pose,
                                            const std::vector<double> &ik_seed_state,
                                            double timeout,
                                            const std::vector<double> &consistency_limits,
                                            std::vector<double> &solution,
                                            const IKCallbackFn &solution_callback,
                                            moveit_msgs::MoveItErrorCodes &error_code,
                                            const kinematics::KinematicsQueryOptions &options) const
{

  if(!active_)
  {
    ROS_ERROR("kinematics not active");
    error_code.val = error_code.FAILURE;
    return false;
  }

  KDL::Frame pose_desired;
  tf::poseMsgToKDL(ik_pose, pose_desired);
  Eigen::Affine3d goal;
  tf::transformKDLToEigen(pose_desired, goal);

  if(dimension_ != ik_seed_state.size()){
    ROS_ERROR("dimension_ and ik_seed_state are of different sizes");
    return(false);
  }
  Eigen::VectorXd seed(dimension_), joint_angles;
  for(size_t ii=0; ii < dimension_; ii++)
  {
    seed(ii) = ik_seed_state[ii];
  }

  //Do the IK
  bool success(true);
  try
  {
    if(!solver_.calcInvKin(goal, seed, planning_scene_, joint_angles))
    {
      ROS_ERROR_STREAM("Unable to find IK solution.");
      error_code.val = error_code.NO_IK_SOLUTION;
      success &= false;
    }
  }
  catch (exception &e)
  {
    ROS_ERROR_STREAM("Caught exception from IK: " << e.what());
    error_code.val = error_code.NO_IK_SOLUTION;
    success &= false;
  }

  solution.resize(dimension_);
  for(size_t ii=0; ii < dimension_; ++ii)
  {
    solution[ii] = joint_angles(ii);
  }

  // If there is a solution callback registered, check before returning
  if (solution_callback)
  {
    solution_callback(ik_pose, solution, error_code);
    if(error_code.val != error_code.SUCCESS)
      success &= false;
  }
  // Default: return successfully
  if (success)
    error_code.val = error_code.SUCCESS;

  return success;

}

bool ConstrainedIKPlugin::getPositionFK(const std::vector<std::string> &link_names,
                                        const std::vector<double> &joint_angles,
                                        std::vector<geometry_msgs::Pose> &poses) const
{
  if(!active_)
  {
    ROS_ERROR("kinematics not active");
    return false;
  }

  Eigen::VectorXd jnt_pos_in;
  geometry_msgs::PoseStamped pose;
  tf::Stamped<tf::Pose> tf_pose;

  jnt_pos_in.resize(dimension_);
  for(int i=0; i < dimension_; i++)
  {
    jnt_pos_in(i) = joint_angles[i];
  }

  poses.resize(link_names.size());

  std::vector<KDL::Frame> kdl_poses;
  bool valid = kin_.linkTransforms(jnt_pos_in, kdl_poses, link_names);
  for(size_t ii=0; ii < kdl_poses.size(); ++ii)
  {
    tf::poseKDLToMsg(kdl_poses[ii],poses[ii]);
  }
  //TODO remove this printing
  ROS_INFO_STREAM("poses: ");
  for (size_t ii=0; ii<poses.size(); ++ii)
  {
    ROS_INFO_STREAM(poses[ii]);
  }
  return valid;
}

const std::vector<std::string>& ConstrainedIKPlugin::getJointNames() const
{
  isActive();
  return joint_names_;
}

const std::vector<std::string>& ConstrainedIKPlugin::getLinkNames() const
{
  isActive();
  return link_names_;
}

} //namespace constrained_ik_plugin
<|endoftext|>


<commit_before>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the Licence is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the Licence for the
specific language governing permissions and limitations
under the Licence.

Author(s) / Copyright (s): Damon Hart-Davis 2016
*/

/*
 * OpenTRV radiator valve physical UI controls and output as an actuator.
 *
 * A base class, a null class, and one or more implementations are provided
 * for different stock behaviour with different hardware.
 *
 * A mixture of template and constructor parameters
 * is used to configure the different types.
 */

#include <stddef.h>
#include <stdint.h>
#include <OTV0p2Base.h>

#include <OTRadValve_ActuatorPhysicalUI.h>

// Use namespaces to help avoid collisions.
namespace OTRadValve
    {

#if defined(ModeButtonAndPotActuatorPhysicalUI_DEFINED)

// Record local manual operation of a physical UI control, eg not remote or via CLI.
// Marks room as occupied amongst other things.
// To be thread-/ISR- safe, everything that this touches or calls must be.
// Thread-safe.
void ModeButtonAndPotActuatorPhysicalUI::markUIControlUsed()
    {
    statusChange = true; // Note user interaction with the system.
    uiTimeoutM.store(UI_DEFAULT_RECENT_USE_TIMEOUT_M); // Ensure that UI controls are kept 'warm' for a little while.
// FIXME
//  #if defined(ENABLE_UI_WAKES_CLI)
//    // Make CLI active for a while (at some slight possibly-significant energy cost).
//    resetCLIActiveTimer(); // Thread-safe.
//  #endif
    // User operation of physical controls is strong indication of presence.
    occupancy->markAsOccupied(); // Thread-safe.

    // Capture possible (near) peak of stack usage, eg when called from ISR.
    OTV0P2BASE::MemoryChecks::recordIfMinSP();
    }

// Record significant local manual operation of a physical UI control, eg not remote or via CLI.
// Marks room as occupied amongst other things.
// As markUIControlUsed() but likely to generate some feedback to the user, ASAP.
// Thread-safe.
void ModeButtonAndPotActuatorPhysicalUI::markUIControlUsedSignificant()
    {
    // Provide some instant visual feedback if possible.
    if(NULL != safeISRLEDonOpt) { safeISRLEDonOpt(); }
    // Flag up need for feedback.
    significantUIOp = true;
    // Do main UI-touched work.
    markUIControlUsed();
    }

// Call this nominally on even numbered seconds to allow the UI to operate.
// In practice call early once per 2s major cycle.
// Should never be skipped, so as to allow the UI to remain responsive.
// Runs in 350ms or less; usually takes only a few milliseconds or microseconds.
// Returns a non-zero value if the user interacted with the system, and maybe caused a status change.
// NOTE: since this is on the minimum idle-loop code path, minimise CPU cycles, esp in frost mode.
// Replaces: bool tickUI(uint_fast8_t sec).
uint8_t ModeButtonAndPotActuatorPhysicalUI::read()
    {
    // True on every 4th tick/call, ie every ~8 seconds.
    const bool forthTick = !((++tickCount) & 3); // True on every 4th tick.

    // Perform any once-per-minute-ish operations, every 32 ticks.
    const bool sec0 = (0 == (tickCount & 0x1f));
    if(sec0)
      { OTV0P2BASE::safeDecIfNZWeak(uiTimeoutM); }

    // Provide enhanced feedback when the has been very recent interaction with the UI,
    // since the user is still quite likely to be continuing.
    const bool enhancedUIFeedback = veryRecentUIControlUse();

    if(NULL != tempPotOpt)
        {
        // Force relatively-frequent re-read of temp pot UI device
        // and if there has been recent UI manual controls activity,
        // to keep the valve UI responsive.
    //  #if !defined(ENABLE_FAST_TEMP_POT_SAMPLING) || !defined(ENABLE_OCCUPANCY_SUPPORT)
    //    if(enhancedUIFeedback || forthTick)
    //  #else
    //    // Even more responsive at some possible energy cost...
        // Polls pot on every tick unless the room has been vacant for a day or two or is in FROST mode.
        if(enhancedUIFeedback || forthTick || (valveMode->inWarmMode() && !occupancy->longLongVacant()))
    //  #endif
          {
          tempPotOpt->read();
      #if 0 && defined(DEBUG)
            DEBUG_SERIAL_PRINT_FLASHSTRING("TP");
            DEBUG_SERIAL_PRINT(TempPot.getRaw());
            DEBUG_SERIAL_PRINTLN();
      #endif
          // Force to FROST mode (and cancel any erroneous BAKE, etc) when at FROST end of dial.
          const bool isLo = tempPotOpt->isAtLoEndStop();
          if(isLo) { valveMode->setWarmModeDebounced(false); }
          // Feed back significant change in pot position, ie at temperature boundaries.
          // Synthesise a target temperature that distinguishes the end stops
          // (FROST as one below minimum, BAKE as one above maximum)
          // from all other valid temperatures and the initial '0' state.
          const uint8_t nominalWarmTarget = isLo ? (tempControl->getMinWARMTargetC() - 1) :
              (tempPotOpt->isAtHiEndStop() ? (tempControl->getMaxWARMTargetC() + 1) :
              tempControl->getWARMTargetC());
          // Record of 'last' nominalWarmTarget; initially 0.
          if(nominalWarmTarget != lastNominalWarmTarget)
            {
            // Note if a boundary was crossed, ignoring any false 'start-up' transient.
            if(0 != lastNominalWarmTarget) { significantUIOp = true; }
            else
              {
              // On initialisation, if the dial is not at FROST,
              // force the device straight to WARM mode.
              if(!isLo) { valveMode->setWarmModeDebounced(true); }
              }
      #if 1 && defined(DEBUG)
            DEBUG_SERIAL_PRINT_FLASHSTRING("WT");
            DEBUG_SERIAL_PRINT(nominalWarmTarget);
            DEBUG_SERIAL_PRINTLN();
      #endif
            // Maximum number of clicks rather than collapsing them all to one.
            constexpr uint8_t maxClicks = 10;
            if(OTV0P2BASE::fnabsdiff(lastNominalWarmTarget, nominalWarmTarget) > maxClicks)
                { lastNominalWarmTarget = nominalWarmTarget; }
            // If small difference show it in successive ticks of feedback to the user (TODO-1045),
            // ie turning dial through xC of temperature change should produce x wiggles/flashes.
            else if(lastNominalWarmTarget < nominalWarmTarget) { ++lastNominalWarmTarget; }
            else { --lastNominalWarmTarget; }
            }
          }
      }

    // Provide extra user feedback for significant UI actions...
    if(significantUIOp) { userOpFeedback(); }

    // Support cycling through modes polling the MODE button.
    // If MODE button is active skip normal LED UI activity.
    if(!pollMODEButton())
      {
      // Keep reporting UI status if the user has just touched the unit in some way or UI feedback is enhanced.
      const bool justTouched = statusChange || enhancedUIFeedback;

      // Capture battery state if available.
      const bool batteryLow = (NULL != lowBattOpt) && lowBattOpt->isSupplyVoltageLow();

      // Minimise LED on duration unless UI just touched, or if battery low (TODO-963).
      const bool minimiseOnTime = (!justTouched) || batteryLow;

      // Mode button not pressed: indicate current mode with flash(es); more flashes if actually calling for heat.
      // Force display while UI controls are being used, eg to indicate temp pot position.
      if(justTouched || valveMode->inWarmMode()) // Generate flash(es) if in WARM mode or fiddling with UI other than Mode button.
        {
        // DHD20131223: only flash if the room not known to be dark so as to save energy and avoid disturbing sleep, etc.
        // In this case force resample of light level frequently in case user turns light on eg to operate unit.
        // Do show LED flash if user has recently operated controls (other than mode button) manually.
        // Flash infrequently if no recently operated controls and not in BAKE mode and not actually calling for heat;
        // this is to conserve batteries for those people who leave the valves in WARM mode all the time.
        if(justTouched ||
           ((forthTick
               || valveController->isCallingForHeat()
               || valveMode->inBakeMode()) && !ambLight->isRoomDark()))
          {
          // First flash to indicate WARM mode (or pot being twiddled).
          LEDon();
          // LED on stepwise proportional to temp pot setting.
          // Small number of steps (3) should help make positioning more obvious.
          const uint8_t wt = tempControl->getWARMTargetC();
          // Makes vtiny|tiny|medium flash for cool|OK|warm temperature target.
          // Stick to minimum length flashes to save energy unless just touched.
          if(minimiseOnTime || tempControl->isEcoTemperature(wt)) { veryTinyPause(); }
          else if(!tempControl->isComfortTemperature(wt)) { tinyPause(); }
          else { mediumPause(); }

          // Second flash to indicate actually calling for heat,
          // or likely to be calling for heat while interacting with the controls, to give fast user feedback (TODO-695).
          if((enhancedUIFeedback && valveController->isUnderTarget()) ||
              valveController->isCallingForHeat() ||
              valveMode->inBakeMode())
            {
            LEDoff();
            offPause(); // V0.09 was mediumPause().
            LEDon(); // flash
            // Stick to minimum length flashes to save energy unless just touched.
            if(minimiseOnTime || tempControl->isEcoTemperature(wt)) { veryTinyPause(); }
            else if(!tempControl->isComfortTemperature(wt)) { veryTinyPause(); veryTinyPause(); }
            else { tinyPause(); }

            if(valveMode->inBakeMode())
              {
              // Third (lengthened) flash to indicate BAKE mode.
              LEDoff();
              mediumPause(); // Note different flash off time to try to distinguish this last flash.
              LEDon();
              // Makes tiny|small|medium flash for eco|OK|comfort temperature target.
              // Stick to minimum length flashes to save energy unless just touched.
              if(minimiseOnTime || tempControl->isEcoTemperature(wt)) { veryTinyPause(); }
              else if(!tempControl->isComfortTemperature(wt)) { smallPause(); }
              else { mediumPause(); }
              }
            }
          }
        }

      // Even in FROST mode, and if actually calling for heat (eg opening the rad valve significantly, etc)
      // then emit a tiny double flash on every 4th tick.
      // This call for heat may be frost protection or pre-warming / anticipating demand.
      // DHD20130528: new 4th-tick flash in FROST mode...
      // DHD20131223: only flash if the room is not dark (ie or if no working light sensor) so as to save energy and avoid disturbing sleep, etc.
      else if(forthTick &&
              !ambLight->isRoomDark() &&
              valveController->isCallingForHeat() /* &&
              NominalRadValve.isControlledValveReallyOpen() */ )
        {
        // Double flash every 4th tick indicates call for heat while in FROST MODE (matches call for heat in WARM mode).
        LEDon(); // flash
        veryTinyPause();
        LEDoff();
        offPause();
        LEDon(); // flash
        veryTinyPause();
        }
      }

    // Ensure that the main UI LED is forced off unconditionally at least once each cycle.
    LEDoff();

    // Handle LEARN buttons (etc) in derived classes.
    // Also can be used to handle any simple user schedules.
    handleOtherUserControls(statusChange);

    const bool statusChanged = statusChange;
    statusChange = false; // Potential race.
    return(statusChanged);
    }

#endif // ModeButtonAndPotActuatorPhysicalUI_DEFINED

    }
<commit_msg>Comment<commit_after>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the Licence is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the Licence for the
specific language governing permissions and limitations
under the Licence.

Author(s) / Copyright (s): Damon Hart-Davis 2016
*/

/*
 * OpenTRV radiator valve physical UI controls and output as an actuator.
 *
 * A base class, a null class, and one or more implementations are provided
 * for different stock behaviour with different hardware.
 *
 * A mixture of template and constructor parameters
 * is used to configure the different types.
 */

#include <stddef.h>
#include <stdint.h>
#include <OTV0p2Base.h>

#include <OTRadValve_ActuatorPhysicalUI.h>

// Use namespaces to help avoid collisions.
namespace OTRadValve
    {

#if defined(ModeButtonAndPotActuatorPhysicalUI_DEFINED)

// Record local manual operation of a physical UI control, eg not remote or via CLI.
// Marks room as occupied amongst other things.
// To be thread-/ISR- safe, everything that this touches or calls must be.
// Thread-safe.
void ModeButtonAndPotActuatorPhysicalUI::markUIControlUsed()
    {
    statusChange = true; // Note user interaction with the system.
    uiTimeoutM.store(UI_DEFAULT_RECENT_USE_TIMEOUT_M); // Ensure that UI controls are kept 'warm' for a little while.
// FIXME
//  #if defined(ENABLE_UI_WAKES_CLI)
//    // Make CLI active for a while (at some slight possibly-significant energy cost).
//    resetCLIActiveTimer(); // Thread-safe.
//  #endif
    // User operation of physical controls is strong indication of presence.
    occupancy->markAsOccupied(); // Thread-safe.

    // Capture possible (near) peak of stack usage, eg when called from ISR.
    OTV0P2BASE::MemoryChecks::recordIfMinSP();
    }

// Record significant local manual operation of a physical UI control, eg not remote or via CLI.
// Marks room as occupied amongst other things.
// As markUIControlUsed() but likely to generate some feedback to the user, ASAP.
// Thread-safe.
void ModeButtonAndPotActuatorPhysicalUI::markUIControlUsedSignificant()
    {
    // Provide some instant visual feedback if possible.
    if(NULL != safeISRLEDonOpt) { safeISRLEDonOpt(); }
    // Flag up need for feedback.
    significantUIOp = true;
    // Do main UI-touched work.
    markUIControlUsed();
    }

// Call this nominally on even numbered seconds to allow the UI to operate.
// In practice call early once per 2s major cycle.
// Should never be skipped, so as to allow the UI to remain responsive.
// Runs in 350ms or less; usually takes only a few milliseconds or microseconds.
// Returns a non-zero value if the user interacted with the system, and maybe caused a status change.
// Note that the return value represents a bool!
// NOTE: since this is on the minimum idle-loop code path, minimise CPU cycles, esp in frost mode.
// Replaces: bool tickUI(uint_fast8_t sec).
uint8_t ModeButtonAndPotActuatorPhysicalUI::read()
    {
    // True on every 4th tick/call, ie every ~8 seconds.
    const bool forthTick = !((++tickCount) & 3); // True on every 4th tick.

    // Perform any once-per-minute-ish operations, every 32 ticks.
    const bool sec0 = (0 == (tickCount & 0x1f));
    if(sec0)
      { OTV0P2BASE::safeDecIfNZWeak(uiTimeoutM); }

    // Provide enhanced feedback when the has been very recent interaction with the UI,
    // since the user is still quite likely to be continuing.
    const bool enhancedUIFeedback = veryRecentUIControlUse();

    if(NULL != tempPotOpt)
        {
        // Force relatively-frequent re-read of temp pot UI device
        // and if there has been recent UI manual controls activity,
        // to keep the valve UI responsive.
    //  #if !defined(ENABLE_FAST_TEMP_POT_SAMPLING) || !defined(ENABLE_OCCUPANCY_SUPPORT)
    //    if(enhancedUIFeedback || forthTick)
    //  #else
    //    // Even more responsive at some possible energy cost...
        // Polls pot on every tick unless the room has been vacant for a day or two or is in FROST mode.
        if(enhancedUIFeedback || forthTick || (valveMode->inWarmMode() && !occupancy->longLongVacant()))
    //  #endif
          {
          tempPotOpt->read();
      #if 0 && defined(DEBUG)
            DEBUG_SERIAL_PRINT_FLASHSTRING("TP");
            DEBUG_SERIAL_PRINT(TempPot.getRaw());
            DEBUG_SERIAL_PRINTLN();
      #endif
          // Force to FROST mode (and cancel any erroneous BAKE, etc) when at FROST end of dial.
          const bool isLo = tempPotOpt->isAtLoEndStop();
          if(isLo) { valveMode->setWarmModeDebounced(false); }
          // Feed back significant change in pot position, ie at temperature boundaries.
          // Synthesise a target temperature that distinguishes the end stops
          // (FROST as one below minimum, BAKE as one above maximum)
          // from all other valid temperatures and the initial '0' state.
          const uint8_t nominalWarmTarget = isLo ? (tempControl->getMinWARMTargetC() - 1) :
              (tempPotOpt->isAtHiEndStop() ? (tempControl->getMaxWARMTargetC() + 1) :
              tempControl->getWARMTargetC());
          // Record of 'last' nominalWarmTarget; initially 0.
          if(nominalWarmTarget != lastNominalWarmTarget)
            {
            // Note if a boundary was crossed, ignoring any false 'start-up' transient.
            if(0 != lastNominalWarmTarget) { significantUIOp = true; }
            else
              {
              // On initialisation, if the dial is not at FROST,
              // force the device straight to WARM mode.
              if(!isLo) { valveMode->setWarmModeDebounced(true); }
              }
      #if 1 && defined(DEBUG)
            DEBUG_SERIAL_PRINT_FLASHSTRING("WT");
            DEBUG_SERIAL_PRINT(nominalWarmTarget);
            DEBUG_SERIAL_PRINTLN();
      #endif
            // Maximum number of clicks rather than collapsing them all to one.
            constexpr uint8_t maxClicks = 10;
            if(OTV0P2BASE::fnabsdiff(lastNominalWarmTarget, nominalWarmTarget) > maxClicks)
                { lastNominalWarmTarget = nominalWarmTarget; }
            // If small difference show it in successive ticks of feedback to the user (TODO-1045),
            // ie turning dial through xC of temperature change should produce x wiggles/flashes.
            else if(lastNominalWarmTarget < nominalWarmTarget) { ++lastNominalWarmTarget; }
            else { --lastNominalWarmTarget; }
            }
          }
      }

    // Provide extra user feedback for significant UI actions...
    if(significantUIOp) { userOpFeedback(); }

    // Support cycling through modes polling the MODE button.
    // If MODE button is active skip normal LED UI activity.
    if(!pollMODEButton())
      {
      // Keep reporting UI status if the user has just touched the unit in some way or UI feedback is enhanced.
      const bool justTouched = statusChange || enhancedUIFeedback;

      // Capture battery state if available.
      const bool batteryLow = (NULL != lowBattOpt) && lowBattOpt->isSupplyVoltageLow();

      // Minimise LED on duration unless UI just touched, or if battery low (TODO-963).
      const bool minimiseOnTime = (!justTouched) || batteryLow;

      // Mode button not pressed: indicate current mode with flash(es); more flashes if actually calling for heat.
      // Force display while UI controls are being used, eg to indicate temp pot position.
      if(justTouched || valveMode->inWarmMode()) // Generate flash(es) if in WARM mode or fiddling with UI other than Mode button.
        {
        // DHD20131223: only flash if the room not known to be dark so as to save energy and avoid disturbing sleep, etc.
        // In this case force resample of light level frequently in case user turns light on eg to operate unit.
        // Do show LED flash if user has recently operated controls (other than mode button) manually.
        // Flash infrequently if no recently operated controls and not in BAKE mode and not actually calling for heat;
        // this is to conserve batteries for those people who leave the valves in WARM mode all the time.
        if(justTouched ||
           ((forthTick
               || valveController->isCallingForHeat()
               || valveMode->inBakeMode()) && !ambLight->isRoomDark()))
          {
          // First flash to indicate WARM mode (or pot being twiddled).
          LEDon();
          // LED on stepwise proportional to temp pot setting.
          // Small number of steps (3) should help make positioning more obvious.
          const uint8_t wt = tempControl->getWARMTargetC();
          // Makes vtiny|tiny|medium flash for cool|OK|warm temperature target.
          // Stick to minimum length flashes to save energy unless just touched.
          if(minimiseOnTime || tempControl->isEcoTemperature(wt)) { veryTinyPause(); }
          else if(!tempControl->isComfortTemperature(wt)) { tinyPause(); }
          else { mediumPause(); }

          // Second flash to indicate actually calling for heat,
          // or likely to be calling for heat while interacting with the controls, to give fast user feedback (TODO-695).
          if((enhancedUIFeedback && valveController->isUnderTarget()) ||
              valveController->isCallingForHeat() ||
              valveMode->inBakeMode())
            {
            LEDoff();
            offPause(); // V0.09 was mediumPause().
            LEDon(); // flash
            // Stick to minimum length flashes to save energy unless just touched.
            if(minimiseOnTime || tempControl->isEcoTemperature(wt)) { veryTinyPause(); }
            else if(!tempControl->isComfortTemperature(wt)) { veryTinyPause(); veryTinyPause(); }
            else { tinyPause(); }

            if(valveMode->inBakeMode())
              {
              // Third (lengthened) flash to indicate BAKE mode.
              LEDoff();
              mediumPause(); // Note different flash off time to try to distinguish this last flash.
              LEDon();
              // Makes tiny|small|medium flash for eco|OK|comfort temperature target.
              // Stick to minimum length flashes to save energy unless just touched.
              if(minimiseOnTime || tempControl->isEcoTemperature(wt)) { veryTinyPause(); }
              else if(!tempControl->isComfortTemperature(wt)) { smallPause(); }
              else { mediumPause(); }
              }
            }
          }
        }

      // Even in FROST mode, and if actually calling for heat (eg opening the rad valve significantly, etc)
      // then emit a tiny double flash on every 4th tick.
      // This call for heat may be frost protection or pre-warming / anticipating demand.
      // DHD20130528: new 4th-tick flash in FROST mode...
      // DHD20131223: only flash if the room is not dark (ie or if no working light sensor) so as to save energy and avoid disturbing sleep, etc.
      else if(forthTick &&
              !ambLight->isRoomDark() &&
              valveController->isCallingForHeat() /* &&
              NominalRadValve.isControlledValveReallyOpen() */ )
        {
        // Double flash every 4th tick indicates call for heat while in FROST MODE (matches call for heat in WARM mode).
        LEDon(); // flash
        veryTinyPause();
        LEDoff();
        offPause();
        LEDon(); // flash
        veryTinyPause();
        }
      }

    // Ensure that the main UI LED is forced off unconditionally at least once each cycle.
    LEDoff();

    // Handle LEARN buttons (etc) in derived classes.
    // Also can be used to handle any simple user schedules.
    handleOtherUserControls(statusChange);

    const bool statusChanged = statusChange;
    statusChange = false; // Potential race.
    return(statusChanged);
    }

#endif // ModeButtonAndPotActuatorPhysicalUI_DEFINED

    }
<|endoftext|>


<commit_before>#include "PresetChooser.hpp"
#include "misc_ui.hpp"

namespace Slic3r { namespace GUI {

PresetChooser::PresetChooser(wxWindow* parent, std::weak_ptr<Print> print) : PresetChooser(parent, print, SLIC3RAPP->settings(), SLIC3RAPP->presets) {}

PresetChooser::PresetChooser(wxWindow* parent, std::weak_ptr<Print> print, Settings* external_settings, preset_store& external_presets) :
    wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, ""),
    _local_sizer(new wxFlexGridSizer(3,3,1,2)), _parent(parent), _settings(external_settings), _print(print), _presets(external_presets)
{
    _local_sizer->AddGrowableCol(1, 1);
    _local_sizer->SetFlexibleDirection(wxHORIZONTAL);

    for (auto group : { preset_t::Print, preset_t::Material, preset_t::Printer }) {
        wxString name = "";
        switch(group) {
            case preset_t::Print:
                name << _("Print settings:");
                break;
            case preset_t::Material:
                name << _("Material:");
                break;
            case preset_t::Printer:
                name << _("Printer:");
                break;
            default:
                break;
        }
//        auto* text {new wxStaticText(this, wxID_ANY, name, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT)};
//        text->SetFont(ui_settings->small_font());

        auto* choice {new wxBitmapComboBox(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY)};
        this->preset_choosers[get_preset(group)].push_back(choice);

        // setup listener.
        // On a combobox event, puts a call to _on_change_combobox() on the evt_idle stack.
        choice->Bind(wxEVT_COMBOBOX,
            [=](wxCommandEvent& e) {
                wxTheApp->CallAfter([=]() { this->_on_change_combobox(group, choice);} );
            });
    }

    this->SetSizer(_local_sizer);
}

void PresetChooser::load(std::array<Presets, preset_types> presets) {

    wxString selected_printer_name {""};
    for (const auto& group : { preset_t::Printer, preset_t::Material, preset_t::Print }) {
        auto current_list = presets.at(get_preset(group));
        // Filter out profiles not compatible with this printer
        current_list = grep(presets.at(get_preset(group)), [selected_printer_name] (const Preset& x) -> bool { return x.compatible(selected_printer_name); });

        // show default names if no other presets visible.
        if (current_list.size() > 1) {
            current_list = grep(presets.at(get_preset(group)), [] (const Preset& x) -> bool { return !x.default_preset; });
        }

        // # Read the current defaults from the settings file
        const auto& settings_defaults {_settings->default_presets.at(get_preset(group))};

        size_t i {0};
        std::vector<std::string> preset_names {};
        // populate the chooser
        for (auto* chooser :  this->preset_choosers[get_preset(group)]) {
            chooser->Clear();
            for (auto preset : current_list) {
                wxBitmap bitmap;
                switch (group) {
                    case preset_t::Print:
                        bitmap = wxBitmap(var("cog.png"), wxBITMAP_TYPE_PNG);
                        break;
                    case preset_t::Material:
                        if (auto config =  preset.config().lock()) {
                            if (preset.default_preset || !config->has("filament_colour"))
                                bitmap = wxBitmap(var("spool.png"), wxBITMAP_TYPE_PNG);
                        } else { // fall back if for some reason the config is dead.
                            bitmap = wxBitmap(var("spool.png"), wxBITMAP_TYPE_PNG);
                        }
                        break;
                    case preset_t::Printer:
                        bitmap = wxBitmap(var("printer_empty.png"), wxBITMAP_TYPE_PNG);
                        break;
                    default: break;
                }
                chooser->Append(preset.name, bitmap);
                __chooser_names[get_preset(group)].push_back(preset.name);
            }
            // Apply default options from settings
            bool updated_from_settings = false;
            if (settings_defaults.size() > i) { // only apply if there is a value from Settings
                updated_from_settings = this->select_preset_by_name(settings_defaults.at(i), chooser);
            }

            if (!updated_from_settings) // default
                chooser->SetSelection(0);

            wxString selected_preset { chooser->GetString(chooser->GetSelection()) };
            if (group == preset_t::Printer) {
                selected_printer_name = selected_preset;
            }

            ++i;
        }
        this->_update_preset_settings(group);
    }
}

bool PresetChooser::select_preset_by_name(wxString name, preset_t group, size_t index = 0) {
    auto& ps_list = this->preset_choosers.at(get_preset(group));
    bool updated = false;
    if (ps_list.size() > index) {
        updated = select_preset_by_name(name, ps_list.at(index));
    }
    if (updated)
        this->_on_select_preset(group);
    return updated;
}

bool PresetChooser::select_preset_by_name(wxString name, wxBitmapComboBox* chooser) {
    auto index { chooser->FindString(name) };
    if (index != wxNOT_FOUND) {
        chooser->SetSelection(index);
        return true;
    }
    return false;
}

void PresetChooser::_update_preset_settings(preset_t preset) {
    auto& settings_presets {_settings->default_presets.at(get_preset(preset))};
    settings_presets.clear(); // make sure previous contents are deconstructed
    settings_presets = this->_get_selected_presets(preset);

}

void PresetChooser::_on_select_preset(preset_t preset) {
    // update settings store
    this->_update_preset_settings(preset);
    // save settings
    _settings->save_settings();
    if (preset == preset_t::Printer) {
        this->load(); // reload print/filament settings to honor compatible printers
    }
}

bool PresetChooser::prompt_unsaved_changes() {
    return true;
}

std::vector<wxString> PresetChooser::_get_selected_presets(preset_t group) const {
        const auto& choosers { this->preset_choosers[get_preset(group)] };
        std::vector<wxString> selected;
        selected.reserve(choosers.size());

        for (auto* chooser :  choosers) {
            selected.push_back(chooser->GetString(chooser->GetSelection()));
        }
        return selected;
}

wxString PresetChooser::_get_selected_preset(preset_t group, size_t index) const {
    auto selected { this->_get_selected_presets(group) };
    if (index > selected.size()) { return wxString(""); }
    return selected.at(index);
}
void PresetChooser::_on_change_combobox(preset_t preset, wxBitmapComboBox* choice) {

    // Prompt for unsaved changes and undo selections if cancelled and return early
    // Callback to close preset editor tab, close editor tabs, reload presets.
    //
    if (!this->prompt_unsaved_changes()) return;
    wxTheApp->CallAfter([this,preset]()
    {
        this->_on_select_preset(preset);
        // reload presets; removes the modified mark
        this->load();
    });
    /*
    sub _on_change_combobox {
    my ($self, $group, $choice) = @_;

    if (0) {
        # This code is disabled because wxPerl doesn't provide GetCurrentSelection
        my $current_name = $self->{preset_choosers_names}{$choice}[$choice->GetCurrentSelection];
        my $current = first { $_->name eq $current_name } @{wxTheApp->presets->{$group}};
        if (!$current->prompt_unsaved_changes($self)) {
            # Restore the previous one
            $choice->SetSelection($choice->GetCurrentSelection);
            return;
        }
    } else {
        return 0 if !$self->prompt_unsaved_changes;
    }
    wxTheApp->CallAfter(sub {
        # Close the preset editor tab if any
        if (exists $self->GetFrame->{preset_editor_tabs}{$group}) {
            my $tabpanel = $self->GetFrame->{tabpanel};
            $tabpanel->DeletePage($tabpanel->GetPageIndex($self->GetFrame->{preset_editor_tabs}{$group}));
            delete $self->GetFrame->{preset_editor_tabs}{$group};
            $tabpanel->SetSelection(0); # without this, a newly created tab will not be selected by wx
        }

        $self->_on_select_preset($group);

        # This will remove the "(modified)" mark from any dirty preset handled here.
        $self->load_presets;
    });
}
*/
}

}} // Slic3r::GUI
<commit_msg>add text and button back to sizer.<commit_after>#include "PresetChooser.hpp"
#include "misc_ui.hpp"

namespace Slic3r { namespace GUI {

PresetChooser::PresetChooser(wxWindow* parent, std::weak_ptr<Print> print) : PresetChooser(parent, print, SLIC3RAPP->settings(), SLIC3RAPP->presets) {}

PresetChooser::PresetChooser(wxWindow* parent, std::weak_ptr<Print> print, Settings* external_settings, preset_store& external_presets) :
    wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, ""),
    _local_sizer(new wxFlexGridSizer(3,3,1,2)), _parent(parent), _settings(external_settings), _print(print), _presets(external_presets)
{
    _local_sizer->AddGrowableCol(1, 1);
    _local_sizer->SetFlexibleDirection(wxHORIZONTAL);

    for (auto group : { preset_t::Print, preset_t::Material, preset_t::Printer }) {
        wxString name = "";
        switch(group) {
            case preset_t::Print:
                name << _("Print settings:");
                break;
            case preset_t::Material:
                name << _("Material:");
                break;
            case preset_t::Printer:
                name << _("Printer:");
                break;
            default:
                break;
        }
        auto* text {new wxStaticText(this, wxID_ANY, name, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT)};
        text->SetFont(_settings->small_font());

        auto* choice {new wxBitmapComboBox(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY)};
        this->preset_choosers[get_preset(group)].push_back(choice);

        // Settings button
        auto* settings_btn {new wxBitmapButton(this, wxID_ANY, wxBitmap(var("cog.png"), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxDefaultSize, wxBORDER_NONE)};

        this->_local_sizer->Add(text, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
        this->_local_sizer->Add(choice, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxBOTTOM, 0);
        this->_local_sizer->Add(settings_btn, 0, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxLEFT, 3);

        // setup listener.
        // On a combobox event, puts a call to _on_change_combobox() on the evt_idle stack.
        choice->Bind(wxEVT_COMBOBOX,
            [=](wxCommandEvent& e) {
                wxTheApp->CallAfter([=]() { this->_on_change_combobox(group, choice);} );
            });
    }

    this->SetSizer(_local_sizer);
}

void PresetChooser::load(std::array<Presets, preset_types> presets) {

    wxString selected_printer_name {""};
    for (const auto& group : { preset_t::Printer, preset_t::Material, preset_t::Print }) {
        auto current_list = presets.at(get_preset(group));
        // Filter out profiles not compatible with this printer
        current_list = grep(presets.at(get_preset(group)), [selected_printer_name] (const Preset& x) -> bool { return x.compatible(selected_printer_name); });

        // show default names if no other presets visible.
        if (current_list.size() > 1) {
            current_list = grep(presets.at(get_preset(group)), [] (const Preset& x) -> bool { return !x.default_preset; });
        }

        // # Read the current defaults from the settings file
        const auto& settings_defaults {_settings->default_presets.at(get_preset(group))};

        size_t i {0};
        std::vector<std::string> preset_names {};
        // populate the chooser
        for (auto* chooser :  this->preset_choosers[get_preset(group)]) {
            chooser->Clear();
            for (auto preset : current_list) {
                wxBitmap bitmap;
                switch (group) {
                    case preset_t::Print:
                        bitmap = wxBitmap(var("cog.png"), wxBITMAP_TYPE_PNG);
                        break;
                    case preset_t::Material:
                        if (auto config =  preset.config().lock()) {
                            if (preset.default_preset || !config->has("filament_colour"))
                                bitmap = wxBitmap(var("spool.png"), wxBITMAP_TYPE_PNG);
                        } else { // fall back if for some reason the config is dead.
                            bitmap = wxBitmap(var("spool.png"), wxBITMAP_TYPE_PNG);
                        }
                        break;
                    case preset_t::Printer:
                        bitmap = wxBitmap(var("printer_empty.png"), wxBITMAP_TYPE_PNG);
                        break;
                    default: break;
                }
                chooser->Append(preset.name, bitmap);
                __chooser_names[get_preset(group)].push_back(preset.name);
            }
            // Apply default options from settings
            bool updated_from_settings = false;
            if (settings_defaults.size() > i) { // only apply if there is a value from Settings
                updated_from_settings = this->select_preset_by_name(settings_defaults.at(i), chooser);
            }

            if (!updated_from_settings) // default
                chooser->SetSelection(0);

            wxString selected_preset { chooser->GetString(chooser->GetSelection()) };
            if (group == preset_t::Printer) {
                selected_printer_name = selected_preset;
            }

            ++i;
        }
        this->_update_preset_settings(group);
    }
}

bool PresetChooser::select_preset_by_name(wxString name, preset_t group, size_t index = 0) {
    auto& ps_list = this->preset_choosers.at(get_preset(group));
    bool updated = false;
    if (ps_list.size() > index) {
        updated = select_preset_by_name(name, ps_list.at(index));
    }
    if (updated)
        this->_on_select_preset(group);
    return updated;
}

bool PresetChooser::select_preset_by_name(wxString name, wxBitmapComboBox* chooser) {
    auto index { chooser->FindString(name) };
    if (index != wxNOT_FOUND) {
        chooser->SetSelection(index);
        return true;
    }
    return false;
}

void PresetChooser::_update_preset_settings(preset_t preset) {
    auto& settings_presets {_settings->default_presets.at(get_preset(preset))};
    settings_presets.clear(); // make sure previous contents are deconstructed
    settings_presets = this->_get_selected_presets(preset);

}

void PresetChooser::_on_select_preset(preset_t preset) {
    // update settings store
    this->_update_preset_settings(preset);
    // save settings
    _settings->save_settings();
    if (preset == preset_t::Printer) {
        this->load(); // reload print/filament settings to honor compatible printers
    }
}

bool PresetChooser::prompt_unsaved_changes() {
    return true;
}

std::vector<wxString> PresetChooser::_get_selected_presets(preset_t group) const {
        const auto& choosers { this->preset_choosers[get_preset(group)] };
        std::vector<wxString> selected;
        selected.reserve(choosers.size());

        for (auto* chooser :  choosers) {
            selected.push_back(chooser->GetString(chooser->GetSelection()));
        }
        return selected;
}

wxString PresetChooser::_get_selected_preset(preset_t group, size_t index) const {
    auto selected { this->_get_selected_presets(group) };
    if (index > selected.size()) { return wxString(""); }
    return selected.at(index);
}
void PresetChooser::_on_change_combobox(preset_t preset, wxBitmapComboBox* choice) {

    // Prompt for unsaved changes and undo selections if cancelled and return early
    // Callback to close preset editor tab, close editor tabs, reload presets.
    //
    if (!this->prompt_unsaved_changes()) return;
    wxTheApp->CallAfter([this,preset]()
    {
        this->_on_select_preset(preset);
        // reload presets; removes the modified mark
        this->load();
    });
    /*
    sub _on_change_combobox {
    my ($self, $group, $choice) = @_;

    if (0) {
        # This code is disabled because wxPerl doesn't provide GetCurrentSelection
        my $current_name = $self->{preset_choosers_names}{$choice}[$choice->GetCurrentSelection];
        my $current = first { $_->name eq $current_name } @{wxTheApp->presets->{$group}};
        if (!$current->prompt_unsaved_changes($self)) {
            # Restore the previous one
            $choice->SetSelection($choice->GetCurrentSelection);
            return;
        }
    } else {
        return 0 if !$self->prompt_unsaved_changes;
    }
    wxTheApp->CallAfter(sub {
        # Close the preset editor tab if any
        if (exists $self->GetFrame->{preset_editor_tabs}{$group}) {
            my $tabpanel = $self->GetFrame->{tabpanel};
            $tabpanel->DeletePage($tabpanel->GetPageIndex($self->GetFrame->{preset_editor_tabs}{$group}));
            delete $self->GetFrame->{preset_editor_tabs}{$group};
            $tabpanel->SetSelection(0); # without this, a newly created tab will not be selected by wx
        }

        $self->_on_select_preset($group);

        # This will remove the "(modified)" mark from any dirty preset handled here.
        $self->load_presets;
    });
}
*/
}

}} // Slic3r::GUI
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2015 Realm Inc.
//
// 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 "results.hpp"

#include <stdexcept>

using namespace realm;

#ifdef __has_cpp_attribute
#define REALM_HAS_CCP_ATTRIBUTE(attr) __has_cpp_attribute(attr)
#else
#define REALM_HAS_CCP_ATTRIBUTE(attr) 0
#endif

#if REALM_HAS_CCP_ATTRIBUTE(clang::fallthrough)
#define REALM_FALLTHROUGH [[clang::fallthrough]]
#else
#define REALM_FALLTHROUGH
#endif

Results::Results(SharedRealm r, Query q, SortOrder s)
: m_realm(std::move(r))
, m_query(std::move(q))
, m_table(m_query.get_table().get())
, m_sort(std::move(s))
, m_mode(Mode::Query)
{
}

Results::Results(SharedRealm r, Table& table)
: m_realm(std::move(r))
, m_table(&table)
, m_mode(Mode::Table)
{
}

void Results::validate_read() const
{
    if (m_realm)
        m_realm->verify_thread();
    if (m_table && !m_table->is_attached())
        throw InvalidatedException();
}

void Results::validate_write() const
{
    validate_read();
    if (!m_realm || !m_realm->is_in_transaction())
        throw InvalidTransactionException("Must be in a write transaction");
}

size_t Results::size()
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty: return 0;
        case Mode::Table: return m_table->size();
        case Mode::Query: return m_query.count();
        case Mode::TableView:
            update_tableview();
            return m_table_view.size();
    }
    REALM_UNREACHABLE();
}

RowExpr Results::get(size_t row_ndx)
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty: break;
        case Mode::Table:
            if (row_ndx < m_table->size())
                return m_table->get(row_ndx);
            break;
        case Mode::Query:
        case Mode::TableView:
            update_tableview();
            if (row_ndx < m_table_view.size())
                return m_table_view.get(row_ndx);
            break;
    }

    throw OutOfBoundsIndexException{row_ndx, size()};
}

util::Optional<RowExpr> Results::first()
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty:
            return none;
        case Mode::Table:
            return m_table->size() == 0 ? util::none : util::Optional<RowExpr>(m_table->front());
        case Mode::Query:
            update_tableview();
            REALM_FALLTHROUGH;
        case Mode::TableView:
            return m_table_view.size() == 0 ? util::none : util::Optional<RowExpr>(m_table_view.front());
    }
    REALM_UNREACHABLE();
}

util::Optional<RowExpr> Results::last()
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty:
            return none;
        case Mode::Table:
            return m_table->size() == 0 ? util::none : util::Optional<RowExpr>(m_table->back());
        case Mode::Query:
            update_tableview();
            REALM_FALLTHROUGH;
        case Mode::TableView:
            return m_table_view.size() == 0 ? util::none : util::Optional<RowExpr>(m_table_view.back());
    }
    REALM_UNREACHABLE();
}

void Results::update_tableview()
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty:
        case Mode::Table:
            return;
        case Mode::Query:
            m_table_view = m_query.find_all();
            if (m_sort) {
                m_table_view.sort(m_sort.columnIndices, m_sort.ascending);
            }
            m_mode = Mode::TableView;
            break;
        case Mode::TableView:
            m_table_view.sync_if_needed();
            break;
    }
}

size_t Results::index_of(Row const& row)
{
    validate_read();
    if (!row) {
        throw DetatchedAccessorException{};
    }
    if (m_table && row.get_table() != m_table) {
        throw IncorrectTableException{
            ObjectStore::object_type_for_table_name(m_table->get_name()),
            ObjectStore::object_type_for_table_name(row.get_table()->get_name())};
    }
    return index_of(row.get_index());
}

size_t Results::index_of(size_t row_ndx)
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty:
            return not_found;
        case Mode::Table:
            return row_ndx;
        case Mode::Query:
            if (!m_sort)
                return m_query.count(row_ndx, row_ndx + 1) ? m_query.count(0, row_ndx) : not_found;
            REALM_FALLTHROUGH;
        case Mode::TableView:
            update_tableview();
            return m_table_view.find_by_source_ndx(row_ndx);
    }
    REALM_UNREACHABLE();
}

template<typename Int, typename Float, typename Double, typename DateTime>
util::Optional<Mixed> Results::aggregate(size_t column, bool return_none_for_empty,
                                         Int agg_int, Float agg_float,
                                         Double agg_double, DateTime agg_datetime)
{
    validate_read();
    if (!m_table)
        return none;
    if (column > m_table->get_column_count())
        throw OutOfBoundsIndexException{column, m_table->get_column_count()};

    auto do_agg = [&](auto const& getter) -> util::Optional<Mixed> {
        switch (m_mode) {
            case Mode::Empty:
                return none;
            case Mode::Table:
                if (return_none_for_empty && m_table->size() == 0)
                    return none;
                return util::Optional<Mixed>(getter(*m_table));
            case Mode::Query:
            case Mode::TableView:
                this->update_tableview();
                if (return_none_for_empty && m_table_view.size() == 0)
                    return none;
                return util::Optional<Mixed>(getter(m_table_view));
        }
        REALM_UNREACHABLE();
    };

    switch (m_table->get_column_type(column))
    {
        case type_DateTime: return do_agg(agg_datetime);
        case type_Double: return do_agg(agg_double);
        case type_Float: return do_agg(agg_float);
        case type_Int: return do_agg(agg_int);
        default:
            throw UnsupportedColumnTypeException{column, m_table};
    }
}

util::Optional<Mixed> Results::max(size_t column)
{
    return aggregate(column, true,
                     [=](auto const& table) { return table.maximum_int(column); },
                     [=](auto const& table) { return table.maximum_float(column); },
                     [=](auto const& table) { return table.maximum_double(column); },
                     [=](auto const& table) { return table.maximum_datetime(column); });
}

util::Optional<Mixed> Results::min(size_t column)
{
    return aggregate(column, true,
                     [=](auto const& table) { return table.minimum_int(column); },
                     [=](auto const& table) { return table.minimum_float(column); },
                     [=](auto const& table) { return table.minimum_double(column); },
                     [=](auto const& table) { return table.minimum_datetime(column); });
}

util::Optional<Mixed> Results::sum(size_t column)
{
    return aggregate(column, false,
                     [=](auto const& table) { return table.sum_int(column); },
                     [=](auto const& table) { return table.sum_float(column); },
                     [=](auto const& table) { return table.sum_double(column); },
                     [=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table}; });
}

util::Optional<Mixed> Results::average(size_t column)
{
    return aggregate(column, true,
                     [=](auto const& table) { return table.average_int(column); },
                     [=](auto const& table) { return table.average_float(column); },
                     [=](auto const& table) { return table.average_double(column); },
                     [=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table}; });
}

void Results::clear()
{
    switch (m_mode) {
        case Mode::Empty:
            return;
        case Mode::Table:
            validate_write();
            m_table->clear();
            break;
        case Mode::Query:
            // Not using Query:remove() because building the tableview and
            // clearing it is actually significantly faster
        case Mode::TableView:
            validate_write();
            update_tableview();
            m_table_view.clear();
            break;
    }
}

Query Results::get_query() const
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty:
        case Mode::Query:
        case Mode::TableView:
            return m_query;
        case Mode::Table:
            return m_table->where();
    }
    REALM_UNREACHABLE();
}

TableView Results::get_tableview()
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty:
            return {};
        case Mode::Query:
        case Mode::TableView:
            update_tableview();
            return m_table_view;
        case Mode::Table:
            return m_table->where().find_all();
    }
    REALM_UNREACHABLE();
}

StringData Results::get_object_type() const noexcept
{
    return ObjectStore::object_type_for_table_name(m_table->get_name());
}

Results Results::sort(realm::SortOrder&& sort) const
{
    return Results(m_realm, get_query(), std::move(sort));
}

Results Results::filter(Query&& q) const
{
    return Results(m_realm, get_query().and_query(q), get_sort());
}

Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table) {
    column_index = column;
    column_name = table->get_column_name(column);
    column_type = table->get_column_type(column);
}
<commit_msg>Use make_optional()<commit_after>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2015 Realm Inc.
//
// 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 "results.hpp"

#include <stdexcept>

using namespace realm;

#ifdef __has_cpp_attribute
#define REALM_HAS_CCP_ATTRIBUTE(attr) __has_cpp_attribute(attr)
#else
#define REALM_HAS_CCP_ATTRIBUTE(attr) 0
#endif

#if REALM_HAS_CCP_ATTRIBUTE(clang::fallthrough)
#define REALM_FALLTHROUGH [[clang::fallthrough]]
#else
#define REALM_FALLTHROUGH
#endif

Results::Results(SharedRealm r, Query q, SortOrder s)
: m_realm(std::move(r))
, m_query(std::move(q))
, m_table(m_query.get_table().get())
, m_sort(std::move(s))
, m_mode(Mode::Query)
{
}

Results::Results(SharedRealm r, Table& table)
: m_realm(std::move(r))
, m_table(&table)
, m_mode(Mode::Table)
{
}

void Results::validate_read() const
{
    if (m_realm)
        m_realm->verify_thread();
    if (m_table && !m_table->is_attached())
        throw InvalidatedException();
}

void Results::validate_write() const
{
    validate_read();
    if (!m_realm || !m_realm->is_in_transaction())
        throw InvalidTransactionException("Must be in a write transaction");
}

size_t Results::size()
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty: return 0;
        case Mode::Table: return m_table->size();
        case Mode::Query: return m_query.count();
        case Mode::TableView:
            update_tableview();
            return m_table_view.size();
    }
    REALM_UNREACHABLE();
}

RowExpr Results::get(size_t row_ndx)
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty: break;
        case Mode::Table:
            if (row_ndx < m_table->size())
                return m_table->get(row_ndx);
            break;
        case Mode::Query:
        case Mode::TableView:
            update_tableview();
            if (row_ndx < m_table_view.size())
                return m_table_view.get(row_ndx);
            break;
    }

    throw OutOfBoundsIndexException{row_ndx, size()};
}

util::Optional<RowExpr> Results::first()
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty:
            return none;
        case Mode::Table:
            return m_table->size() == 0 ? util::none : util::make_optional(m_table->front());
        case Mode::Query:
            update_tableview();
            REALM_FALLTHROUGH;
        case Mode::TableView:
            return m_table_view.size() == 0 ? util::none : util::make_optional(m_table_view.front());
    }
    REALM_UNREACHABLE();
}

util::Optional<RowExpr> Results::last()
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty:
            return none;
        case Mode::Table:
            return m_table->size() == 0 ? util::none : util::make_optional(m_table->back());
        case Mode::Query:
            update_tableview();
            REALM_FALLTHROUGH;
        case Mode::TableView:
            return m_table_view.size() == 0 ? util::none : util::make_optional(m_table_view.back());
    }
    REALM_UNREACHABLE();
}

void Results::update_tableview()
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty:
        case Mode::Table:
            return;
        case Mode::Query:
            m_table_view = m_query.find_all();
            if (m_sort) {
                m_table_view.sort(m_sort.columnIndices, m_sort.ascending);
            }
            m_mode = Mode::TableView;
            break;
        case Mode::TableView:
            m_table_view.sync_if_needed();
            break;
    }
}

size_t Results::index_of(Row const& row)
{
    validate_read();
    if (!row) {
        throw DetatchedAccessorException{};
    }
    if (m_table && row.get_table() != m_table) {
        throw IncorrectTableException{
            ObjectStore::object_type_for_table_name(m_table->get_name()),
            ObjectStore::object_type_for_table_name(row.get_table()->get_name())};
    }
    return index_of(row.get_index());
}

size_t Results::index_of(size_t row_ndx)
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty:
            return not_found;
        case Mode::Table:
            return row_ndx;
        case Mode::Query:
            if (!m_sort)
                return m_query.count(row_ndx, row_ndx + 1) ? m_query.count(0, row_ndx) : not_found;
            REALM_FALLTHROUGH;
        case Mode::TableView:
            update_tableview();
            return m_table_view.find_by_source_ndx(row_ndx);
    }
    REALM_UNREACHABLE();
}

template<typename Int, typename Float, typename Double, typename DateTime>
util::Optional<Mixed> Results::aggregate(size_t column, bool return_none_for_empty,
                                         Int agg_int, Float agg_float,
                                         Double agg_double, DateTime agg_datetime)
{
    validate_read();
    if (!m_table)
        return none;
    if (column > m_table->get_column_count())
        throw OutOfBoundsIndexException{column, m_table->get_column_count()};

    auto do_agg = [&](auto const& getter) -> util::Optional<Mixed> {
        switch (m_mode) {
            case Mode::Empty:
                return none;
            case Mode::Table:
                if (return_none_for_empty && m_table->size() == 0)
                    return none;
                return util::Optional<Mixed>(getter(*m_table));
            case Mode::Query:
            case Mode::TableView:
                this->update_tableview();
                if (return_none_for_empty && m_table_view.size() == 0)
                    return none;
                return util::Optional<Mixed>(getter(m_table_view));
        }
        REALM_UNREACHABLE();
    };

    switch (m_table->get_column_type(column))
    {
        case type_DateTime: return do_agg(agg_datetime);
        case type_Double: return do_agg(agg_double);
        case type_Float: return do_agg(agg_float);
        case type_Int: return do_agg(agg_int);
        default:
            throw UnsupportedColumnTypeException{column, m_table};
    }
}

util::Optional<Mixed> Results::max(size_t column)
{
    return aggregate(column, true,
                     [=](auto const& table) { return table.maximum_int(column); },
                     [=](auto const& table) { return table.maximum_float(column); },
                     [=](auto const& table) { return table.maximum_double(column); },
                     [=](auto const& table) { return table.maximum_datetime(column); });
}

util::Optional<Mixed> Results::min(size_t column)
{
    return aggregate(column, true,
                     [=](auto const& table) { return table.minimum_int(column); },
                     [=](auto const& table) { return table.minimum_float(column); },
                     [=](auto const& table) { return table.minimum_double(column); },
                     [=](auto const& table) { return table.minimum_datetime(column); });
}

util::Optional<Mixed> Results::sum(size_t column)
{
    return aggregate(column, false,
                     [=](auto const& table) { return table.sum_int(column); },
                     [=](auto const& table) { return table.sum_float(column); },
                     [=](auto const& table) { return table.sum_double(column); },
                     [=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table}; });
}

util::Optional<Mixed> Results::average(size_t column)
{
    return aggregate(column, true,
                     [=](auto const& table) { return table.average_int(column); },
                     [=](auto const& table) { return table.average_float(column); },
                     [=](auto const& table) { return table.average_double(column); },
                     [=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table}; });
}

void Results::clear()
{
    switch (m_mode) {
        case Mode::Empty:
            return;
        case Mode::Table:
            validate_write();
            m_table->clear();
            break;
        case Mode::Query:
            // Not using Query:remove() because building the tableview and
            // clearing it is actually significantly faster
        case Mode::TableView:
            validate_write();
            update_tableview();
            m_table_view.clear();
            break;
    }
}

Query Results::get_query() const
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty:
        case Mode::Query:
        case Mode::TableView:
            return m_query;
        case Mode::Table:
            return m_table->where();
    }
    REALM_UNREACHABLE();
}

TableView Results::get_tableview()
{
    validate_read();
    switch (m_mode) {
        case Mode::Empty:
            return {};
        case Mode::Query:
        case Mode::TableView:
            update_tableview();
            return m_table_view;
        case Mode::Table:
            return m_table->where().find_all();
    }
    REALM_UNREACHABLE();
}

StringData Results::get_object_type() const noexcept
{
    return ObjectStore::object_type_for_table_name(m_table->get_name());
}

Results Results::sort(realm::SortOrder&& sort) const
{
    return Results(m_realm, get_query(), std::move(sort));
}

Results Results::filter(Query&& q) const
{
    return Results(m_realm, get_query().and_query(q), get_sort());
}

Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table) {
    column_index = column;
    column_name = table->get_column_name(column);
    column_type = table->get_column_type(column);
}
<|endoftext|>


<commit_before>#include "painterapplication.hpp"
#include <fea/ui/sdl2windowbackend.hpp>
#include <fea/ui/sdl2inputbackend.hpp>
#include "texturemaker.hpp"

PainterApplication::PainterApplication() :
    mWindow(new fea::SDL2WindowBackend()),
    mInputHandler(new fea::SDL2InputBackend()),
    mRenderer(fea::Viewport({800, 600}, {0, 0}, fea::Camera({800.0f / 2.0f, 600.0f / 2.0f}))),
    mDisplayQuad({800, 600})
{
}

void PainterApplication::setup(const std::vector<std::string>& args)
{
    mWindow.create(fea::VideoMode(800, 600), "painter");
    mRenderer.setup();

    mOriginal = makeTexture("image.png");

    mDisplayQuad.setTexture(mOriginal);
    mDisplayQuad.setPosition({-800.0f / 2.0f, -600.0f / 2.0f});
}

void PainterApplication::loop()
{
    //input
    fea::Event event;

    while(mInputHandler.pollEvent(event))
    {
        if(event.type == fea::Event::KEYPRESSED)
        {
            if(event.key.code == fea::Keyboard::ESCAPE)
                quit();
        }
    }

    //process
    mPainter.paint(mOriginal, mResult, 1000);

    //output
    mRenderer.queue(mDisplayQuad);
    mRenderer.render();
    mWindow.swapBuffers();
}

void PainterApplication::destroy()
{
    mWindow.close();
}
<commit_msg>Made it render the output texture<commit_after>#include "painterapplication.hpp"
#include <fea/ui/sdl2windowbackend.hpp>
#include <fea/ui/sdl2inputbackend.hpp>
#include "texturemaker.hpp"

PainterApplication::PainterApplication() :
    mWindow(new fea::SDL2WindowBackend()),
    mInputHandler(new fea::SDL2InputBackend()),
    mRenderer(fea::Viewport({800, 600}, {0, 0}, fea::Camera({800.0f / 2.0f, 600.0f / 2.0f}))),
    mDisplayQuad({800, 600})
{
}

void PainterApplication::setup(const std::vector<std::string>& args)
{
    mWindow.create(fea::VideoMode(800, 600), "painter");
    mRenderer.setup();

    mOriginal = makeTexture("image.png");

    mDisplayQuad.setTexture(mResult);
    mDisplayQuad.setPosition({-800.0f / 2.0f, -600.0f / 2.0f});
}

void PainterApplication::loop()
{
    //input
    fea::Event event;

    while(mInputHandler.pollEvent(event))
    {
        if(event.type == fea::Event::KEYPRESSED)
        {
            if(event.key.code == fea::Keyboard::ESCAPE)
                quit();
        }
    }

    //process
    mPainter.paint(mOriginal, mResult, 1000);

    //output
    mRenderer.queue(mDisplayQuad);
    mRenderer.render();
    mWindow.swapBuffers();
}

void PainterApplication::destroy()
{
    mWindow.close();
}
<|endoftext|>