Program Listing for File range.hpp

Return to documentation for file (include/flom/range.hpp)

//
// Copyright 2018 coord.e
//
// This file is part of Flom.
//
// Flom is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Flom is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Flom.  If not, see <http://www.gnu.org/licenses/>.
//

#ifndef FLOM_RANGE_HPP
#define FLOM_RANGE_HPP

#include "flom/errors.hpp"
#include "flom/frame.hpp"
#include "flom/motion.hpp"

#include <iterator>
#include <map>
#include <memory>
#include <utility>

namespace flom {

// using snake_case, following customs of iterator naming
class frame_iterator {
  friend bool operator==(const frame_iterator &,
                         const frame_iterator &) noexcept;

public:
  using iterator_category = std::input_iterator_tag;
  using value_type = std::pair<double, Frame>;
  using difference_type = double;
  using pointer = Frame *;
  using reference = Frame &;

private:
  class Impl;
  std::unique_ptr<Impl> impl;

  bool is_end = false;

public:
  frame_iterator() noexcept;
  frame_iterator(Motion const &motion, double fps) noexcept;

  frame_iterator(const frame_iterator &);
  frame_iterator(frame_iterator &&);
  frame_iterator &operator=(const frame_iterator &);
  frame_iterator &operator=(frame_iterator &&);

  ~frame_iterator();

  // This is InputIterator because operator* doesn't return reference
  value_type operator*() const;

  frame_iterator &operator++() noexcept;
  frame_iterator operator++(int) noexcept;

  frame_iterator &operator--() noexcept;
  frame_iterator operator--(int) noexcept;

  double current_time() const noexcept;
};

frame_iterator::difference_type operator-(const frame_iterator &,
                                          const frame_iterator &) noexcept;
bool operator==(const frame_iterator &, const frame_iterator &) noexcept;
bool operator!=(const frame_iterator &, const frame_iterator &) noexcept;

class FrameRange {
public:
  using value_type = Frame;
  using iterator = frame_iterator;

private:
  Motion const &motion;
  double fps;

public:
  FrameRange() = delete;
  FrameRange(Motion const &motion_, double fps_) : motion(motion_), fps(fps_) {}
  FrameRange(const FrameRange &) = default;
  FrameRange(FrameRange &&) = default;
  FrameRange &operator=(const FrameRange &) = default;
  FrameRange &operator=(FrameRange &&) = default;

  iterator begin() const noexcept { return {this->motion, this->fps}; }
  iterator end() const noexcept { return {}; }

  iterator cbegin() const noexcept { return this->begin(); }
  iterator cend() const noexcept { return this->end(); }
};

class CheckedFrameRef {
public:
  using reference_type = Frame &;

  CheckedFrameRef(reference_type value_, const Motion *motion_)
      : value(value_), motion(motion_) {}

  CheckedFrameRef &operator=(const Frame &frame) & {
    if (!this->motion->is_valid_frame(frame)) {
      throw errors::InvalidFrameError{"in CheckedFrameWrapper"};
    }
    this->value = frame;
    return *this;
  }

  operator reference_type() const noexcept { return this->value; }

private:
  reference_type value;
  const Motion *motion;
};

class keyframe_iterator {
public:
  using base_iterator = std::map<double, Frame>::iterator;

  using iterator_category = std::bidirectional_iterator_tag;
  using value_type = std::iterator_traits<base_iterator>::value_type;
  using difference_type = std::iterator_traits<base_iterator>::difference_type;
  using pointer = std::iterator_traits<base_iterator>::pointer;
  using reference = std::iterator_traits<base_iterator>::reference;

  using checked_value_type = std::pair<const double, CheckedFrameRef>;

private:
  friend bool operator==(const keyframe_iterator &,
                         const keyframe_iterator &) noexcept;

  base_iterator it;
  const Motion *motion;

public:
  keyframe_iterator() noexcept : it(), motion() {}
  explicit keyframe_iterator(base_iterator it_, const Motion &motion_) noexcept
      : it(it_), motion(&motion_) {}

  keyframe_iterator(const keyframe_iterator &) = default;
  keyframe_iterator(keyframe_iterator &&) = default;
  keyframe_iterator &operator=(const keyframe_iterator &) = default;
  keyframe_iterator &operator=(keyframe_iterator &&) = default;

  const value_type &operator*() const;
  checked_value_type operator*();

  const value_type &operator->() const;
  checked_value_type operator->();

  keyframe_iterator &operator++() noexcept;
  keyframe_iterator operator++(int) noexcept;

  keyframe_iterator &operator--() noexcept;
  keyframe_iterator operator--(int) noexcept;
};

bool operator==(const keyframe_iterator &, const keyframe_iterator &) noexcept;
bool operator!=(const keyframe_iterator &, const keyframe_iterator &) noexcept;

class KeyframeRange {
public:
  using value_type = Frame;
  using iterator = keyframe_iterator;
  using base_iterator = typename std::map<double, Frame>::iterator;

private:
  base_iterator begin_it;
  base_iterator end_it;
  const Motion &motion;

public:
  KeyframeRange() = delete;
  KeyframeRange(base_iterator begin_, base_iterator end_, const Motion &motion_)
      : begin_it(begin_), end_it(end_), motion(motion_) {}
  KeyframeRange(const KeyframeRange &) = default;
  KeyframeRange(KeyframeRange &&) = default;
  KeyframeRange &operator=(const KeyframeRange &) = default;
  KeyframeRange &operator=(KeyframeRange &&) = default;

  iterator begin() noexcept { return iterator{this->begin_it, this->motion}; }
  iterator end() noexcept { return iterator{this->end_it, this->motion}; }
};

class ConstKeyframeRange {
public:
  using value_type = Frame;
  using const_iterator = typename std::map<double, Frame>::const_iterator;

private:
  const_iterator begin_;
  const_iterator end_;

public:
  ConstKeyframeRange() = delete;
  ConstKeyframeRange(const_iterator begin, const_iterator end)
      : begin_(begin), end_(end) {}
  ConstKeyframeRange(const ConstKeyframeRange &) = default;
  ConstKeyframeRange(ConstKeyframeRange &&) = default;
  ConstKeyframeRange &operator=(const ConstKeyframeRange &) = default;
  ConstKeyframeRange &operator=(ConstKeyframeRange &&) = default;

  const_iterator begin() const noexcept { return this->begin_; }
  const_iterator end() const noexcept { return this->end_; }

  const_iterator cbegin() const noexcept { return this->begin_; }
  const_iterator cend() const noexcept { return this->end_; }
};
} // namespace flom

#endif