<commit_before>#include "Players/Minimax_AI.h"

#include <iostream>
#include <string>

#include "Players/Game_Tree_Node_Result.h"
#include "Game/Board.h"
#include "Game/Clock.h"
#include "Game/Game_Result.h"
#include "Moves/Move.h"

#include "Utility/String.h"
#include "Utility/Math.h"
#include "Utility/Random.h"

Minimax_AI::Minimax_AI()
{
}

//! Minimax_AI uses a variable-depth minimax algorithm with alpha-beta pruning.
//
//! The depth of the search is determined by how much time is available.
//! At first, equal time is allocated to each legal move for examination. Time
//! is overcommitted with the expectation that not all of the time will be used
//! due to search cutoffs due to alpha-beta pruning.
//! \param board The current state of the game.
//! \param clock The game clock telling how much time is left in the game.
const Move& Minimax_AI::choose_move(const Board& board, const Clock& clock) const
{
    // Erase data from previous board when starting new game
    if(board.game_record().size() <= 1)
    {
        principal_variation.clear();
        commentary.clear();
    }

    nodes_searched = 0;
    clock_start_time = clock.running_time_left();
    maximum_depth = 0;

    nodes_evaluated = 0;
    total_evaluation_time = 0.0;
    time_at_last_output = clock.running_time_left();

    const auto& legal_moves = board.legal_moves();
    if(legal_moves.size() == 1)
    {
        if(principal_variation.size() > 3 && principal_variation[1] == board.game_record().back())
        {
            // search_game_tree() assumes the principal variation starts
            // with the previous move of this player. If a move was forced,
            // then the principal variation needs to be updated to start with
            // the next move of this side after checking that the immediately
            // preceding move was the expected one.
            principal_variation.erase(principal_variation.begin(),
                                      principal_variation.begin() + 2);
        }
        else
        {
            principal_variation.clear();
        }

        commentary.push_back({commentary.empty() ? 0.0 : commentary.back().corrected_score(board.whose_turn()),
                              board.whose_turn(),
                              principal_variation});

        return *legal_moves.front(); // If there's only one legal move, take it.
    }

    // No useful principal variation if it doesn't include the next possible move
    // or if the opponent did not make the predicted next move.
    if(principal_variation.size() < 2 || principal_variation[1] != board.game_record().back())
    {
        principal_variation.clear();
    }

    auto time_to_use = time_to_examine(board, clock);

    // alpha = highest score found that opponent will allow
    Game_Tree_Node_Result alpha_start = {Math::lose_score,
                                         board.whose_turn(),
                                         {}};

    // beta = score that will cause opponent to choose a different prior move
    Game_Tree_Node_Result beta_start = {Math::win_score,
                                        board.whose_turn(),
                                        {}};

    auto result = search_game_tree(board,
                                   time_to_use,
                                   clock,
                                   1,
                                   alpha_start,
                                   beta_start,
                                   ! principal_variation.empty());

    if(board.thinking_mode() == CECP)
    {
        output_thinking_cecp(result, clock, board.whose_turn());
    }

    commentary.push_back(result);

    if(result.depth() > 2)
    {
        principal_variation = result.variation;
    }
    else
    {
        principal_variation.clear();
    }

    if(nodes_evaluated > 0)
    {
        evaluation_speed = nodes_evaluated / total_evaluation_time;
    }

    return *result.variation.front();
}

Game_Tree_Node_Result Minimax_AI::search_game_tree(const Board& board,
                                                   const double time_to_examine,
                                                   const Clock& clock,
                                                   const size_t depth,
                                                   Game_Tree_Node_Result alpha,
                                                   const Game_Tree_Node_Result& beta,
                                                   bool still_on_principal_variation) const
{
    const auto time_start = clock.running_time_left();
    maximum_depth = std::max(maximum_depth, depth);
    auto all_legal_moves = board.legal_moves();

    // The two items in the principal variation are the last two moves of
    // the non-hypothetical board. So, the first item in the principal variation to
    // consider is at index depth + 1 (since depth starts at 1).
    still_on_principal_variation = (still_on_principal_variation && principal_variation.size() > depth + 1);
    auto start_offset = 0;
    if(still_on_principal_variation)
    {
        auto next_principal_variation_move = principal_variation[depth + 1];
        auto move_iter = std::find(all_legal_moves.begin(),
                                   all_legal_moves.end(),
                                   next_principal_variation_move);

        still_on_principal_variation = (move_iter != all_legal_moves.end());
        if(still_on_principal_variation)
        {
            // Put principal variation move at start of list to allow
            // the most pruning later.
            std::iter_swap(all_legal_moves.begin(), move_iter);
            start_offset = 1;
        }
    }

    // Consider capturing moves first after principal variation move
    auto partition_start = std::next(all_legal_moves.begin(), start_offset);
    std::partition(partition_start, all_legal_moves.end(),
                   [&board](auto move){ return board.move_captures(*move); });

    const auto perspective = board.whose_turn();
    auto moves_left = all_legal_moves.size();

    Game_Tree_Node_Result best_result = {Math::lose_score,
                                         perspective,
                                         {all_legal_moves.front()}};

    // Pre-loop time to assign to each move for more accurate speed calculations
    auto setup_time = (time_start - clock.running_time_left())/moves_left;

    for(const auto& move : all_legal_moves)
    {
        auto evaluate_start_time = clock.running_time_left();
        ++nodes_searched;

        auto next_board = board;

        auto move_result = next_board.submit_move(*move);
        if(move_result.winner() != NONE)
        {
            // This move results in checkmate, no other move can be better.
            return create_result(next_board, perspective, move_result, depth);
        }

        if(alpha.depth() <= depth + 2 && alpha.is_winning_for(perspective))
        {
            // This move will take a longer path to victory
            // than one already found. Use "depth + 2" since,
            // if this move isn't winning (and it isn't, since
            // we're here), then the earliest move that can
            // win is the next one, which is two away (after
            // opponent's move).
            continue;
        }

        double time_left = time_to_examine - (time_start - clock.running_time_left());
        double time_allotted_for_this_move = (time_left / moves_left)*speculation_time_factor(next_board);
        time_allotted_for_this_move = std::min(time_allotted_for_this_move, clock.running_time_left());

        bool recurse;
        if(move_result.game_has_ended())
        {
            recurse = false;
        }
        else if(still_on_principal_variation)
        {
            recurse = true;
        }
        else if(depth > 100)
        {
            recurse = false; // prevent stack overflow
        }
        else
        {
            auto minimum_time_to_recurse = next_board.legal_moves().size() / evaluation_speed;
            recurse = (time_allotted_for_this_move > minimum_time_to_recurse);
        }

        Game_Tree_Node_Result result;
        if(recurse)
        {
            result = search_game_tree(next_board,
                                      time_allotted_for_this_move,
                                      clock,
                                      depth + 1,
                                      beta,
                                      alpha,
                                      still_on_principal_variation);
        }
        else
        {
            result = create_result(next_board, perspective, move_result, depth);
        }

        if(result.value(perspective) > best_result.value(perspective))
        {
            best_result = result;

            if(best_result.value(perspective) > alpha.value(perspective))
            {
                alpha = best_result;
                if(alpha.value(perspective) >= beta.value(perspective))
                {
                    break;
                }
                else if(board.thinking_mode() == CECP && time_since_last_output(clock) > 0.1)
                {
                    output_thinking_cecp(alpha, clock,
                                         depth % 2 == 1 ? perspective : opposite(perspective));
                    time_at_last_output = clock.running_time_left();
                }
            }
        }

        --moves_left;
        still_on_principal_variation = false; // only the first move is part of the principal variation

        if(clock.running_time_left() < 0 || board.must_pick_move_now())
        {
            break;
        }

        if( ! recurse) // This move was scored by genome.evaluate().
        {
            ++nodes_evaluated;
            total_evaluation_time += setup_time + (evaluate_start_time - clock.running_time_left());
        }
    }

    return best_result;
}

void Minimax_AI::output_thinking_cecp(const Game_Tree_Node_Result& thought,
                                      const Clock& clock,
                                      Color perspective) const
{
    auto score = thought.corrected_score(perspective) / centipawn_value();

    // Indicate "mate in N moves" where N == thought.depth
    if(thought.is_winning_for(perspective))
    {
        score = 10000.0 - thought.depth();
    }
    else if(thought.is_losing_for(perspective))
    {
        score = -(10000.0 - thought.depth());
    }

    auto time_so_far = clock_start_time - clock.running_time_left();
    std::cout << thought.depth() // ply
        << " "
        << int(score) // score in what should be centipawns
        << " "
        << int(time_so_far * 100) // search time in centiseconds
        << " "
        << nodes_searched
        << " "
        << maximum_depth
        << " "
        << int(nodes_searched / time_so_far)
        << '\t';

    // Principal variation
    for(const auto& move : thought.variation)
    {
        std::cout << move->coordinate_move() << ' ';
    }

    std::cout << std::endl;
}

double Minimax_AI::time_since_last_output(const Clock& clock) const
{
    return time_at_last_output - clock.running_time_left();
}

Game_Tree_Node_Result Minimax_AI::create_result(const Board& board,
                                                Color perspective,
                                                const Game_Result& move_result,
                                                size_t depth) const
{
    return {evaluate(board, move_result, perspective, depth),
            perspective,
            {board.game_record().end() - int(depth),
            board.game_record().end()}};
}

//! Initial measurement of evaluation speed of the engine.
//
//! The method Minimax_AI::choose_move() keeps track of the time it takes
//! and the number of positions it evaluates. But, it needs an accurate
//! starting value for the first move search. So, this practice move will
//! update the evaluation speed to a more reasonable starting value.
void Minimax_AI::calibrate_thinking_speed() const
{
    evaluation_speed = 100; // very conservative initial guess
    auto calibration_time = 1.0; // seconds
    Board board;
    Clock clock(calibration_time, 1, 0.0);
    clock.start();
    choose_move(board, clock);
}

