Program Listing for File segment2D.hpp

Return to documentation for file (include/slg_msgs/segment2D.hpp)

// Copyright (c) 2017 Alberto J. Tudela Roldán
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     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.

#ifndef SLG_MSGS__SEGMENT2D_HPP_
#define SLG_MSGS__SEGMENT2D_HPP_

#include <vector>
#include <algorithm>
#include <numeric>

#include "slg_msgs/msg/segment.hpp"

#include "point2D.hpp"

namespace slg
{
class Segment2D
{
public:
  Segment2D()
  : id(0),
    label(BACKGROUND),
    angular_distance_to_closest_boundary(0.0),
    last_point_prior_seg(Point2D(0, 0)),
    first_point_next_seg(Point2D(0, 0)),
    last_centroid(Point2D(0, 0)) {}

  Segment2D(int new_id, Point2D prev_point, Point2D curr_point, Point2D next_point)
  : id(new_id),
    label(BACKGROUND),
    angular_distance_to_closest_boundary(0.0),
    points({curr_point}),
    last_point_prior_seg(prev_point),
    first_point_next_seg(next_point),
    last_centroid(curr_point) {}

  Segment2D(const Segment2D & seg)
  : id(seg.get_id()),
    label(seg.get_label()),
    angular_distance_to_closest_boundary(0.0),
    points(seg.get_points()),
    last_point_prior_seg(seg.get_prior_segment()),
    first_point_next_seg(seg.get_next_segment()),
    last_centroid(seg.get_last_centroid()) {}

  explicit Segment2D(const slg_msgs::msg::Segment & segment_msg)
  : id(segment_msg.id),
    label(slg::Label(segment_msg.label)),
    angular_distance_to_closest_boundary(segment_msg.angular_distance),
    last_point_prior_seg(segment_msg.last_point_prior_segment),
    first_point_next_seg(segment_msg.first_point_next_segment)
  {
    points.insert(points.begin(), std::begin(segment_msg.points), std::end(segment_msg.points));
    last_centroid = centroid();
  }

  ~Segment2D() {}

  int size()                              const {return points.size();}
  bool empty()                    const {return points.empty();}
  void clear() {points.clear(); id = 0; label = BACKGROUND;}
  double width()                  const {return (points.back() - points.front()).length();}
  double width_squared()  const {return (points.back() - points.front()).length_squared();}
  Point2D first_point()   const {return points.front();}
  Point2D last_point()    const {return points.back();}
  Point2D vector()                const {return points.back() - points.front();}
  double min_angle()              const {return points.front().angle();}
  double max_angle()              const {return points.back().angle();}
  double mean_angle()             const
  {
    return (points.front().angle() + points.back().angle()) / 2.0;
  }
  int get_id()                    const {return id;}
  Label get_label()               const {return label;}
  std::vector<Point2D> get_points() const {return points;}
  Point2D get_prior_segment()     const {return last_point_prior_seg;}
  Point2D get_next_segment()      const {return first_point_next_seg;}
  Point2D get_last_centroid()     const {return last_centroid;}
  double get_angular_distance_to_closest_boundary() const
  {
    return angular_distance_to_closest_boundary;
  }
  void set_id(int new_id) {this->id = new_id;}
  void set_label(Label new_label) {this->label = new_label;}
  void set_prior_segment(Point2D point) {last_point_prior_seg = point;}
  void set_next_segment(Point2D point) {first_point_next_seg = point;}
  void set_last_centroid(Point2D point) {last_centroid = point;}
  void set_angular_distance_to_closest_boundary(double angle)
  {
    angular_distance_to_closest_boundary = angle;
  }

  double orientation()
  {
    return (vector().y != 0.0) ? Point2D(-1, -vector().x / vector().y).angle() : 0.0;
  }

  Point2D projection(const Point2D & p) const
  {
    Point2D a = points.back() - points.front();
    Point2D b = p - points.front();
    return points.front() + a.dot(b) * a / a.length_squared();
  }

