<commit_before>#include <iostream>
#include <algorithm>
#include <memory>
#include <set>

#include <disir/disir.h>
#include <disir/fslib/util.h>
#include <disir/fslib/json.h>

#include <disir/cli/command_verify.h>
#include <disir/cli/args.hxx>

using namespace disir;

CommandVerify::CommandVerify(void)
    : Command ("verify")
{
}

int
CommandVerify::handle_command (std::vector<std::string> &args)
{
    std::stringstream group_description;
    args::ArgumentParser parser ("Verify configuration entries and their associated molds.");

    setup_parser (parser);
    parser.Prog ("disir verify");

    args::HelpFlag help (parser, "help", "Display the list help menu and exit.",
                         args::Matcher{'h', "help"});

    group_description << "Specify the group to operate on. The loaded default is: "
                      << m_cli->group_id();
    args::ValueFlag<std::string> opt_group_id (parser, "NAME", group_description.str(),
                                               args::Matcher{"group"});

    args::ValueFlag<std::string> opt_text_mold (parser, "TEXT MOLD",
                                                "Verify mold from disk.",
                                                args::Matcher{"text-mold"});
    args::PositionalList<std::string> opt_entries (parser, "entry",
                                                   "A list of entries to verify.");

    try
    {
        parser.ParseArgs (args);
    }
    catch (args::Help)
    {
        std::cout << parser;
        return (0);
    }
    catch (args::ParseError e)
    {
        std::cout << "ParseError: " << e.what() << std::endl;
        std::cerr << "See '" << m_cli->m_program_name << " --help'" << std::endl;
        return (1);
    }
    catch (args::ValidationError e)
    {
        std::cerr << "ValidationError: " << e.what() << std::endl;
        std::cerr << "See '" << m_cli->m_program_name << " --help'" << std::endl;
        return (1);
    }

    if (opt_text_mold)
    {
        enum disir_status status;
        struct disir_mold *mold = NULL;
        struct stat statbuf;
        FILE *file = NULL;
        std::string filepath_mold;

        filepath_mold = args::get (opt_text_mold);

        std::cout << "mold text: " << filepath_mold << std::endl;
        status = fslib_stat_filepath (m_cli->disir(), filepath_mold.c_str(), &statbuf);
        if (status != DISIR_STATUS_OK)
        {
            std::cout << "  " << disir_error (m_cli->disir()) << std::endl;
            return (1);
        }

        file = fopen (filepath_mold.c_str(), "r");
        if (file == NULL)
        {
            std::cerr << "opening for reading " << filepath_mold.c_str()
                                                << ": " <<strerror (errno)
                                                << std::endl;
            return (1);
        }

        // TODO: Check file extension - switch on available molds
        // XXX Hardcode to json unserialize for now
        status = dio_json_unserialize_mold (m_cli->disir(), file, &mold);
        print_verify (status, filepath_mold.c_str(), NULL, mold);

        // TODO: Take entries arguments and verify them as config with this mold ( if the mold is OK)

        // Cleanup
        if (mold)
        {
            disir_mold_finished (&mold);
        }
        fclose (file);

        return (0);
    }

    if (opt_group_id && setup_group (args::get(opt_group_id)))
    {
        return (1);
    }

    // Get the set of entries to verify
    std::set<std::string> entries_to_verify;
    if (opt_entries)
    {
        m_cli->verbose() << "Verifying entries in user supplied list." << std::endl;
        for (const auto& entry : args::get (opt_entries))
        {
            entries_to_verify.insert (entry);
        }
    }
    else
    {
        m_cli->verbose() << "Verifying all available entries." << std::endl;

        enum disir_status status;
        struct disir_entry *config_entries;
        struct disir_entry *next;
        struct disir_entry *current;

        status = disir_config_entries (m_cli->disir(), m_cli->group_id().c_str(), &config_entries);
        if (status != DISIR_STATUS_OK)
        {
            std::cerr << "Failed to retrieve available configs: "
                      << disir_error (m_cli->disir()) << std::endl;
            return (-1);
        }

        current = config_entries;
        while (current != NULL)
        {
            next = current->next;

            entries_to_verify.insert (std::string(current->de_entry_name));

            disir_entry_finished (&current);
            current = next;
        }
    }

    // Iterate each entry, attempt to verify and print result
    std::cout << "In group " << m_cli->group_id() << std::endl;
    if (entries_to_verify.empty())
    {
        std::cout << "  There are no available configs." << std::endl;
        return (0);
    }

    m_cli->verbose() << "There are " << entries_to_verify.size()
                     << " entries to verify." << std::endl;
    std::cout << std::endl;
    for (const auto& entry : entries_to_verify)
    {
        // We simply read the config entry - the return status shall indicate whether it is invalid or not
        enum disir_status status;
        struct disir_config *config = NULL;

        status = disir_config_read (m_cli->disir(), m_cli->group_id().c_str(),
                                    entry.c_str(), NULL, &config);

        print_verify (status, entry.c_str(), config, NULL);
        if (config)
            disir_config_finished (&config);
    }
    std::cout << std::endl;

    return (0);
}

void
CommandVerify::print_verify (enum disir_status status, const char *entry,
                             struct disir_config *config,
                             struct disir_mold *mold)
{
    struct disir_collection *collection = NULL;
    struct disir_context *context = NULL;
    char *resolved_name = NULL;

    if (status == DISIR_STATUS_OK)
    {
        std::cout << "    OK:      " << entry << std::endl;
    }
    else if (status == DISIR_STATUS_INVALID_CONTEXT)
    {
        std::cout << "    INVALID: " << entry << std::endl;

        if (config)
            status = disir_config_valid (config, &collection);
        else if (mold)
            status = disir_mold_valid (mold, &collection);
        if (collection)
        {
            do
            {
                status = dc_collection_next (collection, &context);
                if (status == DISIR_STATUS_EXHAUSTED)
                    break;
                if (status != DISIR_STATUS_OK)
                {
                    std::cerr << "failed to retrieve collection item: "
                              << disir_status_string (status) << std::endl;
                    break;
                }

                status = dc_resolve_root_name (context, &resolved_name);
                const char *name;
                if (status != DISIR_STATUS_OK)
                {
                    name = "UNRESOLVED";
                }
                else
                {
                    name = resolved_name;
                }

                if (dc_context_error (context))
                {
                    std::cout << "               " << name << ": "
                              << dc_context_error (context) << std::endl;
                }
                else if (dc_context_type (context) == DISIR_CONTEXT_KEYVAL)
                {
                    std::cout << "               " << name << ": "
                              << "(entry missing error)" << std::endl;
                }
                if (resolved_name != NULL)
                {
                    free (resolved_name);
                }
                dc_putcontext (&context);
            } while (1);

            if (context)
            {
                dc_putcontext (&context);
            }
            if (collection)
            {
                dc_collection_finished (&collection);
            }
        }
    }
    else
    {
        std::cout << "    ERROR:   " << entry << std::endl;
        std::cout << "               " << disir_status_string (status) << std::endl;
        if (disir_error (m_cli->disir()) != NULL)
        {
            std::cout << "             " << disir_error (m_cli->disir()) << std::endl;
        }
        else
        {
            std::cout << "             (no error registered)" << std::endl;
        }
    }
}
<commit_msg>cli verify: use direct filepath function for --text-mold option<commit_after>#include <iostream>
#include <algorithm>
#include <memory>
#include <set>

#include <disir/disir.h>
#include <disir/fslib/util.h>
#include <disir/fslib/json.h>

#include <disir/cli/command_verify.h>
#include <disir/cli/args.hxx>

using namespace disir;

CommandVerify::CommandVerify(void)
    : Command ("verify")
{
}

int
CommandVerify::handle_command (std::vector<std::string> &args)
{
    std::stringstream group_description;
    args::ArgumentParser parser ("Verify configuration entries and their associated molds.");

    setup_parser (parser);
    parser.Prog ("disir verify");

    args::HelpFlag help (parser, "help", "Display the list help menu and exit.",
                         args::Matcher{'h', "help"});

    group_description << "Specify the group to operate on. The loaded default is: "
                      << m_cli->group_id();
    args::ValueFlag<std::string> opt_group_id (parser, "NAME", group_description.str(),
                                               args::Matcher{"group"});

    args::ValueFlag<std::string> opt_text_mold (parser, "TEXT MOLD",
                                                "Verify mold from disk.",
                                                args::Matcher{"text-mold"});
    args::PositionalList<std::string> opt_entries (parser, "entry",
                                                   "A list of entries to verify.");

    try
    {
        parser.ParseArgs (args);
    }
    catch (args::Help)
    {
        std::cout << parser;
        return (0);
    }
    catch (args::ParseError e)
    {
        std::cout << "ParseError: " << e.what() << std::endl;
        std::cerr << "See '" << m_cli->m_program_name << " --help'" << std::endl;
        return (1);
    }
    catch (args::ValidationError e)
    {
        std::cerr << "ValidationError: " << e.what() << std::endl;
        std::cerr << "See '" << m_cli->m_program_name << " --help'" << std::endl;
        return (1);
    }

    if (opt_text_mold)
    {
        enum disir_status status;
        struct disir_mold *mold = NULL;
        struct stat statbuf;
        std::string filepath_mold;

        filepath_mold = args::get (opt_text_mold);

        std::cout << "mold text: " << filepath_mold << std::endl;
        status = fslib_stat_filepath (m_cli->disir(), filepath_mold.c_str(), &statbuf);
        if (status != DISIR_STATUS_OK)
        {
            std::cout << "  " << disir_error (m_cli->disir()) << std::endl;
            return (1);
        }

        // TODO: Check file extension - switch on available molds
        // XXX Hardcode to json unserialize for now
        status = dio_json_unserialize_mold_filepath (m_cli->disir(), filepath_mold.c_str(), &mold);
        print_verify (status, filepath_mold.c_str(), NULL, mold);

        // TODO: Take entries arguments and verify them as config with this mold ( if the mold is OK)

        // Cleanup
        if (mold)
        {
            disir_mold_finished (&mold);
        }

        return (0);
    }

    if (opt_group_id && setup_group (args::get(opt_group_id)))
    {
        return (1);
    }

    // Get the set of entries to verify
    std::set<std::string> entries_to_verify;
    if (opt_entries)
    {
        m_cli->verbose() << "Verifying entries in user supplied list." << std::endl;
        for (const auto& entry : args::get (opt_entries))
        {
            entries_to_verify.insert (entry);
        }
    }
    else
    {
        m_cli->verbose() << "Verifying all available entries." << std::endl;

        enum disir_status status;
        struct disir_entry *config_entries;
        struct disir_entry *next;
        struct disir_entry *current;

        status = disir_config_entries (m_cli->disir(), m_cli->group_id().c_str(), &config_entries);
        if (status != DISIR_STATUS_OK)
        {
            std::cerr << "Failed to retrieve available configs: "
                      << disir_error (m_cli->disir()) << std::endl;
            return (-1);
        }

        current = config_entries;
        while (current != NULL)
        {
            next = current->next;

            entries_to_verify.insert (std::string(current->de_entry_name));

            disir_entry_finished (&current);
            current = next;
        }
    }

    // Iterate each entry, attempt to verify and print result
    std::cout << "In group " << m_cli->group_id() << std::endl;
    if (entries_to_verify.empty())
    {
        std::cout << "  There are no available configs." << std::endl;
        return (0);
    }

    m_cli->verbose() << "There are " << entries_to_verify.size()
                     << " entries to verify." << std::endl;
    std::cout << std::endl;
    for (const auto& entry : entries_to_verify)
    {
        // We simply read the config entry - the return status shall indicate whether it is invalid or not
        enum disir_status status;
        struct disir_config *config = NULL;

        status = disir_config_read (m_cli->disir(), m_cli->group_id().c_str(),
                                    entry.c_str(), NULL, &config);

        print_verify (status, entry.c_str(), config, NULL);
        if (config)
            disir_config_finished (&config);
    }
    std::cout << std::endl;

    return (0);
}