double Minimax_AI::evaluate(const Board& board, const Game_Result& move_result, Color perspective, size_t depth) const
{
    if(move_result.game_has_ended())
    {
        if(move_result.winner() == NONE) // stalemate
        {
            return 0;
        }
        else if(move_result.winner() == perspective) // checkmate win
        {
            return Math::win_score;
        }
        else // checkmate loss
        {
            return Math::lose_score;
        }
    }

    return internal_evaluate(board, perspective, depth);
}

double Minimax_AI::centipawn_value() const
{
    return value_of_centipawn;
}

//! Approximate the value of 0.01 pawns for reporting scores.
//
//! \returns A numerical value to normalize the scores returned by board evaluations
//!          so that the loss of a random pawn changes the score by about 1.0.
void Minimax_AI::calculate_centipawn_value() const
{
    auto sum_of_diffs = 0.0;
    auto count = 0;
    while(true)
    {
        Board board;
        auto board_is_good = true;
        for(int move = 0; move < 40; ++move)
        {
            auto next_move = Random::random_element(board.legal_moves());
            if(board.submit_move(*next_move).game_has_ended() || board.fen_status().find_first_of("pP") == std::string::npos)
            {
                board_is_good = false;
                break;
            }
        }

        if(board_is_good)
        {
            ++count;
            auto board_without_pawn = board.without_random_pawn();
            auto original_board_result = evaluate(board, {}, WHITE, 0);
            auto minus_pawn_result = evaluate(board_without_pawn, {}, WHITE, 0);
            auto diff = std::abs(original_board_result - minus_pawn_result);
            sum_of_diffs += diff;

            if(count > 100 && diff < 0.01*sum_of_diffs)
            {
                break;
            }
        }
    }

    value_of_centipawn = sum_of_diffs/count/100;
}

//! Prints the expected future variation and score for the chosen move.
//
//! \param board The state of the game just prior to the move being commented on.
std::string Minimax_AI::commentary_for_next_move(const Board& board) const
{
    std::string result;
    auto move_number = board.game_record().size()/2;
    if(move_number < commentary.size() && ! commentary.at(move_number).variation.empty())
    {
        auto temp_board = board;
        Game_Result move_result;
        const auto& comment = commentary.at(move_number);
        for(auto move : comment.variation)
        {
            result += move->game_record_item(temp_board) + " ";
            move_result = temp_board.submit_move(*move);
        }

        if(comment.variation.size() < 2)
        {
            result.clear();
        }

        if( ! move_result.game_has_ended())
        {
            auto score = std::to_string(int(comment.corrected_score(board.whose_turn())/centipawn_value())/100.0);
            auto decimal_point_index = score.find('.');
            if(decimal_point_index != std::string::npos)
            {
                // Truncate to two decimal places
                score = score.substr(0, decimal_point_index + 3);
            }
            result += score;
        }
        else
        {
            result = String::trim_outer_whitespace(result);
        }
    }

    return result;
}

void Minimax_AI::recalibrate_self() const
{
    calibrate_thinking_speed();
    calculate_centipawn_value();
}
<commit_msg>Update doxygen docs<commit_after>#include "Players/Minimax_AI.h"

#include <iostream>
#include <string>

#include "Players/Game_Tree_Node_Result.h"
#include "Game/Board.h"
#include "Game/Clock.h"
#include "Game/Game_Result.h"
#include "Moves/Move.h"

#include "Utility/String.h"
#include "Utility/Math.h"
#include "Utility/Random.h"

Minimax_AI::Minimax_AI()
{
}

//! Minimax_AI uses a variable-depth minimax algorithm with alpha-beta pruning.
//
//! The depth of the search is determined by how much time is available.
//! At first, equal time is allocated to each legal move for examination. Time
//! is overcommitted with the expectation that not all of the time will be used
//! due to search cutoffs due to alpha-beta pruning.
//! \param board The current state of the game.
//! \param clock The game clock telling how much time is left in the game.
const Move& Minimax_AI::choose_move(const Board& board, const Clock& clock) const
{
    // Erase data from previous board when starting new game
    if(board.game_record().size() <= 1)
    {
        principal_variation.clear();
        commentary.clear();
    }

    nodes_searched = 0;
    clock_start_time = clock.running_time_left();
    maximum_depth = 0;

    nodes_evaluated = 0;
    total_evaluation_time = 0.0;
    time_at_last_output = clock.running_time_left();

    const auto& legal_moves = board.legal_moves();
    if(legal_moves.size() == 1)
    {
        if(principal_variation.size() > 3 && principal_variation[1] == board.game_record().back())
        {
            // search_game_tree() assumes the principal variation starts
            // with the previous move of this player. If a move was forced,
            // then the principal variation needs to be updated to start with
            // the next move of this side after checking that the immediately
            // preceding move was the expected one.
            principal_variation.erase(principal_variation.begin(),
                                      principal_variation.begin() + 2);
        }
        else
        {
            principal_variation.clear();
        }

        commentary.push_back({commentary.empty() ? 0.0 : commentary.back().corrected_score(board.whose_turn()),
                              board.whose_turn(),
                              principal_variation});

        return *legal_moves.front(); // If there's only one legal move, take it.
    }

    // No useful principal variation if it doesn't include the next possible move
    // or if the opponent did not make the predicted next move.
    if(principal_variation.size() < 2 || principal_variation[1] != board.game_record().back())
    {
        principal_variation.clear();
    }

    auto time_to_use = time_to_examine(board, clock);

    // alpha = highest score found that opponent will allow
    Game_Tree_Node_Result alpha_start = {Math::lose_score,
                                         board.whose_turn(),
                                         {}};

    // beta = score that will cause opponent to choose a different prior move
    Game_Tree_Node_Result beta_start = {Math::win_score,
                                        board.whose_turn(),
                                        {}};

    auto result = search_game_tree(board,
                                   time_to_use,
                                   clock,
                                   1,
                                   alpha_start,
                                   beta_start,
                                   ! principal_variation.empty());

    if(board.thinking_mode() == CECP)
    {
        output_thinking_cecp(result, clock, board.whose_turn());
    }

    commentary.push_back(result);

    if(result.depth() > 2)
    {
        principal_variation = result.variation;
    }
    else
    {
        principal_variation.clear();
    }

    if(nodes_evaluated > 0)
    {
        evaluation_speed = nodes_evaluated / total_evaluation_time;
    }

    return *result.variation.front();
}

Game_Tree_Node_Result Minimax_AI::search_game_tree(const Board& board,
                                                   const double time_to_examine,
                                                   const Clock& clock,
                                                   const size_t depth,
                                                   Game_Tree_Node_Result alpha,
                                                   const Game_Tree_Node_Result& beta,
                                                   bool still_on_principal_variation) const
{
    const auto time_start = clock.running_time_left();
    maximum_depth = std::max(maximum_depth, depth);
    auto all_legal_moves = board.legal_moves();

    // The two items in the principal variation are the last two moves of
    // the non-hypothetical board. So, the first item in the principal variation to
    // consider is at index depth + 1 (since depth starts at 1).
    still_on_principal_variation = (still_on_principal_variation && principal_variation.size() > depth + 1);
    auto start_offset = 0;
    if(still_on_principal_variation)
    {
        auto next_principal_variation_move = principal_variation[depth + 1];
        auto move_iter = std::find(all_legal_moves.begin(),
                                   all_legal_moves.end(),
                                   next_principal_variation_move);

        still_on_principal_variation = (move_iter != all_legal_moves.end());
        if(still_on_principal_variation)
        {
            // Put principal variation move at start of list to allow
            // the most pruning later.
            std::iter_swap(all_legal_moves.begin(), move_iter);
            start_offset = 1;
        }
    }

    // Consider capturing moves first after principal variation move
    auto partition_start = std::next(all_legal_moves.begin(), start_offset);
    std::partition(partition_start, all_legal_moves.end(),
                   [&board](auto move){ return board.move_captures(*move); });

    const auto perspective = board.whose_turn();
    auto moves_left = all_legal_moves.size();

    Game_Tree_Node_Result best_result = {Math::lose_score,
                                         perspective,
                                         {all_legal_moves.front()}};

    // Pre-loop time to assign to each move for more accurate speed calculations
    auto setup_time = (time_start - clock.running_time_left())/moves_left;

    for(const auto& move : all_legal_moves)
    {
        auto evaluate_start_time = clock.running_time_left();
        ++nodes_searched;

        auto next_board = board;

        auto move_result = next_board.submit_move(*move);
        if(move_result.winner() != NONE)
        {
            // This move results in checkmate, no other move can be better.
            return create_result(next_board, perspective, move_result, depth);
        }

        if(alpha.depth() <= depth + 2 && alpha.is_winning_for(perspective))
        {
            // This move will take a longer path to victory
            // than one already found. Use "depth + 2" since,
            // if this move isn't winning (and it isn't, since
            // we're here), then the earliest move that can
            // win is the next one, which is two away (after
            // opponent's move).
            continue;
        }

        double time_left = time_to_examine - (time_start - clock.running_time_left());
        double time_allotted_for_this_move = (time_left / moves_left)*speculation_time_factor(next_board);
        time_allotted_for_this_move = std::min(time_allotted_for_this_move, clock.running_time_left());

        bool recurse;
        if(move_result.game_has_ended())
        {
            recurse = false;
        }
        else if(still_on_principal_variation)
        {
            recurse = true;
        }
        else if(depth > 100)
        {
            recurse = false; // prevent stack overflow
        }
        else
        {
            auto minimum_time_to_recurse = next_board.legal_moves().size() / evaluation_speed;
            recurse = (time_allotted_for_this_move > minimum_time_to_recurse);
        }

        Game_Tree_Node_Result result;
        if(recurse)
        {
            result = search_game_tree(next_board,
                                      time_allotted_for_this_move,
                                      clock,
                                      depth + 1,
                                      beta,
                                      alpha,
                                      still_on_principal_variation);
        }
        else
        {
            result = create_result(next_board, perspective, move_result, depth);
        }

        if(result.value(perspective) > best_result.value(perspective))
        {
            best_result = result;

            if(best_result.value(perspective) > alpha.value(perspective))
            {
                alpha = best_result;
                if(alpha.value(perspective) >= beta.value(perspective))
                {
                    break;
                }
                else if(board.thinking_mode() == CECP && time_since_last_output(clock) > 0.1)
                {
                    output_thinking_cecp(alpha, clock,
                                         depth % 2 == 1 ? perspective : opposite(perspective));
                    time_at_last_output = clock.running_time_left();
                }
            }
        }

        --moves_left;
        still_on_principal_variation = false; // only the first move is part of the principal variation

        if(clock.running_time_left() < 0 || board.must_pick_move_now())
        {
            break;
        }

        if( ! recurse) // This move was scored by genome.evaluate().
        {
            ++nodes_evaluated;
            total_evaluation_time += setup_time + (evaluate_start_time - clock.running_time_left());
        }
    }

    return best_result;
}

