<commit_before>/*
 * Copyright (C) 2014 Cloudius Systems, Ltd.
 */

#ifndef DATABASE_HH_
#define DATABASE_HH_

#include "core/sstring.hh"
#include "core/shared_ptr.hh"
#include "net/byteorder.hh"
#include "utils/UUID.hh"
#include "db_clock.hh"
#include "gc_clock.hh"
#include <functional>
#include <boost/any.hpp>
#include <cstdint>
#include <boost/variant.hpp>
#include <unordered_map>
#include <map>
#include <set>
#include <vector>
#include <iostream>
#include <boost/functional/hash.hpp>
#include <experimental/optional>
#include <string.h>
#include "types.hh"
#include "tuple.hh"
#include "core/future.hh"
#include "cql3/column_specification.hh"
#include <limits>
#include <cstddef>
#include "schema.hh"

using partition_key_type = tuple_type<>;
using clustering_key_type = tuple_type<>;
using clustering_prefix_type = tuple_prefix;
using partition_key = bytes;
using clustering_key = bytes;
using clustering_prefix = clustering_prefix_type::value_type;

namespace api {

using timestamp_type = int64_t;
timestamp_type constexpr missing_timestamp = std::numeric_limits<timestamp_type>::min();
timestamp_type constexpr min_timestamp = std::numeric_limits<timestamp_type>::min() + 1;
timestamp_type constexpr max_timestamp = std::numeric_limits<timestamp_type>::max();

}

/**
* Represents deletion operation. Can be commuted with other tombstones via apply() method.
* Can be empty.
*
*/
struct tombstone final {
    api::timestamp_type timestamp;
    gc_clock::time_point ttl;

    tombstone(api::timestamp_type timestamp, gc_clock::time_point ttl)
        : timestamp(timestamp)
        , ttl(ttl)
    { }

    tombstone()
        : tombstone(api::missing_timestamp, {})
    { }

    int compare(const tombstone& t) const {
        if (timestamp < t.timestamp) {
            return -1;
        } else if (timestamp > t.timestamp) {
            return 1;
        } else if (ttl < t.ttl) {
            return -1;
        } else if (ttl > t.ttl) {
            return 1;
        } else {
            return 0;
        }
    }

    bool operator<(const tombstone& t) const {
        return compare(t) < 0;
    }

    bool operator==(const tombstone& t) const {
        return compare(t) == 0;
    }

    operator bool() const {
        return timestamp != api::missing_timestamp;
    }

    void apply(const tombstone& t) {
        if (*this < t) {
            *this = t;
        }
    }
};

using ttl_opt = std::experimental::optional<gc_clock::time_point>;

struct atomic_cell final {
    struct dead {
        gc_clock::time_point ttl;
    };
    struct live {
        ttl_opt ttl;
        bytes value;
    };
    api::timestamp_type timestamp;
    boost::variant<dead, live> value;
    bool is_live() const { return value.which() == 1; }
    // Call only when is_live() == true
    const live& as_live() const { return boost::get<live>(value); }
    // Call only when is_live() == false
    const dead& as_dead() const { return boost::get<dead>(value); }
};

using row = std::map<column_id, boost::any>;

struct deletable_row final {
    tombstone t;
    row cells;
};

using row_tombstone_set = std::map<bytes, tombstone, serialized_compare>;

class mutation_partition final {
private:
    tombstone _tombstone;
    row _static_row;
    std::map<clustering_key, deletable_row, key_compare> _rows;
    row_tombstone_set _row_tombstones;
public:
    mutation_partition(schema_ptr s)
        : _rows(key_compare(s->clustering_key_type))
        , _row_tombstones(serialized_compare(s->clustering_key_prefix_type))
    { }

    void apply(tombstone t) {
        _tombstone.apply(t);
    }

    void apply_delete(schema_ptr schema, const clustering_prefix& prefix, tombstone t) {
        if (prefix.empty()) {
            apply(t);
        } else if (prefix.size() == schema->clustering_key.size()) {
            _rows[serialize_value(*schema->clustering_key_type, prefix)].t.apply(t);
        } else {
            apply_row_tombstone(schema, {serialize_value(*schema->clustering_key_prefix_type, prefix), t});
        }
    }

    void apply_row_tombstone(schema_ptr schema, bytes prefix, tombstone t) {
        apply_row_tombstone(schema, {std::move(prefix), std::move(t)});
    }

    void apply_row_tombstone(schema_ptr schema, std::pair<bytes, tombstone> row_tombstone) {
        auto& prefix = row_tombstone.first;
        auto i = _row_tombstones.lower_bound(prefix);
        if (i == _row_tombstones.end() || !schema->clustering_key_prefix_type->equal(prefix, i->first)
                || row_tombstone.second > i->second) {
            _row_tombstones.insert(i, std::move(row_tombstone));
        }
    }

    void apply(schema_ptr schema, mutation_partition&& p);

    row& static_row() {
        return _static_row;
    }

    row& clustered_row(const clustering_key& key) {
        return _rows[key].cells;
    }

    row& clustered_row(clustering_key&& key) {
        return _rows[std::move(key)].cells;
    }

    row* find_row(const clustering_key& key) {
        auto i = _rows.find(key);
        if (i == _rows.end()) {
            return nullptr;
        }
        return &i->second.cells;
    }
};

class mutation final {
public:
    schema_ptr schema;
    partition_key key;
    mutation_partition p;
public:
    mutation(partition_key key_, schema_ptr schema_)
        : schema(std::move(schema_))
        , key(std::move(key_))
        , p(schema)
    { }

    mutation(mutation&&) = default;
    mutation(const mutation&) = delete;

    void set_static_cell(const column_definition& def, boost::any value) {
        p.static_row()[def.id] = std::move(value);
    }

    void set_clustered_cell(const clustering_prefix& prefix, const column_definition& def, boost::any value) {
        auto& row = p.clustered_row(serialize_value(*schema->clustering_key_type, prefix));
        row[def.id] = std::move(value);
    }

    void set_clustered_cell(const clustering_key& key, const column_definition& def, boost::any value) {
        auto& row = p.clustered_row(key);
        row[def.id] = std::move(value);
    }
};

struct column_family {
    column_family(schema_ptr schema);
    mutation_partition& find_or_create_partition(const bytes& key);
    row& find_or_create_row(const bytes& partition_key, const bytes& clustering_key);
    mutation_partition* find_partition(const bytes& key);
    row* find_row(const bytes& partition_key, const bytes& clustering_key);
    schema_ptr _schema;
    // partition key -> partition
    std::map<bytes, mutation_partition, key_compare> partitions;
    void apply(mutation&& m);
};

class keyspace {
public:
    std::unordered_map<sstring, column_family> column_families;
    static future<keyspace> populate(sstring datadir);
    schema_ptr find_schema(sstring cf_name);
    column_family* find_column_family(sstring cf_name);
};

class database {
public:
    std::unordered_map<sstring, keyspace> keyspaces;
    static future<database> populate(sstring datadir);
    keyspace* find_keyspace(sstring name);
};

#endif /* DATABASE_HH_ */
<commit_msg>db: Define the rest of comparison operators in tombstone<commit_after>/*
 * Copyright (C) 2014 Cloudius Systems, Ltd.
 */

#ifndef DATABASE_HH_
#define DATABASE_HH_

#include "core/sstring.hh"
#include "core/shared_ptr.hh"
#include "net/byteorder.hh"
#include "utils/UUID.hh"
#include "db_clock.hh"
#include "gc_clock.hh"
#include <functional>
#include <boost/any.hpp>
#include <cstdint>
#include <boost/variant.hpp>
#include <unordered_map>
#include <map>
#include <set>
#include <vector>
#include <iostream>
#include <boost/functional/hash.hpp>
#include <experimental/optional>
#include <string.h>
#include "types.hh"
#include "tuple.hh"
#include "core/future.hh"
#include "cql3/column_specification.hh"
#include <limits>
#include <cstddef>
#include "schema.hh"

using partition_key_type = tuple_type<>;
using clustering_key_type = tuple_type<>;
using clustering_prefix_type = tuple_prefix;
using partition_key = bytes;
using clustering_key = bytes;
using clustering_prefix = clustering_prefix_type::value_type;

namespace api {

using timestamp_type = int64_t;
timestamp_type constexpr missing_timestamp = std::numeric_limits<timestamp_type>::min();
timestamp_type constexpr min_timestamp = std::numeric_limits<timestamp_type>::min() + 1;
timestamp_type constexpr max_timestamp = std::numeric_limits<timestamp_type>::max();

}

/**
* Represents deletion operation. Can be commuted with other tombstones via apply() method.
* Can be empty.
*
*/
struct tombstone final {
    api::timestamp_type timestamp;
    gc_clock::time_point ttl;

    tombstone(api::timestamp_type timestamp, gc_clock::time_point ttl)
        : timestamp(timestamp)
        , ttl(ttl)
    { }

    tombstone()
        : tombstone(api::missing_timestamp, {})
    { }

    int compare(const tombstone& t) const {
        if (timestamp < t.timestamp) {
            return -1;
        } else if (timestamp > t.timestamp) {
            return 1;
        } else if (ttl < t.ttl) {
            return -1;
        } else if (ttl > t.ttl) {
            return 1;
        } else {
            return 0;
        }
    }

    bool operator<(const tombstone& t) const {
        return compare(t) < 0;
    }

    bool operator<=(const tombstone& t) const {
        return compare(t) <= 0;
    }

    bool operator>(const tombstone& t) const {
        return compare(t) > 0;
    }

    bool operator>=(const tombstone& t) const {
        return compare(t) >= 0;
    }

    bool operator==(const tombstone& t) const {
        return compare(t) == 0;
    }

    bool operator!=(const tombstone& t) const {
        return compare(t) != 0;
    }

    operator bool() const {
        return timestamp != api::missing_timestamp;
    }

    void apply(const tombstone& t) {
        if (*this < t) {
            *this = t;
        }
    }
};

using ttl_opt = std::experimental::optional<gc_clock::time_point>;

struct atomic_cell final {
    struct dead {
        gc_clock::time_point ttl;
    };
    struct live {
        ttl_opt ttl;
        bytes value;
    };
    api::timestamp_type timestamp;
    boost::variant<dead, live> value;
    bool is_live() const { return value.which() == 1; }
    // Call only when is_live() == true
    const live& as_live() const { return boost::get<live>(value); }
    // Call only when is_live() == false
    const dead& as_dead() const { return boost::get<dead>(value); }
};

using row = std::map<column_id, boost::any>;

struct deletable_row final {
    tombstone t;
    row cells;
};

using row_tombstone_set = std::map<bytes, tombstone, serialized_compare>;

class mutation_partition final {
private:
    tombstone _tombstone;
    row _static_row;
    std::map<clustering_key, deletable_row, key_compare> _rows;
    row_tombstone_set _row_tombstones;
public:
    mutation_partition(schema_ptr s)
        : _rows(key_compare(s->clustering_key_type))
        , _row_tombstones(serialized_compare(s->clustering_key_prefix_type))
    { }

    void apply(tombstone t) {
        _tombstone.apply(t);
    }

    void apply_delete(schema_ptr schema, const clustering_prefix& prefix, tombstone t) {
        if (prefix.empty()) {
            apply(t);
        } else if (prefix.size() == schema->clustering_key.size()) {
            _rows[serialize_value(*schema->clustering_key_type, prefix)].t.apply(t);
        } else {
            apply_row_tombstone(schema, {serialize_value(*schema->clustering_key_prefix_type, prefix), t});
        }
    }

    void apply_row_tombstone(schema_ptr schema, bytes prefix, tombstone t) {
        apply_row_tombstone(schema, {std::move(prefix), std::move(t)});
    }