void
CommandVerify::print_verify (enum disir_status status, const char *entry,
                             struct disir_config *config,
                             struct disir_mold *mold)
{
    struct disir_collection *collection = NULL;
    struct disir_context *context = NULL;
    char *resolved_name = NULL;

    if (status == DISIR_STATUS_OK)
    {
        std::cout << "    OK:      " << entry << std::endl;
    }
    else if (status == DISIR_STATUS_INVALID_CONTEXT)
    {
        std::cout << "    INVALID: " << entry << std::endl;

        if (config)
            status = disir_config_valid (config, &collection);
        else if (mold)
            status = disir_mold_valid (mold, &collection);
        if (collection)
        {
            do
            {
                status = dc_collection_next (collection, &context);
                if (status == DISIR_STATUS_EXHAUSTED)
                    break;
                if (status != DISIR_STATUS_OK)
                {
                    std::cerr << "failed to retrieve collection item: "
                              << disir_status_string (status) << std::endl;
                    break;
                }

                status = dc_resolve_root_name (context, &resolved_name);
                const char *name;
                if (status != DISIR_STATUS_OK)
                {
                    name = "UNRESOLVED";
                }
                else
                {
                    name = resolved_name;
                }

                if (dc_context_error (context))
                {
                    std::cout << "               " << name << ": "
                              << dc_context_error (context) << std::endl;
                }
                else if (dc_context_type (context) == DISIR_CONTEXT_KEYVAL)
                {
                    std::cout << "               " << name << ": "
                              << "(entry missing error)" << std::endl;
                }
                if (resolved_name != NULL)
                {
                    free (resolved_name);
                }
                dc_putcontext (&context);
            } while (1);

            if (context)
            {
                dc_putcontext (&context);
            }
            if (collection)
            {
                dc_collection_finished (&collection);
            }
        }
    }
    else
    {
        std::cout << "    ERROR:   " << entry << std::endl;
        std::cout << "               " << disir_status_string (status) << std::endl;
        if (disir_error (m_cli->disir()) != NULL)
        {
            std::cout << "             " << disir_error (m_cli->disir()) << std::endl;
        }
        else
        {
            std::cout << "             (no error registered)" << std::endl;
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>work on hellothread.cpp<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2014 Nicholas Corgan (n.corgan@gmail.com)
 *
 * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
 * or copy at http://opensource.org/licenses/MIT)
 */

#include <string>

#include <boost/locale/encoding_utf.hpp>

#include <pkmn/enums.hpp>
#include <pkmn/database/queries.hpp>
#include <pkmn/types/pokemon_text.hpp>

#include "../library_bridge.hpp"
#include "items.hpp"
#include "pokemon.hpp"
#include "trainer.hpp"

//Copied from PokeLib/lib/Trainer.cpp

uint8_t itemNum[3][8] = {
    {165,50,100,12,40,64,15,30},
    {165,50,100,12,40,64,15,30},
    {165,43,100,12,40,64,24,30},
};
uint16_t itemOffset[3][8] = {
    {0x624,0x8B8,0x980,0xB10,0xB40,0xBE0,0xCE0,0xD1C},
    {0x630,0x8C4,0x98C,0xB1C,0xB4C,0xBEC,0xCEC,0xD28},
    {0x644,0x8D8,0x9A4,0xB34,0xB64,0xC04,0xD04,0xD64},
};

enum {
    offsetName = 0,
    offsetTID, offsetSID, offsetMoney,
    offsetGender, offsetCountry,
    offsetBadges, offsetBadges2,
    offsetAvatar, offsetRival,
};

uint16_t tOffset[][3] = {
    {0x0064, 0x0068, 0x0064}, //TNAME (x8 16 bit)
    {0x0074, 0x0078, 0x0074}, //TID
    {0x0076, 0x007A, 0x0076}, //SID
    {0x0078, 0x007C, 0x0078}, //MONEY
    {0x007C, 0x0080, 0x007C}, //GENDER
    {0x007D, 0x0081, 0x007D}, //COUNTRY
    {0x007E, 0x0082, 0x007E}, //BADGES (HGSS Johto)
    {0x007E, 0x0082, 0x0083}, //BADGES 2 (HGSS Kanto, others mirror)
    {0x007F, 0x0083, 0x007F}, //Multiplayer Avatar
    {0x25A8, 0x27E8, 0x22D4}, //Rival Name (x8 16 bit)
};

namespace pkmn
{
    namespace conversions
    {
        trainer::sptr import_gen1_trainer(rpokesav_gen1_sptr sav)
        {
            pokemon_text trainer_name = sav->get_trainer_name();

            /*
             * Generation I has no way to distinguish between games, so just
             * use Yellow. There aren't enough differences to make a difference.
             */
            trainer::sptr libpkmn_trainer = trainer::make(Versions::YELLOW,
                                                          sav->get_trainer_name(),
                                                          Genders::MALE);

            libpkmn_trainer->set_id(sav->get_trainer_id());
            libpkmn_trainer->set_money(sav->get_money());
            std::array<rpokesav::gen1_item_t,20> rpokesav_bag = sav->get_bag_items();

            import_gen1_bag(rpokesav_bag, libpkmn_trainer->get_bag(),
                            sav->get_bag_item_count());

            std::array<rpokesav::gen1_pokemon,6> rpokesav_team = sav->get_team();
            for(size_t i = 0; i < sav->get_team_size(); i++)
            {
                libpkmn_trainer->set_pokemon(i+1, import_gen1_pokemon(rpokesav_team[i]));
            }

            return libpkmn_trainer;
        }

        trainer::sptr import_gen3_trainer(gba_save_t* libspec_save)
        {
            unsigned int _game_ids[] = {Versions::NONE, Versions::RUBY,
                                        Versions::EMERALD, Versions::FIRERED};

            gba_trainer_t* libspec_trainer = gba_get_trainer(libspec_save);
            gba_party_t* libspec_party = gba_get_party(libspec_save);

            uint16_t name_arr[7];
            gba_text_to_ucs2((char16_t*)name_arr, (char8_t*)libspec_trainer->name, 7);
            pokemon_text trainer_name(boost::locale::conv::utf_to_utf<wchar_t>(name_arr));

            unsigned int game_id = _game_ids[libspec_save->type];
            unsigned int gender_id = (libspec_trainer->gender == 0) ? Genders::MALE : Genders::FEMALE;

            trainer::sptr libpkmn_trainer = trainer::make(game_id, trainer_name, gender_id);

            for(size_t i = 0; i < libspec_party->size; i++)
            {
                libpkmn_trainer->set_pokemon(i+1, conversions::import_gen3_pokemon(&(libspec_party->pokemon[i]),
                                                                                libspec_save->type));
            }
            conversions::import_gen3_items(libpkmn_trainer->get_bag(), libspec_save);

            return libpkmn_trainer;
        }

        trainer::sptr import_gen4_trainer(pokelib_sptr pokelib_save)
        {
            PokeLib::Trainer* pokelib_trainer = pokelib_save->getTrainer();

            unsigned int game_id;
            unsigned int save_type = pokelib_save->getSaveType();

            /*
             * The save type doesn't distinguish between D/P or HG/SS, but
             * Pokemon have a field that does. Parse the party to try and
             * find a Pokemon with the same ID as the trainer and get
             * the correct game ID from that. If no such Pokemon exists,
             * use the save type.
             */
            PokeLib::Party* pokelib_party = pokelib_save->getParty();
            uint16_t pokelib_public_id = pokelib_trainer->getPID();
            uint16_t pokelib_secret_id = pokelib_trainer->getSID();
            bool found = false;

            for(size_t i = 1; i <= (pokelib_party->count()); i++)
            {
                PokeLib::Pokemon pokelib_pokemon = pokelib_party->getPokemon(i);

                if((pokelib_pokemon.pkm->pkm.ot_id == pokelib_public_id)
                    and (pokelib_pokemon.pkm->pkm.ot_sid == pokelib_secret_id))
                {
                    game_id = hometown_to_libpkmn_game(pokelib_pokemon.pkm->pkm.hometown);
                    found = true;
                    break;
                }
            }
            if(not found)
            {
                switch(save_type)
                {
                    case PokeLib::DP:
                        game_id = Versions::DIAMOND;
                        break;

                    case PokeLib::PLAT:
                        game_id = Versions::PLATINUM;
                        break;

                    default:
                        game_id = Versions::HEARTGOLD;
                        break;
                }
            }
            pokemon_text trainer_name = pokelib_trainer->getName();
            unsigned int gender = (pokelib_trainer->getFemale()) ? Genders::FEMALE : Genders::MALE;

            trainer::sptr libpkmn_trainer = trainer::make(game_id, trainer_name, gender);

            libpkmn_trainer->set_public_id(pokelib_public_id);
            libpkmn_trainer->set_secret_id(pokelib_secret_id);
            libpkmn_trainer->set_money(pokelib_trainer->getMoney());

            import_gen4_items(libpkmn_trainer->get_bag(), *pokelib_trainer);

            for(size_t i = 1; i <= (unsigned int)(pokelib_party->count()); i++)
            {
                PokeLib::Pokemon pokelib_pokemon = pokelib_party->getPokemon(i);

                if(pokelib_pokemon.pkm->pkm.species == 0) break;
                else libpkmn_trainer->set_pokemon(i, import_gen4_pokemon(pokelib_pokemon));
            }

            return libpkmn_trainer;
        }

        void export_gen4_trainer(trainer::sptr libpkmn_trainer, pokelib_sptr pokelib_save)
        {
            PokeLib::Trainer* pokelib_trainer = pokelib_save->getTrainer();

            pokelib_trainer->setName(libpkmn_trainer->get_name());
            pokelib_trainer->setFemale(libpkmn_trainer->get_gender().std_string() == "Female");

            export_gen4_items(libpkmn_trainer->get_bag(), pokelib_trainer);

            //PokeLib::Save::getTrainer returns new trainer
            pokelib_save->setTrainer(pokelib_trainer);

            PokeLib::Party* pokelib_party = pokelib_save->getParty();

            for(int i = 1; i <= 6; i++)
            {
                team_pokemon::sptr t_pkmn = libpkmn_trainer->get_pokemon(i);
                if(t_pkmn->get_species_id() == Species::NONE) break;
                else pokelib_party->setPokemon(i, export_gen4_pokemon(t_pkmn));
            }
        }

        trainer::sptr import_gen5_trainer(pkmds_g5_sptr pkmds_save)
        {
            /*
             * PKMDS has no way of distinguishing between the different Gen 5
             * games, and trainer gender hasn't been reverse engineered. To
             * find each, we will parse the party to determine the relevant
             * information from Pokemon with matching ID's. If none are found,
             * use defaults.
             */
            std::array<party_pkm, 6> pkmds_party = pkmds_save->cur.party.pokemon;
            uint16_t pkmds_public_id = pkmds_save->cur.tid;
            uint16_t pkmds_secret_id = pkmds_save->cur.sid;
            unsigned int game_id, gender;
            bool found = false;

            for(size_t i = 0; i < pkmds_party.size(); i++)
            {
                pokemon_obj* pkmds_pokemon = &(pkmds_party[i]);

                if((pkmds_pokemon->tid == pkmds_public_id) and (pkmds_pokemon->sid == pkmds_secret_id))
                {
                    game_id = hometown_to_libpkmn_game(int(pkmds_pokemon->hometown));
                    gender = (get_gen_456_otgender((reinterpret_cast<uint8_t*>(&(pkmds_pokemon->ball)+1)))) ?
                             Genders::FEMALE : Genders::MALE;
                    found = true;
                    break;
                }
            }
            if(not found)
            {
                game_id = Versions::BLACK_2;
                gender = Genders::MALE;
            }

            pokemon_text pkmds_name = ::getsavtrainername(pkmds_save->cur);
            trainer::sptr libpkmn_trainer = trainer::make(game_id, pkmds_name, gender);

            libpkmn_trainer->set_public_id(pkmds_public_id);
            libpkmn_trainer->set_secret_id(pkmds_secret_id);
            libpkmn_trainer->set_money(0);

            import_gen5_items(libpkmn_trainer->get_bag(), &(pkmds_save->cur.bag));

            for(size_t i = 0; i < pkmds_party.size(); i++)
            {
                ::decryptpkm(&pkmds_party[i]);
                libpkmn_trainer->set_pokemon(i+1, import_gen5_pokemon(&(pkmds_party[i])));
                ::encryptpkm(&pkmds_party[i]);
            }

            return libpkmn_trainer;
        }

        void export_gen5_trainer(trainer::sptr libpkmn_trainer, pkmds_g5_sptr pkmds_save)
        {
            std::wstring trainer_name = libpkmn_trainer->get_name();

            for(size_t i = 0; i < trainer_name.size(); i++)
            {
                pkmds_save->cur.trainername[i] = trainer_name[i];
            }

            export_gen5_items(libpkmn_trainer->get_bag(), &(pkmds_save->cur.bag));

            for(size_t i = 1; i <= 6; i++)
            {
                team_pokemon::sptr t_pkmn = libpkmn_trainer->get_pokemon(i);

                if(t_pkmn->get_species_id() == Species::NONE) break;
                else export_gen5_pokemon(t_pkmn, &(pkmds_save->cur.party.pokemon[i-1]));
            }
        }
    } /* namespace conversions */
} /* namespace pkmn */
<commit_msg>gen3: import money<commit_after>/*
 * Copyright (c) 2013-2014 Nicholas Corgan (n.corgan@gmail.com)
 *
 * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
 * or copy at http://opensource.org/licenses/MIT)
 */

#include <string>

#include <boost/locale/encoding_utf.hpp>

#include <pkmn/enums.hpp>
#include <pkmn/database/queries.hpp>
#include <pkmn/types/pokemon_text.hpp>

#include "../library_bridge.hpp"
#include "items.hpp"
#include "pokemon.hpp"
#include "trainer.hpp"

//Copied from PokeLib/lib/Trainer.cpp

uint8_t itemNum[3][8] = {
    {165,50,100,12,40,64,15,30},
    {165,50,100,12,40,64,15,30},
    {165,43,100,12,40,64,24,30},
};
uint16_t itemOffset[3][8] = {
    {0x624,0x8B8,0x980,0xB10,0xB40,0xBE0,0xCE0,0xD1C},
    {0x630,0x8C4,0x98C,0xB1C,0xB4C,0xBEC,0xCEC,0xD28},
    {0x644,0x8D8,0x9A4,0xB34,0xB64,0xC04,0xD04,0xD64},
};

enum {
    offsetName = 0,
    offsetTID, offsetSID, offsetMoney,
    offsetGender, offsetCountry,
    offsetBadges, offsetBadges2,
    offsetAvatar, offsetRival,
};

uint16_t tOffset[][3] = {
    {0x0064, 0x0068, 0x0064}, //TNAME (x8 16 bit)
    {0x0074, 0x0078, 0x0074}, //TID
    {0x0076, 0x007A, 0x0076}, //SID
    {0x0078, 0x007C, 0x0078}, //MONEY
    {0x007C, 0x0080, 0x007C}, //GENDER
    {0x007D, 0x0081, 0x007D}, //COUNTRY
    {0x007E, 0x0082, 0x007E}, //BADGES (HGSS Johto)
    {0x007E, 0x0082, 0x0083}, //BADGES 2 (HGSS Kanto, others mirror)
    {0x007F, 0x0083, 0x007F}, //Multiplayer Avatar
    {0x25A8, 0x27E8, 0x22D4}, //Rival Name (x8 16 bit)
};

namespace pkmn
{
    namespace conversions
    {
        trainer::sptr import_gen1_trainer(rpokesav_gen1_sptr sav)
        {
            pokemon_text trainer_name = sav->get_trainer_name();

            /*
             * Generation I has no way to distinguish between games, so just
             * use Yellow. There aren't enough differences to make a difference.
             */
            trainer::sptr libpkmn_trainer = trainer::make(Versions::YELLOW,
                                                          sav->get_trainer_name(),
                                                          Genders::MALE);

            libpkmn_trainer->set_id(sav->get_trainer_id());
            libpkmn_trainer->set_money(sav->get_money());
            std::array<rpokesav::gen1_item_t,20> rpokesav_bag = sav->get_bag_items();

            import_gen1_bag(rpokesav_bag, libpkmn_trainer->get_bag(),
                            sav->get_bag_item_count());

            std::array<rpokesav::gen1_pokemon,6> rpokesav_team = sav->get_team();
            for(size_t i = 0; i < sav->get_team_size(); i++)
            {
                libpkmn_trainer->set_pokemon(i+1, import_gen1_pokemon(rpokesav_team[i]));
            }

            return libpkmn_trainer;
        }

        trainer::sptr import_gen3_trainer(gba_save_t* libspec_save)
        {
            unsigned int _game_ids[] = {Versions::NONE, Versions::RUBY,
                                        Versions::EMERALD, Versions::FIRERED};

            gba_trainer_t* libspec_trainer = gba_get_trainer(libspec_save);
            gba_party_t* libspec_party = gba_get_party(libspec_save);

            uint16_t name_arr[7];
            gba_text_to_ucs2((char16_t*)name_arr, (char8_t*)libspec_trainer->name, 7);
            pokemon_text trainer_name(boost::locale::conv::utf_to_utf<wchar_t>(name_arr));

            unsigned int game_id = _game_ids[libspec_save->type];
            unsigned int gender_id = (libspec_trainer->gender == 0) ? Genders::MALE : Genders::FEMALE;

            trainer::sptr libpkmn_trainer = trainer::make(game_id, trainer_name, gender_id);
            libpkmn_trainer->set_money(gba_get_money(libspec_save));

            for(size_t i = 0; i < libspec_party->size; i++)
            {
                libpkmn_trainer->set_pokemon(i+1, conversions::import_gen3_pokemon(&(libspec_party->pokemon[i]),
                                                                                libspec_save->type));
            }
            conversions::import_gen3_items(libpkmn_trainer->get_bag(), libspec_save);

            return libpkmn_trainer;
        }

        trainer::sptr import_gen4_trainer(pokelib_sptr pokelib_save)
        {
            PokeLib::Trainer* pokelib_trainer = pokelib_save->getTrainer();

            unsigned int game_id;
            unsigned int save_type = pokelib_save->getSaveType();

            /*
             * The save type doesn't distinguish between D/P or HG/SS, but
             * Pokemon have a field that does. Parse the party to try and
             * find a Pokemon with the same ID as the trainer and get
             * the correct game ID from that. If no such Pokemon exists,
             * use the save type.
             */
            PokeLib::Party* pokelib_party = pokelib_save->getParty();
            uint16_t pokelib_public_id = pokelib_trainer->getPID();
            uint16_t pokelib_secret_id = pokelib_trainer->getSID();
            bool found = false;

            for(size_t i = 1; i <= (pokelib_party->count()); i++)
            {
                PokeLib::Pokemon pokelib_pokemon = pokelib_party->getPokemon(i);

                if((pokelib_pokemon.pkm->pkm.ot_id == pokelib_public_id)
                    and (pokelib_pokemon.pkm->pkm.ot_sid == pokelib_secret_id))
                {
                    game_id = hometown_to_libpkmn_game(pokelib_pokemon.pkm->pkm.hometown);
                    found = true;
                    break;
                }
            }
            if(not found)
            {
                switch(save_type)
                {
                    case PokeLib::DP:
                        game_id = Versions::DIAMOND;
                        break;

                    case PokeLib::PLAT:
                        game_id = Versions::PLATINUM;
                        break;

                    default:
                        game_id = Versions::HEARTGOLD;
                        break;
                }
            }
            pokemon_text trainer_name = pokelib_trainer->getName();
            unsigned int gender = (pokelib_trainer->getFemale()) ? Genders::FEMALE : Genders::MALE;

            trainer::sptr libpkmn_trainer = trainer::make(game_id, trainer_name, gender);

            libpkmn_trainer->set_public_id(pokelib_public_id);
            libpkmn_trainer->set_secret_id(pokelib_secret_id);
            libpkmn_trainer->set_money(pokelib_trainer->getMoney());

            import_gen4_items(libpkmn_trainer->get_bag(), *pokelib_trainer);

            for(size_t i = 1; i <= (unsigned int)(pokelib_party->count()); i++)
            {
                PokeLib::Pokemon pokelib_pokemon = pokelib_party->getPokemon(i);

                if(pokelib_pokemon.pkm->pkm.species == 0) break;
                else libpkmn_trainer->set_pokemon(i, import_gen4_pokemon(pokelib_pokemon));
            }

            return libpkmn_trainer;
        }

        void export_gen4_trainer(trainer::sptr libpkmn_trainer, pokelib_sptr pokelib_save)
        {
            PokeLib::Trainer* pokelib_trainer = pokelib_save->getTrainer();

            pokelib_trainer->setName(libpkmn_trainer->get_name());
            pokelib_trainer->setFemale(libpkmn_trainer->get_gender().std_string() == "Female");

            export_gen4_items(libpkmn_trainer->get_bag(), pokelib_trainer);

            //PokeLib::Save::getTrainer returns new trainer
            pokelib_save->setTrainer(pokelib_trainer);

            PokeLib::Party* pokelib_party = pokelib_save->getParty();

            for(int i = 1; i <= 6; i++)
            {
                team_pokemon::sptr t_pkmn = libpkmn_trainer->get_pokemon(i);
                if(t_pkmn->get_species_id() == Species::NONE) break;
                else pokelib_party->setPokemon(i, export_gen4_pokemon(t_pkmn));
            }
        }

        trainer::sptr import_gen5_trainer(pkmds_g5_sptr pkmds_save)
        {
            /*
             * PKMDS has no way of distinguishing between the different Gen 5
             * games, and trainer gender hasn't been reverse engineered. To
             * find each, we will parse the party to determine the relevant
             * information from Pokemon with matching ID's. If none are found,
             * use defaults.
             */
            std::array<party_pkm, 6> pkmds_party = pkmds_save->cur.party.pokemon;
            uint16_t pkmds_public_id = pkmds_save->cur.tid;
            uint16_t pkmds_secret_id = pkmds_save->cur.sid;
            unsigned int game_id, gender;
            bool found = false;

            for(size_t i = 0; i < pkmds_party.size(); i++)
            {
                pokemon_obj* pkmds_pokemon = &(pkmds_party[i]);

                if((pkmds_pokemon->tid == pkmds_public_id) and (pkmds_pokemon->sid == pkmds_secret_id))
                {
                    game_id = hometown_to_libpkmn_game(int(pkmds_pokemon->hometown));
                    gender = (get_gen_456_otgender((reinterpret_cast<uint8_t*>(&(pkmds_pokemon->ball)+1)))) ?
                             Genders::FEMALE : Genders::MALE;
                    found = true;
                    break;
                }
            }
            if(not found)
            {
                game_id = Versions::BLACK_2;
                gender = Genders::MALE;
            }

            pokemon_text pkmds_name = ::getsavtrainername(pkmds_save->cur);
            trainer::sptr libpkmn_trainer = trainer::make(game_id, pkmds_name, gender);

            libpkmn_trainer->set_public_id(pkmds_public_id);
            libpkmn_trainer->set_secret_id(pkmds_secret_id);
            libpkmn_trainer->set_money(0);

            import_gen5_items(libpkmn_trainer->get_bag(), &(pkmds_save->cur.bag));

            for(size_t i = 0; i < pkmds_party.size(); i++)
            {
                ::decryptpkm(&pkmds_party[i]);
                libpkmn_trainer->set_pokemon(i+1, import_gen5_pokemon(&(pkmds_party[i])));
                ::encryptpkm(&pkmds_party[i]);
            }

            return libpkmn_trainer;
        }

        void export_gen5_trainer(trainer::sptr libpkmn_trainer, pkmds_g5_sptr pkmds_save)
        {
            std::wstring trainer_name = libpkmn_trainer->get_name();

            for(size_t i = 0; i < trainer_name.size(); i++)
            {
                pkmds_save->cur.trainername[i] = trainer_name[i];
            }

            export_gen5_items(libpkmn_trainer->get_bag(), &(pkmds_save->cur.bag));

            for(size_t i = 1; i <= 6; i++)
            {
                team_pokemon::sptr t_pkmn = libpkmn_trainer->get_pokemon(i);

                if(t_pkmn->get_species_id() == Species::NONE) break;
                else export_gen5_pokemon(t_pkmn, &(pkmds_save->cur.party.pokemon[i-1]));
            }
        }
    } /* namespace conversions */
} /* namespace pkmn */
<|endoftext|>


<commit_before>#include "ScgiRequest.h"

#include "InputDevice.h"
#include "ProxyOutputBackend.h"
#include "Request_Backend.h"

#include <QDebug>
#include <QTcpSocket>

namespace FastCgiQt
{
	ScgiRequest::~ScgiRequest()
	{
	}

	ScgiRequest::ScgiRequest(
		Responder::Generator generator,
		QTcpSocket* socket,
		QObject* parent
	)
	: CommunicationInterface::Worker(parent)
	, m_responderGenerator(generator)
	, m_inputDevice(0)
	, m_socket(socket)
	, m_request(new Request::Backend())
	, m_state(NewConnection)
	{
		socket->open(QIODevice::ReadWrite);
		Q_ASSERT(socket->isOpen());
		Q_ASSERT(socket->isReadable());
		Q_ASSERT(socket->isWritable());

		connect(
			socket,
			SIGNAL(readyRead()),
			SLOT(readData())
		);
	}

	void ScgiRequest::readData()
	{
		switch(m_state)
		{
			case NewConnection:
				readHeaders();
				spawnResponder();
				break;
			case HaveHeaders:
				Q_ASSERT(m_inputDevice);
				m_inputDevice->appendData(m_socket->readAll());
				break;
		}
	}

	void ScgiRequest::cleanup(Responder* responder)
	{
		qDebug() << "RESPONDED";
		m_socket->close();
		delete responder;
		emit finished(thread());
		deleteLater();
	}

	void ScgiRequest::spawnResponder()
	{
		m_inputDevice = new InputDevice(this);

		Responder* responder = (*m_responderGenerator)(
			m_request,
			new OutputDevice(new ProxyOutputBackend(m_socket)),
			m_inputDevice,
			this
		);

		connect(
			responder,
			SIGNAL(finished(Responder*)),
			SLOT(cleanup(Responder*))
		);

		responder->start();
	}

	void ScgiRequest::readHeaders()
	{
		// Netstring: "length:blob,"
		QString lengthString;
		Q_FOREVER
		{
			char character;
			const bool readCharacter= m_socket->getChar(&character);
			Q_ASSERT(readCharacter);
			if(character == ':' || !readCharacter)
			{
				break;
			}
			else
			{
				lengthString.append(character);
			}
		}
		bool lengthIsNumeric;
		const unsigned int length = lengthString.toUInt(&lengthIsNumeric);
		Q_ASSERT(lengthIsNumeric);
		const QByteArray headerBlob = m_socket->read(length);
		Q_ASSERT(static_cast<qint64>(length) == headerBlob.length());
		m_socket->getChar(0); // skip ':' in netstring

		QList<QByteArray> parts = headerBlob.split(0);
		Q_ASSERT(parts.count() % 2); // null-terminated, last part is empty
		parts.takeLast();
		QHash<QString, QString> headers;
		while(!parts.isEmpty())
		{
			const QByteArray name = parts.takeFirst();
			const QByteArray value = parts.takeFirst();
			headers.insert(QString::fromLatin1(name), QString::fromLatin1(value));
		}
		Q_ASSERT(headers.contains("CONTENT_LENGTH"));
		Q_ASSERT(headers.contains("SCGI"));
		Q_ASSERT(headers.value("SCGI") == "1");
		m_request.backend()->addServerData(headers);
		m_state = HaveHeaders;
	}
};
<commit_msg>Change socket thread<commit_after>#include "ScgiRequest.h"

#include "InputDevice.h"
#include "ProxyOutputBackend.h"
#include "Request_Backend.h"

#include <QDebug>
#include <QTcpSocket>

namespace FastCgiQt
{
	ScgiRequest::~ScgiRequest()
	{
	}

	ScgiRequest::ScgiRequest(
		Responder::Generator generator,
		QTcpSocket* socket,
		QObject* parent
	)
	: CommunicationInterface::Worker(parent)
	, m_responderGenerator(generator)
	, m_inputDevice(0)
	, m_socket(socket)
	, m_request(new Request::Backend())
	, m_state(NewConnection)
	{
		socket->setParent(this);
		socket->open(QIODevice::ReadWrite);
		Q_ASSERT(socket->isOpen());
		Q_ASSERT(socket->isReadable());
		Q_ASSERT(socket->isWritable());

		connect(
			socket,
			SIGNAL(readyRead()),
			SLOT(readData())
		);
	}

	void ScgiRequest::readData()
	{
		switch(m_state)
		{
			case NewConnection:
				readHeaders();
				spawnResponder();
				break;
			case HaveHeaders:
				Q_ASSERT(m_inputDevice);
				m_inputDevice->appendData(m_socket->readAll());
				break;
		}
	}

	void ScgiRequest::cleanup(Responder* responder)
	{
		qDebug() << "RESPONDED";
		m_socket->close();
		delete responder;
		emit finished(thread());
		deleteLater();
	}

	void ScgiRequest::spawnResponder()
	{
		m_inputDevice = new InputDevice(this);

		Responder* responder = (*m_responderGenerator)(
			m_request,
			new OutputDevice(new ProxyOutputBackend(m_socket)),
			m_inputDevice,
			this
		);

		connect(
			responder,
			SIGNAL(finished(Responder*)),
			SLOT(cleanup(Responder*))
		);

		responder->start();
	}

	void ScgiRequest::readHeaders()
	{
		// Netstring: "length:blob,"
		QString lengthString;
		Q_FOREVER
		{
			char character;
			const bool readCharacter= m_socket->getChar(&character);
			Q_ASSERT(readCharacter);
			if(character == ':' || !readCharacter)
			{
				break;
			}
			else
			{
				lengthString.append(character);
			}
		}
		bool lengthIsNumeric;
		const unsigned int length = lengthString.toUInt(&lengthIsNumeric);
		Q_ASSERT(lengthIsNumeric);
		const QByteArray headerBlob = m_socket->read(length);
		Q_ASSERT(static_cast<qint64>(length) == headerBlob.length());
		m_socket->getChar(0); // skip ':' in netstring

		QList<QByteArray> parts = headerBlob.split(0);
		Q_ASSERT(parts.count() % 2); // null-terminated, last part is empty
		parts.takeLast();
		QHash<QString, QString> headers;
		while(!parts.isEmpty())
		{
			const QByteArray name = parts.takeFirst();
			const QByteArray value = parts.takeFirst();
			headers.insert(QString::fromLatin1(name), QString::fromLatin1(value));
		}
		Q_ASSERT(headers.contains("CONTENT_LENGTH"));
		Q_ASSERT(headers.contains("SCGI"));
		Q_ASSERT(headers.value("SCGI") == "1");
		m_request.backend()->addServerData(headers);
		m_state = HaveHeaders;
	}
};
<|endoftext|>


<commit_before>
/* listItem.cc			KPilot
**
** Copyright (C) 1998-2001 by Dan Pilone
**
** Program description
*/

/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program in a file called COPYING; if not, write to
** the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
** MA 02139, USA.
*/

/*
** Bug reports and questions can be sent to adridg@cs.kun.nl
*/
#include "options.h"
#include <qstring.h>
#include <qlistbox.h>
#include "listItems.moc"
#include "listItems.h"

PilotListItem::PilotListItem(const QString &text,
	int pilotid,
	void *r) : QListBoxText(text),
	fid(pilotid),
	fr(r)
{
	FUNCTIONSETUP;
}

// $Log:$
<commit_msg>Removed spurious .moc file<commit_after>
/* listItem.cc			KPilot
**
** Copyright (C) 1998-2001 by Dan Pilone
**
** Program description
*/

/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program in a file called COPYING; if not, write to
** the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
** MA 02139, USA.
*/

/*
** Bug reports and questions can be sent to adridg@cs.kun.nl
*/
#include "options.h"
#include <qstring.h>
#include <qlistbox.h>
#include "listItems.h"

PilotListItem::PilotListItem(const QString &text,
	int pilotid,
	void *r) : QListBoxText(text),
	fid(pilotid),
	fr(r)
{
	FUNCTIONSETUP;
}

// $Log$
// Revision 1.1  2001/03/04 11:22:12  adridg
// In response to bug 21392, replaced fixed-length lookup table by a subclass
// of QListBoxItem inserted into list box. This subclass carries data to
// lookup the relevant pilot record.
//
<|endoftext|>


<commit_before>
#include "ksp_plugin/planetarium.hpp"

#include <vector>

#include "geometry/point.hpp"
#include "quantities/elementary_functions.hpp"

namespace principia {
namespace ksp_plugin {
namespace internal_planetarium {

using geometry::Position;
using geometry::RP2Line;
using quantities::Pow;
using quantities::Sin;
using quantities::Sqrt;
using quantities::Tan;
using quantities::Speed;
using quantities::Square;
using quantities::Time;

Planetarium::Parameters::Parameters(double const sphere_radius_multiplier,
                                    Angle const& angular_resolution,
                                    Angle const& field_of_view)
    : sphere_radius_multiplier_(sphere_radius_multiplier),
      sin²_angular_resolution_(Pow<2>(Sin(angular_resolution))),
      tan_angular_resolution_(Tan(angular_resolution)),
      tan_field_of_view_(Tan(field_of_view)) {}

Planetarium::Planetarium(
    Parameters const& parameters,
    Perspective<Navigation, Camera, Length, OrthogonalMap> const& perspective,
    not_null<Ephemeris<Barycentric> const*> const ephemeris,
    not_null<NavigationFrame const*> const plotting_frame)
    : parameters_(parameters),
      perspective_(perspective),
      ephemeris_(ephemeris),
      plotting_frame_(plotting_frame) {}

RP2Lines<Length, Camera> Planetarium::PlotMethod0(
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end,
    Instant const& now) const {
  auto const plottable_spheres = ComputePlottableSpheres(now);
  auto const plottable_segments = ComputePlottableSegments(plottable_spheres,
                                                           begin, end);

  auto const field_of_view_radius² =
      perspective_.focal() * perspective_.focal() *
      parameters_.tan_field_of_view_ * parameters_.tan_field_of_view_;
  std::experimental::optional<Position<Navigation>> previous_position;
  RP2Lines<Length, Camera> rp2_lines;
  for (auto const& plottable_segment : plottable_segments) {
    // Apply the projection to the current plottable segment.
    auto const rp2_first = perspective_(plottable_segment.first);
    auto const rp2_second = perspective_(plottable_segment.second);

    // If the segment is entirely outside the field of view, ignore it.
    Length const x1 = rp2_first.x();
    Length const y1 = rp2_first.y();
    Length const x2 = rp2_second.x();
    Length const y2 = rp2_second.y();
    if (x1 * x1 + y1 * y1 > field_of_view_radius² &&
        x2 * x2 + y2 * y2 > field_of_view_radius²) {
      continue;
    }

    // Create a new ℝP² line when two segments are not consecutive.  Don't
    // compare ℝP² points for equality, that's expensive.
    bool const are_consecutive =
        previous_position == plottable_segment.first;
    previous_position = plottable_segment.second;

    if (are_consecutive) {
      rp2_lines.back().push_back(rp2_second);
    } else {
      RP2Line<Length, Camera> const rp2_line = {rp2_first, rp2_second};
      rp2_lines.push_back(rp2_line);
    }
  }
  return rp2_lines;
}

RP2Lines<Length, Camera> Planetarium::PlotMethod1(
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end,
    Instant const& now) const {
  Length const focal_plane_tolerance =
      perspective_.focal() * parameters_.tan_angular_resolution_;
  auto const focal_plane_tolerance² =
      focal_plane_tolerance * focal_plane_tolerance;

  auto const rp2_lines = PlotMethod0(begin, end, now);

  int skipped = 0;
  int total = 0;
  RP2Lines<Length, Camera> new_rp2_lines;
  for (auto const& rp2_line : rp2_lines) {
    RP2Line<Length, Camera> new_rp2_line;
    std::experimental::optional<RP2Point<Length, Camera>> start_rp2_point;
    for (int i = 0; i < rp2_line.size(); ++i) {
      RP2Point<Length, Camera> const& rp2_point = rp2_line[i];
      if (i == 0) {
        new_rp2_line.push_back(rp2_point);
        start_rp2_point = rp2_point;
      } else if (Pow<2>(rp2_point.x() - start_rp2_point->x()) +
                 Pow<2>(rp2_point.y() - start_rp2_point->y()) >
                     focal_plane_tolerance²) {
        // TODO(phl): This creates a segment if the tolerance is exceeded.  It
        // should probably create a segment that stays just below the tolerance.
        new_rp2_line.push_back(rp2_point);
        start_rp2_point = rp2_point;
      } else if (i == rp2_line.size() - 1) {
        new_rp2_line.push_back(rp2_point);
      } else {
        ++skipped;
      }
      ++total;
    }
    new_rp2_lines.push_back(std::move(new_rp2_line));
  }
  LOG(INFO) << "Skipped " << skipped << " points out of " << total;
  return new_rp2_lines;
}

RP2Lines<Length, Camera> Planetarium::PlotMethod2(
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end,
    Instant const& now) const {
  RP2Lines<Length, Camera> lines;
  if (begin == end) {
    return lines;
  }
  auto last = end;
  --last;
  if (begin == last) {
    return lines;
  }

  auto const plottable_spheres = ComputePlottableSpheres(now);

  auto const final_time = last.time();
  auto const& trajectory = *begin.trajectory();

  auto const squared_tolerance = Pow<2>(parameters_.tan_angular_resolution_);

  auto previous_time = begin.time();
  auto previous_degrees_of_freedom = begin.degrees_of_freedom();

  Position<Navigation> previous_position_in_navigation;
  Length previous_projected_x;
  Length previous_projected_y;
  Speed previous_projected_velocity_x;
  Speed previous_projected_velocity_y;
  Time step;

  {
    previous_position_in_navigation =
        plotting_frame_->ToThisFrameAtTime(
            previous_time).rigid_transformation()(
                previous_degrees_of_freedom.position());
    auto const initial_displacement_from_camera =
        perspective_.to_camera()(previous_position_in_navigation) -
        Camera::origin;
    auto const initial_velocity_in_camera =
        perspective_.to_camera().linear_map()(
            plotting_frame_->ToThisFrameAtTime(previous_time)
                .orthogonal_map()(previous_degrees_of_freedom.velocity()));

    previous_projected_x = perspective_.focal() *
                           initial_displacement_from_camera.coordinates().x /
                           initial_displacement_from_camera.coordinates().z;
    previous_projected_y = perspective_.focal() *
                           initial_displacement_from_camera.coordinates().y /
                           initial_displacement_from_camera.coordinates().z;

    previous_projected_velocity_x =
        perspective_.focal() *
        (initial_displacement_from_camera.coordinates().z *
             initial_velocity_in_camera.coordinates().x -
         initial_displacement_from_camera.coordinates().x *
             initial_velocity_in_camera.coordinates().z) /
        Pow<2>(initial_displacement_from_camera.coordinates().z);
    previous_projected_velocity_y =
        perspective_.focal() *
        (initial_displacement_from_camera.coordinates().z *
             initial_velocity_in_camera.coordinates().y -
         initial_displacement_from_camera.coordinates().y *
             initial_velocity_in_camera.coordinates().z) /
        Pow<2>(initial_displacement_from_camera.coordinates().z);

    step = parameters_.sin²_angular_resolution_ * perspective_.focal() /
           Sqrt(Pow<2>(previous_projected_velocity_x) +
                Pow<2>(previous_projected_velocity_y));
  }

  Instant t;
  double squared_error_estimate;
  Position<Navigation> position_in_navigation;
  Displacement<Camera> displacement_from_camera;

  std::experimental::optional<Position<Navigation>> last_endpoint;

  goto estimate_segment_error;

  while (previous_time < final_time) {
    do {
      step *= Sqrt(Sqrt(squared_tolerance / squared_error_estimate));
    estimate_segment_error:
      t = previous_time + step;
      if (t > final_time) {
        t = final_time;
        step = t - previous_time;
      }
      Length const estimated_projected_x =
          previous_projected_x + step * previous_projected_velocity_x;
      Length const estimated_projected_y =
          previous_projected_y + step * previous_projected_velocity_y;

      Displacement<Camera> estimated_unprojected(
          {estimated_projected_x, estimated_projected_y, perspective_.focal()});

      position_in_navigation =
          plotting_frame_->ToThisFrameAtTime(t).rigid_transformation()(
              trajectory.EvaluatePosition(t));
      displacement_from_camera =
          perspective_.to_camera()(position_in_navigation) - Camera::origin;

      auto const wedge = Wedge(estimated_unprojected, displacement_from_camera);
      squared_error_estimate =
          InnerProduct(wedge, wedge) /
          Pow<2>(InnerProduct(estimated_unprojected, displacement_from_camera));
    } while (squared_error_estimate > squared_tolerance);
    auto const segments =
        perspective_.VisibleSegments(
            Segment<Displacement<Navigation>>(previous_position_in_navigation,
                                              position_in_navigation),
            plottable_spheres);
    for (auto const& segment : segments) {
      if (last_endpoint != segment.first) {
        lines.emplace_back();
        lines.back().push_back(perspective_(segment.first));
      }
      lines.back().push_back(perspective_(segment.second));
      last_endpoint = segment.second;
    }

    auto const velocity_in_camera = perspective_.to_camera().linear_map()(
        plotting_frame_->ToThisFrameAtTime(t).orthogonal_map()(
            trajectory.EvaluateVelocity(t)));

    previous_time = t;
    previous_position_in_navigation = position_in_navigation;
    previous_projected_x = perspective_.focal() *
                           displacement_from_camera.coordinates().x /
                           displacement_from_camera.coordinates().z;
    previous_projected_y = perspective_.focal() *
                           displacement_from_camera.coordinates().y /
                           displacement_from_camera.coordinates().z;
    previous_projected_velocity_x =
        perspective_.focal() *
        (displacement_from_camera.coordinates().z *
             velocity_in_camera.coordinates().x -
         displacement_from_camera.coordinates().x *
             velocity_in_camera.coordinates().z) /
        Pow<2>(displacement_from_camera.coordinates().z);
    previous_projected_velocity_y =
        perspective_.focal() *
        (displacement_from_camera.coordinates().z *
             velocity_in_camera.coordinates().y -
         displacement_from_camera.coordinates().y *
             velocity_in_camera.coordinates().z) /
        Pow<2>(displacement_from_camera.coordinates().z);
  }
  return lines;
}

std::vector<Sphere<Length, Navigation>> Planetarium::ComputePlottableSpheres(
    Instant const& now) const {
  RigidMotion<Barycentric, Navigation> const rigid_motion_at_now =
      plotting_frame_->ToThisFrameAtTime(now);
  std::vector<Sphere<Length, Navigation>> plottable_spheres;

  auto const& bodies = ephemeris_->bodies();
  for (auto const body : bodies) {
    auto const trajectory = ephemeris_->trajectory(body);
    Length const mean_radius = body->mean_radius();
    Position<Barycentric> const centre_in_barycentric =
        trajectory->EvaluatePosition(now);
    Sphere<Length, Navigation> plottable_sphere(
        rigid_motion_at_now.rigid_transformation()(centre_in_barycentric),
        parameters_.sphere_radius_multiplier_ * mean_radius);
    // If the sphere is seen under an angle that is very small it doesn't
    // participate in hiding.
    if (perspective_.SphereSin²HalfAngle(plottable_sphere) >
        parameters_.sin²_angular_resolution_) {
      plottable_spheres.emplace_back(std::move(plottable_sphere));
    }
  }
  return plottable_spheres;
}

Segments<Displacement<Navigation>> Planetarium::ComputePlottableSegments(
    const std::vector<Sphere<Length, Navigation>>& plottable_spheres,
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end) const {
  std::vector<Segment<Displacement<Navigation>>> all_segments;
  if (begin == end) {
    return all_segments;
  }
  auto it1 = begin;
  Instant t1 = it1.time();
  RigidMotion<Barycentric, Navigation> rigid_motion_at_t1 =
      plotting_frame_->ToThisFrameAtTime(t1);
  Position<Navigation> p1 =
      rigid_motion_at_t1(it1.degrees_of_freedom()).position();

  auto it2 = it1;
  while (++it2 != end) {
    // Processing one segment of the trajectory.
    Instant const t2 = it2.time();

    // Transform the degrees of freedom to the plotting frame.
    RigidMotion<Barycentric, Navigation> const rigid_motion_at_t2 =
        plotting_frame_->ToThisFrameAtTime(t2);
    Position<Navigation> const p2 =
        rigid_motion_at_t2(it2.degrees_of_freedom()).position();

    // Find the part of the segment that is behind the focal plane.  We don't
    // care about things that are in front of the focal plane.
    const Segment<Displacement<Navigation>> segment = {p1, p2};
    auto const segment_behind_focal_plane =
        perspective_.SegmentBehindFocalPlane(segment);
    if (segment_behind_focal_plane) {
      // Find the part(s) of the segment that are not hidden by spheres.  These
      // are the ones we want to plot.
      auto segments = perspective_.VisibleSegments(*segment_behind_focal_plane,
                                                   plottable_spheres);
      std::move(segments.begin(),
                segments.end(),
                std::back_inserter(all_segments));
    }

    it1 = it2;
    t1 = t2;
    rigid_motion_at_t1 = rigid_motion_at_t2;
    p1 = p2;
  }

  return all_segments;
}

}  // namespace internal_planetarium
}  // namespace ksp_plugin
}  // namespace principia
<commit_msg>don't project by hand<commit_after>
#include "ksp_plugin/planetarium.hpp"

#include <vector>

#include "geometry/point.hpp"
#include "quantities/elementary_functions.hpp"

namespace principia {
namespace ksp_plugin {
namespace internal_planetarium {

using geometry::Position;
using geometry::RP2Line;
using geometry::Velocity;
using quantities::Pow;
using quantities::Sin;
using quantities::Sqrt;
using quantities::Tan;
using quantities::Speed;
using quantities::Square;
using quantities::Time;

Planetarium::Parameters::Parameters(double const sphere_radius_multiplier,
                                    Angle const& angular_resolution,
                                    Angle const& field_of_view)
    : sphere_radius_multiplier_(sphere_radius_multiplier),
      sin²_angular_resolution_(Pow<2>(Sin(angular_resolution))),
      tan_angular_resolution_(Tan(angular_resolution)),
      tan_field_of_view_(Tan(field_of_view)) {}

Planetarium::Planetarium(
    Parameters const& parameters,
    Perspective<Navigation, Camera, Length, OrthogonalMap> const& perspective,
    not_null<Ephemeris<Barycentric> const*> const ephemeris,
    not_null<NavigationFrame const*> const plotting_frame)
    : parameters_(parameters),
      perspective_(perspective),
      ephemeris_(ephemeris),
      plotting_frame_(plotting_frame) {}

RP2Lines<Length, Camera> Planetarium::PlotMethod0(
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end,
    Instant const& now) const {
  auto const plottable_spheres = ComputePlottableSpheres(now);
  auto const plottable_segments = ComputePlottableSegments(plottable_spheres,
                                                           begin, end);

  auto const field_of_view_radius² =
      perspective_.focal() * perspective_.focal() *
      parameters_.tan_field_of_view_ * parameters_.tan_field_of_view_;
  std::experimental::optional<Position<Navigation>> previous_position;
  RP2Lines<Length, Camera> rp2_lines;
  for (auto const& plottable_segment : plottable_segments) {
    // Apply the projection to the current plottable segment.
    auto const rp2_first = perspective_(plottable_segment.first);
    auto const rp2_second = perspective_(plottable_segment.second);

    // If the segment is entirely outside the field of view, ignore it.
    Length const x1 = rp2_first.x();
    Length const y1 = rp2_first.y();
    Length const x2 = rp2_second.x();
    Length const y2 = rp2_second.y();
    if (x1 * x1 + y1 * y1 > field_of_view_radius² &&
        x2 * x2 + y2 * y2 > field_of_view_radius²) {
      continue;
    }

    // Create a new ℝP² line when two segments are not consecutive.  Don't
    // compare ℝP² points for equality, that's expensive.
    bool const are_consecutive =
        previous_position == plottable_segment.first;
    previous_position = plottable_segment.second;

    if (are_consecutive) {
      rp2_lines.back().push_back(rp2_second);
    } else {
      RP2Line<Length, Camera> const rp2_line = {rp2_first, rp2_second};
      rp2_lines.push_back(rp2_line);
    }
  }
  return rp2_lines;
}

RP2Lines<Length, Camera> Planetarium::PlotMethod1(
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end,
    Instant const& now) const {
  Length const focal_plane_tolerance =
      perspective_.focal() * parameters_.tan_angular_resolution_;
  auto const focal_plane_tolerance² =
      focal_plane_tolerance * focal_plane_tolerance;

  auto const rp2_lines = PlotMethod0(begin, end, now);

  int skipped = 0;
  int total = 0;
  RP2Lines<Length, Camera> new_rp2_lines;
  for (auto const& rp2_line : rp2_lines) {
    RP2Line<Length, Camera> new_rp2_line;
    std::experimental::optional<RP2Point<Length, Camera>> start_rp2_point;
    for (int i = 0; i < rp2_line.size(); ++i) {
      RP2Point<Length, Camera> const& rp2_point = rp2_line[i];
      if (i == 0) {
        new_rp2_line.push_back(rp2_point);
        start_rp2_point = rp2_point;
      } else if (Pow<2>(rp2_point.x() - start_rp2_point->x()) +
                 Pow<2>(rp2_point.y() - start_rp2_point->y()) >
                     focal_plane_tolerance²) {
        // TODO(phl): This creates a segment if the tolerance is exceeded.  It
        // should probably create a segment that stays just below the tolerance.
        new_rp2_line.push_back(rp2_point);
        start_rp2_point = rp2_point;
      } else if (i == rp2_line.size() - 1) {
        new_rp2_line.push_back(rp2_point);
      } else {
        ++skipped;
      }
      ++total;
    }
    new_rp2_lines.push_back(std::move(new_rp2_line));
  }
  LOG(INFO) << "PlotMethod1 skipped " << skipped << " points out of " << total
            << ".";
  return new_rp2_lines;
}

RP2Lines<Length, Camera> Planetarium::PlotMethod2(
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end,
    Instant const& now) const {
  RP2Lines<Length, Camera> lines;
  if (begin == end) {
    return lines;
  }
  auto last = end;
  --last;
  if (begin == last) {
    return lines;
  }

  auto const plottable_spheres = ComputePlottableSpheres(now);

  auto const final_time = last.time();
  auto const& trajectory = *begin.trajectory();

  auto const squared_tolerance = Pow<2>(parameters_.tan_angular_resolution_);

  auto previous_time = begin.time();
  auto previous_degrees_of_freedom = begin.degrees_of_freedom();

  Position<Navigation> previous_position_in_navigation;
  Velocity<Navigation> previous_velocity_in_navigation;
  Time step;

  previous_position_in_navigation =
      plotting_frame_->ToThisFrameAtTime(previous_time)
          .rigid_transformation()(previous_degrees_of_freedom.position());
  previous_velocity_in_navigation =
      plotting_frame_->ToThisFrameAtTime(previous_time)
          .orthogonal_map()(previous_degrees_of_freedom.velocity());

  step = final_time - previous_time;

  Instant t;
  double squared_error_estimate;
  Position<Navigation> position_in_navigation;
  Displacement<Camera> displacement_from_camera;

  std::experimental::optional<Position<Navigation>> last_endpoint;

  int steps_accepted = 0;
  int steps_attempted = 0;

  goto estimate_segment_error;

  while (previous_time < final_time) {
    do {
      step *= Sqrt(Sqrt(squared_tolerance / squared_error_estimate));
    estimate_segment_error:
      t = previous_time + step;
      if (t > final_time) {
        t = final_time;
        step = t - previous_time;
      }
      Position<Navigation> const estimated_position =
          previous_position_in_navigation +
          previous_velocity_in_navigation * step;
      auto const estimated_displacement_from_camera =
          perspective_.to_camera()(estimated_position) - Camera::origin;

      position_in_navigation =
          plotting_frame_->ToThisFrameAtTime(t).rigid_transformation()(
              trajectory.EvaluatePosition(t));
      displacement_from_camera =
          perspective_.to_camera()(position_in_navigation) - Camera::origin;

      auto const wedge =
          Wedge(estimated_displacement_from_camera, displacement_from_camera);
      squared_error_estimate =
          InnerProduct(wedge, wedge) /
          Pow<2>(InnerProduct(estimated_displacement_from_camera,
                              displacement_from_camera));
      ++steps_attempted;
    } while (squared_error_estimate > squared_tolerance);
    ++steps_accepted;
    auto const segments =
        perspective_.VisibleSegments(
            Segment<Displacement<Navigation>>(previous_position_in_navigation,
                                              position_in_navigation),
            plottable_spheres);
    for (auto const& segment : segments) {
      if (last_endpoint != segment.first) {
        lines.emplace_back();
        lines.back().push_back(perspective_(segment.first));
      }
      lines.back().push_back(perspective_(segment.second));
      last_endpoint = segment.second;
    }

    previous_time = t;
    previous_position_in_navigation = position_in_navigation;
    previous_velocity_in_navigation =
        plotting_frame_->ToThisFrameAtTime(t).orthogonal_map()(
            trajectory.EvaluateVelocity(t));
  }
  LOG(INFO) << "PlotMethod2 took " << steps_accepted << " steps, attempted "
            << steps_attempted << " steps.";
  return lines;
}

std::vector<Sphere<Length, Navigation>> Planetarium::ComputePlottableSpheres(
    Instant const& now) const {
  RigidMotion<Barycentric, Navigation> const rigid_motion_at_now =
      plotting_frame_->ToThisFrameAtTime(now);
  std::vector<Sphere<Length, Navigation>> plottable_spheres;

  auto const& bodies = ephemeris_->bodies();
  for (auto const body : bodies) {
    auto const trajectory = ephemeris_->trajectory(body);
    Length const mean_radius = body->mean_radius();
    Position<Barycentric> const centre_in_barycentric =
        trajectory->EvaluatePosition(now);
    Sphere<Length, Navigation> plottable_sphere(
        rigid_motion_at_now.rigid_transformation()(centre_in_barycentric),
        parameters_.sphere_radius_multiplier_ * mean_radius);
    // If the sphere is seen under an angle that is very small it doesn't
    // participate in hiding.
    if (perspective_.SphereSin²HalfAngle(plottable_sphere) >
        parameters_.sin²_angular_resolution_) {
      plottable_spheres.emplace_back(std::move(plottable_sphere));
    }
  }
  return plottable_spheres;
}

Segments<Displacement<Navigation>> Planetarium::ComputePlottableSegments(
    const std::vector<Sphere<Length, Navigation>>& plottable_spheres,
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end) const {
  std::vector<Segment<Displacement<Navigation>>> all_segments;
  if (begin == end) {
    return all_segments;
  }
  auto it1 = begin;
  Instant t1 = it1.time();
  RigidMotion<Barycentric, Navigation> rigid_motion_at_t1 =
      plotting_frame_->ToThisFrameAtTime(t1);
  Position<Navigation> p1 =
      rigid_motion_at_t1(it1.degrees_of_freedom()).position();

  auto it2 = it1;
  while (++it2 != end) {
    // Processing one segment of the trajectory.
    Instant const t2 = it2.time();

    // Transform the degrees of freedom to the plotting frame.
    RigidMotion<Barycentric, Navigation> const rigid_motion_at_t2 =
        plotting_frame_->ToThisFrameAtTime(t2);
    Position<Navigation> const p2 =
        rigid_motion_at_t2(it2.degrees_of_freedom()).position();

    // Find the part of the segment that is behind the focal plane.  We don't
    // care about things that are in front of the focal plane.
    const Segment<Displacement<Navigation>> segment = {p1, p2};
    auto const segment_behind_focal_plane =
        perspective_.SegmentBehindFocalPlane(segment);
    if (segment_behind_focal_plane) {
      // Find the part(s) of the segment that are not hidden by spheres.  These
      // are the ones we want to plot.
      auto segments = perspective_.VisibleSegments(*segment_behind_focal_plane,
                                                   plottable_spheres);
      std::move(segments.begin(),
                segments.end(),
                std::back_inserter(all_segments));
    }

    it1 = it2;
    t1 = t2;
    rigid_motion_at_t1 = rigid_motion_at_t2;
    p1 = p2;
  }

  return all_segments;
}

}  // namespace internal_planetarium
}  // namespace ksp_plugin
}  // namespace principia
<|endoftext|>


<commit_before>
#include "ksp_plugin/planetarium.hpp"

#include <vector>

#include "geometry/point.hpp"
#include "quantities/elementary_functions.hpp"

namespace principia {
namespace ksp_plugin {
namespace internal_planetarium {

using geometry::Position;
using geometry::RP2Line;
using geometry::Velocity;
using quantities::Pow;
using quantities::Sin;
using quantities::Sqrt;
using quantities::Tan;
using quantities::Time;

Planetarium::Parameters::Parameters(double const sphere_radius_multiplier,
                                    Angle const& angular_resolution,
                                    Angle const& field_of_view)
    : sphere_radius_multiplier_(sphere_radius_multiplier),
      sin²_angular_resolution_(Pow<2>(Sin(angular_resolution))),
      tan_angular_resolution_(Tan(angular_resolution)),
      tan_field_of_view_(Tan(field_of_view)) {}

Planetarium::Planetarium(
    Parameters const& parameters,
    Perspective<Navigation, Camera, Length, OrthogonalMap> const& perspective,
    not_null<Ephemeris<Barycentric> const*> const ephemeris,
    not_null<NavigationFrame const*> const plotting_frame)
    : parameters_(parameters),
      perspective_(perspective),
      ephemeris_(ephemeris),
      plotting_frame_(plotting_frame) {}

RP2Lines<Length, Camera> Planetarium::PlotMethod0(
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end,
    Instant const& now) const {
  auto const plottable_spheres = ComputePlottableSpheres(now);
  auto const plottable_segments = ComputePlottableSegments(plottable_spheres,
                                                           begin, end);

  auto const field_of_view_radius² =
      perspective_.focal() * perspective_.focal() *
      parameters_.tan_field_of_view_ * parameters_.tan_field_of_view_;
  std::experimental::optional<Position<Navigation>> previous_position;
  RP2Lines<Length, Camera> rp2_lines;
  for (auto const& plottable_segment : plottable_segments) {
    // Apply the projection to the current plottable segment.
    auto const rp2_first = perspective_(plottable_segment.first);
    auto const rp2_second = perspective_(plottable_segment.second);

    // If the segment is entirely outside the field of view, ignore it.
    Length const x1 = rp2_first.x();
    Length const y1 = rp2_first.y();
    Length const x2 = rp2_second.x();
    Length const y2 = rp2_second.y();
    if (x1 * x1 + y1 * y1 > field_of_view_radius² &&
        x2 * x2 + y2 * y2 > field_of_view_radius²) {
      continue;
    }

    // Create a new ℝP² line when two segments are not consecutive.  Don't
    // compare ℝP² points for equality, that's expensive.
    bool const are_consecutive =
        previous_position == plottable_segment.first;
    previous_position = plottable_segment.second;

    if (are_consecutive) {
      rp2_lines.back().push_back(rp2_second);
    } else {
      RP2Line<Length, Camera> const rp2_line = {rp2_first, rp2_second};
      rp2_lines.push_back(rp2_line);
    }
  }
  return rp2_lines;
}

RP2Lines<Length, Camera> Planetarium::PlotMethod1(
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end,
    Instant const& now) const {
  Length const focal_plane_tolerance =
      perspective_.focal() * parameters_.tan_angular_resolution_;
  auto const focal_plane_tolerance² =
      focal_plane_tolerance * focal_plane_tolerance;

  auto const rp2_lines = PlotMethod0(begin, end, now);

  int skipped = 0;
  int total = 0;
  RP2Lines<Length, Camera> new_rp2_lines;
  for (auto const& rp2_line : rp2_lines) {
    RP2Line<Length, Camera> new_rp2_line;
    std::experimental::optional<RP2Point<Length, Camera>> start_rp2_point;
    for (int i = 0; i < rp2_line.size(); ++i) {
      RP2Point<Length, Camera> const& rp2_point = rp2_line[i];
      if (i == 0) {
        new_rp2_line.push_back(rp2_point);
        start_rp2_point = rp2_point;
      } else if (Pow<2>(rp2_point.x() - start_rp2_point->x()) +
                 Pow<2>(rp2_point.y() - start_rp2_point->y()) >
                     focal_plane_tolerance²) {
        // TODO(phl): This creates a segment if the tolerance is exceeded.  It
        // should probably create a segment that stays just below the tolerance.
        new_rp2_line.push_back(rp2_point);
        start_rp2_point = rp2_point;
      } else if (i == rp2_line.size() - 1) {
        new_rp2_line.push_back(rp2_point);
      } else {
        ++skipped;
      }
      ++total;
    }
    new_rp2_lines.push_back(std::move(new_rp2_line));
  }
  LOG(INFO) << "PlotMethod1 skipped " << skipped << " points out of " << total
            << ", emitting " << total - skipped << " points";
  return new_rp2_lines;
}

RP2Lines<Length, Camera> Planetarium::PlotMethod2(
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end,
    Instant const& now) const {
  RP2Lines<Length, Camera> lines;
  if (begin == end) {
    return lines;
  }
  auto last = end;
  --last;
  if (begin == last) {
    return lines;
  }

  double const tan²_angular_resolution =
      Pow<2>(parameters_.tan_angular_resolution_);
  auto const plottable_spheres = ComputePlottableSpheres(now);
  auto const& trajectory = *begin.trajectory();
  auto const final_time = last.time();

  auto previous_time = begin.time();
  RigidMotion<Barycentric, Navigation> to_plotting_frame_at_t =
      plotting_frame_->ToThisFrameAtTime(previous_time);
  Position<Navigation> previous_position =
      to_plotting_frame_at_t.rigid_transformation()(
          begin.degrees_of_freedom().position());
  Velocity<Navigation> previous_velocity =
      to_plotting_frame_at_t.orthogonal_map()(
          begin.degrees_of_freedom().velocity());
  Time Δt = final_time - previous_time;

  Instant t;
  double estimated_tan²_error;
  Position<Navigation> position;

  std::experimental::optional<Position<Navigation>> last_endpoint;

  int steps_accepted = 0;
  int steps_attempted = 0;

  goto estimate_tan²_error;

  while (previous_time < final_time) {
    do {
      // One square root because we have squared errors, another one because the
      // errors are quadratic in time (in other words, two square roots because
      // the squared errors are quartic in time).
      // A safety factor prevents catastrophic retries.
      Δt *= 0.9 * Sqrt(Sqrt(tan²_angular_resolution / estimated_tan²_error));
    estimate_tan²_error:
      t = previous_time + Δt;
      if (t > final_time) {
        t = final_time;
        Δt = t - previous_time;
      }
      Position<Navigation> const extrapolated_position =
          previous_position + previous_velocity * Δt;
      to_plotting_frame_at_t = plotting_frame_->ToThisFrameAtTime(t);
      position = to_plotting_frame_at_t.rigid_transformation()(
                     trajectory.EvaluatePosition(t));

      // The quadratic term of the error between the linear interpolation and
      // the actual function is maximized halfway through the segment, so it is
      // 1/2 (Δt/2)² f″(t-Δt) = (1/2 Δt² f″(t-Δt)) / 4; the squared error is
      // thus (1/2 Δt² f″(t-Δt))² / 16.
      estimated_tan²_error =
          perspective_.Tan²AngularDistance(extrapolated_position, position) /
          16;
      ++steps_attempted;
    } while (estimated_tan²_error > tan²_angular_resolution);
    ++steps_accepted;

    // TODO(egg): also limit to field of view.
    auto const segment_behind_focal_plane =
        perspective_.SegmentBehindFocalPlane(
            Segment<Displacement<Navigation>>(previous_position, position));

    previous_time = t;
    previous_position = position;
    previous_velocity =
        to_plotting_frame_at_t.orthogonal_map()(trajectory.EvaluateVelocity(t));

    if (!segment_behind_focal_plane) {
      continue;
    }

    auto const visible_segments = perspective_.VisibleSegments(
                                      *segment_behind_focal_plane,
                                      plottable_spheres);
    for (auto const& segment : visible_segments) {
      if (last_endpoint != segment.first) {
        lines.emplace_back();
        lines.back().push_back(perspective_(segment.first));
      }
      lines.back().push_back(perspective_(segment.second));
      last_endpoint = segment.second;
    }
  }
  LOG(INFO) << "PlotMethod2 took " << steps_accepted << " steps, attempted "
            << steps_attempted << " steps";
  return lines;
}

std::vector<Sphere<Length, Navigation>> Planetarium::ComputePlottableSpheres(
    Instant const& now) const {
  RigidMotion<Barycentric, Navigation> const rigid_motion_at_now =
      plotting_frame_->ToThisFrameAtTime(now);
  std::vector<Sphere<Length, Navigation>> plottable_spheres;

  auto const& bodies = ephemeris_->bodies();
  for (auto const body : bodies) {
    auto const trajectory = ephemeris_->trajectory(body);
    Length const mean_radius = body->mean_radius();
    Position<Barycentric> const centre_in_barycentric =
        trajectory->EvaluatePosition(now);
    Sphere<Length, Navigation> plottable_sphere(
        rigid_motion_at_now.rigid_transformation()(centre_in_barycentric),
        parameters_.sphere_radius_multiplier_ * mean_radius);
    // If the sphere is seen under an angle that is very small it doesn't
    // participate in hiding.
    if (perspective_.SphereSin²HalfAngle(plottable_sphere) >
        parameters_.sin²_angular_resolution_) {
      plottable_spheres.emplace_back(std::move(plottable_sphere));
    }
  }
  return plottable_spheres;
}

Segments<Displacement<Navigation>> Planetarium::ComputePlottableSegments(
    const std::vector<Sphere<Length, Navigation>>& plottable_spheres,
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end) const {
  std::vector<Segment<Displacement<Navigation>>> all_segments;
  if (begin == end) {
    return all_segments;
  }
  auto it1 = begin;
  Instant t1 = it1.time();
  RigidMotion<Barycentric, Navigation> rigid_motion_at_t1 =
      plotting_frame_->ToThisFrameAtTime(t1);
  Position<Navigation> p1 =
      rigid_motion_at_t1(it1.degrees_of_freedom()).position();

  auto it2 = it1;
  while (++it2 != end) {
    // Processing one segment of the trajectory.
    Instant const t2 = it2.time();

    // Transform the degrees of freedom to the plotting frame.
    RigidMotion<Barycentric, Navigation> const rigid_motion_at_t2 =
        plotting_frame_->ToThisFrameAtTime(t2);
    Position<Navigation> const p2 =
        rigid_motion_at_t2(it2.degrees_of_freedom()).position();

    // Find the part of the segment that is behind the focal plane.  We don't
    // care about things that are in front of the focal plane.
    const Segment<Displacement<Navigation>> segment = {p1, p2};
    auto const segment_behind_focal_plane =
        perspective_.SegmentBehindFocalPlane(segment);
    if (segment_behind_focal_plane) {
      // Find the part(s) of the segment that are not hidden by spheres.  These
      // are the ones we want to plot.
      auto segments = perspective_.VisibleSegments(*segment_behind_focal_plane,
                                                   plottable_spheres);
      std::move(segments.begin(),
                segments.end(),
                std::back_inserter(all_segments));
    }

    it1 = it2;
    t1 = t2;
    rigid_motion_at_t1 = rigid_motion_at_t2;
    p1 = p2;
  }

  return all_segments;
}

}  // namespace internal_planetarium
}  // namespace ksp_plugin
}  // namespace principia
<commit_msg>that was embarrassing<commit_after>
#include "ksp_plugin/planetarium.hpp"

#include <vector>

#include "geometry/point.hpp"
#include "quantities/elementary_functions.hpp"

namespace principia {
namespace ksp_plugin {
namespace internal_planetarium {

using geometry::Position;
using geometry::RP2Line;
using geometry::Velocity;
using quantities::Pow;
using quantities::Sin;
using quantities::Sqrt;
using quantities::Tan;
using quantities::Time;

Planetarium::Parameters::Parameters(double const sphere_radius_multiplier,
                                    Angle const& angular_resolution,
                                    Angle const& field_of_view)
    : sphere_radius_multiplier_(sphere_radius_multiplier),
      sin²_angular_resolution_(Pow<2>(Sin(angular_resolution))),
      tan_angular_resolution_(Tan(angular_resolution)),
      tan_field_of_view_(Tan(field_of_view)) {}

Planetarium::Planetarium(
    Parameters const& parameters,
    Perspective<Navigation, Camera, Length, OrthogonalMap> const& perspective,
    not_null<Ephemeris<Barycentric> const*> const ephemeris,
    not_null<NavigationFrame const*> const plotting_frame)
    : parameters_(parameters),
      perspective_(perspective),
      ephemeris_(ephemeris),
      plotting_frame_(plotting_frame) {}

RP2Lines<Length, Camera> Planetarium::PlotMethod0(
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end,
    Instant const& now) const {
  auto const plottable_spheres = ComputePlottableSpheres(now);
  auto const plottable_segments = ComputePlottableSegments(plottable_spheres,
                                                           begin, end);

  auto const field_of_view_radius² =
      perspective_.focal() * perspective_.focal() *
      parameters_.tan_field_of_view_ * parameters_.tan_field_of_view_;
  std::experimental::optional<Position<Navigation>> previous_position;
  RP2Lines<Length, Camera> rp2_lines;
  for (auto const& plottable_segment : plottable_segments) {
    // Apply the projection to the current plottable segment.
    auto const rp2_first = perspective_(plottable_segment.first);
    auto const rp2_second = perspective_(plottable_segment.second);

    // If the segment is entirely outside the field of view, ignore it.
    Length const x1 = rp2_first.x();
    Length const y1 = rp2_first.y();
    Length const x2 = rp2_second.x();
    Length const y2 = rp2_second.y();
    if (x1 * x1 + y1 * y1 > field_of_view_radius² &&
        x2 * x2 + y2 * y2 > field_of_view_radius²) {
      continue;
    }

    // Create a new ℝP² line when two segments are not consecutive.  Don't
    // compare ℝP² points for equality, that's expensive.
    bool const are_consecutive =
        previous_position == plottable_segment.first;
    previous_position = plottable_segment.second;

    if (are_consecutive) {
      rp2_lines.back().push_back(rp2_second);
    } else {
      RP2Line<Length, Camera> const rp2_line = {rp2_first, rp2_second};
      rp2_lines.push_back(rp2_line);
    }
  }
  return rp2_lines;
}

RP2Lines<Length, Camera> Planetarium::PlotMethod1(
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end,
    Instant const& now) const {
  Length const focal_plane_tolerance =
      perspective_.focal() * parameters_.tan_angular_resolution_;
  auto const focal_plane_tolerance² =
      focal_plane_tolerance * focal_plane_tolerance;

  auto const rp2_lines = PlotMethod0(begin, end, now);

  int skipped = 0;
  int total = 0;
  RP2Lines<Length, Camera> new_rp2_lines;
  for (auto const& rp2_line : rp2_lines) {
    RP2Line<Length, Camera> new_rp2_line;
    std::experimental::optional<RP2Point<Length, Camera>> start_rp2_point;
    for (int i = 0; i < rp2_line.size(); ++i) {
      RP2Point<Length, Camera> const& rp2_point = rp2_line[i];
      if (i == 0) {
        new_rp2_line.push_back(rp2_point);
        start_rp2_point = rp2_point;
      } else if (Pow<2>(rp2_point.x() - start_rp2_point->x()) +
                 Pow<2>(rp2_point.y() - start_rp2_point->y()) >
                     focal_plane_tolerance²) {
        // TODO(phl): This creates a segment if the tolerance is exceeded.  It
        // should probably create a segment that stays just below the tolerance.
        new_rp2_line.push_back(rp2_point);
        start_rp2_point = rp2_point;
      } else if (i == rp2_line.size() - 1) {
        new_rp2_line.push_back(rp2_point);
      } else {
        ++skipped;
      }
      ++total;
    }
    new_rp2_lines.push_back(std::move(new_rp2_line));
  }
  LOG(INFO) << "PlotMethod1 skipped " << skipped << " points out of " << total
            << ", emitting " << total - skipped << " points";
  return new_rp2_lines;
}

RP2Lines<Length, Camera> Planetarium::PlotMethod2(
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end,
    Instant const& now) const {
  RP2Lines<Length, Camera> lines;
  if (begin == end) {
    return lines;
  }
  auto last = end;
  --last;
  if (begin == last) {
    return lines;
  }

  double const tan²_angular_resolution =
      Pow<2>(parameters_.tan_angular_resolution_);
  auto const plottable_spheres = ComputePlottableSpheres(now);
  auto const& trajectory = *begin.trajectory();
  auto const final_time = last.time();

  auto previous_time = begin.time();
  RigidMotion<Barycentric, Navigation> to_plotting_frame_at_t =
      plotting_frame_->ToThisFrameAtTime(previous_time);
  DegreesOfFreedom<Navigation> const initial_degrees_of_freedom =
      to_plotting_frame_at_t(begin.degrees_of_freedom());
  Position<Navigation> previous_position =
      initial_degrees_of_freedom.position();
  Velocity<Navigation> previous_velocity =
      initial_degrees_of_freedom.velocity();
  Time Δt = final_time - previous_time;

  Instant t;
  double estimated_tan²_error;
  Position<Barycentric> position_in_barycentric;
  Position<Navigation> position;

  std::experimental::optional<Position<Navigation>> last_endpoint;

  int steps_accepted = 0;
  int steps_attempted = 0;

  goto estimate_tan²_error;

  while (previous_time < final_time) {
    do {
      // One square root because we have squared errors, another one because the
      // errors are quadratic in time (in other words, two square roots because
      // the squared errors are quartic in time).
      // A safety factor prevents catastrophic retries.
      Δt *= 0.9 * Sqrt(Sqrt(tan²_angular_resolution / estimated_tan²_error));
    estimate_tan²_error:
      t = previous_time + Δt;
      if (t > final_time) {
        t = final_time;
        Δt = t - previous_time;
      }
      Position<Navigation> const extrapolated_position =
          previous_position + previous_velocity * Δt;
      to_plotting_frame_at_t = plotting_frame_->ToThisFrameAtTime(t);
      position_in_barycentric = trajectory.EvaluatePosition(t);
      position = to_plotting_frame_at_t.rigid_transformation()(
                     position_in_barycentric);

      // The quadratic term of the error between the linear interpolation and
      // the actual function is maximized halfway through the segment, so it is
      // 1/2 (Δt/2)² f″(t-Δt) = (1/2 Δt² f″(t-Δt)) / 4; the squared error is
      // thus (1/2 Δt² f″(t-Δt))² / 16.
      estimated_tan²_error =
          perspective_.Tan²AngularDistance(extrapolated_position, position) /
          16;
      ++steps_attempted;
    } while (estimated_tan²_error > tan²_angular_resolution);
    ++steps_accepted;

    // TODO(egg): also limit to field of view.
    auto const segment_behind_focal_plane =
        perspective_.SegmentBehindFocalPlane(
            Segment<Displacement<Navigation>>(previous_position, position));

    previous_time = t;
    previous_position = position;
    previous_velocity =
        to_plotting_frame_at_t({position_in_barycentric,
                                trajectory.EvaluateVelocity(t)}).velocity();

    if (!segment_behind_focal_plane) {
      continue;
    }

    auto const visible_segments = perspective_.VisibleSegments(
                                      *segment_behind_focal_plane,
                                      plottable_spheres);
    for (auto const& segment : visible_segments) {
      if (last_endpoint != segment.first) {
        lines.emplace_back();
        lines.back().push_back(perspective_(segment.first));
      }
      lines.back().push_back(perspective_(segment.second));
      last_endpoint = segment.second;
    }
  }
  LOG(INFO) << "PlotMethod2 took " << steps_accepted << " steps, attempted "
            << steps_attempted << " steps";
  return lines;
}

std::vector<Sphere<Length, Navigation>> Planetarium::ComputePlottableSpheres(
    Instant const& now) const {
  RigidMotion<Barycentric, Navigation> const rigid_motion_at_now =
      plotting_frame_->ToThisFrameAtTime(now);
  std::vector<Sphere<Length, Navigation>> plottable_spheres;

  auto const& bodies = ephemeris_->bodies();
  for (auto const body : bodies) {
    auto const trajectory = ephemeris_->trajectory(body);
    Length const mean_radius = body->mean_radius();
    Position<Barycentric> const centre_in_barycentric =
        trajectory->EvaluatePosition(now);
    Sphere<Length, Navigation> plottable_sphere(
        rigid_motion_at_now.rigid_transformation()(centre_in_barycentric),
        parameters_.sphere_radius_multiplier_ * mean_radius);
    // If the sphere is seen under an angle that is very small it doesn't
    // participate in hiding.
    if (perspective_.SphereSin²HalfAngle(plottable_sphere) >
        parameters_.sin²_angular_resolution_) {
      plottable_spheres.emplace_back(std::move(plottable_sphere));
    }
  }
  return plottable_spheres;
}

Segments<Displacement<Navigation>> Planetarium::ComputePlottableSegments(
    const std::vector<Sphere<Length, Navigation>>& plottable_spheres,
    DiscreteTrajectory<Barycentric>::Iterator const& begin,
    DiscreteTrajectory<Barycentric>::Iterator const& end) const {
  std::vector<Segment<Displacement<Navigation>>> all_segments;
  if (begin == end) {
    return all_segments;
  }
  auto it1 = begin;
  Instant t1 = it1.time();
  RigidMotion<Barycentric, Navigation> rigid_motion_at_t1 =
      plotting_frame_->ToThisFrameAtTime(t1);
  Position<Navigation> p1 =
      rigid_motion_at_t1(it1.degrees_of_freedom()).position();

  auto it2 = it1;
  while (++it2 != end) {
    // Processing one segment of the trajectory.
    Instant const t2 = it2.time();

    // Transform the degrees of freedom to the plotting frame.
    RigidMotion<Barycentric, Navigation> const rigid_motion_at_t2 =
        plotting_frame_->ToThisFrameAtTime(t2);
    Position<Navigation> const p2 =
        rigid_motion_at_t2(it2.degrees_of_freedom()).position();

    // Find the part of the segment that is behind the focal plane.  We don't
    // care about things that are in front of the focal plane.
    const Segment<Displacement<Navigation>> segment = {p1, p2};
    auto const segment_behind_focal_plane =
        perspective_.SegmentBehindFocalPlane(segment);
    if (segment_behind_focal_plane) {
      // Find the part(s) of the segment that are not hidden by spheres.  These
      // are the ones we want to plot.
      auto segments = perspective_.VisibleSegments(*segment_behind_focal_plane,
                                                   plottable_spheres);
      std::move(segments.begin(),
                segments.end(),
                std::back_inserter(all_segments));
    }

    it1 = it2;
    t1 = t2;
    rigid_motion_at_t1 = rigid_motion_at_t2;
    p1 = p2;
  }

  return all_segments;
}

}  // namespace internal_planetarium
}  // namespace ksp_plugin
}  // namespace principia
<|endoftext|>