void Minimax_AI::output_thinking_cecp(const Game_Tree_Node_Result& thought,
                                      const Clock& clock,
                                      Color perspective) const
{
    auto score = thought.corrected_score(perspective) / centipawn_value();

    // Indicate "mate in N moves" where N == thought.depth
    if(thought.is_winning_for(perspective))
    {
        score = 10000.0 - thought.depth();
    }
    else if(thought.is_losing_for(perspective))
    {
        score = -(10000.0 - thought.depth());
    }

    auto time_so_far = clock_start_time - clock.running_time_left();
    std::cout << thought.depth() // ply
        << " "
        << int(score) // score in what should be centipawns
        << " "
        << int(time_so_far * 100) // search time in centiseconds
        << " "
        << nodes_searched
        << " "
        << maximum_depth
        << " "
        << int(nodes_searched / time_so_far)
        << '\t';

    // Principal variation
    for(const auto& move : thought.variation)
    {
        std::cout << move->coordinate_move() << ' ';
    }

    std::cout << std::endl;
}

double Minimax_AI::time_since_last_output(const Clock& clock) const
{
    return time_at_last_output - clock.running_time_left();
}

Game_Tree_Node_Result Minimax_AI::create_result(const Board& board,
                                                Color perspective,
                                                const Game_Result& move_result,
                                                size_t depth) const
{
    return {evaluate(board, move_result, perspective, depth),
            perspective,
            {board.game_record().end() - int(depth),
            board.game_record().end()}};
}

//! Initial measurement of evaluation speed of the engine.
//
//! The method Minimax_AI::choose_move() keeps track of the time it takes
//! and the number of positions it evaluates. But, it needs an accurate
//! starting value for the first move search. So, this practice move will
//! update the evaluation speed to a more reasonable starting value.
void Minimax_AI::calibrate_thinking_speed() const
{
    evaluation_speed = 100; // very conservative initial guess
    auto calibration_time = 1.0; // seconds
    Board board;
    Clock clock(calibration_time, 1, 0.0);
    clock.start();
    choose_move(board, clock);
}

double Minimax_AI::evaluate(const Board& board, const Game_Result& move_result, Color perspective, size_t depth) const
{
    if(move_result.game_has_ended())
    {
        if(move_result.winner() == NONE) // stalemate
        {
            return 0;
        }
        else if(move_result.winner() == perspective) // checkmate win
        {
            return Math::win_score;
        }
        else // checkmate loss
        {
            return Math::lose_score;
        }
    }

    return internal_evaluate(board, perspective, depth);
}

double Minimax_AI::centipawn_value() const
{
    return value_of_centipawn;
}

//! Approximate the value of 0.01 pawns for reporting scores.
//
//! \returns A numerical value to normalize the scores returned by board evaluations
//!          so that the loss of a random pawn changes the score by about 1.0.
void Minimax_AI::calculate_centipawn_value() const
{
    auto sum_of_diffs = 0.0;
    auto count = 0;
    while(true)
    {
        Board board;
        auto board_is_good = true;
        for(int move = 0; move < 40; ++move)
        {
            auto next_move = Random::random_element(board.legal_moves());
            if(board.submit_move(*next_move).game_has_ended() || board.fen_status().find_first_of("pP") == std::string::npos)
            {
                board_is_good = false;
                break;
            }
        }

        if(board_is_good)
        {
            ++count;
            auto board_without_pawn = board.without_random_pawn();
            auto original_board_result = evaluate(board, {}, WHITE, 0);
            auto minus_pawn_result = evaluate(board_without_pawn, {}, WHITE, 0);
            auto diff = std::abs(original_board_result - minus_pawn_result);
            sum_of_diffs += diff;

            if(count > 100 && diff < 0.01*sum_of_diffs)
            {
                break;
            }
        }
    }

    value_of_centipawn = sum_of_diffs/count/100;
}

//! Prints the expected future variation and score for the chosen move.
//
//! \param board The state of the game just prior to the move being commented on.
std::string Minimax_AI::commentary_for_next_move(const Board& board) const
{
    std::string result;
    auto move_number = board.game_record().size()/2;
    if(move_number < commentary.size() && ! commentary.at(move_number).variation.empty())
    {
        auto temp_board = board;
        Game_Result move_result;
        const auto& comment = commentary.at(move_number);
        for(auto move : comment.variation)
        {
            result += move->game_record_item(temp_board) + " ";
            move_result = temp_board.submit_move(*move);
        }

        if(comment.variation.size() < 2)
        {
            result.clear();
        }

        if( ! move_result.game_has_ended())
        {
            auto score = std::to_string(int(comment.corrected_score(board.whose_turn())/centipawn_value())/100.0);
            auto decimal_point_index = score.find('.');
            if(decimal_point_index != std::string::npos)
            {
                // Truncate to two decimal places
                score = score.substr(0, decimal_point_index + 3);
            }
            result += score;
        }
        else
        {
            result = String::trim_outer_whitespace(result);
        }
    }

    return result;
}

//! Recalculate values that will last the lifetime of the instance.
//
//! In this case, the values are an initial estimate of the speed of
//! searching the game tree and the value of a centipawn for reporting
//! scores of board positions.
void Minimax_AI::recalibrate_self() const
{
    calibrate_thinking_speed();
    calculate_centipawn_value();
}
<|endoftext|>


<commit_before>/*
 * LinkVisual.cpp
 *
 *  Created on: Sep 24, 2013
 *      Author: mklingen
 */

#include "LinkVisual.h"

#include "../Converters.h"
#include <OgreSceneNode.h>
#include <OgreSceneManager.h>
#include <OgreEntity.h>
#include <Ogre.h>
#include <OgreSubEntity.h>
#include <OgreMovableObject.h>
#include <rviz/mesh_loader.h>
#include <rviz/ogre_helpers/shape.h>
#include <rviz/ogre_helpers/object.h>
#include <OgreException.h>
#include <OgreMeshManager.h>
#include <OgreMaterialManager.h>
#include <OgreMaterial.h>
#include <OgreMeshSerializer.h>
#include "KinBodyVisual.h"
#include <boost/filesystem.hpp>

namespace or_rviz
{

    LinkVisual::LinkVisual(KinBodyVisual* kinBody, OpenRAVE::KinBody::LinkPtr link, Ogre::SceneNode* parent, Ogre::SceneManager* sceneManager) :
            m_kinBody(kinBody), m_link(link), m_parentNode(parent), m_sceneManager(sceneManager)
    {
        m_sceneNode = m_parentNode->createChildSceneNode();
        CreateParts();
    }

    LinkVisual::~LinkVisual()
    {
        m_sceneManager->destroySceneNode(m_sceneNode);
    }

    void DeleteRepeatedVertices(const OpenRAVE::TriMesh& trimesh, std::vector<Ogre::Vector3>& verts, std::vector<int>& indices, bool remove)
    {
        for(size_t i = 0; i < trimesh.indices.size(); i++)
        {
            indices.push_back(trimesh.indices.at(i));
        }

        float epsilon = 0.0001;
        for(size_t i = 0; i < trimesh.vertices.size(); i++)
        {
            Ogre::Vector3 vec = converters::ToOgreVector(trimesh.vertices.at(i));

            bool copyFound = false;

            if(remove)
            {
                for(size_t j = 0; j < verts.size(); j++)
                {
                    if(fabs(vec.x - verts[j].x) < epsilon && fabs(vec.y - verts[j].y) < epsilon && fabs(vec.z - verts[j].z) < epsilon)
                    {
                        copyFound = true;
                        indices[i] = j;
                        break;
                    }
                }
            }

            if(copyFound)
            {
                continue;
            }

            if(remove)
            {
                indices[i] = (int)verts.size();
            }
            verts.push_back(vec);
        }
    }

    Ogre::MeshPtr LinkVisual::meshToOgre(const OpenRAVE::TriMesh& trimesh, std::string name)
    {
        Ogre::MeshPtr existingMesh = Ogre::ResourceGroupManager::getSingleton()._getResourceManager("Mesh")->getByName(name, "General");

        if(existingMesh.get())
        {
            RAVELOG_DEBUG("Mesh %s exists. returning.\n", name.c_str());
            return existingMesh;
        }

        Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().createManual(name, "General");
        Ogre::SubMesh* subMesh = mesh->createSubMesh();

        std::vector<Ogre::Vector3> verts;
        std::vector<int> index;

        DeleteRepeatedVertices(trimesh, verts, index, false);

        std::vector<Ogre::Vector3> normals(verts.size(), Ogre::Vector3(0, 0, 0));

        for (std::vector<int>::const_iterator i = index.begin(); i != index.end(); std::advance(i, 3))
        {
            Ogre::Vector3 v[3] = { (verts.at(*i)), (verts.at(*(i + 1))), ((verts.at(*(i + 2)))) };
            Ogre::Vector3 normal = (v[1] - v[0]).crossProduct(v[2] - v[0]);

            for (int j = 0; j < 3; ++j)
            {
                Ogre::Vector3 a = v[(j + 1) % 3] - v[j];
                Ogre::Vector3 b = v[(j + 2) % 3] - v[j];

                float weight = acos(a.dotProduct(b) / (a.length() * b.length()));
                normals[*(i + j)] += weight * normal;
            }
        }

        for (size_t i = 0; i < normals.size(); i++)
        {
            normals.at(i).normalise();
        }

        /* create the vertex data structure */
        mesh->sharedVertexData = new Ogre::VertexData;
        mesh->sharedVertexData->vertexCount = verts.size();

        /* declare how the vertices will be represented */
        Ogre::VertexDeclaration *decl = mesh->sharedVertexData->vertexDeclaration;
        size_t offset = 0;

        /* the first three floats of each vertex represent the position */
        decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
        offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);

        decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
        offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);

        /* create the vertex buffer */
        Ogre::HardwareVertexBufferSharedPtr vertexBuffer = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(offset, mesh->sharedVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC);

        /* lock the buffer so we can get exclusive access to its data */
        float *vertices = static_cast<float *>(vertexBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL));

        Ogre::Vector3 min(9999, 9999, 9999);
        Ogre::Vector3 max(-9999, -9999, -9999);

        size_t i = 0;
        for (std::vector<Ogre::Vector3>::const_iterator it = verts.begin(); it != verts.end(); it++)
        {
            vertices[i * 6 + 0] = it->x;
            vertices[i * 6 + 1] = it->y;
            vertices[i * 6 + 2] = it->z;
            vertices[i * 6 + 3] = normals[i].x;
            vertices[i * 6 + 4] = normals[i].y;
            vertices[i * 6 + 5] = normals[i].z;

            min.x = fmin(it->x, min.x);
            min.y = fmin(it->y, min.y);
            min.z = fmin(it->z, min.z);

            max.x = fmax(it->x, max.x);
            max.y = fmax(it->y, max.y);
            max.z = fmax(it->z, max.z);
            i++;
        }

        /* unlock the buffer */
        vertexBuffer->unlock();

        /* create the index buffer */
        Ogre::HardwareIndexBufferSharedPtr indexBuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, trimesh.indices.size(), Ogre::HardwareBuffer::HBU_STATIC);

        /* lock the buffer so we can get exclusive access to its data */
        uint16_t *indices = static_cast<uint16_t *>(indexBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL));

        i = 0;
        for (std::vector<int>::const_iterator it = index.begin(); it != index.end(); it++)
        {
            indices[i] = static_cast<uint16_t>(*it);
            i++;
        }

        /* unlock the buffer */
        indexBuffer->unlock();

        /* attach the buffers to the mesh */
        mesh->sharedVertexData->vertexBufferBinding->setBinding(0, vertexBuffer);
        subMesh->useSharedVertices = true;

        subMesh->indexData->indexBuffer = indexBuffer;
        subMesh->indexData->indexCount = index.size();
        subMesh->indexData->indexStart = 0;

        RAVELOG_DEBUG("Mesh %s\n", name.c_str());
        RAVELOG_DEBUG("Min: %f %f %f\n", min.x, min.y, min.z);
        RAVELOG_DEBUG("Max: %f %f %f\n", max.x, max.y, max.z);

        /* set the bounds of the mesh */
        mesh->_setBounds(Ogre::AxisAlignedBox(min.x, min.y, min.z, max.x, max.y, max.z));

        /* notify the mesh that we're all ready */
        mesh->load();

        return mesh;
    }

    void LinkVisual::CreateParts()
    {
        std::vector<OpenRAVE::KinBody::Link::GeometryPtr> geometries =GetLink()->GetGeometries();
        static int id = 0;

        for (size_t i = 0; i < geometries.size(); i++)
        {

            OpenRAVE::KinBody::Link::GeometryPtr geom = geometries.at(i);

            if(!geom->IsVisible())
            {
                continue;
            }

            std::stringstream ss;
            std::stringstream iterSS;
            iterSS << i;
            ss << id;
            id++;
            Ogre::SceneNode* offsetNode = m_sceneNode->createChildSceneNode();
            Ogre::Entity* entity = NULL;
            Ogre::Vector3 scale(Ogre::Vector3::UNIT_SCALE);
            Ogre::Vector3 offset_position(Ogre::Vector3::ZERO);
            Ogre::Quaternion offset_orientation(Ogre::Quaternion::IDENTITY);

            {
                offset_position = converters::ToOgreVector(geom->GetTransform().trans);
                offset_orientation = converters::ToOgreQuaternion(geom->GetTransform().rot);
            }

            std::string objectName = GetLink()->GetName() + " " + m_kinBody->GetKinBody()->GetName();

            switch (geom->GetType())
            {
                case OpenRAVE::GT_Box:
                {
                    entity = rviz::Shape::createEntity("Box" + objectName + ss.str(), rviz::Shape::Cube, m_sceneManager);
                    scale = converters::ToOgreVector(geom->GetBoxExtents() * 2);
                    break;
                }
                case OpenRAVE::GT_Cylinder:
                {
                    Ogre::Quaternion rotX;
                    rotX.FromAngleAxis(Ogre::Degree(90), Ogre::Vector3::UNIT_X);
                    offset_orientation = offset_orientation * rotX;

                    entity = rviz::Shape::createEntity("Cylinder" + objectName + ss.str(), rviz::Shape::Cylinder, m_sceneManager);
                    scale = Ogre::Vector3(geom->GetCylinderRadius() * 2, geom->GetCylinderHeight(), geom->GetCylinderRadius() * 2);
                    break;
                }
                case OpenRAVE::GT_Sphere:
                {
                    entity = rviz::Shape::createEntity("Sphere" + objectName + ss.str(), rviz::Shape::Sphere, m_sceneManager);
                    scale = Ogre::Vector3(geom->GetSphereRadius() * 2, geom->GetSphereRadius() * 2, geom->GetSphereRadius() * 2);
                    break;
                }
                case OpenRAVE::GT_TriMesh:
                {

                    if(geom->GetInfo()._filenamerender.size() == 0)
                    {
                       break;
                    }

                    Ogre::MeshPtr mesh;

                    try
                    {
                        mesh = rviz::loadMeshFromResource ("file://" + geom->GetInfo()._filenamerender);
                    }
                    catch (Ogre::Exception& e)
                    {
                        RAVELOG_DEBUG("Mesh %s can't be loaded by STL or .mesh loader. %s Falling back to OpenRAVE geometry.\n", geom->GetInfo()._filenamerender.c_str(), e.what());
                    }

                    if(!mesh.get())
                    {
                        RAVELOG_DEBUG("Fell back to OpenRAVE geometry. Was unable to load mesh %s.\n", geom->GetInfo()._filenamerender.c_str());
                        boost::shared_ptr<OpenRAVE::TriMesh> myMesh;
                        myMesh.reset(new OpenRAVE::TriMesh());
                        m_kinBody->GetKinBody()->GetEnv()->ReadTrimeshFile(myMesh, geom->GetRenderFilename());

                        if(myMesh && myMesh->vertices.size() >= 3)
                        {
                            mesh = meshToOgre(*myMesh, geom->GetInfo()._filenamerender);
                        }
                    }
                    else
                    {
                        RAVELOG_DEBUG("Successfully loaded mesh: %s\n", geom->GetInfo()._filenamerender.c_str());
                    }

                    if(mesh.get())
                    {
                        entity = m_sceneManager->createEntity("Mesh " + objectName + ss.str(), mesh->getName(), mesh->getGroup());
                        entity->setVisible(true);
                        scale = converters::ToOgreVector(geom->GetRenderScale());
                    }

                    break;
                }
                default:
                {
                    RAVELOG_DEBUG("Unrecognized geometry type.");
                    break;
                }
            }

            if (entity)
            {

                Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton();
                Ogre::ResourceManager::ResourceCreateOrRetrieveResult result = matMgr.createOrRetrieve(objectName + " Material" + iterSS.str(), Ogre::ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME);
                if(result.second)
                {
                    Ogre::MaterialPtr mat = (Ogre::MaterialPtr)result.first;
                    mat->setReceiveShadows(true);

                    mat->createTechnique();
                    Ogre::Pass *pass1 = mat->getTechnique(0)->createPass();
                    pass1->setAmbient(geom->GetAmbientColor().x, geom->GetAmbientColor().y, geom->GetAmbientColor().z);
                    pass1->setDiffuse(geom->GetDiffuseColor().x, geom->GetDiffuseColor().y, geom->GetDiffuseColor().z, 1.0);
                    pass1->setSpecular(0.02, 0.02, 0.02, 12.5);
                    mat->setShadingMode(Ogre::SO_GOURAUD);
                    pass1->setShadingMode(Ogre::SO_GOURAUD);
                    mat->compile();

                }

                offsetNode->attachObject(entity);
                offsetNode->setScale(scale);
                offsetNode->setPosition(offset_position);
                offsetNode->setOrientation(offset_orientation);

                for (uint32_t i = 0; i < entity->getNumSubEntities(); ++i)
                {
                    Ogre::SubEntity* sub = entity->getSubEntity(i);

                    sub->setMaterialName(objectName + " Material" + iterSS.str());

                }

            }
        }
    }

} /* namespace superviewer */
<commit_msg>Override collision geometry if the render filename is specified.<commit_after>/*
 * LinkVisual.cpp
 *
 *  Created on: Sep 24, 2013
 *      Author: mklingen
 */

#include "LinkVisual.h"

#include "../Converters.h"
#include <OgreSceneNode.h>
#include <OgreSceneManager.h>
#include <OgreEntity.h>
#include <Ogre.h>
#include <OgreSubEntity.h>
#include <OgreMovableObject.h>
#include <rviz/mesh_loader.h>
#include <rviz/ogre_helpers/shape.h>
#include <rviz/ogre_helpers/object.h>
#include <OgreException.h>
#include <OgreMeshManager.h>
#include <OgreMaterialManager.h>
#include <OgreMaterial.h>
#include <OgreMeshSerializer.h>
#include "KinBodyVisual.h"
#include <boost/filesystem.hpp>