    void apply_row_tombstone(schema_ptr schema, std::pair<bytes, tombstone> row_tombstone) {
        auto& prefix = row_tombstone.first;
        auto i = _row_tombstones.lower_bound(prefix);
        if (i == _row_tombstones.end() || !schema->clustering_key_prefix_type->equal(prefix, i->first)
                || row_tombstone.second > i->second) {
            _row_tombstones.insert(i, std::move(row_tombstone));
        }
    }

    void apply(schema_ptr schema, mutation_partition&& p);

    row& static_row() {
        return _static_row;
    }

    row& clustered_row(const clustering_key& key) {
        return _rows[key].cells;
    }

    row& clustered_row(clustering_key&& key) {
        return _rows[std::move(key)].cells;
    }

    row* find_row(const clustering_key& key) {
        auto i = _rows.find(key);
        if (i == _rows.end()) {
            return nullptr;
        }
        return &i->second.cells;
    }
};

class mutation final {
public:
    schema_ptr schema;
    partition_key key;
    mutation_partition p;
public:
    mutation(partition_key key_, schema_ptr schema_)
        : schema(std::move(schema_))
        , key(std::move(key_))
        , p(schema)
    { }

    mutation(mutation&&) = default;
    mutation(const mutation&) = delete;

    void set_static_cell(const column_definition& def, boost::any value) {
        p.static_row()[def.id] = std::move(value);
    }

    void set_clustered_cell(const clustering_prefix& prefix, const column_definition& def, boost::any value) {
        auto& row = p.clustered_row(serialize_value(*schema->clustering_key_type, prefix));
        row[def.id] = std::move(value);
    }

    void set_clustered_cell(const clustering_key& key, const column_definition& def, boost::any value) {
        auto& row = p.clustered_row(key);
        row[def.id] = std::move(value);
    }
};

struct column_family {
    column_family(schema_ptr schema);
    mutation_partition& find_or_create_partition(const bytes& key);
    row& find_or_create_row(const bytes& partition_key, const bytes& clustering_key);
    mutation_partition* find_partition(const bytes& key);
    row* find_row(const bytes& partition_key, const bytes& clustering_key);
    schema_ptr _schema;
    // partition key -> partition
    std::map<bytes, mutation_partition, key_compare> partitions;
    void apply(mutation&& m);
};

class keyspace {
public:
    std::unordered_map<sstring, column_family> column_families;
    static future<keyspace> populate(sstring datadir);
    schema_ptr find_schema(sstring cf_name);
    column_family* find_column_family(sstring cf_name);
};

class database {
public:
    std::unordered_map<sstring, keyspace> keyspaces;
    static future<database> populate(sstring datadir);
    keyspace* find_keyspace(sstring name);
};

#endif /* DATABASE_HH_ */
<|endoftext|>


<commit_before>// Copyright (c) 2016 ASMlover. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list ofconditions and the following disclaimer.
//
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materialsprovided with the
//    distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <sys/time.h>
#include <inttypes.h>
#include <time.h>
#include "TTimestamp.h"

namespace tyr { namespace basic {

static_assert(sizeof(Timestamp) == sizeof(int64_t), "Timestamp is same size as int64_t");

std::string Timestamp::to_string(void) const {
  char buf[32] = {0};
  int64_t sec = epoch_msec_ / kMicroSecondsPerSecond;
  int64_t msec = epoch_msec_ % kMicroSecondsPerSecond;
  snprintf(buf, sizeof(buf) - 1, "%" PRId64 ".%06" PRId64 "", sec, msec);
  return buf;
}

std::string Timestamp::to_formatted_string(bool show_msec) const {
  char buf[32] = {0};
  time_t sec = static_cast<time_t>(epoch_msec_ / kMicroSecondsPerSecond);
  struct tm tm_time;
  gmtime_r(&sec, &tm_time);

  if (show_msec) {
    int msec = static_cast<int>(epoch_msec_ % kMicroSecondsPerSecond);
    snprintf(buf,
        sizeof(buf),
        "%04d%02d%02d %02d:%02d:%02d.%06d",
        tm_time.tm_year + 1900,
        tm_time.tm_mon + 1,
        tm_time.tm_mday,
        tm_time.tm_hour,
        tm_time.tm_min,
        tm_time.tm_sec,
        msec);
  }
  else {
    snprintf(buf,
        sizeof(buf),
        "%04d%02d%02d %02d:%02d:%02d",
        tm_time.tm_year + 1900,
        tm_time.tm_mon + 1,
        tm_time.tm_mday,
        tm_time.tm_hour,
        tm_time.tm_min,
        tm_time.tm_sec);
  }

  return buf;
}

Timestamp Timestamp::now(void) {
  struct timeval tv;
  gettimeofday(&tv, nullptr);
  int64_t sec = tv.tv_sec;
  return Timestamp(sec * kMicroSecondsPerSecond + tv.tv_usec);
}

Timestamp Timestamp::invalid(void) {
  return Timestamp();
}

Timestamp Timestamp::from_unix_time(time_t t) {
  return from_unix_time(t, 0);
}

Timestamp Timestamp::from_unix_time(time_t t, int msec) {
  return Timestamp(static_cast<int64_t>(t) * kMicroSecondsPerSecond + msec);
}

}}
<commit_msg>chore(timestamp): updated the implementation of timestamp<commit_after>// Copyright (c) 2016 ASMlover. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list ofconditions and the following disclaimer.
//
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materialsprovided with the
//    distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <inttypes.h>
#include <time.h>
#include "TPlatform.h"
#include "TTimestamp.h"

namespace tyr { namespace basic {

static_assert(sizeof(Timestamp) == sizeof(int64_t), "Timestamp is same size as int64_t");

std::string Timestamp::to_string(void) const {
  char buf[32] = {0};
  int64_t sec = epoch_msec_ / kMicroSecondsPerSecond;
  int64_t msec = epoch_msec_ % kMicroSecondsPerSecond;
  snprintf(buf, sizeof(buf) - 1, "%" PRId64 ".%06" PRId64 "", sec, msec);
  return buf;
}

std::string Timestamp::to_formatted_string(bool show_msec) const {
  char buf[32] = {0};
  time_t time = static_cast<time_t>(epoch_msec_ / kMicroSecondsPerSecond);
  struct tm result;
  gmtime_r(&time, &result);

  if (show_msec) {
    int msec = static_cast<int>(epoch_msec_ % kMicroSecondsPerSecond);
    snprintf(buf,
        sizeof(buf),
        "%04d%02d%02d %02d:%02d:%02d.%06d",
        result.tm_year + 1900,
        result.tm_mon + 1,
        result.tm_mday,
        result.tm_hour,
        result.tm_min,
        result.tm_sec,
        msec);
  }
  else {
    snprintf(buf,
        sizeof(buf),
        "%04d%02d%02d %02d:%02d:%02d",
        result.tm_year + 1900,
        result.tm_mon + 1,
        result.tm_mday,
        result.tm_hour,
        result.tm_min,
        result.tm_sec);
  }

  return buf;
}

Timestamp Timestamp::now(void) {
  struct timeval tv;
  gettimeofday(&tv, nullptr);
  int64_t sec = tv.tv_sec;
  return Timestamp(sec * kMicroSecondsPerSecond + tv.tv_usec);
}

Timestamp Timestamp::invalid(void) {
  return Timestamp();
}

Timestamp Timestamp::from_unix_time(time_t t) {
  return from_unix_time(t, 0);
}

Timestamp Timestamp::from_unix_time(time_t t, int msec) {
  return Timestamp(static_cast<int64_t>(t) * kMicroSecondsPerSecond + msec);
}

}}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include "arrow/util/tdigest.h"

#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
#include <tuple>
#include <vector>

#include "arrow/status.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

namespace arrow {
namespace internal {

namespace {

// a numerically stable lerp is unbelievably complex
// but we are *approximating* the quantile, so let's keep it simple
double Lerp(double a, double b, double t) { return a + t * (b - a); }

// histogram bin
struct Centroid {
  double mean;
  double weight;  // # data points in this bin

  // merge with another centroid
  void Merge(const Centroid& centroid) {
    weight += centroid.weight;
    mean += (centroid.mean - mean) * centroid.weight / weight;
  }
};

// scale function K0: linear function, as baseline
struct ScalerK0 {
  explicit ScalerK0(uint32_t delta) : delta_norm(delta / 2.0) {}

  double K(double q) const { return delta_norm * q; }
  double Q(double k) const { return k / delta_norm; }

  const double delta_norm;
};

// scale function K1
struct ScalerK1 {
  explicit ScalerK1(uint32_t delta) : delta_norm(delta / (2.0 * M_PI)) {}

  double K(double q) const { return delta_norm * std::asin(2 * q - 1); }
  double Q(double k) const { return (std::sin(k / delta_norm) + 1) / 2; }

  const double delta_norm;
};

// implements t-digest merging algorithm
template <class T = ScalerK1>
class TDigestMerger : private T {
 public:
  explicit TDigestMerger(uint32_t delta) : T(delta) { Reset(0, nullptr); }

  void Reset(double total_weight, std::vector<Centroid>* tdigest) {
    total_weight_ = total_weight;
    tdigest_ = tdigest;
    if (tdigest_) {
      tdigest_->resize(0);
    }
    weight_so_far_ = 0;
    weight_limit_ = -1;  // trigger first centroid merge
  }

  // merge one centroid from a sorted centroid stream
  void Add(const Centroid& centroid) {
    auto& td = *tdigest_;
    const double weight = weight_so_far_ + centroid.weight;
    if (weight <= weight_limit_) {
      td.back().Merge(centroid);
    } else {
      const double quantile = weight_so_far_ / total_weight_;
      const double next_weight_limit = total_weight_ * this->Q(this->K(quantile) + 1);
      // weight limit should be strictly increasing, until the last centroid
      if (next_weight_limit <= weight_limit_) {
        weight_limit_ = total_weight_;
      } else {
        weight_limit_ = next_weight_limit;
      }
      td.push_back(centroid);  // should never exceed capacity and trigger reallocation
    }
    weight_so_far_ = weight;
  }

  // validate k-size of a tdigest
  Status Validate(const std::vector<Centroid>& tdigest, double total_weight) const {
    double q_prev = 0, k_prev = this->K(0);
    for (size_t i = 0; i < tdigest.size(); ++i) {
      const double q = q_prev + tdigest[i].weight / total_weight;
      const double k = this->K(q);
      if (tdigest[i].weight != 1 && (k - k_prev) > 1.001) {
        return Status::Invalid("oversized centroid: ", k - k_prev);
      }
      k_prev = k;
      q_prev = q;
    }
    return Status::OK();
  }

 private:
  double total_weight_;   // total weight of this tdigest
  double weight_so_far_;  // accumulated weight till current bin
  double weight_limit_;   // max accumulated weight to move to next bin
  std::vector<Centroid>* tdigest_;
};

}  // namespace

class TDigest::TDigestImpl {
 public:
  explicit TDigestImpl(uint32_t delta)
      : delta_(delta > 10 ? delta : 10), merger_(delta_) {
    tdigests_[0].reserve(delta_);
    tdigests_[1].reserve(delta_);
    Reset();
  }

  void Reset() {
    tdigests_[0].resize(0);
    tdigests_[1].resize(0);
    current_ = 0;
    total_weight_ = 0;
    min_ = std::numeric_limits<double>::max();
    max_ = std::numeric_limits<double>::lowest();
    merger_.Reset(0, nullptr);
  }

