Skip to content
This repository has been archived by the owner on Feb 22, 2024. It is now read-only.

Add Point2D library #6

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions robocin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
add_subdirectory(utility)
add_subdirectory(geometry)
5 changes: 5 additions & 0 deletions robocin/geometry/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
robocin_cpp_library(
NAME point2d
HDRS point2d.h
SRCS point2d.cpp
)
185 changes: 185 additions & 0 deletions robocin/geometry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# geometry

A collection of geometric classes.

## Table of Contents

- [Point2D](#point2d)

<a name="point2d"></a>

## [`Point2D`](point2d.h)

The `Point2D` templated struct represents a 2-dimensional point (vector) in a Cartesian coordinate system. It provides
various methods and operators for manipulating and performing calculations with 2d-points.

### Member Types

- `value_type`: The type of the point's coordinates.
- `reference`: A reference to `value_type`.
- `size_type`: An unsigned integer type used for size and indexing.
- `iterator`: An iterator type for iterating over mutable `Point2D` objects.
- `const_iterator`: An iterator type for iterating over const `Point2D` objects.
- `reverse_iterator`: A reverse iterator type for reverse iteration over mutable `Point2D` objects.
- `const_reverse_iterator`: A reverse iterator type for reverse iteration over const `Point2D` objects.

### Constructors

- `Point2D()`: Default constructor. Initializes the point with coordinates (0, 0).
- `Point2D(const Point2D& point)`: Copy constructor. Creates a new point by copying the coordinates of another point.
- `Point2D(Point2D&& point)`: Move constructor. Creates a new point by moving the coordinates of another point.
- `Point2D(value_type x, value_type y)`: Constructor. Initializes the point with the specified coordinates.
- `Point2D(const OtherStructPoint2D auto& point)`: Initializes the point with the coordinates (`point.x`, `point.y`).
- `Point2D(const ClassPoint2D auto& point)`: Initializes the point with the coordinates (`point.x()`, `point.y()`).
- `template <class U, class V> Point2D(const std::pair<U, V>& pair)`: Constructor. Initializes the point with the
coordinates from a `std::pair`.

### Static Constructors

- `static Point2D origin()`: Returns a point representing the origin (0, 0).
- `static Point2D fromPolar(value_type angle) requires(std::floating_point<value_type>)`: Creates a point from polar
coordinates with the given angle.
- `static Point2D fromPolar(value_type angle, value_type length) requires(std::floating_point<value_type>)`: Creates a
point from polar coordinates with the given angle and length.

### Validators

- `bool isNull() const`: Checks if the point is null (coordinates are both zero). Returns `true`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- `bool isNull() const`: Checks if the point is null (coordinates are both zero). Returns `true`
- `bool isOrigin() const`: Checks if the point is null (coordinates are both zero). Returns `true`

discard null word for this case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was originally inspired on QPointF::isNull, but I agree that isOrigin looks better for this purpose...

However, perhaps isNull is a common interface for other geometric representations such as QRectF, QVector3D...

wdyt?

if the point is null, `false` otherwise.

### Arithmetic-Assignment Operators

- `Point2D& operator+=(const Point2D& other)`: Adds the coordinates of another point to this point and updates its
value. Returns a reference to the modified point.
- `Point2D& operator-=(const Point2D& other)`: Subtracts the coordinates of another point from this point and updates
its value. Returns a reference to the modified point.
- `Point2D& operator*=(value_type factor)`: Multiplies the coordinates of this point by a scalar factor and updates its
value. Returns a reference to the modified point.
- `Point2D& operator/=(value_type factor)`: Divides the coordinates of this point by a scalar factor and updates its
value. Returns a reference to the modified point.

### Arithmetic Operators

- `Point2D operator+(const Point2D& other) const`: Addition operator that returns the sum of two points.
- `Point2D operator-(const Point2D& other) const`: Subtraction operator that returns the difference between two points.
- `Point2D operator*(value_type factor) const`: Multiplication operator that scales the point by a factor.
- `Point2D operator/(value_type factor) const`: Division operator that scales the point by the inverse of a factor.

### Arithmetic Friend Operators

- `friend Point2D operator*(value_type factor, const Point2D& point)`: Friend operator that allows multiplication of a
point by a factor in the reverse order.

### Sign Operators

- `Point2D operator+() const`: Unary plus operator that returns the point itself.
- `Point2D operator-() const`: Unary minus operator that returns the negation of the point.

### Comparison Operators

- `bool operator==(const Point2D& other) const`: Equality operator that checks if two points are equal.
- `auto operator<=>(const Point2D& other) const`: Three-way comparison operator that compares two points and returns
their relative ordering.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these operators sufficient to do sorting?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sure! Three way comparisons are able to replace all relational comparators.


### Swap

- `void swap(Point2D& other) noexcept`: Swaps the contents of the current point with another point.

### Geometry

- `value_type dot(const Point2D& other) const`: Computes the dot product of the current point and another point.
- `value_type cross(const Point2D& other) const`: Computes the cross product of the current point and another point.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dot and cross are common vectors operations. Some operations are specific for vector or point interpretation, hardly for both.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I change here in the description only? Or implement Point / Vector separately?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just description I think.

- `value_type manhattanLength() const`: Computes the Manhattan distance between the origin and the current point.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This a example of point only operation.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont like that. So for manhattan that commonly a binary function, I have to do:
(b-a).manhattanLength()
Instead of:
a.manhattanDistance(b)?
I think that manhattan is more related to distance than length.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very well observed! Again, this was partly inspired by QPointF::manhattanLength.

Perhaps keeping both is reasonable?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

- `value_type lengthSquared() const`: Computes the square of the Euclidean length of the current point.
- `auto length() const`: Computes the Euclidean length of the current point.
- `auto norm() const`: Computes the Euclidean length of the current point.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A point doesn't have length.

- `value_type distSquaredTo(const Point2D& other) const`: Computes the square of the Euclidean distance between the
current point and another point.
- `auto distTo(const Point2D& other) const`: Computes the Euclidean distance between the current point and another
point.
- `auto angle() const`: Computes the angle (in radians) between the positive x-axis and the vector from the origin to
the current point.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[-Pi , +PI]?

- `auto angleTo(const Point2D& other) const`: Computes the angle (in radians) between the vector from the origin to the
current point and the vector from the origin to another point.
joseviccruz marked this conversation as resolved.
Show resolved Hide resolved

### Rotations

- `void rotateCW90() &`: Rotates the current point 90 degrees clockwise.
- `Point2D rotatedCW90() &&`: Returns a new point obtained by rotating the current point 90 degrees clockwise.
- `Point2D rotatedCW90() const&`: Returns a new point obtained by rotating a copy of the current point 90 degrees
clockwise.


- `void rotateCCW90() &`: Rotates the current point 90 degrees counterclockwise.
- `Point2D rotatedCCW90() &&`: Returns a new point obtained by rotating the current point 90 degrees counterclockwise.
- `Point2D rotatedCCW90() const&`: Returns a new point obtained by rotating a copy of the current point 90 degrees
counterclockwise.


- `void rotateCW(value_type t) &`: Rotates the current point clockwise by a specified angle `t` (in radians).
- `Point2D rotatedCW(value_type t) &&`: Returns a new point obtained by rotating the current point clockwise by a
specified angle `t` (in radians).
- `Point2D rotatedCW(value_type t) const&`: Returns a new point obtained by rotating a copy of the current point
clockwise by a specified angle `t` (in radians).


- `void rotateCCW(value_type t) &`: Rotates the current point counterclockwise by a specified angle `t` (in radians).
- `Point2D rotatedCCW(value_type t) &&`: Returns a new point obtained by rotating the current point counterclockwise by
a specified angle `t` (in radians).
- `Point2D rotatedCCW(value_type t) const&`: Returns a new point obtained by rotating a copy of the current point
counterclockwise by a specified angle `t` (in radians).

### Resizing and Normalization

- `void resize(value_type t) &`: Resizes the current point by a factor `t`.
- `Point2D resized(value_type t) &&`: Returns a new point obtained by resizing the current point by a factor `t`.
- `Point2D resized(value_type t) const&`: Returns a new point obtained by resizing a copy of the current point by a
factor `t`.


- `void normalize() &`: Normalizes the current point to have unit length.

> **Note:** The normalization differs for real and integer numbers, being equivalent to `resize(1)` for real numbers,
> and the division of coordinates by their gcd for integer numbers.

- `Point2D normalized() &&`: Returns a new point obtained by normalizing the current point to have unit length.
- `Point2D normalized() const&`: Returns a new point obtained by normalizing a copy of the current point to have unit
length.


- `void axesNormalize() &`: Normalizes the current point to have coordinates of -1, 0, or 1.
- `Point2D axesNormalized() &&`: Returns a new point obtained by normalizing the current point to have coordinates of
-1, 0, or 1.
- `Point2D axesNormalized() const&`: Returns a new point obtained by normalizing a copy of the current point to have
coordinates of -1, 0, or 1.

### Array-like

- `static size_type size()`: Returns the size of the point (always 2).
- `reference operator[](size_type pos)`: Provides access to the elements of the point using the subscript operator.
- `value_type operator[](size_type pos) const`: Provides access to the elements of the point using the subscript
operator.

### Iterators

The following iterator methods provide support for iterating over the elements of the point:

- `iterator begin() noexcept`.
- `const_iterator begin() const noexcept`.
- `iterator end() noexcept`.
- `const_iterator end() const noexcept`.
- `reverse_iterator rbegin() noexcept`.
- `const_reverse_iterator rbegin() const noexcept`.
- `reverse_iterator rend() noexcept`.
- `const_reverse_iterator rend() const noexcept`.
- `const_iterator cbegin() const noexcept`.
- `const_iterator cend() const noexcept`.
- `const_reverse_iterator crbegin() const noexcept`.
- `const_reverse_iterator crend() const noexcept`.

### Input/Output Operators

The following operators allow input and output of `Point2D` objects:

- `std::istream& operator>>(std::istream& is, Point2D& point)`: Reads a `Point2D` object from an input stream.
- `std::ostream& operator<<(std::ostream& os, const Point2D& point)`: Writes a `Point2D` object to an output stream.
108 changes: 108 additions & 0 deletions robocin/geometry/internal/point2d_internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//
// Created by José Cruz <joseviccruz> on 24/02/23.
// Copyright (c) 2023 RobôCIn.
//

#ifndef ROBOCIN_GEOMETRY_POINT2D_INTERNAL_H
#define ROBOCIN_GEOMETRY_POINT2D_INTERNAL_H

#include <type_traits>

namespace robocin::point2d_internal {

template <class PT>
concept StructPoint = requires(PT point) {
point.x;
point.y;
};

template <class PT>
concept ClassPoint = requires(PT point) {
point.x();
point.y();
};

template <class PT>
class iterator {
using point_type = PT;
using point_pointer = point_type*;

template <class T>
using dependent_const_t = std::conditional_t<std::is_const_v<point_type>, const T, T>;

public:
// NOLINTNEXTLINE(readability-identifier-naming)
using iterator_category = std::random_access_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = dependent_const_t<typename point_type::value_type>;
using pointer = value_type*;
using reference = value_type&;

// Constructors ----------------------------------------------------------------------------------
constexpr iterator() = default;
constexpr iterator(const iterator&) = default;
constexpr iterator(iterator&&) noexcept = default;
constexpr iterator(point_pointer ptr, difference_type index) : ptr_(ptr), index_(index) {}

// Assignment operators --------------------------------------------------------------------------
constexpr iterator& operator=(const iterator&) = default;
constexpr iterator& operator=(iterator&&) noexcept = default;

// Destructor ------------------------------------------------------------------------------------
constexpr ~iterator() = default;

// Dereference operators -------------------------------------------------------------------------
constexpr reference operator*() const {
switch (index_) {
case 0: return ptr_->x;
case 1: return ptr_->y;
default: throw std::out_of_range("Point2D::iterator operator*: index out of range.");
}
}

constexpr reference operator[](difference_type n) const {
switch (index_ + n) {
case 0: return ptr_->x;
case 1: return ptr_->y;
default: throw std::out_of_range("Point2D::iterator operator[]: index out of range.");
}
}

// Arithmetic-assignment operators ---------------------------------------------------------------
constexpr iterator& operator+=(difference_type n) { return index_ += n, *this; }
constexpr iterator& operator-=(difference_type n) { return index_ -= n, *this; }

// Arithmetic operators --------------------------------------------------------------------------
constexpr iterator operator+(difference_type n) const { return iterator(*this) += n; }
constexpr iterator operator-(difference_type n) const { return iterator(*this) -= n; }

constexpr difference_type operator-(const iterator& other) const { return index_ - other.index_; }

// Arithmetic friend operator --------------------------------------------------------------------
friend constexpr iterator operator+(difference_type n, const iterator& it) { return it + n; }

// Increment and decrement operators -------------------------------------------------------------
constexpr iterator& operator++() { return ++index_, *this; }
constexpr iterator operator++(int) { // NOLINT(cert-dcl21-cpp)
iterator result = *this;
return ++index_, result;
}

constexpr iterator& operator--() { return --index_, *this; }
constexpr iterator operator--(int) { // NOLINT(cert-dcl21-cpp)
iterator result = *this;
return --index_, result;
}

// Comparison operators --------------------------------------------------------------------------
inline constexpr bool operator==(const iterator& other) const = default;
inline constexpr auto operator<=>(const iterator& other) const = default;

private:
point_pointer ptr_{nullptr};
difference_type index_{0};
};

} // namespace robocin::point2d_internal

#endif // ROBOCIN_GEOMETRY_POINT2D_INTERNAL_H
17 changes: 17 additions & 0 deletions robocin/geometry/point2d.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// Created by José Cruz <joseviccruz> on 24/02/23.
// Copyright (c) 2023 RobôCIn.
//

#include "robocin/geometry/point2d.h"

namespace robocin {

template struct Point2D<std::int16_t>;
template struct Point2D<std::int32_t>;
template struct Point2D<std::int64_t>;
template struct Point2D<float>;
template struct Point2D<double>;
template struct Point2D<long double>;

} // namespace robocin
Loading