namespace or_rviz
{

    LinkVisual::LinkVisual(KinBodyVisual* kinBody, OpenRAVE::KinBody::LinkPtr link, Ogre::SceneNode* parent, Ogre::SceneManager* sceneManager) :
            m_kinBody(kinBody), m_link(link), m_parentNode(parent), m_sceneManager(sceneManager)
    {
        m_sceneNode = m_parentNode->createChildSceneNode();
        CreateParts();
    }

    LinkVisual::~LinkVisual()
    {
        m_sceneManager->destroySceneNode(m_sceneNode);
    }

    void DeleteRepeatedVertices(const OpenRAVE::TriMesh& trimesh, std::vector<Ogre::Vector3>& verts, std::vector<int>& indices, bool remove)
    {
        for(size_t i = 0; i < trimesh.indices.size(); i++)
        {
            indices.push_back(trimesh.indices.at(i));
        }

        float epsilon = 0.0001;
        for(size_t i = 0; i < trimesh.vertices.size(); i++)
        {
            Ogre::Vector3 vec = converters::ToOgreVector(trimesh.vertices.at(i));

            bool copyFound = false;

            if(remove)
            {
                for(size_t j = 0; j < verts.size(); j++)
                {
                    if(fabs(vec.x - verts[j].x) < epsilon && fabs(vec.y - verts[j].y) < epsilon && fabs(vec.z - verts[j].z) < epsilon)
                    {
                        copyFound = true;
                        indices[i] = j;
                        break;
                    }
                }
            }

            if(copyFound)
            {
                continue;
            }

            if(remove)
            {
                indices[i] = (int)verts.size();
            }
            verts.push_back(vec);
        }
    }

    Ogre::MeshPtr LinkVisual::meshToOgre(const OpenRAVE::TriMesh& trimesh, std::string name)
    {
        Ogre::MeshPtr existingMesh = Ogre::ResourceGroupManager::getSingleton()._getResourceManager("Mesh")->getByName(name, "General");

        if(existingMesh.get())
        {
            RAVELOG_DEBUG("Mesh %s exists. returning.\n", name.c_str());
            return existingMesh;
        }

        Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().createManual(name, "General");
        Ogre::SubMesh* subMesh = mesh->createSubMesh();

        std::vector<Ogre::Vector3> verts;
        std::vector<int> index;

        DeleteRepeatedVertices(trimesh, verts, index, false);

        std::vector<Ogre::Vector3> normals(verts.size(), Ogre::Vector3(0, 0, 0));

        for (std::vector<int>::const_iterator i = index.begin(); i != index.end(); std::advance(i, 3))
        {
            Ogre::Vector3 v[3] = { (verts.at(*i)), (verts.at(*(i + 1))), ((verts.at(*(i + 2)))) };
            Ogre::Vector3 normal = (v[1] - v[0]).crossProduct(v[2] - v[0]);

            for (int j = 0; j < 3; ++j)
            {
                Ogre::Vector3 a = v[(j + 1) % 3] - v[j];
                Ogre::Vector3 b = v[(j + 2) % 3] - v[j];

                float weight = acos(a.dotProduct(b) / (a.length() * b.length()));
                normals[*(i + j)] += weight * normal;
            }
        }

        for (size_t i = 0; i < normals.size(); i++)
        {
            normals.at(i).normalise();
        }

        /* create the vertex data structure */
        mesh->sharedVertexData = new Ogre::VertexData;
        mesh->sharedVertexData->vertexCount = verts.size();

        /* declare how the vertices will be represented */
        Ogre::VertexDeclaration *decl = mesh->sharedVertexData->vertexDeclaration;
        size_t offset = 0;

        /* the first three floats of each vertex represent the position */
        decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
        offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);

        decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
        offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);

        /* create the vertex buffer */
        Ogre::HardwareVertexBufferSharedPtr vertexBuffer = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(offset, mesh->sharedVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC);

        /* lock the buffer so we can get exclusive access to its data */
        float *vertices = static_cast<float *>(vertexBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL));

        Ogre::Vector3 min(9999, 9999, 9999);
        Ogre::Vector3 max(-9999, -9999, -9999);

        size_t i = 0;
        for (std::vector<Ogre::Vector3>::const_iterator it = verts.begin(); it != verts.end(); it++)
        {
            vertices[i * 6 + 0] = it->x;
            vertices[i * 6 + 1] = it->y;
            vertices[i * 6 + 2] = it->z;
            vertices[i * 6 + 3] = normals[i].x;
            vertices[i * 6 + 4] = normals[i].y;
            vertices[i * 6 + 5] = normals[i].z;

            min.x = fmin(it->x, min.x);
            min.y = fmin(it->y, min.y);
            min.z = fmin(it->z, min.z);

            max.x = fmax(it->x, max.x);
            max.y = fmax(it->y, max.y);
            max.z = fmax(it->z, max.z);
            i++;
        }

        /* unlock the buffer */
        vertexBuffer->unlock();

        /* create the index buffer */
        Ogre::HardwareIndexBufferSharedPtr indexBuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, trimesh.indices.size(), Ogre::HardwareBuffer::HBU_STATIC);

        /* lock the buffer so we can get exclusive access to its data */
        uint16_t *indices = static_cast<uint16_t *>(indexBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL));

        i = 0;
        for (std::vector<int>::const_iterator it = index.begin(); it != index.end(); it++)
        {
            indices[i] = static_cast<uint16_t>(*it);
            i++;
        }

        /* unlock the buffer */
        indexBuffer->unlock();

        /* attach the buffers to the mesh */
        mesh->sharedVertexData->vertexBufferBinding->setBinding(0, vertexBuffer);
        subMesh->useSharedVertices = true;

        subMesh->indexData->indexBuffer = indexBuffer;
        subMesh->indexData->indexCount = index.size();
        subMesh->indexData->indexStart = 0;

        RAVELOG_DEBUG("Mesh %s\n", name.c_str());
        RAVELOG_DEBUG("Min: %f %f %f\n", min.x, min.y, min.z);
        RAVELOG_DEBUG("Max: %f %f %f\n", max.x, max.y, max.z);

        /* set the bounds of the mesh */
        mesh->_setBounds(Ogre::AxisAlignedBox(min.x, min.y, min.z, max.x, max.y, max.z));

        /* notify the mesh that we're all ready */
        mesh->load();

        return mesh;
    }

    void LinkVisual::CreateParts()
    {
        std::vector<OpenRAVE::KinBody::Link::GeometryPtr> geometries =GetLink()->GetGeometries();
        static int id = 0;

        for (size_t i = 0; i < geometries.size(); i++)
        {

            OpenRAVE::KinBody::Link::GeometryPtr geom = geometries.at(i);

            if(!geom->IsVisible())
            {
                continue;
            }

            std::stringstream ss;
            std::stringstream iterSS;
            iterSS << i;
            ss << id;
            id++;
            Ogre::SceneNode* offsetNode = m_sceneNode->createChildSceneNode();
            Ogre::Entity* entity = NULL;
            Ogre::Vector3 scale(Ogre::Vector3::UNIT_SCALE);
            Ogre::Vector3 offset_position(Ogre::Vector3::ZERO);
            Ogre::Quaternion offset_orientation(Ogre::Quaternion::IDENTITY);

            {
                offset_position = converters::ToOgreVector(geom->GetTransform().trans);
                offset_orientation = converters::ToOgreQuaternion(geom->GetTransform().rot);
            }

            std::string objectName = GetLink()->GetName() + " " + m_kinBody->GetKinBody()->GetName();

            // If there is a render mesh we will ignore all of the other geometry.
            if(!geom->GetInfo()._filenamerender.empty())
            {
                Ogre::MeshPtr mesh;

                try
                {
                    mesh = rviz::loadMeshFromResource ("file://" + geom->GetInfo()._filenamerender);
                }
                catch (Ogre::Exception& e)
                {
                    RAVELOG_DEBUG("Mesh %s can't be loaded by STL or .mesh loader. %s Falling back to OpenRAVE geometry.\n", geom->GetInfo()._filenamerender.c_str(), e.what());
                }

                if(!mesh.get())
                {
                    RAVELOG_DEBUG("Fell back to OpenRAVE geometry. Was unable to load mesh %s.\n", geom->GetInfo()._filenamerender.c_str());
                    boost::shared_ptr<OpenRAVE::TriMesh> myMesh;
                    myMesh.reset(new OpenRAVE::TriMesh());
                    m_kinBody->GetKinBody()->GetEnv()->ReadTurimeshFile(myMesh, geom->GetRenderFilename());

                    if(myMesh && myMesh->vertices.size() >= 3)
                    {
                        mesh = meshToOgre(*myMesh, geom->GetInfo()._filenamerender);
                    }
                }
                else
                {
                    RAVELOG_DEBUG("Successfully loaded mesh: %s\n", geom->GetInfo()._filenamerender.c_str());
                }

                if(mesh.get())
                {
                    entity = m_sceneManager->createEntity("Mesh " + objectName + ss.str(), mesh->getName(), mesh->getGroup());
                    entity->setVisible(true);
                    scale = converters::ToOgreVector(geom->GetRenderScale());
                }
            } else {
                switch (geom->GetType())
                {
                    case OpenRAVE::GT_Box:
                    {
                        entity = rviz::Shape::createEntity("Box" + objectName + ss.str(), rviz::Shape::Cube, m_sceneManager);
                        scale = converters::ToOgreVector(geom->GetBoxExtents() * 2);
                        break;
                    }
                    case OpenRAVE::GT_Cylinder:
                    {
                        Ogre::Quaternion rotX;
                        rotX.FromAngleAxis(Ogre::Degree(90), Ogre::Vector3::UNIT_X);
                        offset_orientation = offset_orientation * rotX;

                        entity = rviz::Shape::createEntity("Cylinder" + objectName + ss.str(), rviz::Shape::Cylinder, m_sceneManager);
                        scale = Ogre::Vector3(geom->GetCylinderRadius() * 2, geom->GetCylinderHeight(), geom->GetCylinderRadius() * 2);
                        break;
                    }
                    case OpenRAVE::GT_Sphere:
                    {
                        entity = rviz::Shape::createEntity("Sphere" + objectName + ss.str(), rviz::Shape::Sphere, m_sceneManager);
                        scale = Ogre::Vector3(geom->GetSphereRadius() * 2, geom->GetSphereRadius() * 2, geom->GetSphereRadius() * 2);
                        break;
                    }
                    case OpenRAVE::GT_TriMesh:
                    {
                        RAVELOG_WARN("Attempted to load TriMesh with no render filename. This is unsupported.");
                        break;
                    }
                    default:
                    {
                        RAVELOG_DEBUG("Unrecognized geometry type.");
                        break;
                    }
                }
            }

            if (entity)
            {

                Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton();
                Ogre::ResourceManager::ResourceCreateOrRetrieveResult result = matMgr.createOrRetrieve(objectName + " Material" + iterSS.str(), Ogre::ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME);
                if(result.second)
                {
                    Ogre::MaterialPtr mat = (Ogre::MaterialPtr)result.first;
                    mat->setReceiveShadows(true);

                    mat->createTechnique();
                    Ogre::Pass *pass1 = mat->getTechnique(0)->createPass();
                    pass1->setAmbient(geom->GetAmbientColor().x, geom->GetAmbientColor().y, geom->GetAmbientColor().z);
                    pass1->setDiffuse(geom->GetDiffuseColor().x, geom->GetDiffuseColor().y, geom->GetDiffuseColor().z, 1.0);
                    pass1->setSpecular(0.02, 0.02, 0.02, 12.5);
                    mat->setShadingMode(Ogre::SO_GOURAUD);
                    pass1->setShadingMode(Ogre::SO_GOURAUD);
                    mat->compile();

                }

                offsetNode->attachObject(entity);
                offsetNode->setScale(scale);
                offsetNode->setPosition(offset_position);
                offsetNode->setOrientation(offset_orientation);

                for (uint32_t i = 0; i < entity->getNumSubEntities(); ++i)
                {
                    Ogre::SubEntity* sub = entity->getSubEntity(i);

                    sub->setMaterialName(objectName + " Material" + iterSS.str());

                }

            }
        }
    }

} /* namespace superviewer */
<|endoftext|>