  Status Validate() const {
    // check weight, centroid order
    double total_weight = 0, prev_mean = std::numeric_limits<double>::lowest();
    for (const auto& centroid : tdigests_[current_]) {
      if (std::isnan(centroid.mean) || std::isnan(centroid.weight)) {
        return Status::Invalid("NAN found in tdigest");
      }
      if (centroid.mean < prev_mean) {
        return Status::Invalid("centroid mean decreases");
      }
      if (centroid.weight < 1) {
        return Status::Invalid("invalid centroid weight");
      }
      prev_mean = centroid.mean;
      total_weight += centroid.weight;
    }
    if (total_weight != total_weight_) {
      return Status::Invalid("tdigest total weight mismatch");
    }
    // check if buffer expanded
    if (tdigests_[0].capacity() > delta_ || tdigests_[1].capacity() > delta_) {
      return Status::Invalid("oversized tdigest buffer");
    }
    // check k-size
    return merger_.Validate(tdigests_[current_], total_weight_);
  }

  void Dump() const {
    const auto& td = tdigests_[current_];
    for (size_t i = 0; i < td.size(); ++i) {
      std::cerr << i << ": mean = " << td[i].mean << ", weight = " << td[i].weight
                << std::endl;
    }
    std::cerr << "min = " << min_ << ", max = " << max_ << std::endl;
  }

  // merge with other tdigests
  void Merge(const std::vector<const TDigestImpl*>& tdigest_impls) {
    // current and end iterator
    using CentroidIter = std::vector<Centroid>::const_iterator;
    using CentroidIterPair = std::pair<CentroidIter, CentroidIter>;
    // use a min-heap to find next minimal centroid from all tdigests
    auto centroid_gt = [](const CentroidIterPair& lhs, const CentroidIterPair& rhs) {
      return lhs.first->mean > rhs.first->mean;
    };
    using CentroidQueue =
        std::priority_queue<CentroidIterPair, std::vector<CentroidIterPair>,
                            decltype(centroid_gt)>;

    // trivial dynamic memory allocated at runtime
    std::vector<CentroidIterPair> queue_buffer;
    queue_buffer.reserve(tdigest_impls.size() + 1);
    CentroidQueue queue(std::move(centroid_gt), std::move(queue_buffer));

    const auto& this_tdigest = tdigests_[current_];
    if (this_tdigest.size() > 0) {
      queue.emplace(this_tdigest.cbegin(), this_tdigest.cend());
    }
    for (const TDigestImpl* td : tdigest_impls) {
      const auto& other_tdigest = td->tdigests_[td->current_];
      if (other_tdigest.size() > 0) {
        queue.emplace(other_tdigest.cbegin(), other_tdigest.cend());
        total_weight_ += td->total_weight_;
        min_ = std::min(min_, td->min_);
        max_ = std::max(max_, td->max_);
      }
    }

    merger_.Reset(total_weight_, &tdigests_[1 - current_]);
    CentroidIter current_iter, end_iter;
    // do k-way merge till one buffer left
    while (queue.size() > 1) {
      std::tie(current_iter, end_iter) = queue.top();
      merger_.Add(*current_iter);
      queue.pop();
      if (++current_iter != end_iter) {
        queue.emplace(current_iter, end_iter);
      }
    }
    // merge last buffer
    if (!queue.empty()) {
      std::tie(current_iter, end_iter) = queue.top();
      while (current_iter != end_iter) {
        merger_.Add(*current_iter++);
      }
    }
    merger_.Reset(0, nullptr);

    current_ = 1 - current_;
  }

  // merge input data with current tdigest
  void MergeInput(std::vector<double>& input) {
    total_weight_ += input.size();

    std::sort(input.begin(), input.end());
    min_ = std::min(min_, input.front());
    max_ = std::max(max_, input.back());

    // pick next minimal centroid from input and tdigest, feed to merger
    merger_.Reset(total_weight_, &tdigests_[1 - current_]);
    const auto& td = tdigests_[current_];
    uint32_t tdigest_index = 0, input_index = 0;
    while (tdigest_index < td.size() && input_index < input.size()) {
      if (td[tdigest_index].mean < input[input_index]) {
        merger_.Add(td[tdigest_index++]);
      } else {
        merger_.Add(Centroid{input[input_index++], 1});
      }
    }
    while (tdigest_index < td.size()) {
      merger_.Add(td[tdigest_index++]);
    }
    while (input_index < input.size()) {
      merger_.Add(Centroid{input[input_index++], 1});
    }
    merger_.Reset(0, nullptr);

    input.resize(0);
    current_ = 1 - current_;
  }

  double Quantile(double q) const {
    const auto& td = tdigests_[current_];

    if (q < 0 || q > 1 || td.size() == 0) {
      return NAN;
    }

    const double index = q * total_weight_;
    if (index <= 1) {
      return min_;
    } else if (index >= total_weight_ - 1) {
      return max_;
    }

    // find centroid contains the index
    uint32_t ci = 0;
    double weight_sum = 0;
    for (; ci < td.size(); ++ci) {
      weight_sum += td[ci].weight;
      if (index <= weight_sum) {
        break;
      }
    }
    DCHECK_LT(ci, td.size());

    // deviation of index from the centroid center
    double diff = index + td[ci].weight / 2 - weight_sum;

    // index happen to be in a unit weight centroid
    if (td[ci].weight == 1 && std::abs(diff) < 0.5) {
      return td[ci].mean;
    }

    // find adjacent centroids for interpolation
    uint32_t ci_left = ci, ci_right = ci;
    if (diff > 0) {
      if (ci_right == td.size() - 1) {
        // index larger than center of last bin
        DCHECK_EQ(weight_sum, total_weight_);
        const Centroid* c = &td[ci_right];
        DCHECK_GE(c->weight, 2);
        return Lerp(c->mean, max_, diff / (c->weight / 2));
      }
      ++ci_right;
    } else {
      if (ci_left == 0) {
        // index smaller than center of first bin
        const Centroid* c = &td[0];
        DCHECK_GE(c->weight, 2);
        return Lerp(min_, c->mean, index / (c->weight / 2));
      }
      --ci_left;
      diff += td[ci_left].weight / 2 + td[ci_right].weight / 2;
    }

    // interpolate from adjacent centroids
    diff /= (td[ci_left].weight / 2 + td[ci_right].weight / 2);
    return Lerp(td[ci_left].mean, td[ci_right].mean, diff);
  }

  double Mean() const {
    double sum = 0;
    for (const auto& centroid : tdigests_[current_]) {
      sum += centroid.mean * centroid.weight;
    }
    return total_weight_ == 0 ? NAN : sum / total_weight_;
  }

  double total_weight() const { return total_weight_; }

 private:
  // must be delcared before merger_, see constructor initialization list
  const uint32_t delta_;

  TDigestMerger<> merger_;
  double total_weight_;
  double min_, max_;

  // ping-pong buffer holds two tdigests, size = 2 * delta * sizeof(Centroid)
  std::vector<Centroid> tdigests_[2];
  // index of active tdigest buffer, 0 or 1
  int current_;
};

TDigest::TDigest(uint32_t delta, uint32_t buffer_size) : impl_(new TDigestImpl(delta)) {
  input_.reserve(buffer_size);
  Reset();
}

TDigest::~TDigest() = default;
TDigest::TDigest(TDigest&&) = default;
TDigest& TDigest::operator=(TDigest&&) = default;

void TDigest::Reset() {
  input_.resize(0);
  impl_->Reset();
}

Status TDigest::Validate() {
  MergeInput();
  return impl_->Validate();
}

void TDigest::Dump() {
  MergeInput();
  impl_->Dump();
}

void TDigest::Merge(std::vector<TDigest>* tdigests) {
  MergeInput();

  std::vector<const TDigestImpl*> tdigest_impls;
  tdigest_impls.reserve(tdigests->size());
  for (auto& td : *tdigests) {
    td.MergeInput();
    tdigest_impls.push_back(td.impl_.get());
  }
  impl_->Merge(tdigest_impls);
}

double TDigest::Quantile(double q) {
  MergeInput();
  return impl_->Quantile(q);
}

double TDigest::Mean() {
  MergeInput();
  return impl_->Mean();
}

bool TDigest::is_empty() const {
  return input_.size() == 0 && impl_->total_weight() == 0;
}

void TDigest::MergeInput() {
  if (input_.size() > 0) {
    impl_->MergeInput(input_);  // will mutate input_
  }
}

}  // namespace internal
}  // namespace arrow
<commit_msg>ARROW-13290: [C++] Add missing include<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include "arrow/util/tdigest.h"

#include <algorithm>
#include <cmath>
#include <iostream>
#include <limits>
#include <queue>
#include <tuple>
#include <vector>

#include "arrow/status.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

namespace arrow {
namespace internal {

namespace {

// a numerically stable lerp is unbelievably complex
// but we are *approximating* the quantile, so let's keep it simple
double Lerp(double a, double b, double t) { return a + t * (b - a); }

// histogram bin
struct Centroid {
  double mean;
  double weight;  // # data points in this bin

  // merge with another centroid
  void Merge(const Centroid& centroid) {
    weight += centroid.weight;
    mean += (centroid.mean - mean) * centroid.weight / weight;
  }
};

// scale function K0: linear function, as baseline
struct ScalerK0 {
  explicit ScalerK0(uint32_t delta) : delta_norm(delta / 2.0) {}

  double K(double q) const { return delta_norm * q; }
  double Q(double k) const { return k / delta_norm; }

  const double delta_norm;
};

// scale function K1
struct ScalerK1 {
  explicit ScalerK1(uint32_t delta) : delta_norm(delta / (2.0 * M_PI)) {}

  double K(double q) const { return delta_norm * std::asin(2 * q - 1); }
  double Q(double k) const { return (std::sin(k / delta_norm) + 1) / 2; }

  const double delta_norm;
};

// implements t-digest merging algorithm
template <class T = ScalerK1>
class TDigestMerger : private T {
 public:
  explicit TDigestMerger(uint32_t delta) : T(delta) { Reset(0, nullptr); }

  void Reset(double total_weight, std::vector<Centroid>* tdigest) {
    total_weight_ = total_weight;
    tdigest_ = tdigest;
    if (tdigest_) {
      tdigest_->resize(0);
    }
    weight_so_far_ = 0;
    weight_limit_ = -1;  // trigger first centroid merge
  }

  // merge one centroid from a sorted centroid stream
  void Add(const Centroid& centroid) {
    auto& td = *tdigest_;
    const double weight = weight_so_far_ + centroid.weight;
    if (weight <= weight_limit_) {
      td.back().Merge(centroid);
    } else {
      const double quantile = weight_so_far_ / total_weight_;
      const double next_weight_limit = total_weight_ * this->Q(this->K(quantile) + 1);
      // weight limit should be strictly increasing, until the last centroid
      if (next_weight_limit <= weight_limit_) {
        weight_limit_ = total_weight_;
      } else {
        weight_limit_ = next_weight_limit;
      }
      td.push_back(centroid);  // should never exceed capacity and trigger reallocation
    }
    weight_so_far_ = weight;
  }

  // validate k-size of a tdigest
  Status Validate(const std::vector<Centroid>& tdigest, double total_weight) const {
    double q_prev = 0, k_prev = this->K(0);
    for (size_t i = 0; i < tdigest.size(); ++i) {
      const double q = q_prev + tdigest[i].weight / total_weight;
      const double k = this->K(q);
      if (tdigest[i].weight != 1 && (k - k_prev) > 1.001) {
        return Status::Invalid("oversized centroid: ", k - k_prev);
      }
      k_prev = k;
      q_prev = q;
    }
    return Status::OK();
  }

 private:
  double total_weight_;   // total weight of this tdigest
  double weight_so_far_;  // accumulated weight till current bin
  double weight_limit_;   // max accumulated weight to move to next bin
  std::vector<Centroid>* tdigest_;
};

}  // namespace

class TDigest::TDigestImpl {
 public:
  explicit TDigestImpl(uint32_t delta)
      : delta_(delta > 10 ? delta : 10), merger_(delta_) {
    tdigests_[0].reserve(delta_);
    tdigests_[1].reserve(delta_);
    Reset();
  }