  double distance_to(const Point2D & p) const
  {
    return (p - projection(p)).length();
  }

  Point2D centroid() const
  {
    Point2D sum = std::accumulate(points.begin(), points.end(), Point2D(0.0, 0.0));
    return sum / points.size();
  }

  double height() const
  {
    auto maxPoint = std::max_element(
      points.begin(), points.end(), [](Point2D p1, Point2D p2) {
        return p1.y < p2.y;
      });
    return distance_to(*maxPoint);
  }

  void add_point(Point2D point)
  {
    points.push_back(point);
    last_centroid = centroid();
  }

  void add_points(std::vector<Point2D> newPoints)
  {
    points.insert(points.end(), newPoints.begin(), newPoints.end());
  }

  void merge(Segment2D seg)
  {
    if (label != seg.get_label()) {label = BACKGROUND;}

    std::vector<Point2D> newPoints = seg.get_points();
    points.insert(points.end(), newPoints.begin(), newPoints.end());
    first_point_next_seg = seg.get_next_segment();
    last_centroid = centroid();
  }

  Segment2D left_split(int index)
  {
    std::vector<Point2D> left(points.begin(), points.begin() + index);

    Segment2D splited_segment;
    splited_segment.add_points(left);
    splited_segment.set_id(this->id);
    splited_segment.set_label(this->label);
    splited_segment.set_prior_segment(this->last_point_prior_seg);
    splited_segment.set_next_segment(points[index + 1]);

    return splited_segment;
  }

  Segment2D right_split(int index)
  {
    std::vector<Point2D> right(points.begin() + index, points.end());

    Segment2D splited_segment;
    splited_segment.add_points(right);
    splited_segment.set_id(this->id);
    splited_segment.set_label(this->label);
    splited_segment.set_prior_segment(points[index - 1]);
    splited_segment.set_next_segment(this->first_point_next_seg);

    return splited_segment;
  }

  operator slg_msgs::msg::Segment() const {
    slg_msgs::msg::Segment segment_msg;

    // Transform the segment in message
    segment_msg.id = id;
    segment_msg.label = label;
    segment_msg.angular_distance = angular_distance_to_closest_boundary;
    segment_msg.last_point_prior_segment.x = last_point_prior_seg.x;
    segment_msg.last_point_prior_segment.y = last_point_prior_seg.y;
    segment_msg.first_point_next_segment.x = first_point_next_seg.x;
    segment_msg.first_point_next_segment.y = first_point_next_seg.y;

    for (const auto & point : points) {
      segment_msg.points.push_back(point);
    }

    return segment_msg;
  }

  Segment2D & operator=(const Segment2D & seg)
  {
    if (this != &seg) {
      this->id = seg.get_id();
      this->label = seg.get_label();
      this->points = seg.get_points();
      this->last_point_prior_seg = seg.get_prior_segment();
      this->first_point_next_seg = seg.get_next_segment();
      this->last_centroid = seg.get_last_centroid();
    }
    return *this;
  }

  Segment2D & operator=(const slg_msgs::msg::Segment & segment_msg)
  {
    *this = Segment2D(segment_msg);
    return *this;
  }

  friend bool operator==(const Segment2D & s1, const Segment2D & s2)
  {
    return s1.id == s2.id && s1.label == s2.label && s1.points == s2.points &&
           s1.last_point_prior_seg == s2.last_point_prior_seg &&
           s1.first_point_next_seg == s2.first_point_next_seg &&
           s1.last_centroid == s2.last_centroid;
  }

  friend bool operator!=(const Segment2D & s1, const Segment2D & s2) {return !(s1 == s2);}

private:
  int id;
  Label label;
  double angular_distance_to_closest_boundary;
  std::vector<Point2D> points;
  Point2D last_point_prior_seg, first_point_next_seg, last_centroid;
};
}  // namespace slg

#endif  // SLG_MSGS__SEGMENT2D_HPP_