<commit_before>/*	libscratch - Multipurpose objective C++ library.

		Copyright (c) 2013 - 2016 Angelo Geels <spansjh@gmail.com>

		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.
*/

#pragma once

#include <cstdlib>
#include <cstring>

#ifndef SCRATCH_NO_THREADSAFE
#include "Mutex.hpp"
#endif

SCRATCH_NAMESPACE_BEGIN;

template<class Type>
class SCRATCH_EXPORT StackArray
{
public:
	Type** sa_pItems;
	int32_t sa_ctSlots;
	int32_t sa_ctUsed;
	bool sa_bOnlyPop;
#ifndef SCRATCH_NO_THREADSAFE
	Mutex sa_mutex;
#endif

public:
	StackArray();
	StackArray(const StackArray<Type> &copy); // Note: If this ever gets called, you're most likely writing bad code.
	StackArray<Type> &operator=(const StackArray<Type> &copy);
	~StackArray();

	/// Push to the beginning of the stack, return a reference to the newly made object
	Type& PushBegin();
	/// Push to the stack, return a reference to the newly made object
	Type& Push();
	/// Push a pointer to the stack
	void Push(Type* pObj);
	/// Pop top object from the stack
	Type* Pop();
	/// Pop a certain index from the stack
	Type* PopAt(int32_t iIndex);

	/// Pop all objects from the stack
	void PopAll();
	/// Clear all objects in the stack
	void Clear();

	/// Return how many objects there currently are in the stack
	int32_t Count();

	/// Find the index of the given object in the stack
	int32_t Find(const Type &obj);
	/// Find the index of the given pointer in the stack
	int32_t FindPointer(const Type* pObj);
	/// Find the index of the given condition in the stack
	template<typename Func>
	int32_t FindAny(Func f);

	/// Returns whether the given object is currently in the stack
	bool Contains(const Type &obj);
	/// Returns whether the given pointer is currently in the stack
	bool ContainsPointer(const Type* pObj);
	/// Returns whether the given condition is currently in the stack
	template<typename Func>
	bool ContainsAny(Func f);

	Type& operator[](int32_t iIndex);

private:
	void AllocateSlots(int32_t ctSlots);
};

template<class Type>
StackArray<Type>::StackArray()
{
	sa_pItems = nullptr;
	sa_ctSlots = 0;
	sa_ctUsed = 0;
	sa_bOnlyPop = false;
	AllocateSlots(256);
}

template<class Type>
StackArray<Type>::StackArray(const StackArray<Type> &copy)
{
	operator=(copy);
}

template<class Type>
StackArray<Type> &StackArray<Type>::operator=(const StackArray<Type> &copy)
{
	sa_pItems = nullptr;
	sa_ctSlots = 0;
	sa_ctUsed = 0;
	sa_bOnlyPop = copy.sa_bOnlyPop;

	AllocateSlots(copy.sa_ctSlots);

	// copy meta information
	sa_ctUsed = copy.sa_ctUsed;

	// copy data to other slots
	for (int32_t i = 0; i < sa_ctUsed; i++) {
		// allocate memory for it
		sa_pItems[i] = new Type;

		// this should call the copy constructor
		*sa_pItems[i] = *copy.sa_pItems[i];
	}

	return *this;
}

template<class Type>
StackArray<Type>::~StackArray()
{
	if (sa_bOnlyPop) {
		PopAll();
	} else {
		Clear();
	}

	// free allocated memory for data
	if (sa_pItems != nullptr) {
		free(sa_pItems);
	}
}

template<class Type>
void StackArray<Type>::AllocateSlots(int32_t ctSlots)
{
	// allocate some memory
	sa_ctSlots += ctSlots;
	Type** pNewMem = (Type**)malloc(sizeof(Type*) * sa_ctSlots);

	// if we allocated memory before
	if (sa_pItems != nullptr) {
		// copy data to the new memory
		memcpy(pNewMem, sa_pItems, sizeof(Type*) * (sa_ctSlots - ctSlots));
		// free previously allocated memory
		free(sa_pItems);
	}

	// update the pointer
	sa_pItems = pNewMem;
}

/// Push to the beginning of the stack, return a reference to the newly made object
template<class Type>
Type& StackArray<Type>::PushBegin()
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// PushBegin() in combination with sa_bOnlyPop will cause memory leaking if not manually Clear()'d
	ASSERT(!sa_bOnlyPop);

	// if we need more slots
	if (sa_ctUsed >= sa_ctSlots) {
		// allocate some more
		AllocateSlots(256);
	}

	// create the new object
	Type* tNewObject = new Type;

	// make some room
	for (int i = sa_ctUsed; i > 0; i--) {
		sa_pItems[i] = sa_pItems[i - 1];
	}

	// push it onto the beginning of the stack
	sa_pItems[0] = tNewObject;

	// increase iterator
	sa_ctUsed++;

	// return new object
	return *tNewObject;
}

/// Push to the stack, return a reference to the newly made object
template<class Type>
Type& StackArray<Type>::Push()
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// Push() in combination with sa_bOnlyPop will cause memory leaking if not manually Clear()'d
	ASSERT(!sa_bOnlyPop);

	// if we need more slots
	if (sa_ctUsed >= sa_ctSlots) {
		// allocate some more
		AllocateSlots(256);
	}

	// create the new object
	Type* tNewObject = new Type;

	// push it onto the stack
	sa_pItems[sa_ctUsed] = tNewObject;

	// increase iterator
	sa_ctUsed++;

	// return new object
	return *tNewObject;
}

/// Push a pointer to the stack
template<class Type>
void StackArray<Type>::Push(Type* pObj)
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// if we need more slots
	if (sa_ctUsed >= sa_ctSlots) {
		// allocate some more
		AllocateSlots(256);
	}

	// push it onto the stack
	sa_pItems[sa_ctUsed] = pObj;

	// increase iterator
	sa_ctUsed++;
}

/// Pop top object from the stack
template<class Type>
Type* StackArray<Type>::Pop()
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	ASSERT(sa_ctUsed > 0);

	// decrease iterator
	sa_ctUsed--;

	// get the item on top of the stack
	Type* tObject = sa_pItems[sa_ctUsed];

	// set the remaining pointer to nullptr (just to be sure)
	sa_pItems[sa_ctUsed] = nullptr;

	// return the object
	return tObject;
}

/// Pop a certain index from the stack
template<class Type>
Type* StackArray<Type>::PopAt(int32_t iIndex)
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	ASSERT(iIndex < sa_ctUsed);

	// decrease iterator
	sa_ctUsed--;

	// get the item in the stack
	Type* tObject = sa_pItems[iIndex];

	// move pointers at the right one point to the left
	for (int32_t i = iIndex; i < sa_ctUsed; i++) {
		// set pointer to next one in stack
		sa_pItems[i] = sa_pItems[i + 1];
	}

	// set last pointer to nullptr (just in case)
	sa_pItems[sa_ctUsed] = nullptr;

	// return the object
	return tObject;
}

/// Pop all objects from the stack
template<class Type>
void StackArray<Type>::PopAll()
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// for every object
	for (int32_t i = 0; i < sa_ctUsed; i++) {
		// set remaining pointer to nullptr (just to be sure)
		sa_pItems[i] = nullptr;
	}

	// reset iterator to 0
	sa_ctUsed = 0;
}