  void Reset() {
    tdigests_[0].resize(0);
    tdigests_[1].resize(0);
    current_ = 0;
    total_weight_ = 0;
    min_ = std::numeric_limits<double>::max();
    max_ = std::numeric_limits<double>::lowest();
    merger_.Reset(0, nullptr);
  }

  Status Validate() const {
    // check weight, centroid order
    double total_weight = 0, prev_mean = std::numeric_limits<double>::lowest();
    for (const auto& centroid : tdigests_[current_]) {
      if (std::isnan(centroid.mean) || std::isnan(centroid.weight)) {
        return Status::Invalid("NAN found in tdigest");
      }
      if (centroid.mean < prev_mean) {
        return Status::Invalid("centroid mean decreases");
      }
      if (centroid.weight < 1) {
        return Status::Invalid("invalid centroid weight");
      }
      prev_mean = centroid.mean;
      total_weight += centroid.weight;
    }
    if (total_weight != total_weight_) {
      return Status::Invalid("tdigest total weight mismatch");
    }
    // check if buffer expanded
    if (tdigests_[0].capacity() > delta_ || tdigests_[1].capacity() > delta_) {
      return Status::Invalid("oversized tdigest buffer");
    }
    // check k-size
    return merger_.Validate(tdigests_[current_], total_weight_);
  }

  void Dump() const {
    const auto& td = tdigests_[current_];
    for (size_t i = 0; i < td.size(); ++i) {
      std::cerr << i << ": mean = " << td[i].mean << ", weight = " << td[i].weight
                << std::endl;
    }
    std::cerr << "min = " << min_ << ", max = " << max_ << std::endl;
  }

  // merge with other tdigests
  void Merge(const std::vector<const TDigestImpl*>& tdigest_impls) {
    // current and end iterator
    using CentroidIter = std::vector<Centroid>::const_iterator;
    using CentroidIterPair = std::pair<CentroidIter, CentroidIter>;
    // use a min-heap to find next minimal centroid from all tdigests
    auto centroid_gt = [](const CentroidIterPair& lhs, const CentroidIterPair& rhs) {
      return lhs.first->mean > rhs.first->mean;
    };
    using CentroidQueue =
        std::priority_queue<CentroidIterPair, std::vector<CentroidIterPair>,
                            decltype(centroid_gt)>;

    // trivial dynamic memory allocated at runtime
    std::vector<CentroidIterPair> queue_buffer;
    queue_buffer.reserve(tdigest_impls.size() + 1);
    CentroidQueue queue(std::move(centroid_gt), std::move(queue_buffer));

    const auto& this_tdigest = tdigests_[current_];
    if (this_tdigest.size() > 0) {
      queue.emplace(this_tdigest.cbegin(), this_tdigest.cend());
    }
    for (const TDigestImpl* td : tdigest_impls) {
      const auto& other_tdigest = td->tdigests_[td->current_];
      if (other_tdigest.size() > 0) {
        queue.emplace(other_tdigest.cbegin(), other_tdigest.cend());
        total_weight_ += td->total_weight_;
        min_ = std::min(min_, td->min_);
        max_ = std::max(max_, td->max_);
      }
    }

    merger_.Reset(total_weight_, &tdigests_[1 - current_]);
    CentroidIter current_iter, end_iter;
    // do k-way merge till one buffer left
    while (queue.size() > 1) {
      std::tie(current_iter, end_iter) = queue.top();
      merger_.Add(*current_iter);
      queue.pop();
      if (++current_iter != end_iter) {
        queue.emplace(current_iter, end_iter);
      }
    }
    // merge last buffer
    if (!queue.empty()) {
      std::tie(current_iter, end_iter) = queue.top();
      while (current_iter != end_iter) {
        merger_.Add(*current_iter++);
      }
    }
    merger_.Reset(0, nullptr);

    current_ = 1 - current_;
  }

  // merge input data with current tdigest
  void MergeInput(std::vector<double>& input) {
    total_weight_ += input.size();

    std::sort(input.begin(), input.end());
    min_ = std::min(min_, input.front());
    max_ = std::max(max_, input.back());

    // pick next minimal centroid from input and tdigest, feed to merger
    merger_.Reset(total_weight_, &tdigests_[1 - current_]);
    const auto& td = tdigests_[current_];
    uint32_t tdigest_index = 0, input_index = 0;
    while (tdigest_index < td.size() && input_index < input.size()) {
      if (td[tdigest_index].mean < input[input_index]) {
        merger_.Add(td[tdigest_index++]);
      } else {
        merger_.Add(Centroid{input[input_index++], 1});
      }
    }
    while (tdigest_index < td.size()) {
      merger_.Add(td[tdigest_index++]);
    }
    while (input_index < input.size()) {
      merger_.Add(Centroid{input[input_index++], 1});
    }
    merger_.Reset(0, nullptr);

    input.resize(0);
    current_ = 1 - current_;
  }

  double Quantile(double q) const {
    const auto& td = tdigests_[current_];

    if (q < 0 || q > 1 || td.size() == 0) {
      return NAN;
    }

    const double index = q * total_weight_;
    if (index <= 1) {
      return min_;
    } else if (index >= total_weight_ - 1) {
      return max_;
    }

    // find centroid contains the index
    uint32_t ci = 0;
    double weight_sum = 0;
    for (; ci < td.size(); ++ci) {
      weight_sum += td[ci].weight;
      if (index <= weight_sum) {
        break;
      }
    }
    DCHECK_LT(ci, td.size());

    // deviation of index from the centroid center
    double diff = index + td[ci].weight / 2 - weight_sum;

    // index happen to be in a unit weight centroid
    if (td[ci].weight == 1 && std::abs(diff) < 0.5) {
      return td[ci].mean;
    }

    // find adjacent centroids for interpolation
    uint32_t ci_left = ci, ci_right = ci;
    if (diff > 0) {
      if (ci_right == td.size() - 1) {
        // index larger than center of last bin
        DCHECK_EQ(weight_sum, total_weight_);
        const Centroid* c = &td[ci_right];
        DCHECK_GE(c->weight, 2);
        return Lerp(c->mean, max_, diff / (c->weight / 2));
      }
      ++ci_right;
    } else {
      if (ci_left == 0) {
        // index smaller than center of first bin
        const Centroid* c = &td[0];
        DCHECK_GE(c->weight, 2);
        return Lerp(min_, c->mean, index / (c->weight / 2));
      }
      --ci_left;
      diff += td[ci_left].weight / 2 + td[ci_right].weight / 2;
    }

    // interpolate from adjacent centroids
    diff /= (td[ci_left].weight / 2 + td[ci_right].weight / 2);
    return Lerp(td[ci_left].mean, td[ci_right].mean, diff);
  }

  double Mean() const {
    double sum = 0;
    for (const auto& centroid : tdigests_[current_]) {
      sum += centroid.mean * centroid.weight;
    }
    return total_weight_ == 0 ? NAN : sum / total_weight_;
  }

  double total_weight() const { return total_weight_; }

 private:
  // must be delcared before merger_, see constructor initialization list
  const uint32_t delta_;

  TDigestMerger<> merger_;
  double total_weight_;
  double min_, max_;

  // ping-pong buffer holds two tdigests, size = 2 * delta * sizeof(Centroid)
  std::vector<Centroid> tdigests_[2];
  // index of active tdigest buffer, 0 or 1
  int current_;
};

TDigest::TDigest(uint32_t delta, uint32_t buffer_size) : impl_(new TDigestImpl(delta)) {
  input_.reserve(buffer_size);
  Reset();
}

TDigest::~TDigest() = default;
TDigest::TDigest(TDigest&&) = default;
TDigest& TDigest::operator=(TDigest&&) = default;

void TDigest::Reset() {
  input_.resize(0);
  impl_->Reset();
}

Status TDigest::Validate() {
  MergeInput();
  return impl_->Validate();
}

void TDigest::Dump() {
  MergeInput();
  impl_->Dump();
}

void TDigest::Merge(std::vector<TDigest>* tdigests) {
  MergeInput();

  std::vector<const TDigestImpl*> tdigest_impls;
  tdigest_impls.reserve(tdigests->size());
  for (auto& td : *tdigests) {
    td.MergeInput();
    tdigest_impls.push_back(td.impl_.get());
  }
  impl_->Merge(tdigest_impls);
}

double TDigest::Quantile(double q) {
  MergeInput();
  return impl_->Quantile(q);
}

double TDigest::Mean() {
  MergeInput();
  return impl_->Mean();
}

bool TDigest::is_empty() const {
  return input_.size() == 0 && impl_->total_weight() == 0;
}

void TDigest::MergeInput() {
  if (input_.size() > 0) {
    impl_->MergeInput(input_);  // will mutate input_
  }
}

}  // namespace internal
}  // namespace arrow
<|endoftext|>


<commit_before>#include "ImagePanel.h"

#include <set>

using namespace std;

wxBitmapPtr ToBitmap( const LinearImage& image )
{
    vector< unsigned char > color, alpha;
    image.GetSrgb( color, alpha );
    return wxBitmapPtr( new wxBitmap(
        alpha.empty()
        ? wxImage( image.GetWidth(), image.GetHeight(), &color[0], true )
        : wxImage( image.GetWidth(), image.GetHeight(), &color[0], &alpha[0], true )
        ) );
}

vector< wxRect > GetCoverage( const wxRect& viewport, const wxRect& canvas, const wxSize& gridSize )
{
    const wxRect clippedViewport( canvas.Intersect( viewport ) );

    vector< wxRect > coverage;
    const int top    = clippedViewport.GetTop()    / gridSize.y;
    const int bottom = clippedViewport.GetBottom() / gridSize.y;
    const int left   = clippedViewport.GetLeft()   / gridSize.x;
    const int right  = clippedViewport.GetRight()  / gridSize.x;
    for( int y = top; y <= bottom; ++y )
    {
        for( int x = left; x <= right; ++x )
        {
            const wxRect candidate( x * gridSize.x, y * gridSize.y, gridSize.x, gridSize.y );
            const wxRect clipped( canvas.Intersect( candidate ) );
            coverage.push_back( clipped );
        }
    }
    return coverage;
}

template< typename T >
T clamp( const T& val, const T& minVal, const T& maxVal )
{
    if( val < minVal )  return minVal;
    if( val > maxVal )  return maxVal;
    return val;
}

wxPoint ClampPosition( const wxRect& viewport, const wxRect& extent )
{
    const wxSize delta( viewport.GetSize() - extent.GetSize() );

    wxPoint newTopLeft( viewport.GetPosition() );

    if( delta.x < 0 )
    {
        // viewport smaller than extent
        int minX = extent.GetPosition().x;
        int maxX = ( extent.GetPosition().x + extent.GetSize().x ) - viewport.GetSize().x;
        newTopLeft.x = clamp( newTopLeft.x, minX, maxX );
    }
    else
    {
        // viewport larger than extent
        newTopLeft.x = extent.GetPosition().x - ( delta.x / 2 );
    }

    if( delta.y < 0 )
    {
        // viewport smaller than extent
        int minY = extent.GetPosition().y;
        int maxY = ( extent.GetPosition().y + extent.GetSize().y ) - viewport.GetSize().y;
        newTopLeft.y = clamp( newTopLeft.y, minY, maxY );
    }
    else
    {
        // viewport larger than extent
        newTopLeft.y = extent.GetPosition().y - ( delta.y / 2 );
    }

    return newTopLeft;
}

wxBitmapPtr GetScaledSubrect( const LinearImage& src, auto_ptr< Resampler > resamplers[ 4 ], const wxRect& rect )
{
    LinearImage dst( rect.GetWidth(), rect.GetHeight(), src.GetNumChannels() == 4 ? true : false, NULL );

    for( size_t c = 0; c < src.GetNumChannels(); ++c )
    {
        resamplers[ c ]->StartResample
            (
            rect.x, rect.y,
            rect.GetWidth(), rect.GetHeight()
            );
    }

    size_t dstY = 0;
    for( size_t y = 0; y < src.GetHeight(); ++y )
    {
        for( size_t c = 0; c < src.GetNumChannels(); ++c )
        {
            resamplers[ c ]->PutLine( src.GetRow( c, y ) );
        }

        while( true )
        {
            bool missedLine = false;
            for( size_t c = 0; c < src.GetNumChannels(); ++c )
            {
                const float* line = resamplers[ c ]->GetLine();
                if( NULL == line )
                {
                    missedLine = true;
                    break;
                }

                copy( line, line + dst.GetWidth(), dst.GetRow( c, dstY ) );
            }

            if( missedLine )
            {
                break;
            }

            dstY++;
        }
    }

    return ToBitmap( dst );
}

wxImagePanel::wxImagePanel( wxWindow* parent )
    : wxWindow( parent, wxID_ANY )
    , mPosition( 0, 0 )
    , mScale( 1.0 )
{
    // for wxAutoBufferedPaintDC
    SetBackgroundStyle( wxBG_STYLE_PAINT );

    SetBackgroundColour( *wxBLACK );

    Bind( wxEVT_SIZE        , &wxImagePanel::OnSize         , this );
    Bind( wxEVT_PAINT       , &wxImagePanel::OnPaint        , this );
    Bind( wxEVT_KEY_DOWN    , &wxImagePanel::OnKeyDown      , this );
    Bind( wxEVT_KEY_UP      , &wxImagePanel::OnKeyUp        , this );
    Bind( wxEVT_LEFT_DOWN   , &wxImagePanel::OnButtonDown   , this );
    Bind( wxEVT_RIGHT_DOWN  , &wxImagePanel::OnButtonDown   , this );
    Bind( wxEVT_MIDDLE_DOWN , &wxImagePanel::OnButtonDown   , this );
    Bind( wxEVT_MOTION      , &wxImagePanel::OnMotion       , this );
}

void wxImagePanel::OnSize( wxSizeEvent& event )
{
    mPosition = ClampPosition( mPosition );

    // invalidate entire panel since we need to redraw everything
    Refresh( true );

    // skip the event so sizers can do their thing
    event.Skip();
}

void wxImagePanel::OnButtonDown( wxMouseEvent& event )
{
    if( event.LeftDown() )
    {
        mLeftPositionStart = mPosition;
        mLeftMouseStart = event.GetPosition();
    }
}

void wxImagePanel::OnMotion( wxMouseEvent& event )
{
    if( event.LeftIsDown() && event.Dragging() )
    {
        wxPoint newPos( mLeftPositionStart - ( event.GetPosition() - mLeftMouseStart ) );
        if( newPos != mPosition )
        {
            ScrollToPosition( newPos );
        }
    }
}

void wxImagePanel::OnIdle( wxIdleEvent& event )
{
    wxPoint newPos( mPosition );
    const int step = 1;
    if( wxGetKeyState( WXK_LEFT  ) )    newPos += step * wxPoint( -1,  0 );
    if( wxGetKeyState( WXK_RIGHT ) )    newPos += step * wxPoint(  1,  0 );
    if( wxGetKeyState( WXK_UP    ) )    newPos += step * wxPoint(  0, -1 );
    if( wxGetKeyState( WXK_DOWN  ) )    newPos += step * wxPoint(  0,  1 );

    if( newPos == mPosition )
    {
        Unbind( wxEVT_IDLE, &wxImagePanel::OnIdle, this );
        return;
    }

    ScrollToPosition( newPos );

    //event.RequestMore( true );
    //wxMilliSleep( 1 );
}

void wxImagePanel::OnKeyDown( wxKeyEvent& event )
{
    switch( event.GetKeyCode() )
    {
        case WXK_LEFT:
        case WXK_RIGHT:
        case WXK_UP:
        case WXK_DOWN:
            Bind( wxEVT_IDLE, &wxImagePanel::OnIdle, this );
            break;
        default:
            break;
    }
}

wxPoint wxImagePanel::ClampPosition( const wxPoint& newPos )
{
    if( NULL == mImage )
    {
        return newPos;
    }

    const wxSize scaledSize( mImage->GetWidth() * mScale, mImage->GetHeight() * mScale );
    return ::ClampPosition
        (
        wxRect( newPos, GetSize() ),
        wxRect( wxPoint(0,0), scaledSize )
        );
}

void wxImagePanel::OnKeyUp( wxKeyEvent& event )
{
    switch( event.GetKeyCode() )
    {
        case WXK_ADD:
        case WXK_NUMPAD_ADD:
            SetScale( mScale * 1.1 );
            break;
        case WXK_SUBTRACT:
        case WXK_NUMPAD_SUBTRACT:
            SetScale( mScale / 1.1 );
            break;
        default:
            break;
    }
}

void wxImagePanel::ScrollToPosition( const wxPoint& newPos )
{
    const wxPoint clamped = ClampPosition( newPos );
    wxPoint delta( clamped - mPosition );
    ScrollWindow( -delta.x, -delta.y );
    mPosition = clamped;
}

void wxImagePanel::OnPaint( wxPaintEvent& event )
{
    wxPaintDC dc(this);
    //wxAutoBufferedPaintDC dc( this );

    dc.SetDeviceOrigin( -mPosition.x, -mPosition.y );

    dc.Clear();

    if( NULL == mImage )
    {
        return;
    }

    const wxSize scaledSize( mImage->GetWidth() * mScale, mImage->GetHeight() * mScale );
    const wxRect scaledRect( wxPoint( 0, 0 ), scaledSize );
    const wxSize gridSize( TILE_SIZE, TILE_SIZE );

    // get the set of tiles we need to draw
    set< wxRect, wxRectCmp > covered;
    for( wxRegionIterator upd( GetUpdateRegion() ); upd.HaveRects(); ++upd )
    {
        wxRect rect( upd.GetRect() );
        rect.SetPosition( rect.GetPosition() + mPosition );

        const vector< wxRect > ret = GetCoverage( rect, scaledRect, gridSize );
        covered.insert( ret.begin(), ret.end() );
    }

    for( const wxRect& rect : covered )
    {
        map< wxRect, wxBitmapPtr >::iterator it = mBitmapCache.find( rect );
        if( mBitmapCache.end() == it )
        {
            it = mBitmapCache.insert( make_pair( rect, GetScaledSubrect( *mImage, mResamplers, rect ) ) ).first;
        }

        dc.DrawBitmap( *(it->second), rect.GetPosition() );
    }
}

void wxImagePanel::SetImage( const LinearImage* newImage )
{
    mImage = newImage;
    SetScale( mScale );
    mPosition = ClampPosition( mPosition );
}

void wxImagePanel::SetScale( const double newScale )
{
    mScale = newScale;
    mBitmapCache.clear();
    mPosition = ClampPosition( mPosition );

    // invalidate entire panel since we need to redraw everything
    Refresh( true );

    if( NULL == mImage )
    {
        return;
    }

    // regenerate resamplers
    mContribLists.reset( new Resampler::ContribLists
        (
        mImage->GetWidth(), mImage->GetHeight(),
        mImage->GetWidth() * mScale, mImage->GetHeight() * mScale
        ) );
    for( size_t i = 0; i < 4; ++i )
    {
        mResamplers[ i ].reset( new Resampler( *mContribLists ) );
    }
}<commit_msg>Add fit-to-window and reset zoom controls<commit_after>#include "ImagePanel.h"

#include <set>

using namespace std;

wxBitmapPtr ToBitmap( const LinearImage& image )
{
    vector< unsigned char > color, alpha;
    image.GetSrgb( color, alpha );
    return wxBitmapPtr( new wxBitmap(
        alpha.empty()
        ? wxImage( image.GetWidth(), image.GetHeight(), &color[0], true )
        : wxImage( image.GetWidth(), image.GetHeight(), &color[0], &alpha[0], true )
        ) );
}

vector< wxRect > GetCoverage( const wxRect& viewport, const wxRect& canvas, const wxSize& gridSize )
{
    const wxRect clippedViewport( canvas.Intersect( viewport ) );

    vector< wxRect > coverage;
    const int top    = clippedViewport.GetTop()    / gridSize.y;
    const int bottom = clippedViewport.GetBottom() / gridSize.y;
    const int left   = clippedViewport.GetLeft()   / gridSize.x;
    const int right  = clippedViewport.GetRight()  / gridSize.x;
    for( int y = top; y <= bottom; ++y )
    {
        for( int x = left; x <= right; ++x )
        {
            const wxRect candidate( x * gridSize.x, y * gridSize.y, gridSize.x, gridSize.y );
            const wxRect clipped( canvas.Intersect( candidate ) );
            coverage.push_back( clipped );
        }
    }
    return coverage;
}

template< typename T >
T clamp( const T& val, const T& minVal, const T& maxVal )
{
    if( val < minVal )  return minVal;
    if( val > maxVal )  return maxVal;
    return val;
}

wxPoint ClampPosition( const wxRect& viewport, const wxRect& extent )
{
    const wxSize delta( viewport.GetSize() - extent.GetSize() );

    wxPoint newTopLeft( viewport.GetPosition() );

    if( delta.x < 0 )
    {
        // viewport smaller than extent
        int minX = extent.GetPosition().x;
        int maxX = ( extent.GetPosition().x + extent.GetSize().x ) - viewport.GetSize().x;
        newTopLeft.x = clamp( newTopLeft.x, minX, maxX );
    }
    else
    {
        // viewport larger than extent
        newTopLeft.x = extent.GetPosition().x - ( delta.x / 2 );
    }

    if( delta.y < 0 )
    {
        // viewport smaller than extent
        int minY = extent.GetPosition().y;
        int maxY = ( extent.GetPosition().y + extent.GetSize().y ) - viewport.GetSize().y;
        newTopLeft.y = clamp( newTopLeft.y, minY, maxY );
    }
    else
    {
        // viewport larger than extent
        newTopLeft.y = extent.GetPosition().y - ( delta.y / 2 );
    }

    return newTopLeft;
}

wxBitmapPtr GetScaledSubrect( const LinearImage& src, auto_ptr< Resampler > resamplers[ 4 ], const wxRect& rect )
{
    LinearImage dst( rect.GetWidth(), rect.GetHeight(), src.GetNumChannels() == 4 ? true : false, NULL );

    for( size_t c = 0; c < src.GetNumChannels(); ++c )
    {
        resamplers[ c ]->StartResample
            (
            rect.x, rect.y,
            rect.GetWidth(), rect.GetHeight()
            );
    }

    size_t dstY = 0;
    for( size_t y = 0; y < src.GetHeight(); ++y )
    {
        for( size_t c = 0; c < src.GetNumChannels(); ++c )
        {
            resamplers[ c ]->PutLine( src.GetRow( c, y ) );
        }

        while( true )
        {
            bool missedLine = false;
            for( size_t c = 0; c < src.GetNumChannels(); ++c )
            {
                const float* line = resamplers[ c ]->GetLine();
                if( NULL == line )
                {
                    missedLine = true;
                    break;
                }

                copy( line, line + dst.GetWidth(), dst.GetRow( c, dstY ) );
            }

            if( missedLine )
            {
                break;
            }

            dstY++;
        }
    }

    return ToBitmap( dst );
}