/// Clear all objects in the stack
template<class Type>
void StackArray<Type>::Clear()
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// for every object
	for (int32_t i = 0; i < sa_ctUsed; i++) {
		// delete it
		delete sa_pItems[i];
		// set remaining pointer to nullptr (just to be sure)
		sa_pItems[i] = nullptr;
	}

	// reset iterator to 0
	sa_ctUsed = 0;
}

/// Return how many objects there currently are in the stack
template<class Type>
int32_t StackArray<Type>::Count()
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif
	return sa_ctUsed;
}

/// Find the index of the given object in the stack
template<class Type>
int32_t StackArray<Type>::Find(const Type &obj)
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// for every object
	for (int32_t i = 0; i < sa_ctUsed; i++) {
		// test if it's the given one
		if (obj == *sa_pItems[i]) {
			return i;
		}
	}

	return -1;
}

/// Find the index of the given pointer in the stack
template<class Type>
int32_t StackArray<Type>::FindPointer(const Type* pObj)
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// for every object
	for (int32_t i = 0; i < sa_ctUsed; i++) {
		// test if it's the given one
		if (pObj == sa_pItems[i]) {
			return i;
		}
	}

	return -1;
}

/// Find the index of the given condition in the stack
template<class Type>
template<typename Func>
int32_t StackArray<Type>::FindAny(Func f)
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// for every object
	for (int32_t i = 0; i < sa_ctUsed; i++) {
		// test with function
		if (f(*sa_pItems[i])) {
			return i;
		}
	}
	return -1;
}

/// Returns whether the given object is currently in the stack
template<class Type>
bool StackArray<Type>::Contains(const Type &obj)
{
	return Find(obj) != -1;
}

/// Returns whether the given pointer is currently in the stack
template<class Type>
bool StackArray<Type>::ContainsPointer(const Type* pObj)
{
	return FindPointer(pObj) != -1;
}

/// Returns whether the given condition exists on any of the objects currently in the stack
template<class Type>
template<typename Func>
bool StackArray<Type>::ContainsAny(Func f)
{
	return FindAny(f) != -1;
}

template<class Type>
Type& StackArray<Type>::operator[](int32_t iIndex)
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif
	ASSERT(iIndex >= 0 && iIndex < sa_ctUsed);
	return *sa_pItems[iIndex];
}

SCRATCH_NAMESPACE_END;
<commit_msg>Implement StackArrayIterator<commit_after>/*	libscratch - Multipurpose objective C++ library.

		Copyright (c) 2013 - 2016 Angelo Geels <spansjh@gmail.com>

		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.
*/

#pragma once

#include <cstdlib>
#include <cstring>

#ifndef SCRATCH_NO_THREADSAFE
#include "Mutex.hpp"
#endif

SCRATCH_NAMESPACE_BEGIN;

template<class Type>
class StackArray;

template<class Type>
class StackArrayIterator
{
public:
	StackArray<Type>* m_stack;
	int m_index;

public:
	StackArrayIterator(StackArray<Type>* stack, int index)
	{
		m_stack = stack;
		m_index = index;
	}

	bool operator!=(const StackArrayIterator<Type> &i)
	{
		return m_index != i.m_index;
	}

	StackArrayIterator<Type> &operator++()
	{
		m_index++;
		return *this;
	}

	Type &operator*()
	{
		return (*m_stack)[m_index];
	}
};

template<class Type>
class SCRATCH_EXPORT StackArray
{
public:
	Type** sa_pItems;
	int32_t sa_ctSlots;
	int32_t sa_ctUsed;
	bool sa_bOnlyPop;
#ifndef SCRATCH_NO_THREADSAFE
	Mutex sa_mutex;
#endif

public:
	StackArray();
	StackArray(const StackArray<Type> &copy); // Note: If this ever gets called, you're most likely writing bad code.
	StackArray<Type> &operator=(const StackArray<Type> &copy);
	~StackArray();

	/// Push to the beginning of the stack, return a reference to the newly made object
	Type& PushBegin();
	/// Push to the stack, return a reference to the newly made object
	Type& Push();
	/// Push a pointer to the stack
	void Push(Type* pObj);
	/// Pop top object from the stack
	Type* Pop();
	/// Pop a certain index from the stack
	Type* PopAt(int32_t iIndex);

	/// Pop all objects from the stack
	void PopAll();
	/// Clear all objects in the stack
	void Clear();

	/// Return how many objects there currently are in the stack
	int32_t Count();

	/// Find the index of the given object in the stack
	int32_t Find(const Type &obj);
	/// Find the index of the given pointer in the stack
	int32_t FindPointer(const Type* pObj);
	/// Find the index of the given condition in the stack
	template<typename Func>
	int32_t FindAny(Func f);

	/// Returns whether the given object is currently in the stack
	bool Contains(const Type &obj);
	/// Returns whether the given pointer is currently in the stack
	bool ContainsPointer(const Type* pObj);
	/// Returns whether the given condition is currently in the stack
	template<typename Func>
	bool ContainsAny(Func f);

	Type& operator[](int32_t iIndex);

	StackArrayIterator<Type> begin();
	StackArrayIterator<Type> end();

private:
	void AllocateSlots(int32_t ctSlots);
};

template<class Type>
StackArray<Type>::StackArray()
{
	sa_pItems = nullptr;
	sa_ctSlots = 0;
	sa_ctUsed = 0;
	sa_bOnlyPop = false;
	AllocateSlots(256);
}

template<class Type>
StackArray<Type>::StackArray(const StackArray<Type> &copy)
{
	operator=(copy);
}

template<class Type>
StackArray<Type> &StackArray<Type>::operator=(const StackArray<Type> &copy)
{
	sa_pItems = nullptr;
	sa_ctSlots = 0;
	sa_ctUsed = 0;
	sa_bOnlyPop = copy.sa_bOnlyPop;

	AllocateSlots(copy.sa_ctSlots);

	// copy meta information
	sa_ctUsed = copy.sa_ctUsed;

	// copy data to other slots
	for (int32_t i = 0; i < sa_ctUsed; i++) {
		// allocate memory for it
		sa_pItems[i] = new Type;

		// this should call the copy constructor
		*sa_pItems[i] = *copy.sa_pItems[i];
	}

	return *this;
}

template<class Type>
StackArray<Type>::~StackArray()
{
	if (sa_bOnlyPop) {
		PopAll();
	} else {
		Clear();
	}

	// free allocated memory for data
	if (sa_pItems != nullptr) {
		free(sa_pItems);
	}
}

template<class Type>
void StackArray<Type>::AllocateSlots(int32_t ctSlots)
{
	// allocate some memory
	sa_ctSlots += ctSlots;
	Type** pNewMem = (Type**)malloc(sizeof(Type*) * sa_ctSlots);

	// if we allocated memory before
	if (sa_pItems != nullptr) {
		// copy data to the new memory
		memcpy(pNewMem, sa_pItems, sizeof(Type*) * (sa_ctSlots - ctSlots));
		// free previously allocated memory
		free(sa_pItems);
	}

	// update the pointer
	sa_pItems = pNewMem;
}

/// Push to the beginning of the stack, return a reference to the newly made object
template<class Type>
Type& StackArray<Type>::PushBegin()
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// PushBegin() in combination with sa_bOnlyPop will cause memory leaking if not manually Clear()'d
	ASSERT(!sa_bOnlyPop);

	// if we need more slots
	if (sa_ctUsed >= sa_ctSlots) {
		// allocate some more
		AllocateSlots(256);
	}

	// create the new object
	Type* tNewObject = new Type;

	// make some room
	for (int i = sa_ctUsed; i > 0; i--) {
		sa_pItems[i] = sa_pItems[i - 1];
	}

	// push it onto the beginning of the stack
	sa_pItems[0] = tNewObject;

	// increase iterator
	sa_ctUsed++;

	// return new object
	return *tNewObject;
}

/// Push to the stack, return a reference to the newly made object
template<class Type>
Type& StackArray<Type>::Push()
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// Push() in combination with sa_bOnlyPop will cause memory leaking if not manually Clear()'d
	ASSERT(!sa_bOnlyPop);

	// if we need more slots
	if (sa_ctUsed >= sa_ctSlots) {
		// allocate some more
		AllocateSlots(256);
	}

	// create the new object
	Type* tNewObject = new Type;

	// push it onto the stack
	sa_pItems[sa_ctUsed] = tNewObject;

	// increase iterator
	sa_ctUsed++;

	// return new object
	return *tNewObject;
}

/// Push a pointer to the stack
template<class Type>
void StackArray<Type>::Push(Type* pObj)
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// if we need more slots
	if (sa_ctUsed >= sa_ctSlots) {
		// allocate some more
		AllocateSlots(256);
	}

	// push it onto the stack
	sa_pItems[sa_ctUsed] = pObj;

	// increase iterator
	sa_ctUsed++;
}

/// Pop top object from the stack
template<class Type>
Type* StackArray<Type>::Pop()
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	ASSERT(sa_ctUsed > 0);

	// decrease iterator
	sa_ctUsed--;

	// get the item on top of the stack
	Type* tObject = sa_pItems[sa_ctUsed];

	// set the remaining pointer to nullptr (just to be sure)
	sa_pItems[sa_ctUsed] = nullptr;

	// return the object
	return tObject;
}

/// Pop a certain index from the stack
template<class Type>
Type* StackArray<Type>::PopAt(int32_t iIndex)
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	ASSERT(iIndex < sa_ctUsed);

	// decrease iterator
	sa_ctUsed--;

	// get the item in the stack
	Type* tObject = sa_pItems[iIndex];

	// move pointers at the right one point to the left
	for (int32_t i = iIndex; i < sa_ctUsed; i++) {
		// set pointer to next one in stack
		sa_pItems[i] = sa_pItems[i + 1];
	}

	// set last pointer to nullptr (just in case)
	sa_pItems[sa_ctUsed] = nullptr;

	// return the object
	return tObject;
}

/// Pop all objects from the stack
template<class Type>
void StackArray<Type>::PopAll()
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// for every object
	for (int32_t i = 0; i < sa_ctUsed; i++) {
		// set remaining pointer to nullptr (just to be sure)
		sa_pItems[i] = nullptr;
	}

	// reset iterator to 0
	sa_ctUsed = 0;
}