wxImagePanel::wxImagePanel( wxWindow* parent )
    : wxWindow( parent, wxID_ANY )
    , mPosition( 0, 0 )
    , mScale( 1.0 )
{
    // for wxAutoBufferedPaintDC
    SetBackgroundStyle( wxBG_STYLE_PAINT );

    SetBackgroundColour( *wxBLACK );

    Bind( wxEVT_SIZE        , &wxImagePanel::OnSize         , this );
    Bind( wxEVT_PAINT       , &wxImagePanel::OnPaint        , this );
    Bind( wxEVT_KEY_DOWN    , &wxImagePanel::OnKeyDown      , this );
    Bind( wxEVT_KEY_UP      , &wxImagePanel::OnKeyUp        , this );
    Bind( wxEVT_LEFT_DOWN   , &wxImagePanel::OnButtonDown   , this );
    Bind( wxEVT_RIGHT_DOWN  , &wxImagePanel::OnButtonDown   , this );
    Bind( wxEVT_MIDDLE_DOWN , &wxImagePanel::OnButtonDown   , this );
    Bind( wxEVT_MOTION      , &wxImagePanel::OnMotion       , this );
}

void wxImagePanel::OnSize( wxSizeEvent& event )
{
    mPosition = ClampPosition( mPosition );

    // invalidate entire panel since we need to redraw everything
    Refresh( true );

    // skip the event so sizers can do their thing
    event.Skip();
}

void wxImagePanel::OnButtonDown( wxMouseEvent& event )
{
    if( event.LeftDown() )
    {
        mLeftPositionStart = mPosition;
        mLeftMouseStart = event.GetPosition();
    }
}

void wxImagePanel::OnMotion( wxMouseEvent& event )
{
    if( event.LeftIsDown() && event.Dragging() )
    {
        wxPoint newPos( mLeftPositionStart - ( event.GetPosition() - mLeftMouseStart ) );
        if( newPos != mPosition )
        {
            ScrollToPosition( newPos );
        }
    }
}

void wxImagePanel::OnIdle( wxIdleEvent& event )
{
    wxPoint newPos( mPosition );
    const int step = 1;
    if( wxGetKeyState( WXK_LEFT  ) )    newPos += step * wxPoint( -1,  0 );
    if( wxGetKeyState( WXK_RIGHT ) )    newPos += step * wxPoint(  1,  0 );
    if( wxGetKeyState( WXK_UP    ) )    newPos += step * wxPoint(  0, -1 );
    if( wxGetKeyState( WXK_DOWN  ) )    newPos += step * wxPoint(  0,  1 );

    if( newPos == mPosition )
    {
        Unbind( wxEVT_IDLE, &wxImagePanel::OnIdle, this );
        return;
    }

    ScrollToPosition( newPos );

    //event.RequestMore( true );
    //wxMilliSleep( 1 );
}

void wxImagePanel::OnKeyDown( wxKeyEvent& event )
{
    switch( event.GetKeyCode() )
    {
        case WXK_LEFT:
        case WXK_RIGHT:
        case WXK_UP:
        case WXK_DOWN:
            Bind( wxEVT_IDLE, &wxImagePanel::OnIdle, this );
            break;
        default:
            break;
    }
}

wxPoint wxImagePanel::ClampPosition( const wxPoint& newPos )
{
    if( NULL == mImage )
    {
        return newPos;
    }

    const wxSize scaledSize( mImage->GetWidth() * mScale, mImage->GetHeight() * mScale );
    return ::ClampPosition
        (
        wxRect( newPos, GetSize() ),
        wxRect( wxPoint(0,0), scaledSize )
        );
}

void wxImagePanel::OnKeyUp( wxKeyEvent& event )
{
    if( NULL == mImage )
    {
        return;
    }

    switch( event.GetKeyCode() )
    {
        case WXK_ADD:
        case WXK_NUMPAD_ADD:
            SetScale( mScale * 1.1 );
            break;
        case WXK_SUBTRACT:
        case WXK_NUMPAD_SUBTRACT:
            SetScale( mScale / 1.1 );
            break;
        case WXK_NUMPAD_MULTIPLY:
            {
                const int iMax = max( mImage->GetWidth(), mImage->GetHeight() );
                const int wMin = min( GetSize().x, GetSize().y );
                SetScale( wMin / (double)iMax );
            }
            break;
        case WXK_NUMPAD_DIVIDE:
            SetScale( 1.0 );
            break;
        default:
            break;
    }
}

void wxImagePanel::ScrollToPosition( const wxPoint& newPos )
{
    const wxPoint clamped = ClampPosition( newPos );
    wxPoint delta( clamped - mPosition );
    ScrollWindow( -delta.x, -delta.y );
    mPosition = clamped;
}

void wxImagePanel::OnPaint( wxPaintEvent& event )
{
    wxPaintDC dc(this);
    //wxAutoBufferedPaintDC dc( this );

    dc.SetDeviceOrigin( -mPosition.x, -mPosition.y );

    dc.Clear();

    if( NULL == mImage )
    {
        return;
    }

    const wxSize scaledSize( mImage->GetWidth() * mScale, mImage->GetHeight() * mScale );
    const wxRect scaledRect( wxPoint( 0, 0 ), scaledSize );
    const wxSize gridSize( TILE_SIZE, TILE_SIZE );

    // get the set of tiles we need to draw
    set< wxRect, wxRectCmp > covered;
    for( wxRegionIterator upd( GetUpdateRegion() ); upd.HaveRects(); ++upd )
    {
        wxRect rect( upd.GetRect() );
        rect.SetPosition( rect.GetPosition() + mPosition );

        const vector< wxRect > ret = GetCoverage( rect, scaledRect, gridSize );
        covered.insert( ret.begin(), ret.end() );
    }

    for( const wxRect& rect : covered )
    {
        map< wxRect, wxBitmapPtr >::iterator it = mBitmapCache.find( rect );
        if( mBitmapCache.end() == it )
        {
            it = mBitmapCache.insert( make_pair( rect, GetScaledSubrect( *mImage, mResamplers, rect ) ) ).first;
        }

        dc.DrawBitmap( *(it->second), rect.GetPosition() );
    }
}

void wxImagePanel::SetImage( const LinearImage* newImage )
{
    mImage = newImage;
    SetScale( mScale );
    mPosition = ClampPosition( mPosition );
}

void wxImagePanel::SetScale( const double newScale )
{
    mScale = newScale;
    mBitmapCache.clear();
    mPosition = ClampPosition( mPosition );

    // invalidate entire panel since we need to redraw everything
    Refresh( true );

    if( NULL == mImage )
    {
        return;
    }

    // regenerate resamplers
    mContribLists.reset( new Resampler::ContribLists
        (
        mImage->GetWidth(), mImage->GetHeight(),
        mImage->GetWidth() * mScale, mImage->GetHeight() * mScale
        ) );
    for( size_t i = 0; i < 4; ++i )
    {
        mResamplers[ i ].reset( new Resampler( *mContribLists ) );
    }
}<|endoftext|>


<commit_before>#ifndef __GLYPH_HPP_INCLUDED
#define __GLYPH_HPP_INCLUDED

#include "species_or_glyph.hpp"
#include "text3D.hpp"
#include "vector_font.hpp"
#include "render_templates.hpp"
#include "cpp/ylikuutio/hierarchy/hierarchy_templates.hpp"

// Include GLEW
#ifndef __GL_GLEW_H_INCLUDED
#define __GL_GLEW_H_INCLUDED
#include <GL/glew.h> // GLfloat, GLuint etc.
#endif

// Include standard headers
#include <queue>    // std::queue
#include <stdint.h> // uint32_t etc.
#include <string>   // std::string
#include <vector>   // std::vector

namespace model
{
    class Object;

    class Glyph
    {
        public:
            // constructor.
            Glyph(GlyphStruct glyph_struct);

            // destructor.
            // glyphs should be destroyed only by destroying the entire `VectorFont`.
            ~Glyph();

            // this method sets a object pointer.
            void set_object_pointer(uint32_t childID, void* parent_pointer);

            // this method gets a object ID and removes it from the `free_objectID_queue` if it was popped from the queue.
            uint32_t get_objectID();

            glm::vec3 light_position;                    // light position.

            // The rest fields are created in the constructor.
            uint32_t image_width;
            uint32_t image_height;

            GLuint vertexPosition_modelspaceID;
            GLuint vertexUVID;
            GLuint vertexNormal_modelspaceID;

            std::vector<glm::vec3> vertices;       // vertices of the object.
            std::vector<glm::vec2> UVs;            // UVs of the object.
            std::vector<glm::vec3> normals;        // normals of the object.

            std::vector<uint32_t> indices;         // the deleted vertices will be reused (though it is not required, if there's enough memory).
            std::vector<glm::vec3> indexed_vertices;
            std::vector<glm::vec2> indexed_UVs;
            std::vector<glm::vec3> indexed_normals;

            GLuint vertexbuffer;
            GLuint uvbuffer;
            GLuint normalbuffer;
            GLuint elementbuffer;

            model::VectorFont* parent_pointer;       // pointer to `VectorFont`.

            friend class Object;
            template<class T1>
                friend void render_children(std::vector<void*> &child_pointer_vector);
            template<class T1>
                friend void hierarchy::bind_child_to_parent(T1 child_pointer, std::vector<void*> &child_pointer_vector, std::queue<uint32_t> &free_childID_queue);
            template<class T1>
                friend void render_species_or_glyph(T1 species_or_glyph_pointer);

        private:
            void bind_to_parent();

            // this method renders all objects of this species.
            void render();

            uint32_t childID;                      // glyph ID, returned by `model::VectorFont->get_glyphID()`.
            GLuint lightID;                        // light ID, returned by `glGetUniformLocation(programID, "LightPosition_worldspace");`.

            std::string* glyph_name_pointer;

            std::vector<void*> object_pointer_vector;
            std::queue<uint32_t> free_objectID_queue;
    };
}

#endif
<commit_msg>`class Glyph`: new variables and one new `#include`.<commit_after>#ifndef __GLYPH_HPP_INCLUDED
#define __GLYPH_HPP_INCLUDED

#include "species_or_glyph.hpp"
#include "text3D.hpp"
#include "vector_font.hpp"
#include "render_templates.hpp"
#include "cpp/ylikuutio/hierarchy/hierarchy_templates.hpp"

// Include GLEW
#ifndef __GL_GLEW_H_INCLUDED
#define __GL_GLEW_H_INCLUDED
#include <GL/glew.h> // GLfloat, GLuint etc.
#endif

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp> // glm
#endif

// Include standard headers
#include <queue>    // std::queue
#include <stdint.h> // uint32_t etc.
#include <string>   // std::string
#include <vector>   // std::vector

namespace model
{
    class Object;

    class Glyph
    {
        public:
            // constructor.
            Glyph(GlyphStruct glyph_struct);

            // destructor.
            // glyphs should be destroyed only by destroying the entire `VectorFont`.
            ~Glyph();

            // this method sets a object pointer.
            void set_object_pointer(uint32_t childID, void* parent_pointer);

            // this method gets a object ID and removes it from the `free_objectID_queue` if it was popped from the queue.
            uint32_t get_objectID();

            glm::vec3 light_position;                    // light position.

            // The rest fields are created in the constructor.
            uint32_t image_width;
            uint32_t image_height;

            GLuint vertexPosition_modelspaceID;
            GLuint vertexUVID;
            GLuint vertexNormal_modelspaceID;

            std::vector<glm::vec3> vertices;       // vertices of the object.
            std::vector<glm::vec2> UVs;            // UVs of the object.
            std::vector<glm::vec3> normals;        // normals of the object.

            std::vector<uint32_t> indices;         // the deleted vertices will be reused (though it is not required, if there's enough memory).
            std::vector<glm::vec3> indexed_vertices;
            std::vector<glm::vec2> indexed_UVs;
            std::vector<glm::vec3> indexed_normals;

            GLuint vertexbuffer;
            GLuint uvbuffer;
            GLuint normalbuffer;
            GLuint elementbuffer;

            model::VectorFont* parent_pointer;       // pointer to `VectorFont`.

            friend class Object;
            template<class T1>
                friend void render_children(std::vector<void*> &child_pointer_vector);
            template<class T1>
                friend void hierarchy::bind_child_to_parent(T1 child_pointer, std::vector<void*> &child_pointer_vector, std::queue<uint32_t> &free_childID_queue);
            template<class T1>
                friend void render_species_or_glyph(T1 species_or_glyph_pointer);

        private:
            void bind_to_parent();

            // this method renders all objects of this species.
            void render();

            uint32_t childID;                      // glyph ID, returned by `model::VectorFont->get_glyphID()`.
            GLuint lightID;                        // light ID, returned by `glGetUniformLocation(programID, "LightPosition_worldspace");`.

            std::vector<std::vector<glm::vec2>>* glyph_vertex_data;
            std::string* glyph_name_pointer;         // we need only a pointer, because glyphs are always created by the `VectorFont` constructor.
            std::string* unicode_string_pointer;     // we need only a pointer, because glyphs are always created by the `VectorFont` constructor.

            std::vector<void*> object_pointer_vector;
            std::queue<uint32_t> free_objectID_queue;
    };
}

#endif
<|endoftext|>


<commit_before>#include "simplesp.h"
#include "spex/spgl.h"

using namespace sp;

class ModelTrackGUI : public BaseWindow {

    // camera
    CamParam m_cam;

    // image
    Mem2<Col3> m_img;

    // map
    Mem2<VecPN3> m_map;

    // model
    Mem1<Mesh3> m_model;

    // pose
    Pose m_pose;

    // pose model
    Mem1<PoseModel> m_pmodels;

    // mode
    int m_mode;

private:

    void help() {
        printf("'r' key : render\n");
        printf("'c' key : track 2d\n");
        printf("'v' key : track 3d\n");
        printf("'ESC' key : exit\n");
        printf("\n");
    }

    virtual void init() {
        help();

        m_mode = 0;

        m_cam = getCamParam(640, 480);

        if (loadBunny(m_model, SP_DATA_DIR "/stanford/bun_zipper.ply") == false) {

            // if could not find stanford bunny, load dummy model
            loadGeodesicDorm(m_model, 100.0, 1);
        }

        const double distance = getModelDistance(m_model, m_cam);

        printf("please wait...\n");
        const int level = 2;
        m_pmodels = getPoseModel(m_model, distance, level);

        m_pose = getPose(getVec(0.0, 0.0, distance));
    }

    virtual void keyFun(int key, int scancode, int action, int mods) {

        if (m_keyAction[GLFW_KEY_R] == 1) {
            m_map.zero();
            renderVecPN(m_map, m_cam, m_pose, m_model);

            cnvNormalToImg(m_img, m_map);
        }

        if (m_keyAction[GLFW_KEY_C] > 0) {
            if (m_img.size() == 0) return;
            m_mode = 0;

            Mem2<Byte> gry;
            cnvImg(gry, m_img);

            track2D(m_pose, gry, m_cam, m_pmodels, 50, 1);
        }

        if (m_keyAction[GLFW_KEY_V] > 0) {
            if (m_map.size() == 0) return;
            m_mode = 1;

            track3D(m_pose, m_map, m_cam, m_pmodels, 1);
        }
    }

    virtual void display() {

        // render image
        {

            glLoadView2D(m_cam, m_viewPos, m_viewScale);
            glRenderImg(m_img);
        }

        // render model
        {
            glLoadView3D(m_cam, m_viewPos, m_viewScale);

            glClear(GL_DEPTH_BUFFER_BIT);
            {
                glLoadMatrix(m_pose);

                glRenderOutline(m_model);

                // render points
                glPointSize(5.f);
                glBegin(GL_POINTS);
                glColor3f(0.2f, 0.7f, 0.2f);

                const int id = findPoseModel(m_pmodels, m_pose);

                if (m_mode == 0) {
                    for (int i = 0; i < m_pmodels[id].edges.size(); i++) {
                        glVertex(m_pmodels[id].edges[i].pos);
                    }
                }
                else {
                    for (int i = 0; i < m_pmodels[id].pnts.size(); i++) {
                        glVertex(m_pmodels[id].pnts[i].pos);
                    }
                }
                glEnd();
            }

            renderAxis();
        }

    }

    void renderAxis() {
        glLoadMatrix(m_pose);

        glLineWidth(2.f);
        glBegin(GL_LINES);
        glAxis(100.0);
        glEnd();
    }

    virtual void mousePos(double x, double y) {
        controlPose(m_pose, m_mouse, m_wcam, m_viewScale);
    }

    virtual void mouseScroll(double x, double y) {
        controlPose(m_pose, m_mouse, m_wcam, m_viewScale);
    }

};

int main(){

    ModelTrackGUI win;
    win.execute("modeltrack", 800, 600);

    return 0;
}<commit_msg>modify gl render<commit_after>#include "simplesp.h"
#include "spex/spgl.h"

using namespace sp;

class ModelTrackGUI : public BaseWindow {

    // camera
    CamParam m_cam;

    // image
    Mem2<Col3> m_img;

    // map
    Mem2<VecPN3> m_map;

    // model
    Mem1<Mesh3> m_model;

    // pose
    Pose m_pose;

    // pose model
    Mem1<PoseModel> m_pmodels;

    // mode
    int m_mode;

private:

    void help() {
        printf("'r' key : render\n");
        printf("'c' key : track 2d\n");
        printf("'v' key : track 3d\n");
        printf("'ESC' key : exit\n");
        printf("\n");
    }

    virtual void init() {
        help();

        m_mode = 0;

        m_cam = getCamParam(640, 480);

        if (loadBunny(m_model, SP_DATA_DIR "/stanford/bun_zipper.ply") == false) {

            // if could not find stanford bunny, load dummy model
            loadGeodesicDorm(m_model, 100.0, 1);
        }

        const double distance = getModelDistance(m_model, m_cam);

        printf("please wait...\n");
        const int level = 2;
        m_pmodels = getPoseModel(m_model, distance, level);

        m_pose = getPose(getVec(0.0, 0.0, distance));
    }

    virtual void keyFun(int key, int scancode, int action, int mods) {

        if (m_keyAction[GLFW_KEY_R] == 1) {
            m_map.zero();
            renderVecPN(m_map, m_cam, m_pose, m_model);

            cnvNormalToImg(m_img, m_map);
        }

        if (m_keyAction[GLFW_KEY_C] > 0) {
            if (m_img.size() == 0) return;
            m_mode = 0;

            Mem2<Byte> gry;
            cnvImg(gry, m_img);

            track2D(m_pose, gry, m_cam, m_pmodels, 50, 1);
        }

        if (m_keyAction[GLFW_KEY_V] > 0) {
            if (m_map.size() == 0) return;
            m_mode = 1;

            track3D(m_pose, m_map, m_cam, m_pmodels, 1);
        }
    }

    virtual void display() {

        // render image
        {

            glLoadView2D(m_cam, m_viewPos, m_viewScale);
            glRenderImg(m_img);
        }

        // render model
        {
            glLoadView3D(m_cam, m_viewPos, m_viewScale);

            glLoadMatrix(m_pose);

            glClear(GL_DEPTH_BUFFER_BIT);
            {

                glRenderOutline(m_model);
            }

            glClear(GL_DEPTH_BUFFER_BIT);
            {
                // render points
                glPointSize(5.f);
                glBegin(GL_POINTS);
                glColor3f(0.2f, 0.7f, 0.2f);

                const int id = findPoseModel(m_pmodels, m_pose);

                if (m_mode == 0) {
                    for (int i = 0; i < m_pmodels[id].edges.size(); i++) {
                        glVertex(m_pmodels[id].edges[i].pos);
                    }
                }
                else {
                    for (int i = 0; i < m_pmodels[id].pnts.size(); i++) {
                        glVertex(m_pmodels[id].pnts[i].pos);
                    }
                }
                glEnd();
            }

            glClear(GL_DEPTH_BUFFER_BIT);
            renderAxis();
        }

    }

    void renderAxis() {
        glLoadMatrix(m_pose);

        glLineWidth(2.f);
        glBegin(GL_LINES);
        glAxis(100.0);
        glEnd();
    }

    virtual void mousePos(double x, double y) {
        controlPose(m_pose, m_mouse, m_wcam, m_viewScale);
    }

    virtual void mouseScroll(double x, double y) {
        controlPose(m_pose, m_mouse, m_wcam, m_viewScale);
    }

};

int main(){

    ModelTrackGUI win;
    win.execute("modeltrack", 800, 600);

    return 0;
}<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: template.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 20:25:05 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifdef PCH
#include "ui_pch.hxx"
#endif

#pragma hdrstop

#include "template.hxx"

//------------------------------------------------------------------------

ScTemplateDlg::ScTemplateDlg(Window * pParent, USHORT nAppResource) :
//  SfxTemplateDlg( pParent, nAppResource )
    SfxTemplateDialog( pParent )
{
}

//------------------------------------------------------------------------

__EXPORT ScTemplateDlg::~ScTemplateDlg()
{
}

//------------------------------------------------------------------------

BOOL ScTemplateDlg::New(String &rNewName)
{
    return TRUE;
}

void ScTemplateDlg::Edit(const String &)
{
}

BOOL ScTemplateDlg::Delete(const String &)
{
    return TRUE;
}

void ScTemplateDlg::InvalidateTemplates()
{
}

void ScTemplateDlg::ToggleApplyTemplate()
{
}

<commit_msg>INTEGRATION: CWS pchfix01 (1.4.216); FILE MERGED 2006/07/12 10:02:21 kaib 1.4.216.1: #i67080# Converted cxx files in sc, added initial project level pch and stripped old PCH definitions.<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: template.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: kz $ $Date: 2006-07-21 13:11:09 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"

#include "template.hxx"

//------------------------------------------------------------------------

ScTemplateDlg::ScTemplateDlg(Window * pParent, USHORT nAppResource) :
//  SfxTemplateDlg( pParent, nAppResource )
    SfxTemplateDialog( pParent )
{
}

//------------------------------------------------------------------------

__EXPORT ScTemplateDlg::~ScTemplateDlg()
{
}

//------------------------------------------------------------------------

BOOL ScTemplateDlg::New(String &rNewName)
{
    return TRUE;
}

void ScTemplateDlg::Edit(const String &)
{
}

BOOL ScTemplateDlg::Delete(const String &)
{
    return TRUE;
}

void ScTemplateDlg::InvalidateTemplates()
{
}

void ScTemplateDlg::ToggleApplyTemplate()
{
}

<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <cctype>

using std::cout;
using std::endl;
using std::string;

bool hasUppercase(const string &str)
{
    for (auto c : str)
        if (isupper(c)) return true;
    return false;
}

void makeLowercase(string &str)
{
    for (auto& c : str)
        if (isupper(c)) c = tolower(c);
}

int main()
{
    string str("Hello World!");
    cout << boolalpha << hasUppercase(str) << endl;
    makeLowercase(str);
    cout << str << endl;

    return 0;
}
<commit_msg>Update ex6_17.cpp<commit_after>#include <iostream>
#include <string>
#include <cctype>

using std::cout;
using std::endl;
using std::string;

bool hasUppercase(const string &str)
{
    for (auto c : str)
        if (isupper(c)) return true;
    return false;
}

void makeLowercase(string &str)
{
    for (auto &c : str)
        if (isupper(c)) c = tolower(c);
}

int main()
{
    string str("Hello World!");
    cout << boolalpha << hasUppercase(str) << endl;
    makeLowercase(str);
    cout << str << endl;

    return 0;
}
<|endoftext|>