/// Clear all objects in the stack
template<class Type>
void StackArray<Type>::Clear()
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// for every object
	for (int32_t i = 0; i < sa_ctUsed; i++) {
		// delete it
		delete sa_pItems[i];
		// set remaining pointer to nullptr (just to be sure)
		sa_pItems[i] = nullptr;
	}

	// reset iterator to 0
	sa_ctUsed = 0;
}

/// Return how many objects there currently are in the stack
template<class Type>
int32_t StackArray<Type>::Count()
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif
	return sa_ctUsed;
}

/// Find the index of the given object in the stack
template<class Type>
int32_t StackArray<Type>::Find(const Type &obj)
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// for every object
	for (int32_t i = 0; i < sa_ctUsed; i++) {
		// test if it's the given one
		if (obj == *sa_pItems[i]) {
			return i;
		}
	}

	return -1;
}

/// Find the index of the given pointer in the stack
template<class Type>
int32_t StackArray<Type>::FindPointer(const Type* pObj)
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// for every object
	for (int32_t i = 0; i < sa_ctUsed; i++) {
		// test if it's the given one
		if (pObj == sa_pItems[i]) {
			return i;
		}
	}

	return -1;
}

/// Find the index of the given condition in the stack
template<class Type>
template<typename Func>
int32_t StackArray<Type>::FindAny(Func f)
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif

	// for every object
	for (int32_t i = 0; i < sa_ctUsed; i++) {
		// test with function
		if (f(*sa_pItems[i])) {
			return i;
		}
	}
	return -1;
}

/// Returns whether the given object is currently in the stack
template<class Type>
bool StackArray<Type>::Contains(const Type &obj)
{
	return Find(obj) != -1;
}

/// Returns whether the given pointer is currently in the stack
template<class Type>
bool StackArray<Type>::ContainsPointer(const Type* pObj)
{
	return FindPointer(pObj) != -1;
}

/// Returns whether the given condition exists on any of the objects currently in the stack
template<class Type>
template<typename Func>
bool StackArray<Type>::ContainsAny(Func f)
{
	return FindAny(f) != -1;
}

template<class Type>
Type& StackArray<Type>::operator[](int32_t iIndex)
{
#ifndef SCRATCH_NO_THREADSAFE
	MutexWait wait(sa_mutex);
#endif
	ASSERT(iIndex >= 0 && iIndex < sa_ctUsed);
	return *sa_pItems[iIndex];
}

template<class Type>
StackArrayIterator<Type> StackArray<Type>::begin()
{
	return StackArrayIterator<Type>(this, 0);
}

template<class Type>
StackArrayIterator<Type> StackArray<Type>::end()
{
	return StackArrayIterator<Type>(this, sa_ctUsed);
}

SCRATCH_NAMESPACE_END;
<|endoftext|>


<commit_before>#include "StreamSet.h"
#include <iostream>
#include <cassert>

namespace sensekit {

    StreamSet::StreamSet() :
      m_logger("StreamSet")
    { }
    StreamSet::~StreamSet()
    { }

    StreamConnection* StreamSet::create_stream_connection(const sensekit_stream_desc_t& desc)
    {
        m_logger.info("create_stream_connection for %d, %d", desc.type, desc.subtype);
        Stream* stream = find_stream_by_type_subtype_impl(desc.type, desc.subtype);

        if (stream == nullptr)
        {
            stream = create_stream_placeholder(desc);
        }

        return stream->create_connection();
    }

    bool StreamSet::destroy_stream_connection(StreamConnection* connection)
    {
        assert(connection != nullptr);

        Stream* stream = connection->get_stream();
        stream->destroy_connection(connection);

        if (!stream->has_connections()
            && !stream->is_available())
        {
            m_streamCollection.erase(stream);
            delete stream;
        }

        return true;
    }

    Stream* StreamSet::create_stream(sensekit_stream_desc_t desc,
                                     stream_callbacks_t callbacks)
    {
        Stream* stream = find_stream_by_type_subtype_impl(desc.type, desc.subtype);

        if (stream == nullptr)
        {
            m_logger.info("create_stream for %d,%d", desc.type, desc.subtype);
            stream = new Stream(desc);
            m_streamCollection.insert(stream);
        }
        else
        {
            m_logger.info("create_stream for %d,%d already exists, inflating placeholder stream", desc.type, desc.subtype);
        }

        stream->set_callbacks(callbacks);

        return stream;
    }

    Stream* StreamSet::create_stream_placeholder(sensekit_stream_desc_t desc)
    {
        m_logger.info("create_stream_placeholder for %d,%d", desc.type, desc.subtype);
        Stream* stream = new Stream(desc);
        m_streamCollection.insert(stream);

        return stream;
    }

    void StreamSet::destroy_stream(Stream* stream)
    {
        assert(stream != nullptr);

        auto it = m_streamCollection.find(stream);
        if (it != m_streamCollection.end())
        {
            (*it)->clear_callbacks();
        }
    }

    bool StreamSet::is_member(sensekit_stream_t streamHandle) const
    {
        assert(streamHandle != nullptr);

        Stream* stream = reinterpret_cast<Stream*>(streamHandle);
        return m_streamCollection.find(stream) != m_streamCollection.end();
    }

    sensekit_stream_t StreamSet::find_stream_by_type_subtype(sensekit_stream_type_t type,
                                                             sensekit_stream_subtype_t subtype) const
    {
        Stream* stream = find_stream_by_type_subtype_impl(type, subtype);

        sensekit_stream_t streamHandle = reinterpret_cast<sensekit_stream_t>(stream);
        return streamHandle;
    }

    void StreamSet::visit_streams(std::function<void(Stream*)> visitorMethod)
    {
        for (auto* stream : m_streamCollection)
        {
            visitorMethod(stream);
        }
    }

    Stream* StreamSet::find_stream_by_type_subtype_impl(sensekit_stream_type_t type,
                                                        sensekit_stream_subtype_t subtype) const
    {
        for (auto* stream : m_streamCollection)
        {
            const sensekit_stream_desc_t& desc = stream->get_description();

            if (desc.type == type && desc.subtype == subtype)
            {
                return stream;
            }
        }

        return nullptr;
    }
}
<commit_msg>Add warning and assert to protect against duplicate plugin streams<commit_after>#include "StreamSet.h"
#include <iostream>
#include <cassert>

namespace sensekit {

    StreamSet::StreamSet() :
      m_logger("StreamSet")
    { }
    StreamSet::~StreamSet()
    { }

    StreamConnection* StreamSet::create_stream_connection(const sensekit_stream_desc_t& desc)
    {
        m_logger.info("create_stream_connection for %d, %d", desc.type, desc.subtype);
        Stream* stream = find_stream_by_type_subtype_impl(desc.type, desc.subtype);

        if (stream == nullptr)
        {
            stream = create_stream_placeholder(desc);
        }

        return stream->create_connection();
    }

    bool StreamSet::destroy_stream_connection(StreamConnection* connection)
    {
        assert(connection != nullptr);

        Stream* stream = connection->get_stream();
        stream->destroy_connection(connection);

        if (!stream->has_connections()
            && !stream->is_available())
        {
            m_streamCollection.erase(stream);
            delete stream;
        }

        return true;
    }

    Stream* StreamSet::create_stream(sensekit_stream_desc_t desc,
                                     stream_callbacks_t callbacks)
    {
        Stream* stream = find_stream_by_type_subtype_impl(desc.type, desc.subtype);

        if (stream == nullptr)
        {
            m_logger.info("create_stream for %d,%d", desc.type, desc.subtype);
            stream = new Stream(desc);
            m_streamCollection.insert(stream);
        }
        else if (stream->is_available())
        {
            m_logger.warn("create_stream for %d,%d already exists, already inflated", desc.type, desc.subtype);
            assert(!stream->is_available());
            return nullptr;
        }
        else
        {
            m_logger.info("create_stream for %d,%d already exists, inflating placeholder stream", desc.type, desc.subtype);
        }

        stream->set_callbacks(callbacks);

        return stream;
    }

    Stream* StreamSet::create_stream_placeholder(sensekit_stream_desc_t desc)
    {
        m_logger.info("create_stream_placeholder for %d,%d", desc.type, desc.subtype);
        Stream* stream = new Stream(desc);
        m_streamCollection.insert(stream);

        return stream;
    }

    void StreamSet::destroy_stream(Stream* stream)
    {
        assert(stream != nullptr);

        auto it = m_streamCollection.find(stream);
        if (it != m_streamCollection.end())
        {
            (*it)->clear_callbacks();
        }
    }

    bool StreamSet::is_member(sensekit_stream_t streamHandle) const
    {
        assert(streamHandle != nullptr);

        Stream* stream = reinterpret_cast<Stream*>(streamHandle);
        return m_streamCollection.find(stream) != m_streamCollection.end();
    }

    sensekit_stream_t StreamSet::find_stream_by_type_subtype(sensekit_stream_type_t type,
                                                             sensekit_stream_subtype_t subtype) const
    {
        Stream* stream = find_stream_by_type_subtype_impl(type, subtype);

        sensekit_stream_t streamHandle = reinterpret_cast<sensekit_stream_t>(stream);
        return streamHandle;
    }

    void StreamSet::visit_streams(std::function<void(Stream*)> visitorMethod)
    {
        for (auto* stream : m_streamCollection)
        {
            visitorMethod(stream);
        }
    }

    Stream* StreamSet::find_stream_by_type_subtype_impl(sensekit_stream_type_t type,
                                                        sensekit_stream_subtype_t subtype) const
    {
        for (auto* stream : m_streamCollection)
        {
            const sensekit_stream_desc_t& desc = stream->get_description();

            if (desc.type == type && desc.subtype == subtype)
            {
                return stream;
            }
        }

        return nullptr;
    }
}
<|endoftext|>