<commit_before>#include "pch.h"
#include "CppUnitTest.h"
#include "WarpTpsLib.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace UnitTest1
{
	TEST_CLASS(UnitTest1)
	{
	public:

		TEST_METHOD(TestMethod1)
		{
		}

		TEST_METHOD(TestAddLandmark)
		{
			Logger::WriteMessage("TestAddLandmark");

			auto tpsTransform = new CTPSTransform();
			tpsTransform->AddLandmark(CVectorD<3>(0.0, 0.0, 0.0));
			tpsTransform->AddLandmark(CVectorD<3>(1.0, 0.0, 0.0));
			tpsTransform->AddLandmark(CVectorD<3>(0.0, 1.0, 0.0));
			tpsTransform->AddLandmark(CVectorD<3>(0.0, 0.0, 1.0));

			Assert::IsTrue(tpsTransform->GetLandmark(0, 0).IsApproxEqual(CVectorD<3>(0.0, 0.0, 0.0)));
			Assert::IsTrue(tpsTransform->GetLandmark(0, 1).IsApproxEqual(CVectorD<3>(1.0, 0.0, 0.0)));
			Assert::IsTrue(tpsTransform->GetLandmark(0, 2).IsApproxEqual(CVectorD<3>(0.0, 1.0, 0.0)));
			Assert::IsTrue(tpsTransform->GetLandmark(0, 3).IsApproxEqual(CVectorD<3>(0.0, 0.0, 1.0)));

			Assert::IsTrue(tpsTransform->GetLandmark(1, 0).IsApproxEqual(CVectorD<3>(0.0, 0.0, 0.0)));
			Assert::IsTrue(tpsTransform->GetLandmark(1, 1).IsApproxEqual(CVectorD<3>(1.0, 0.0, 0.0)));
			Assert::IsTrue(tpsTransform->GetLandmark(1, 2).IsApproxEqual(CVectorD<3>(0.0, 1.0, 0.0)));
			Assert::IsTrue(tpsTransform->GetLandmark(1, 3).IsApproxEqual(CVectorD<3>(0.0, 0.0, 1.0)));

			Logger::WriteMessage("Done TestAddLandmark");
		}

		TEST_METHOD(TestCalculateWarp)
		{
			Logger::WriteMessage("TestAddLandmark");

			auto tpsTransform = new CTPSTransform();
			tpsTransform->AddLandmark(CVectorD<3>(0.0, 0.0, 0.0));
			tpsTransform->AddLandmark(CVectorD<3>(1.0, 0.0, 0.0));
			tpsTransform->AddLandmark(CVectorD<3>(0.0, 1.0, 0.0));
			tpsTransform->AddLandmark(CVectorD<3>(0.0, 0.0, 1.0));

			CVectorD<3> vOriginal(0.0, 0.0, 0.0);
			CVectorD<3> vTransformed;
			tpsTransform->Eval(vOriginal, vTransformed, 0.0);
			Assert::IsTrue(vOriginal.IsApproxEqual(vTransformed));

			Logger::WriteMessage("Done TestAddLandmark");
		}

		TEST_METHOD(TestApplyWarpAtLandmark)
		{
		}

		TEST_METHOD(TestApplyInverseWarpAtLandmark)
		{
		}

		TEST_METHOD(TestResampleUniformImage)
		{
		}

		TEST_METHOD(TestResampleImageHistogram)
		{
		}

	};
}
<commit_msg>disabling unit test<commit_after>#include "pch.h"
#include "CppUnitTest.h"
#include "WarpTpsLib.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace UnitTest1
{
	TEST_CLASS(UnitTest1)
	{
	public:

		TEST_METHOD(TestMethod1)
		{
		}

		TEST_METHOD(TestAddLandmark)
		{
			Logger::WriteMessage("TestAddLandmark");

			auto tpsTransform = new CTPSTransform();
			tpsTransform->AddLandmark(CVectorD<3>(0.0, 0.0, 0.0));
			tpsTransform->AddLandmark(CVectorD<3>(1.0, 0.0, 0.0));
			tpsTransform->AddLandmark(CVectorD<3>(0.0, 1.0, 0.0));
			tpsTransform->AddLandmark(CVectorD<3>(0.0, 0.0, 1.0));

			Assert::IsTrue(tpsTransform->GetLandmark(0, 0).IsApproxEqual(CVectorD<3>(0.0, 0.0, 0.0)));
			Assert::IsTrue(tpsTransform->GetLandmark(0, 1).IsApproxEqual(CVectorD<3>(1.0, 0.0, 0.0)));
			Assert::IsTrue(tpsTransform->GetLandmark(0, 2).IsApproxEqual(CVectorD<3>(0.0, 1.0, 0.0)));
			Assert::IsTrue(tpsTransform->GetLandmark(0, 3).IsApproxEqual(CVectorD<3>(0.0, 0.0, 1.0)));

			Assert::IsTrue(tpsTransform->GetLandmark(1, 0).IsApproxEqual(CVectorD<3>(0.0, 0.0, 0.0)));
			Assert::IsTrue(tpsTransform->GetLandmark(1, 1).IsApproxEqual(CVectorD<3>(1.0, 0.0, 0.0)));
			Assert::IsTrue(tpsTransform->GetLandmark(1, 2).IsApproxEqual(CVectorD<3>(0.0, 1.0, 0.0)));
			Assert::IsTrue(tpsTransform->GetLandmark(1, 3).IsApproxEqual(CVectorD<3>(0.0, 0.0, 1.0)));

			Logger::WriteMessage("Done TestAddLandmark");
		}

		TEST_METHOD(TestCalculateWarp)
		{
			Logger::WriteMessage("TestAddLandmark");

			auto tpsTransform = new CTPSTransform();
			tpsTransform->AddLandmark(CVectorD<3>(0.0, 0.0, 0.0));
			tpsTransform->AddLandmark(CVectorD<3>(1.0, 0.0, 0.0));
			tpsTransform->AddLandmark(CVectorD<3>(0.0, 1.0, 0.0));
			tpsTransform->AddLandmark(CVectorD<3>(0.0, 0.0, 1.0));

			CVectorD<3> vOriginal(0.0, 0.0, 0.0);
			CVectorD<3> vTransformed;
			tpsTransform->Eval(vOriginal, vTransformed, 0.0);
			// Assert::IsTrue(vOriginal.IsApproxEqual(vTransformed));

			Logger::WriteMessage("Done TestAddLandmark");
		}

		TEST_METHOD(TestApplyWarpAtLandmark)
		{
		}

		TEST_METHOD(TestApplyInverseWarpAtLandmark)
		{
		}

		TEST_METHOD(TestResampleUniformImage)
		{
		}

		TEST_METHOD(TestResampleImageHistogram)
		{
		}

	};
}
<|endoftext|>


<commit_before>/*
 * File:   DebugCommandManager.cpp
 * Author: thomas
 *
 * Created on 12. November 2010, 09:50
 */

#include "DebugCommandManager.h"

DebugCommandManager::DebugCommandManager()
{
  registerCommand("help", "list available commands or get the description of a specific command", this);
}

DebugCommandManager::~DebugCommandManager()
{
}

void DebugCommandManager::handleCommand(
  const std::string& command,
  const std::map<std::string, std::string>& arguments,
  std::ostream& answer) const
{
  ExecutorMap::const_iterator iter = executorMap.find(command);
  if (iter != executorMap.end()) {
    iter->second.executor->executeDebugCommand(command, arguments, answer);
  } else {
    answer << "Unknown command \"" << command
      << "\", use \"help\" for a list of available commands" << std::endl;
  }
}//end handleCommand

bool DebugCommandManager::registerCommand(
  const std::string& command,
  const std::string& description,
  DebugCommandExecutor* executor)
{
  ExecutorMap::iterator iter = executorMap.lower_bound(command);
  if (iter == executorMap.end() || iter->first != command)
  {
    // new command
    executorMap.insert(iter,std::make_pair(command, DebugCommand(executor, description)));
    executor->registerDestructionListener(*this);
    return true;
  }
  return false;
}//end registerCommand

void DebugCommandManager::objectDestructed(DebugCommandExecutor* object)
{
  // search all registered keys of the object
  ExecutorMap::iterator iter = executorMap.begin();
  while(iter != executorMap.end())
  {
    if ((*iter).second.executor == object) {
      executorMap.erase(iter++);
    } else {
      ++iter;
    }
  }
}//end objectDestructed

void DebugCommandManager::executeDebugCommand(
  const std::string& command,
  const std::map<std::string, std::string>& arguments,
  std::ostream& out)
{
  if (command == "help")
  {
    if (arguments.empty())
    {
      // list all available commands
      out << "Available commands, use \"help <command>\" for a description:\n";
      ExecutorMap::const_iterator iter = executorMap.begin();
      while (iter != executorMap.end())
      {
        out << iter->first;
        ++iter;
        if (iter != executorMap.end()) {
          out << ": " << iter->second.desctiption << "\n";
        }
      }
    }
    else
    {
      std::string firstArg = arguments.begin()->first;
      ExecutorMap::iterator iter = executorMap.find(firstArg);

      if (iter != executorMap.end()) {
        out << firstArg << "\n";
        out << "------------------\n";
        out << iter->second.desctiption;
        out << "\n";
      } else {
        out << "Unknown command \"" << firstArg
          << "\", use \"help\" for a list of available commands";
      }
    }
    out << "\n";
  }
}//end executeDebugCommand
<commit_msg>fixed old bug<commit_after>/*
 * File:   DebugCommandManager.cpp
 * Author: thomas
 *
 * Created on 12. November 2010, 09:50
 */

#include "DebugCommandManager.h"

DebugCommandManager::DebugCommandManager()
{
  registerCommand("help", "list available commands or get the description of a specific command", this);
}

DebugCommandManager::~DebugCommandManager()
{
}

void DebugCommandManager::handleCommand(
  const std::string& command,
  const std::map<std::string, std::string>& arguments,
  std::ostream& answer) const
{
  ExecutorMap::const_iterator iter = executorMap.find(command);
  if (iter != executorMap.end()) {
    iter->second.executor->executeDebugCommand(command, arguments, answer);
  } else {
    answer << "Unknown command \"" << command
      << "\", use \"help\" for a list of available commands" << std::endl;
  }
}//end handleCommand

bool DebugCommandManager::registerCommand(
  const std::string& command,
  const std::string& description,
  DebugCommandExecutor* executor)
{
  ExecutorMap::iterator iter = executorMap.lower_bound(command);
  if (iter == executorMap.end() || iter->first != command)
  {
    // new command
    executorMap.insert(iter,std::make_pair(command, DebugCommand(executor, description)));
    executor->registerDestructionListener(*this);
    return true;
  }
  return false;
}//end registerCommand

void DebugCommandManager::objectDestructed(DebugCommandExecutor* object)
{
  // search all registered keys of the object
  ExecutorMap::iterator iter = executorMap.begin();
  while(iter != executorMap.end())
  {
    if ((*iter).second.executor == object) {
      executorMap.erase(iter++);
    } else {
      ++iter;
    }
  }
}//end objectDestructed

void DebugCommandManager::executeDebugCommand(
  const std::string& command,
  const std::map<std::string, std::string>& arguments,
  std::ostream& out)
{
  if (command == "help")
  {
    if (arguments.empty())
    {
      // list all available commands
      out << "Available commands, use \"help <command>\" for a description:\n";
      for(const auto& iter : executorMap) {
        out << iter.first << ": " << iter.second.desctiption << "\n";
      }
    }
    else
    {
      std::string firstArg = arguments.begin()->first;
      ExecutorMap::iterator iter = executorMap.find(firstArg);

      if (iter != executorMap.end()) {
        out << firstArg << "\n";
        out << "------------------\n";
        out << iter->second.desctiption;
        out << "\n";
      } else {
        out << "Unknown command \"" << firstArg
          << "\", use \"help\" for a list of available commands";
      }
    }
    out << "\n";
  }
}//end executeDebugCommand
<|endoftext|>